snapshot: modularize workflow views by domain

This commit is contained in:
Md Bayazid Bostame
2026-03-28 08:56:43 +01:00
parent b2686522c7
commit ee323106e9
9 changed files with 3385 additions and 2940 deletions

View File

@@ -0,0 +1,410 @@
from datetime import timedelta
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.utils.translation import gettext as _
from .app_registry import get_portal_app_registry_rows, normalize_portal_app_sort_orders
from .branding import get_portal_trial_config, is_trial_expired
from .forms import PortalBrandingForm, PortalCompanyConfigForm, PortalTrialConfigForm, UserManagementCreateForm
from .models import PortalAppConfig, PortalBranding, PortalCompanyConfig, UserNotification, UserProfile
from .notifications import notify_user
from .roles import ROLE_GROUP_NAMES, ROLE_PLATFORM_OWNER, get_user_role_key
def portal_app_registry_page_impl(request, *, translate_choice_list):
return render(
request,
'workflows/app_registry.html',
{
'rows': get_portal_app_registry_rows(),
'section_choices': translate_choice_list(PortalAppConfig.SECTION_CHOICES),
},
)
def save_portal_app_registry_impl(request, *, audit_fn):
rows = get_portal_app_registry_rows()
updated_configs = []
for row in rows:
config = row['config']
key = config.key
config.section = (request.POST.get(f'section__{key}') or config.section).strip()
if config.section not in dict(PortalAppConfig.SECTION_CHOICES):
config.section = row['default_section']
config.is_enabled = request.POST.get(f'is_enabled__{key}') == 'on'
config.visible_to_super_admin = request.POST.get(f'visible_to_super_admin__{key}') == 'on'
config.visible_to_admin = request.POST.get(f'visible_to_admin__{key}') == 'on'
config.visible_to_it_staff = request.POST.get(f'visible_to_it_staff__{key}') == 'on'
config.visible_to_staff = request.POST.get(f'visible_to_staff__{key}') == 'on'
try:
config.sort_order = int((request.POST.get(f'sort_order__{key}') or '').strip() or row['default_sort_order'])
except ValueError:
config.sort_order = row['default_sort_order']
config.title_override = (request.POST.get(f'title_override__{key}') or '').strip()
config.title_override_en = (request.POST.get(f'title_override_en__{key}') or '').strip()
config.description_override = (request.POST.get(f'description_override__{key}') or '').strip()
config.description_override_en = (request.POST.get(f'description_override_en__{key}') or '').strip()
config.action_label_override = (request.POST.get(f'action_label_override__{key}') or '').strip()
config.action_label_override_en = (request.POST.get(f'action_label_override_en__{key}') or '').strip()
config.save()
updated_configs.append(config)
normalize_portal_app_sort_orders()
audit_fn(
request,
'portal_app_registry_saved',
target_type='portal_app_registry',
target_label='Portal App Registry',
details={'updated_apps': len(rows)},
)
messages.success(request, _('App-Registry gespeichert.'))
return redirect('portal_app_registry_page')
def user_management_page_impl(request, *, render_user_management_fn):
return render_user_management_fn(request)
def portal_branding_page_impl(request, *, build_branding_sections_fn):
branding, created = PortalBranding.objects.get_or_create(name='Default')
form = PortalBrandingForm(instance=branding)
return render(
request,
'workflows/branding_settings.html',
{
'form': form,
'branding': branding,
'branding_sections': build_branding_sections_fn(form, branding),
'editing_branding_section': '',
},
)
def save_portal_branding_impl(request, *, audit_fn, build_branding_sections_fn):
branding, created = PortalBranding.objects.get_or_create(name='Default')
section_key = (request.POST.get('section_key') or '').strip()
data = request.POST.copy()
for field_name in PortalBrandingForm.Meta.fields:
if field_name not in data:
field = PortalBranding._meta.get_field(field_name)
if getattr(field, 'many_to_many', False):
continue
if getattr(field, 'null', False) and getattr(branding, field_name, None) is None:
data[field_name] = ''
else:
data[field_name] = getattr(branding, field_name, '') or ''
form = PortalBrandingForm(data, request.FILES, instance=branding)
if not form.is_valid():
messages.error(request, _('Branding konnte nicht gespeichert werden. Bitte prüfen Sie die Eingaben.'))
return render(
request,
'workflows/branding_settings.html',
{
'form': form,
'branding': branding,
'branding_sections': build_branding_sections_fn(form, branding),
'editing_branding_section': section_key,
},
status=400,
)
branding = form.save()
audit_fn(
request,
'portal_branding_saved',
target_type='portal_branding',
target_id=branding.id,
target_label=branding.portal_title,
details={
'company_name': branding.company_name,
'support_email': branding.support_email,
'default_language': branding.default_language,
'has_custom_logo': bool(branding.logo_image),
'has_custom_letterhead': bool(branding.pdf_letterhead),
},
)
messages.success(request, _('Portal-Branding wurde gespeichert.'))
return render(
request,
'workflows/branding_settings.html',
{
'form': PortalBrandingForm(instance=branding),
'branding': branding,
'branding_sections': build_branding_sections_fn(PortalBrandingForm(instance=branding), branding),
'editing_branding_section': '',
},
)
def portal_company_config_page_impl(request, *, build_company_config_sections_fn):
company_config, created = PortalCompanyConfig.objects.get_or_create(name='Default')
form = PortalCompanyConfigForm(instance=company_config)
return render(
request,
'workflows/company_config.html',
{
'form': form,
'company_config': company_config,
'company_config_sections': build_company_config_sections_fn(form, company_config),
'editing_company_section': '',
},
)
def save_portal_company_config_impl(request, *, audit_fn, build_company_config_sections_fn):
company_config, created = PortalCompanyConfig.objects.get_or_create(name='Default')
section_key = (request.POST.get('section_key') or '').strip()
data = request.POST.copy()
for field_name in PortalCompanyConfigForm.Meta.fields:
if field_name not in data:
data[field_name] = getattr(company_config, field_name, '') or ''
form = PortalCompanyConfigForm(data, instance=company_config)
if not form.is_valid():
messages.error(request, _('Firmenkonfiguration konnte nicht gespeichert werden. Bitte prüfen Sie die Eingaben.'))
return render(
request,
'workflows/company_config.html',
{
'form': form,
'company_config': company_config,
'company_config_sections': build_company_config_sections_fn(form, company_config),
'editing_company_section': section_key,
},
status=400,
)
company_config = form.save()
audit_fn(
request,
'portal_company_config_saved',
target_type='portal_company_config',
target_id=company_config.id,
target_label=company_config.legal_company_name or 'Default',
details={
'website_url': company_config.website_url,
'imprint_url': company_config.imprint_url,
'privacy_url': company_config.privacy_url,
'hr_contact_email': company_config.hr_contact_email,
'it_contact_email': company_config.it_contact_email,
'operations_contact_email': company_config.operations_contact_email,
},
)
messages.success(request, _('Firmenkonfiguration wurde gespeichert.'))
return render(
request,
'workflows/company_config.html',
{
'form': PortalCompanyConfigForm(instance=company_config),
'company_config': company_config,
'company_config_sections': build_company_config_sections_fn(PortalCompanyConfigForm(instance=company_config), company_config),
'editing_company_section': '',
},
)
def portal_trial_config_page_impl(request):
trial_config = get_portal_trial_config()
form = PortalTrialConfigForm(instance=trial_config)
return render(
request,
'workflows/trial_management.html',
{
'form': form,
'trial_config': trial_config,
'trial_is_expired': is_trial_expired(),
},
)
def save_portal_trial_config_impl(request, *, audit_fn):
trial_config = get_portal_trial_config()
form = PortalTrialConfigForm(request.POST, instance=trial_config)
if not form.is_valid():
messages.error(request, _('Trial-Konfiguration konnte nicht gespeichert werden. Bitte prüfen Sie die Eingaben.'))
return render(
request,
'workflows/trial_management.html',
{
'form': form,
'trial_config': trial_config,
'trial_is_expired': is_trial_expired(),
},
status=400,
)
trial_config = form.save()
audit_fn(
request,
'portal_trial_config_saved',
target_type='portal_trial_config',
target_id=trial_config.id,
target_label='Default',
details={
'is_trial_mode': trial_config.is_trial_mode,
'trial_started_at': trial_config.trial_started_at.isoformat() if trial_config.trial_started_at else '',
'trial_expires_at': trial_config.trial_expires_at.isoformat() if trial_config.trial_expires_at else '',
'restrict_production_integrations': trial_config.restrict_production_integrations,
'auto_cleanup_enabled': trial_config.auto_cleanup_enabled,
},
)
if trial_config.is_trial_mode and trial_config.trial_expires_at:
remaining = trial_config.trial_expires_at - timezone.now()
if remaining.total_seconds() <= 0:
notify_user(
user=request.user,
title=_('Trial ist abgelaufen'),
body=_('Der Trial-Zeitraum ist überschritten. Nicht-Platform-Owner werden jetzt blockiert.'),
level=UserNotification.LEVEL_WARNING,
link_url='/admin-tools/trial/',
event_key=UserProfile.NOTIFICATION_TRIAL_ALERTS,
)
elif remaining <= timedelta(days=7):
notify_user(
user=request.user,
title=_('Trial läuft bald ab'),
body=_('Der Trial endet am %(date)s.') % {'date': timezone.localtime(trial_config.trial_expires_at).strftime('%d.%m.%Y %H:%M')},
level=UserNotification.LEVEL_WARNING,
link_url='/admin-tools/trial/',
event_key=UserProfile.NOTIFICATION_TRIAL_ALERTS,
)
elif not trial_config.is_trial_mode:
notify_user(
user=request.user,
title=_('Trial-Modus deaktiviert'),
body=_('Der Trial-Modus wurde ausgeschaltet.'),
level=UserNotification.LEVEL_INFO,
link_url='/admin-tools/trial/',
event_key=UserProfile.NOTIFICATION_TRIAL_ALERTS,
)
messages.success(request, _('Trial-Konfiguration wurde gespeichert.'))
return render(
request,
'workflows/trial_management.html',
{
'form': PortalTrialConfigForm(instance=trial_config),
'trial_config': trial_config,
'trial_is_expired': is_trial_expired(),
},
)
def create_user_from_admin_impl(request, *, render_user_management_fn, send_user_access_email_fn, audit_fn, display_user_name_fn):
form = UserManagementCreateForm(request.POST, include_product_owner=(get_user_role_key(request.user) == ROLE_PLATFORM_OWNER))
if not form.is_valid():
messages.error(request, _('Benutzer konnte nicht erstellt werden. Bitte prüfen Sie die Eingaben.'))
return render_user_management_fn(request, create_form=form, status_code=400)
user = form.save()
send_user_access_email_fn(request, user, invitation=True)
audit_fn(
request,
'user_created',
target_type='user',
target_id=user.id,
target_label=display_user_name_fn(user),
details={'username': user.username, 'role': get_user_role_key(user), 'invitation_sent': True},
)
messages.success(request, _('Benutzer wurde erstellt und eingeladen: %(username)s') % {'username': user.username})
return redirect('user_management_page')
def update_user_from_admin_impl(request, user_id: int, *, would_remove_last_platform_owner_fn, would_remove_last_super_admin_fn, audit_fn, display_user_name_fn):
user_model = get_user_model()
target_user = get_object_or_404(user_model, id=user_id)
role_key = (request.POST.get('role_key') or '').strip()
is_active = request.POST.get('is_active') == 'on'
new_password = (request.POST.get('new_password') or '').strip()
if role_key not in ROLE_GROUP_NAMES:
messages.error(request, _('Ungültige Rolle.'))
return redirect('user_management_page')
if role_key == ROLE_PLATFORM_OWNER and get_user_role_key(request.user) != ROLE_PLATFORM_OWNER:
messages.error(request, _('Nur Platform Owner dürfen diese Rolle vergeben.'))
return redirect('user_management_page')
current_role = get_user_role_key(request.user)
if target_user == request.user and current_role == ROLE_PLATFORM_OWNER and (role_key != ROLE_PLATFORM_OWNER or not is_active):
messages.error(request, _('Der aktuell angemeldete Platform Owner kann sich hier nicht selbst sperren oder herabstufen.'))
return redirect('user_management_page')
if target_user == request.user and current_role == ROLE_SUPER_ADMIN and (role_key != ROLE_SUPER_ADMIN or not is_active):
messages.error(request, _('Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren oder herabstufen.'))
return redirect('user_management_page')
if would_remove_last_platform_owner_fn(target_user, new_role_key=role_key, new_is_active=is_active):
messages.error(request, _('Der letzte aktive Platform Owner kann nicht deaktiviert oder herabgestuft werden.'))
return redirect('user_management_page')
if would_remove_last_super_admin_fn(target_user, new_role_key=role_key, new_is_active=is_active):
messages.error(request, _('Der letzte aktive Super Admin kann nicht deaktiviert oder herabgestuft werden.'))
return redirect('user_management_page')
assign_user_role(target_user, role_key)
target_user.is_active = is_active
if new_password:
target_user.set_password(new_password)
target_user.save()
audit_fn(
request,
'user_updated',
target_type='user',
target_id=target_user.id,
target_label=display_user_name_fn(target_user),
details={'username': target_user.username, 'role': role_key, 'is_active': is_active, 'password_changed': bool(new_password)},
)
messages.success(request, _('Benutzer wurde aktualisiert: %(username)s') % {'username': target_user.username})
return redirect('user_management_page')
def send_password_reset_from_admin_impl(request, user_id: int, *, send_user_access_email_fn, audit_fn, display_user_name_fn):
user_model = get_user_model()
target_user = get_object_or_404(user_model, id=user_id)
try:
send_user_access_email_fn(request, target_user, invitation=False)
except ValueError as exc:
messages.error(request, str(exc))
return redirect('user_management_page')
audit_fn(
request,
'user_password_reset_sent',
target_type='user',
target_id=target_user.id,
target_label=display_user_name_fn(target_user),
details={'username': target_user.username, 'email': target_user.email},
)
messages.success(request, _('Passwort-Reset-Link wurde versendet: %(username)s') % {'username': target_user.username})
return redirect('user_management_page')
def delete_user_from_admin_impl(request, user_id: int, *, would_remove_last_platform_owner_fn, would_remove_last_super_admin_fn, audit_fn, display_user_name_fn):
user_model = get_user_model()
target_user = get_object_or_404(user_model, id=user_id)
current_role = get_user_role_key(request.user)
if target_user == request.user and current_role == ROLE_PLATFORM_OWNER:
messages.error(request, _('Der aktuell angemeldete Platform Owner kann sich hier nicht selbst löschen.'))
return redirect('user_management_page')
if target_user == request.user:
messages.error(request, _('Der aktuell angemeldete Super Admin kann sich hier nicht selbst löschen.'))
return redirect('user_management_page')
if would_remove_last_platform_owner_fn(target_user, deleting=True):
messages.error(request, _('Der letzte aktive Platform Owner kann nicht gelöscht werden.'))
return redirect('user_management_page')
if would_remove_last_super_admin_fn(target_user, deleting=True):
messages.error(request, _('Der letzte aktive Super Admin kann nicht gelöscht werden.'))
return redirect('user_management_page')
target_label = display_user_name_fn(target_user)
username = target_user.username
target_user.delete()
audit_fn(
request,
'user_deleted',
target_type='user',
target_label=target_label,
details={'username': username},
)
messages.success(request, _('Benutzer wurde gelöscht: %(username)s') % {'username': username})
return redirect('user_management_page')
def handbook_page_impl(request):
return render(request, 'workflows/handbook.html')
def project_wiki_page_impl(request):
return render(request, 'workflows/project_wiki.html')
def developer_handbook_page_impl(request):
return render(request, 'workflows/developer_handbook.html')
def release_checklist_page_impl(request):
return render(request, 'workflows/release_checklist.html')