diff --git a/backend/config/settings.py b/backend/config/settings.py index 4e4d7b2..5707e2c 100644 --- a/backend/config/settings.py +++ b/backend/config/settings.py @@ -58,6 +58,7 @@ TEMPLATES = [ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'workflows.context_processors.role_context', ], }, }, diff --git a/backend/locale/en/LC_MESSAGES/django.mo b/backend/locale/en/LC_MESSAGES/django.mo index bb415ac..f50c5ec 100644 Binary files a/backend/locale/en/LC_MESSAGES/django.mo and b/backend/locale/en/LC_MESSAGES/django.mo differ diff --git a/backend/locale/en/LC_MESSAGES/django.po b/backend/locale/en/LC_MESSAGES/django.po index 6d0b407..04ef874 100644 --- a/backend/locale/en/LC_MESSAGES/django.po +++ b/backend/locale/en/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: tubco-portal\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-03-26 00:10+0000\n" +"POT-Creation-Date: 2026-03-26 09:06+0000\n" "PO-Revision-Date: 2026-03-24 00:00+0000\n" "Language: en\n" "MIME-Version: 1.0\n" @@ -55,29 +55,74 @@ msgstr "" msgid "Remote Backup in Nextcloud konnte nicht gelöscht werden." msgstr "" -#: workflows/forms.py:338 +#: workflows/forms.py:102 +msgid "Vorname" +msgstr "" + +#: workflows/forms.py:103 +msgid "Nachname" +msgstr "" + +#: workflows/forms.py:104 workflows/templates/workflows/user_management.html:81 +msgid "Benutzername" +msgstr "" + +#: workflows/forms.py:105 +#, fuzzy +#| msgid "E-Mail" +msgid "E-Mail-Adresse" +msgstr "Email" + +#: workflows/forms.py:106 workflows/templates/workflows/user_management.html:83 +#: workflows/templates/workflows/user_management.html:102 +#, fuzzy +#| msgid "Rolle:" +msgid "Rolle" +msgstr "Role:" + +#: workflows/forms.py:107 +msgid "Passwort" +msgstr "Password" + +#: workflows/forms.py:108 +msgid "Passwort bestätigen" +msgstr "Confirm password" + +#: workflows/forms.py:121 +msgid "Dieser Benutzername ist bereits vergeben." +msgstr "This username is already taken." + +#: workflows/forms.py:130 workflows/views.py:433 +msgid "Ungültige Rolle." +msgstr "Invalid role." + +#: workflows/forms.py:138 +msgid "Die Passwörter stimmen nicht überein." +msgstr "The passwords do not match." + +#: workflows/forms.py:394 #, python-format msgid "" "Das Übergabedatum muss mindestens %(days)s Tage in der Zukunft liegen " "(frühestens %(date)s)." msgstr "" -#: workflows/models.py:55 workflows/views.py:181 +#: workflows/models.py:55 workflows/views.py:199 msgid "Eingereicht" msgstr "Submitted" -#: workflows/models.py:56 workflows/views.py:182 +#: workflows/models.py:56 workflows/views.py:200 msgid "In Bearbeitung" msgstr "Processing" -#: workflows/models.py:57 workflows/models.py:372 workflows/views.py:183 +#: workflows/models.py:57 workflows/models.py:372 workflows/views.py:201 msgid "Abgeschlossen" msgstr "Completed" #: workflows/models.py:58 workflows/models.py:312 #: workflows/templates/workflows/backup_recovery.html:70 #: workflows/templates/workflows/requests_dashboard.html:222 -#: workflows/templates/workflows/welcome_emails.html:108 workflows/views.py:184 +#: workflows/templates/workflows/welcome_emails.html:108 workflows/views.py:202 msgid "Fehlgeschlagen" msgstr "Failed" @@ -137,19 +182,19 @@ msgstr "" msgid "Automatisch" msgstr "" -#: workflows/models.py:171 workflows/views.py:87 +#: workflows/models.py:171 workflows/views.py:94 msgid "Stammdaten" msgstr "Master data" -#: workflows/models.py:172 workflows/views.py:88 +#: workflows/models.py:172 workflows/views.py:95 msgid "Vertrag" msgstr "Contract" -#: workflows/models.py:173 workflows/views.py:89 +#: workflows/models.py:173 workflows/views.py:96 msgid "IT-Setup" msgstr "IT setup" -#: workflows/models.py:174 workflows/views.py:90 +#: workflows/models.py:174 workflows/views.py:97 msgid "Abschluss" msgstr "Finish" @@ -318,6 +363,22 @@ msgstr "" msgid "NFS" msgstr "" +#: workflows/roles.py:20 +msgid "Super Admin" +msgstr "Super Admin" + +#: workflows/roles.py:21 +msgid "Admin" +msgstr "Admin" + +#: workflows/roles.py:22 +msgid "IT Staff" +msgstr "IT Staff" + +#: workflows/roles.py:23 +msgid "Mitarbeiter" +msgstr "Staff" + #: workflows/tasks.py:591 #, python-format msgid "%(item)s übergeben und Grundfunktionen erklärt" @@ -428,7 +489,7 @@ msgstr "Sign in" #: workflows/templates/workflows/audit_log.html:4 #: workflows/templates/workflows/audit_log.html:15 -#: workflows/templates/workflows/home.html:120 +#: workflows/templates/workflows/home.html:132 msgid "Audit Log" msgstr "" @@ -524,7 +585,7 @@ msgstr "No requests available yet." #: workflows/templates/workflows/backup_recovery.html:4 #: workflows/templates/workflows/backup_recovery.html:12 -#: workflows/templates/workflows/home.html:125 +#: workflows/templates/workflows/home.html:139 msgid "Backup & Recovery" msgstr "Backup & Recovery" @@ -535,6 +596,7 @@ msgid "" msgstr "Create database and media backups and verify existing bundles safely." #: workflows/templates/workflows/backup_recovery.html:20 +#: workflows/templates/workflows/user_management.html:87 msgid "Aktionen" msgstr "Actions" @@ -542,12 +604,22 @@ msgstr "Actions" msgid "" "Erstellung und Verifikation laufen im App-Kontext. Restore bleibt bewusst " "CLI-only." -msgstr "Creation and verification run inside the app context. Restore intentionally remains CLI-only." +msgstr "" +"Creation and verification run inside the app context. Restore intentionally " +"remains CLI-only." #: workflows/templates/workflows/backup_recovery.html:23 msgid "Neues Backup jetzt erstellen?" msgstr "Create a new backup now?" +#: workflows/templates/workflows/backup_recovery.html:23 +msgid "Backup wird erstellt" +msgstr "Backup is being created" + +#: workflows/templates/workflows/backup_recovery.html:23 +msgid "Bitte warten. Datenbank- und Media-Bundle werden gerade vorbereitet." +msgstr "Please wait. The database and media bundle are being prepared." + #: workflows/templates/workflows/backup_recovery.html:25 msgid "Backup erstellen" msgstr "Create backup" @@ -570,7 +642,7 @@ msgid "Verifiziert" msgstr "Verified" #: workflows/templates/workflows/backup_recovery.html:40 -#: workflows/templates/workflows/home.html:98 +#: workflows/templates/workflows/home.html:99 #: workflows/templates/workflows/onboarding_intro_session.html:37 #: workflows/templates/workflows/request_timeline.html:70 #: workflows/templates/workflows/requests_dashboard.html:136 @@ -608,80 +680,53 @@ msgstr "Disabled" msgid "Lokal" msgstr "Local" +#: workflows/templates/workflows/backup_recovery.html:79 msgid "Lokal gespeichert" msgstr "Stored locally" +#: workflows/templates/workflows/backup_recovery.html:81 msgid "Lokal nicht vorhanden" msgstr "Not stored locally" -#: workflows/templates/workflows/backup_recovery.html:90 +#: workflows/templates/workflows/backup_recovery.html:95 msgid "Backup jetzt verifizieren?" msgstr "Verify backup now?" -#: workflows/templates/workflows/backup_recovery.html:92 +#: workflows/templates/workflows/backup_recovery.html:95 +msgid "Backup wird verifiziert" +msgstr "Backup is being verified" + +#: workflows/templates/workflows/backup_recovery.html:95 +msgid "Bitte warten. Bundle, Datenbank-Dump und Media-Archiv werden geprüft." +msgstr "" +"Please wait. The bundle, database dump, and media archive are being checked." + +#: workflows/templates/workflows/backup_recovery.html:97 msgid "Verifizieren" msgstr "Verify" -#: workflows/templates/workflows/backup_recovery.html:94 +#: workflows/templates/workflows/backup_recovery.html:99 msgid "Backup-Bundle wirklich löschen?" msgstr "Delete this backup bundle?" -#: workflows/templates/workflows/backup_recovery.html:96 +#: workflows/templates/workflows/backup_recovery.html:101 #: workflows/templates/workflows/form_builder.html:92 #: workflows/templates/workflows/form_builder.html:107 #: workflows/templates/workflows/integrations_setup.html:265 #: workflows/templates/workflows/intro_builder.html:66 #: workflows/templates/workflows/intro_builder.html:102 -#: workflows/templates/workflows/requests_dashboard.html:279 +#: workflows/templates/workflows/requests_dashboard.html:286 +#: workflows/templates/workflows/user_management.html:136 #: workflows/templates/workflows/welcome_emails.html:70 msgid "Löschen" msgstr "Delete" -#: workflows/templates/workflows/backup_recovery.html:106 +#: workflows/templates/workflows/backup_recovery.html:111 #, fuzzy #| msgid "Noch keine Vorgänge vorhanden." msgid "Noch keine Backup-Bundles vorhanden." msgstr "No backup bundles available yet." -msgid "Bitte warten" -msgstr "Please wait" - -msgid "Aktion läuft" -msgstr "Action in progress" - -msgid "Die Aktion wird im aktuellen Tab ausgeführt." -msgstr "The action is running in the current tab." - -msgid "Backup läuft" -msgstr "Backup in progress" - -msgid "Bitte warten. Die Aktion wird im aktuellen Tab ausgeführt." -msgstr "Please wait. The action is running in the current tab." - -msgid "Backup wird erstellt" -msgstr "Backup is being created" - -msgid "Bitte warten. Datenbank- und Media-Bundle werden gerade vorbereitet." -msgstr "Please wait. The database and media bundle are being prepared." - -msgid "Backup wird verifiziert" -msgstr "Backup is being verified" - -msgid "Bitte warten. Bundle, Datenbank-Dump und Media-Archiv werden geprüft." -msgstr "Please wait. The bundle, database dump, and media archive are being checked." - -msgid "Nextcloud-Test läuft" -msgstr "Nextcloud test in progress" - -msgid "Bitte warten. Verbindung und Upload in das konfigurierte Ziel werden geprüft." -msgstr "Please wait. The connection and upload to the configured target are being checked." - -msgid "SMTP-Test läuft" -msgstr "SMTP test in progress" - -msgid "Bitte warten. SMTP-Verbindung und Testversand werden geprüft." -msgstr "Please wait. The SMTP connection and test delivery are being checked." - #: workflows/templates/workflows/base_shell.html:24 msgid "Bitte bestätigen" msgstr "" @@ -695,9 +740,21 @@ msgstr "Cancel" msgid "Bestätigen" msgstr "" +#: workflows/templates/workflows/base_shell.html:36 +msgid "Bitte warten" +msgstr "Please wait" + +#: workflows/templates/workflows/base_shell.html:37 +msgid "Aktion läuft" +msgstr "Action in progress" + +#: workflows/templates/workflows/base_shell.html:38 +msgid "Die Aktion wird im aktuellen Tab ausgeführt." +msgstr "The action is running in the current tab." + #: workflows/templates/workflows/form_builder.html:4 #: workflows/templates/workflows/form_builder.html:14 -#: workflows/templates/workflows/home.html:135 +#: workflows/templates/workflows/home.html:153 msgid "Form Builder" msgstr "Form Builder" @@ -760,6 +817,7 @@ msgstr "Label (EN)" #: workflows/templates/workflows/form_builder.html:91 #: workflows/templates/workflows/integrations_setup.html:263 #: workflows/templates/workflows/intro_builder.html:65 +#: workflows/templates/workflows/user_management.html:84 msgid "Aktiv" msgstr "Active" @@ -821,7 +879,7 @@ msgstr "Save field text" #: workflows/templates/workflows/handbook.html:4 #: workflows/templates/workflows/handbook.html:15 -#: workflows/templates/workflows/home.html:145 +#: workflows/templates/workflows/home.html:165 msgid "Handbook" msgstr "Handbook" @@ -944,7 +1002,7 @@ msgstr "" #: workflows/templates/workflows/home.html:4 #: workflows/templates/workflows/home.html:35 -#: workflows/templates/workflows/requests_dashboard.html:295 +#: workflows/templates/workflows/requests_dashboard.html:303 msgid "TUBCO Onboarding & Offboarding Portal" msgstr "TUBCO Onboarding & Offboarding Portal" @@ -969,25 +1027,19 @@ msgstr "" msgid "Rolle:" msgstr "Role:" -#: workflows/templates/workflows/home.html:38 -msgid "Admin" -msgstr "Admin" - -#: workflows/templates/workflows/home.html:38 -msgid "Mitarbeiter" -msgstr "Staff" - #: workflows/templates/workflows/home.html:40 msgid "Nextcloud:" msgstr "Nextcloud:" #: workflows/templates/workflows/home.html:40 #: workflows/templates/workflows/integrations_setup.html:60 +#: workflows/templates/workflows/user_management.html:112 msgid "aktiv" msgstr "active" #: workflows/templates/workflows/home.html:40 #: workflows/templates/workflows/integrations_setup.html:60 +#: workflows/templates/workflows/user_management.html:112 msgid "inaktiv" msgstr "inactive" @@ -1063,13 +1115,13 @@ msgstr "IT return" msgid "Offboarding starten" msgstr "Start offboarding" -#: workflows/templates/workflows/home.html:94 +#: workflows/templates/workflows/home.html:95 #: workflows/templates/workflows/requests_dashboard.html:4 #: workflows/templates/workflows/requests_dashboard.html:33 msgid "Anfragen Dashboard" msgstr "Requests Dashboard" -#: workflows/templates/workflows/home.html:95 +#: workflows/templates/workflows/home.html:96 msgid "" "Status, Suchfunktion, PDF-Links und Verlauf aller Onboarding-/Offboarding-" "Anfragen." @@ -1077,89 +1129,100 @@ msgstr "" "Status, search, PDF links, and history of all onboarding/offboarding " "requests." -#: workflows/templates/workflows/home.html:97 +#: workflows/templates/workflows/home.html:98 msgid "Suche" msgstr "Search" -#: workflows/templates/workflows/home.html:99 +#: workflows/templates/workflows/home.html:100 msgid "PDF Zugriff" msgstr "PDF access" -#: workflows/templates/workflows/home.html:103 +#: workflows/templates/workflows/home.html:104 msgid "Dashboard öffnen" msgstr "Open dashboard" -#: workflows/templates/workflows/home.html:110 +#: workflows/templates/workflows/home.html:112 msgid "Admin Apps" msgstr "Admin Apps" -#: workflows/templates/workflows/home.html:111 +#: workflows/templates/workflows/home.html:113 msgid "Konfiguration, Tests und Steuerung." msgstr "Configuration, tests, and controls." -#: workflows/templates/workflows/home.html:115 +#: workflows/templates/workflows/home.html:118 msgid "Integrationen" msgstr "Integrations" -#: workflows/templates/workflows/home.html:116 +#: workflows/templates/workflows/home.html:119 msgid "Nextcloud- und E-Mail-Setup." msgstr "Nextcloud and email setup." -#: workflows/templates/workflows/home.html:117 -#: workflows/templates/workflows/home.html:122 +#: workflows/templates/workflows/home.html:120 #: workflows/templates/workflows/home.html:127 -#: workflows/templates/workflows/home.html:132 -#: workflows/templates/workflows/home.html:137 -#: workflows/templates/workflows/home.html:142 -#: workflows/templates/workflows/home.html:147 -#: workflows/templates/workflows/home.html:152 +#: workflows/templates/workflows/home.html:134 +#: workflows/templates/workflows/home.html:141 +#: workflows/templates/workflows/home.html:148 +#: workflows/templates/workflows/home.html:155 +#: workflows/templates/workflows/home.html:160 +#: workflows/templates/workflows/home.html:167 +#: workflows/templates/workflows/home.html:174 msgid "Öffnen" msgstr "Open" -#: workflows/templates/workflows/home.html:121 +#: workflows/templates/workflows/home.html:125 +#: workflows/templates/workflows/user_management.html:4 +#: workflows/templates/workflows/user_management.html:14 +msgid "Benutzer & Rollen" +msgstr "Users & roles" + +#: workflows/templates/workflows/home.html:126 +msgid "Benutzer anlegen, Rollen zuweisen und Zugriffe steuern." +msgstr "Create users, assign roles, and control access." + +#: workflows/templates/workflows/home.html:133 msgid "Wichtige Admin-Aktionen nachvollziehen und prüfen." msgstr "" -#: workflows/templates/workflows/home.html:126 +#: workflows/templates/workflows/home.html:140 msgid "Backups erstellen und sicher verifizieren." msgstr "" -#: workflows/templates/workflows/home.html:130 +#: workflows/templates/workflows/home.html:146 #: workflows/templates/workflows/welcome_emails.html:4 msgid "Welcome E-Mails" msgstr "Welcome Emails" -#: workflows/templates/workflows/home.html:131 +#: workflows/templates/workflows/home.html:147 msgid "Geplante Welcome Mails verwalten." msgstr "Manage scheduled welcome emails." -#: workflows/templates/workflows/home.html:136 +#: workflows/templates/workflows/home.html:154 msgid "Felder, Schritte und Optionen verwalten." msgstr "Manage fields, steps, and options." -#: workflows/templates/workflows/home.html:140 +#: workflows/templates/workflows/home.html:158 #: workflows/templates/workflows/intro_builder.html:4 #: workflows/templates/workflows/intro_builder.html:17 msgid "Einweisungs-Builder" msgstr "Introduction Builder" -#: workflows/templates/workflows/home.html:141 +#: workflows/templates/workflows/home.html:159 msgid "Checklistenpunkte für das Einweisungsprotokoll konfigurieren." msgstr "Configure checklist items for the introduction protocol." -#: workflows/templates/workflows/home.html:146 +#: workflows/templates/workflows/home.html:166 msgid "Project wiki and developer documentation in one place." msgstr "Project wiki and developer documentation in one place." -#: workflows/templates/workflows/home.html:150 +#: workflows/templates/workflows/home.html:172 msgid "Django Admin" msgstr "Django Admin" -#: workflows/templates/workflows/home.html:151 +#: workflows/templates/workflows/home.html:173 msgid "Vollständige Datenverwaltung." msgstr "Full data management." -#: workflows/templates/workflows/home.html:158 +#: workflows/templates/workflows/home.html:181 msgid "Tipp: Die letzten Vorgänge sehen Sie jederzeit im Anfragen Dashboard." msgstr "Tip: You can always see the latest requests in the Requests Dashboard." @@ -1210,13 +1273,24 @@ msgstr "Backup target" msgid "Nextcloud speichern" msgstr "Save Nextcloud" +#: workflows/templates/workflows/integrations_setup.html:55 +msgid "Nextcloud-Test läuft" +msgstr "Nextcloud test in progress" + +#: workflows/templates/workflows/integrations_setup.html:55 +msgid "" +"Bitte warten. Verbindung und Upload in das konfigurierte Ziel werden geprüft." +msgstr "" +"Please wait. The connection and upload to the configured target are being " +"checked." + #: workflows/templates/workflows/integrations_setup.html:55 msgid "Nextcloud-Test starten" msgstr "Run Nextcloud test" #: workflows/templates/workflows/integrations_setup.html:59 #: workflows/templates/workflows/integrations_setup.html:121 -#: workflows/templates/workflows/requests_dashboard.html:262 +#: workflows/templates/workflows/requests_dashboard.html:266 msgid "Status:" msgstr "Status:" @@ -1253,6 +1327,14 @@ msgstr "SMTP TLS" msgid "Mail speichern" msgstr "Save mail settings" +#: workflows/templates/workflows/integrations_setup.html:117 +msgid "SMTP-Test läuft" +msgstr "SMTP test in progress" + +#: workflows/templates/workflows/integrations_setup.html:117 +msgid "Bitte warten. SMTP-Verbindung und Testversand werden geprüft." +msgstr "Please wait. The SMTP connection and test delivery are being checked." + #: workflows/templates/workflows/integrations_setup.html:117 msgid "SMTP-Test starten" msgstr "Run SMTP test" @@ -1408,27 +1490,27 @@ msgstr "Remote backup enabled" msgid "Remote Kopie nach lokalem Bundle erstellen" msgstr "Create remote copy after local bundle creation" -#: workflows/templates/workflows/integrations_setup.html:378 +#: workflows/templates/workflows/integrations_setup.html:379 msgid "Remote Backup Zieltyp" msgstr "Remote backup target type" -#: workflows/templates/workflows/integrations_setup.html:386 +#: workflows/templates/workflows/integrations_setup.html:387 msgid "Nextcloud Backup-Verzeichnis" msgstr "Nextcloud backup directory" -#: workflows/templates/workflows/integrations_setup.html:390 +#: workflows/templates/workflows/integrations_setup.html:391 msgid "S3 Bucket (optional)" msgstr "S3 bucket (optional)" -#: workflows/templates/workflows/integrations_setup.html:394 +#: workflows/templates/workflows/integrations_setup.html:395 msgid "NFS Pfad (optional)" msgstr "NFS path (optional)" -#: workflows/templates/workflows/integrations_setup.html:399 +#: workflows/templates/workflows/integrations_setup.html:401 msgid "Backup-Einstellungen speichern" msgstr "Save backup settings" -#: workflows/templates/workflows/integrations_setup.html:401 +#: workflows/templates/workflows/integrations_setup.html:403 msgid "" "Empfehlung: Nextcloud als erstes Remote-Ziel verwenden. S3 und NFS sind als " "Zieltypen vorbereitet, aber noch nicht aktiv implementiert." @@ -1436,7 +1518,7 @@ msgstr "" "Recommendation: use Nextcloud as the first remote target. S3 and NFS are " "prepared as target types but not yet actively implemented." -#: workflows/templates/workflows/integrations_setup.html:402 +#: workflows/templates/workflows/integrations_setup.html:404 msgid "" "Das Backup-Verzeichnis muss getrennt vom normalen Nextcloud Dokumentenordner " "sein, z. B. Group-on-off-boarding-backups." @@ -1692,6 +1774,7 @@ msgstr "Employee" #: workflows/templates/workflows/onboarding_intro_session.html:27 #: workflows/templates/workflows/request_timeline.html:66 +#: workflows/templates/workflows/user_management.html:80 msgid "Name" msgstr "Name" @@ -1704,7 +1787,7 @@ msgid "Dienstliche E-Mail" msgstr "Work email" #: workflows/templates/workflows/onboarding_intro_session.html:31 -#: workflows/views.py:487 +#: workflows/views.py:688 msgid "Vertragsbeginn" msgstr "Contract start" @@ -1776,7 +1859,7 @@ msgid "Live-Protokoll erzeugen" msgstr "Generate live protocol" #: workflows/templates/workflows/onboarding_intro_session.html:94 -#: workflows/templates/workflows/requests_dashboard.html:239 +#: workflows/templates/workflows/requests_dashboard.html:241 msgid "Live-Protokoll öffnen" msgstr "Open live protocol" @@ -1964,6 +2047,7 @@ msgstr "" #: workflows/templates/workflows/request_timeline.html:74 #: workflows/templates/workflows/requests_dashboard.html:190 +#: workflows/templates/workflows/user_management.html:82 msgid "E-Mail" msgstr "Email" @@ -2096,52 +2180,121 @@ msgstr "Introduction" msgid "Noch nicht verfügbar" msgstr "Not available yet" -#: workflows/templates/workflows/requests_dashboard.html:237 +#: workflows/templates/workflows/requests_dashboard.html:238 msgid "Einweisung öffnen" msgstr "Open introduction" -#: workflows/templates/workflows/requests_dashboard.html:244 +#: workflows/templates/workflows/requests_dashboard.html:247 msgid "Standard-Einweisungs-PDF" msgstr "Standard introduction PDF" -#: workflows/templates/workflows/requests_dashboard.html:249 +#: workflows/templates/workflows/requests_dashboard.html:252 msgid "Neu erzeugen" msgstr "Regenerate" -#: workflows/templates/workflows/requests_dashboard.html:251 +#: workflows/templates/workflows/requests_dashboard.html:254 msgid "Standard-PDF öffnen" msgstr "Open standard PDF" -#: workflows/templates/workflows/requests_dashboard.html:255 +#: workflows/templates/workflows/requests_dashboard.html:258 msgid "PDF erzeugen" msgstr "Generate PDF" -#: workflows/templates/workflows/requests_dashboard.html:266 +#: workflows/templates/workflows/requests_dashboard.html:270 msgid "Nicht relevant" msgstr "Not relevant" -#: workflows/templates/workflows/requests_dashboard.html:270 +#: workflows/templates/workflows/requests_dashboard.html:276 msgid "Timeline" msgstr "" -#: workflows/templates/workflows/requests_dashboard.html:272 +#: workflows/templates/workflows/requests_dashboard.html:278 msgid "Eintrag erneut verarbeiten?" msgstr "" -#: workflows/templates/workflows/requests_dashboard.html:274 +#: workflows/templates/workflows/requests_dashboard.html:280 msgid "Erneut versuchen" msgstr "" -#: workflows/templates/workflows/requests_dashboard.html:277 +#: workflows/templates/workflows/requests_dashboard.html:284 #, fuzzy #| msgid "Option wirklich löschen?" msgid "Eintrag wirklich löschen?" msgstr "Delete this option?" -#: workflows/templates/workflows/requests_dashboard.html:286 +#: workflows/templates/workflows/requests_dashboard.html:294 msgid "Noch keine Vorgänge vorhanden." msgstr "No requests available yet." +#: workflows/templates/workflows/user_management.html:15 +msgid "Super Admins verwalten Benutzerkonten, Rollen und den aktiven Zugriff." +msgstr "Super admins manage user accounts, roles, and active access." + +#: workflows/templates/workflows/user_management.html:22 +msgid "Benutzer anlegen" +msgstr "Create user" + +#: workflows/templates/workflows/user_management.html:64 +msgid "Benutzer erstellen" +msgstr "Create user" + +#: workflows/templates/workflows/user_management.html:72 +msgid "Benutzerübersicht" +msgstr "User overview" + +#: workflows/templates/workflows/user_management.html:73 +msgid "Rollen ändern, Zugriffe sperren oder ein neues Passwort setzen." +msgstr "Change roles, block access, or set a new password." + +#: workflows/templates/workflows/user_management.html:85 +msgid "Letzte Anmeldung" +msgstr "Last login" + +#: workflows/templates/workflows/user_management.html:86 +#: workflows/templates/workflows/user_management.html:117 +msgid "Neues Passwort" +msgstr "New password" + +#: workflows/templates/workflows/user_management.html:96 +msgid "Sie selbst" +msgstr "You" + +#: workflows/templates/workflows/user_management.html:118 +msgid "Optional" +msgstr "Optional" + +#: workflows/templates/workflows/user_management.html:124 +msgid "Speichern" +msgstr "Save" + +#: workflows/templates/workflows/user_management.html:128 +msgid "Reset-Link senden" +msgstr "" + +#: workflows/templates/workflows/user_management.html:131 +#, fuzzy +#| msgid "Welcome E-Mails" +msgid "Keine E-Mail" +msgstr "Welcome Emails" + +#: workflows/templates/workflows/user_management.html:136 +#, fuzzy +#| msgid "Option wirklich löschen?" +msgid "Benutzer wirklich löschen?" +msgstr "Delete this option?" + +#: workflows/templates/workflows/user_management.html:143 +msgid "Es sind noch keine Benutzer vorhanden." +msgstr "No users exist yet." + +#: workflows/templates/workflows/user_management.html:149 +msgid "" +"Hinweis: Der aktuell angemeldete Super Admin kann sich hier nicht selbst " +"deaktivieren oder auf eine niedrigere Rolle setzen." +msgstr "" +"Note: The currently signed-in super admin cannot deactivate themselves or " +"assign a lower role here." + #: workflows/templates/workflows/welcome_emails.html:14 msgid "Geplante Welcome E-Mails" msgstr "Scheduled welcome emails" @@ -2228,326 +2381,437 @@ msgstr "Resume" msgid "Keine geplanten Welcome E-Mails vorhanden." msgstr "No scheduled welcome emails available." -#: workflows/views.py:87 +#: workflows/views.py:94 msgid "Person, Rolle, Abteilung" msgstr "Person, role, department" -#: workflows/views.py:88 +#: workflows/views.py:95 msgid "Beschäftigung und Termine" msgstr "Employment and dates" -#: workflows/views.py:89 +#: workflows/views.py:96 msgid "Geräte, Software und Zugänge" msgstr "Devices, software, and access" -#: workflows/views.py:90 +#: workflows/views.py:97 msgid "Notizen und Freigabe" msgstr "Notes and approval" -#: workflows/views.py:191 +#: workflows/views.py:128 workflows/views.py:774 workflows/views.py:779 +msgid "Sie haben keine Berechtigung für diese Aktion." +msgstr "You do not have permission for this action." + +#: workflows/views.py:209 #, fuzzy #| msgid "Vorgänge" msgid "Vorgänge gelöscht" msgstr "Requests" -#: workflows/views.py:192 +#: workflows/views.py:210 msgid "Vorgang gelöscht" msgstr "" -#: workflows/views.py:193 +#: workflows/views.py:211 msgid "Vorgang erneut angestoßen" msgstr "" -#: workflows/views.py:194 +#: workflows/views.py:212 #, fuzzy #| msgid "Einweisung" msgid "Einweisungs-PDF erzeugt" msgstr "Introduction" -#: workflows/views.py:195 +#: workflows/views.py:213 #, fuzzy #| msgid "Live-Protokoll erzeugen" msgid "Live-Protokoll erzeugt" msgstr "Generate live protocol" -#: workflows/views.py:196 +#: workflows/views.py:214 #, fuzzy #| msgid "Einweisung wurde zurückgesetzt." msgid "Einweisung zurückgesetzt" msgstr "Introduction was reset." -#: workflows/views.py:197 +#: workflows/views.py:215 #, fuzzy #| msgid "Einweisung wurde als Entwurf gespeichert." msgid "Einweisung als Entwurf gespeichert" msgstr "Introduction was saved as draft." -#: workflows/views.py:198 +#: workflows/views.py:216 #, fuzzy #| msgid "Einweisung wurde als abgeschlossen gespeichert." msgid "Einweisung abgeschlossen" msgstr "Introduction was saved as completed." -#: workflows/views.py:199 +#: workflows/views.py:217 msgid "Formularoption gelöscht" msgstr "" -#: workflows/views.py:200 +#: workflows/views.py:218 #, fuzzy #| msgid "Optionen speichern" msgid "Formularoptionen gespeichert" msgstr "Save options" -#: workflows/views.py:201 +#: workflows/views.py:219 #, fuzzy #| msgid "Feldtexte speichern" msgid "Feldtexte gespeichert" msgstr "Save field text" -#: workflows/views.py:202 +#: workflows/views.py:220 #, fuzzy #| msgid "Offboarding-Anfrage speichern" msgid "Formularlayout gespeichert" msgstr "Save offboarding request" -#: workflows/views.py:203 +#: workflows/views.py:221 msgid "Einweisungs-Checkpunkt gelöscht" msgstr "" -#: workflows/views.py:204 +#: workflows/views.py:222 msgid "Einweisungs-Checkpunkt hinzugefügt" msgstr "" -#: workflows/views.py:205 +#: workflows/views.py:223 #, fuzzy #| msgid "Checkliste speichern" msgid "Einweisungs-Checkliste gespeichert" msgstr "Save checklist" -#: workflows/views.py:206 +#: workflows/views.py:224 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail sofort ausgelöst" msgstr "Welcome Emails" -#: workflows/views.py:207 +#: workflows/views.py:225 #, fuzzy #| msgid "Welcome-Einstellungen speichern" msgid "Welcome E-Mail Einstellungen gespeichert" msgstr "Save welcome settings" -#: workflows/views.py:208 +#: workflows/views.py:226 msgid "Welcome E-Mail Sammelaktion ausgeführt" msgstr "" -#: workflows/views.py:209 +#: workflows/views.py:227 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail pausiert" msgstr "Welcome Emails" -#: workflows/views.py:210 +#: workflows/views.py:228 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail fortgesetzt" msgstr "Welcome Emails" -#: workflows/views.py:211 +#: workflows/views.py:229 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail abgebrochen" msgstr "Welcome Emails" -#: workflows/views.py:212 +#: workflows/views.py:230 #, fuzzy #| msgid "SMTP-Test" msgid "SMTP-Test gesendet" msgstr "SMTP test" -#: workflows/views.py:213 +#: workflows/views.py:231 #, fuzzy #| msgid "Nextcloud-Test" msgid "Nextcloud-Testupload ausgeführt" msgstr "Nextcloud test" -#: workflows/views.py:214 +#: workflows/views.py:232 #, fuzzy #| msgid "Nextcloud schalten" msgid "Nextcloud-Modus umgeschaltet" msgstr "Toggle Nextcloud" -#: workflows/views.py:215 +#: workflows/views.py:233 msgid "E-Mail-Modus umgeschaltet" msgstr "" -#: workflows/views.py:216 +#: workflows/views.py:234 #, fuzzy #| msgid "Integrationen Setup" msgid "Integrationen gespeichert" msgstr "Integrations Setup" -#: workflows/views.py:217 +#: workflows/views.py:235 #, fuzzy #| msgid "Welcome-Einstellungen speichern" msgid "Nextcloud-Einstellungen gespeichert" msgstr "Save welcome settings" -#: workflows/views.py:218 +#: workflows/views.py:236 #, fuzzy #| msgid "Welcome-Einstellungen speichern" msgid "Mail-Einstellungen gespeichert" msgstr "Save welcome settings" -#: workflows/views.py:219 +#: workflows/views.py:237 #, fuzzy #| msgid "E-Mail Routing & Vorlagen speichern" msgid "E-Mail-Routing gespeichert" msgstr "Save email routing & templates" -#: workflows/views.py:220 +#: workflows/views.py:238 #, fuzzy #| msgid "Offboarding-Anfrage speichern" msgid "Benachrichtigungsregeln gespeichert" msgstr "Save offboarding request" -#: workflows/views.py:221 +#: workflows/views.py:239 +#, fuzzy +#| msgid "Anfrage gespeichert" +msgid "Benutzer erstellt" +msgstr "Request saved" + +#: workflows/views.py:240 +msgid "Benutzer aktualisiert" +msgstr "" + +#: workflows/views.py:241 +msgid "Passwort-Reset-Link versendet" +msgstr "" + +#: workflows/views.py:242 +#, fuzzy +#| msgid "Benutzerübersicht" +msgid "Benutzer gelöscht" +msgstr "User overview" + +#: workflows/views.py:243 #, fuzzy #| msgid "Anfrage gespeichert" msgid "Backup erstellt" msgstr "Request saved" -#: workflows/views.py:222 +#: workflows/views.py:244 msgid "Backup verifiziert" msgstr "" -#: workflows/views.py:223 +#: workflows/views.py:245 #, fuzzy #| msgid "Anfrage gespeichert" msgid "Backup gelöscht" msgstr "Request saved" -#: workflows/views.py:224 +#: workflows/views.py:246 #, fuzzy #| msgid "Welcome-Einstellungen speichern" msgid "Backup-Einstellungen gespeichert" msgstr "Save welcome settings" -#: workflows/views.py:408 +#: workflows/views.py:407 +msgid "Benutzer konnte nicht erstellt werden. Bitte prüfen Sie die Eingaben." +msgstr "User could not be created. Please check the input." + +#: workflows/views.py:419 +#, python-format +msgid "Benutzer wurde erstellt: %(username)s" +msgstr "User created: %(username)s" + +#: workflows/views.py:437 +msgid "" +"Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren oder " +"herabstufen." +msgstr "" +"The currently signed-in super admin cannot lock or downgrade themselves here." + +#: workflows/views.py:440 +#, fuzzy +#| msgid "" +#| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " +#| "oder herabstufen." +msgid "" +"Der letzte aktive Super Admin kann nicht deaktiviert oder herabgestuft " +"werden." +msgstr "" +"The currently signed-in super admin cannot lock or downgrade themselves here." + +#: workflows/views.py:457 +#, python-format +msgid "Benutzer wurde aktualisiert: %(username)s" +msgstr "User updated: %(username)s" + +#: workflows/views.py:468 +msgid "Für diesen Benutzer ist keine E-Mail-Adresse hinterlegt." +msgstr "" + +#: workflows/views.py:477 +#, python-format +msgid "Passwort zurücksetzen für %(username)s" +msgstr "" + +#: workflows/views.py:479 +#, python-format +msgid "" +"Hallo %(name)s,\n" +"\n" +"für Ihr Konto wurde ein Link zum Zurücksetzen des Passworts erstellt.\n" +"Bitte öffnen Sie den folgenden Link:\n" +"%(url)s\n" +"\n" +"Wenn Sie diese Anfrage nicht erwartet haben, können Sie diese E-Mail " +"ignorieren." +msgstr "" + +#: workflows/views.py:498 +#, fuzzy, python-format +#| msgid "Benutzer wurde erstellt: %(username)s" +msgid "Passwort-Reset-Link wurde versendet: %(username)s" +msgstr "User created: %(username)s" + +#: workflows/views.py:509 +#, fuzzy +#| msgid "" +#| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " +#| "oder herabstufen." +msgid "" +"Der aktuell angemeldete Super Admin kann sich hier nicht selbst löschen." +msgstr "" +"The currently signed-in super admin cannot lock or downgrade themselves here." + +#: workflows/views.py:512 +#, fuzzy +#| msgid "" +#| "Der aktuell angemeldete Super Admin kann sich hier nicht selbst sperren " +#| "oder herabstufen." +msgid "Der letzte aktive Super Admin kann nicht gelöscht werden." +msgstr "" +"The currently signed-in super admin cannot lock or downgrade themselves here." + +#: workflows/views.py:525 +#, fuzzy, python-format +#| msgid "Benutzer wurde erstellt: %(username)s" +msgid "Benutzer wurde gelöscht: %(username)s" +msgstr "User created: %(username)s" + +#: workflows/views.py:612 #, python-format msgid "Backup wurde erstellt: %(name)s" msgstr "" -#: workflows/views.py:410 +#: workflows/views.py:614 #, python-format msgid "Backup konnte nicht erstellt werden: %(error)s" msgstr "" -#: workflows/views.py:427 +#: workflows/views.py:630 #, python-format msgid "Backup wurde verifiziert: %(name)s" msgstr "" -#: workflows/views.py:429 +#: workflows/views.py:632 #, python-format msgid "Backup-Verifikation fehlgeschlagen: %(error)s" msgstr "" -#: workflows/views.py:446 +#: workflows/views.py:648 #, python-format msgid "Backup wurde gelöscht: %(name)s" msgstr "" -#: workflows/views.py:448 +#: workflows/views.py:650 #, python-format msgid "Backup konnte nicht gelöscht werden: %(error)s" msgstr "" -#: workflows/views.py:475 +#: workflows/views.py:676 #, fuzzy #| msgid "Anfrage gespeichert" msgid "Anfrage erstellt" msgstr "Request saved" -#: workflows/views.py:477 +#: workflows/views.py:678 #, fuzzy, python-format #| msgid "Sitzungsstatus" msgid "Status: %(status)s" msgstr "Session status" -#: workflows/views.py:489 +#: workflows/views.py:690 #, fuzzy #| msgid "Geplant für" msgid "Geplanter Start" msgstr "Scheduled for" -#: workflows/views.py:499 +#: workflows/views.py:700 msgid "Geräteübergabe / Hardware-Abholung" msgstr "" -#: workflows/views.py:501 +#: workflows/views.py:702 msgid "Geplanter Hardware-Termin" msgstr "" -#: workflows/views.py:510 +#: workflows/views.py:711 #, fuzzy #| msgid "Noch nicht verfügbar" msgid "PDF verfügbar" msgstr "Not available yet" -#: workflows/views.py:536 +#: workflows/views.py:737 #, fuzzy #| msgid "Einweisung" msgid "Einweisungssitzung" msgstr "Introduction" -#: workflows/views.py:548 +#: workflows/views.py:749 #, fuzzy #| msgid "Welcome E-Mails" msgid "Welcome E-Mail" msgstr "Welcome Emails" -#: workflows/views.py:574 -msgid "Sie haben keine Berechtigung für diese Aktion." -msgstr "You do not have permission for this action." - -#: workflows/views.py:583 +#: workflows/views.py:788 msgid "Keine Einträge ausgewählt." msgstr "No entries selected." -#: workflows/views.py:626 +#: workflows/views.py:831 #, python-format msgid "%(count)s Eintrag/Einträge gelöscht." msgstr "%(count)s entry/entries deleted." -#: workflows/views.py:628 +#: workflows/views.py:833 #, python-format msgid "%(count)s Auswahl(en) konnten nicht verarbeitet werden." msgstr "%(count)s selection(s) could not be processed." -#: workflows/views.py:630 +#: workflows/views.py:835 msgid "Keine passenden Einträge gefunden." msgstr "No matching entries found." -#: workflows/views.py:850 +#: workflows/views.py:1062 msgid "Einweisungs- und Übergabeprotokoll wurde erzeugt." msgstr "Introduction and handover protocol was generated." -#: workflows/views.py:868 +#: workflows/views.py:1079 msgid "Einweisungsprotokoll aus Live-Status wurde erzeugt." msgstr "Introduction protocol from live status was generated." -#: workflows/views.py:898 +#: workflows/views.py:1108 msgid "Einweisung wurde zurückgesetzt." msgstr "Introduction was reset." -#: workflows/views.py:912 +#: workflows/views.py:1122 msgid "Einweisung wurde als abgeschlossen gespeichert." msgstr "Introduction was saved as completed." -#: workflows/views.py:925 +#: workflows/views.py:1135 msgid "Einweisung wurde als Entwurf gespeichert." msgstr "Introduction was saved as draft." +#~ msgid "Backup läuft" +#~ msgstr "Backup in progress" + +#~ msgid "Bitte warten. Die Aktion wird im aktuellen Tab ausgeführt." +#~ msgstr "Please wait. The action is running in the current tab." + #~ msgid "Anmeldung fehlgeschlagen. Bitte Zugangsdaten prüfen." #~ msgstr "Login failed. Please check your credentials." diff --git a/backend/workflows/apps.py b/backend/workflows/apps.py index 756018c..bf4f6e7 100644 --- a/backend/workflows/apps.py +++ b/backend/workflows/apps.py @@ -4,3 +4,6 @@ from django.apps import AppConfig class WorkflowsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'workflows' + + def ready(self): + from . import signals # noqa: F401 diff --git a/backend/workflows/context_processors.py b/backend/workflows/context_processors.py new file mode 100644 index 0000000..64bad7f --- /dev/null +++ b/backend/workflows/context_processors.py @@ -0,0 +1,5 @@ +from .roles import template_role_context + + +def role_context(request): + return template_role_context(getattr(request, 'user', None)) diff --git a/backend/workflows/forms.py b/backend/workflows/forms.py index 2cea892..0c326ad 100644 --- a/backend/workflows/forms.py +++ b/backend/workflows/forms.py @@ -1,11 +1,13 @@ from django import forms from pathlib import Path from datetime import timedelta +from django.contrib.auth import get_user_model from django.utils import timezone from django.utils.translation import get_language, gettext as _ from .form_builder import apply_form_field_config from .models import EmployeeProfile, FormOption, OffboardingRequest, OnboardingRequest, WorkflowConfig +from .roles import ROLE_ADMIN, ROLE_GROUP_NAMES, ROLE_IT_STAFF, ROLE_LABELS, ROLE_STAFF, ROLE_SUPER_ADMIN, assign_user_role YES_NO_CHOICES = [('', '--'), ('ja', 'Ja'), ('nein', 'Nein')] @@ -96,6 +98,60 @@ HARDWARE_EXTRA_CHOICES = [('Smartphone', 'Smartphone'), ('Anderes', 'Anderes')] SOFTWARE_EXTRA_CHOICES = [('Adobe Acrobat Pro (Abonnement: Zusätzliche Kosten)', 'Adobe Acrobat Pro (Abonnement: Zusätzliche Kosten)'), ('Anderes', 'Anderes')] +class UserManagementCreateForm(forms.Form): + first_name = forms.CharField(label=_('Vorname'), max_length=150, required=False) + last_name = forms.CharField(label=_('Nachname'), max_length=150, required=False) + username = forms.CharField(label=_('Benutzername'), max_length=150) + email = forms.EmailField(label=_('E-Mail-Adresse')) + role_key = forms.ChoiceField(label=_('Rolle')) + password1 = forms.CharField(label=_('Passwort'), widget=forms.PasswordInput()) + password2 = forms.CharField(label=_('Passwort bestätigen'), widget=forms.PasswordInput()) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['role_key'].choices = [ + (role_key, str(ROLE_LABELS[role_key])) + for role_key in (ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF, ROLE_STAFF) + ] + + def clean_username(self): + username = (self.cleaned_data.get('username') or '').strip() + user_model = get_user_model() + if user_model.objects.filter(username=username).exists(): + raise forms.ValidationError(_('Dieser Benutzername ist bereits vergeben.')) + return username + + def clean_email(self): + return (self.cleaned_data.get('email') or '').strip().lower() + + def clean_role_key(self): + role_key = (self.cleaned_data.get('role_key') or '').strip() + if role_key not in ROLE_GROUP_NAMES: + raise forms.ValidationError(_('Ungültige Rolle.')) + return role_key + + def clean(self): + cleaned = super().clean() + password1 = cleaned.get('password1') + password2 = cleaned.get('password2') + if password1 and password2 and password1 != password2: + self.add_error('password2', _('Die Passwörter stimmen nicht überein.')) + return cleaned + + def save(self): + user_model = get_user_model() + user = user_model.objects.create_user( + username=self.cleaned_data['username'], + email=self.cleaned_data['email'], + password=self.cleaned_data['password1'], + first_name=self.cleaned_data.get('first_name', ''), + last_name=self.cleaned_data.get('last_name', ''), + is_active=True, + ) + assign_user_role(user, self.cleaned_data['role_key']) + return user + + class OnboardingRequestForm(forms.ModelForm): first_name = forms.CharField(label='Vorname', required=False) last_name = forms.CharField(label='Nachname', required=False) diff --git a/backend/workflows/management/commands/bootstrap_initial_users.py b/backend/workflows/management/commands/bootstrap_initial_users.py index bb6b872..5adfb7f 100644 --- a/backend/workflows/management/commands/bootstrap_initial_users.py +++ b/backend/workflows/management/commands/bootstrap_initial_users.py @@ -1,6 +1,7 @@ from django.contrib.auth import get_user_model from django.core.management.base import BaseCommand +from workflows.roles import ROLE_STAFF, ROLE_SUPER_ADMIN, assign_user_role, ensure_role_groups DEFAULT_USERS = [ { @@ -43,6 +44,8 @@ class Command(BaseCommand): is_staff=item['is_staff'], is_superuser=item['is_superuser'], ) + ensure_role_groups() + assign_user_role(user, ROLE_SUPER_ADMIN if item['username'] == 'admin_test' else ROLE_STAFF) self.stdout.write(f'created {user.username}') self.stdout.write(self.style.SUCCESS('initial users created')) diff --git a/backend/workflows/roles.py b/backend/workflows/roles.py new file mode 100644 index 0000000..f487490 --- /dev/null +++ b/backend/workflows/roles.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group +from django.utils.translation import gettext_lazy as _ + +ROLE_SUPER_ADMIN = 'super_admin' +ROLE_ADMIN = 'admin' +ROLE_IT_STAFF = 'it_staff' +ROLE_STAFF = 'staff' + +ROLE_GROUP_NAMES = { + ROLE_SUPER_ADMIN: 'Super Admin', + ROLE_ADMIN: 'Admin', + ROLE_IT_STAFF: 'IT Staff', + ROLE_STAFF: 'Staff', +} + +ROLE_LABELS = { + ROLE_SUPER_ADMIN: _('Super Admin'), + ROLE_ADMIN: _('Admin'), + ROLE_IT_STAFF: _('IT Staff'), + ROLE_STAFF: _('Mitarbeiter'), +} + +CAPABILITIES = { + 'manage_users': {ROLE_SUPER_ADMIN}, + 'access_requests_dashboard': {ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF}, + 'run_intro_session': {ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF}, + 'generate_intro_pdfs': {ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF}, + 'retry_requests': {ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF}, + 'delete_requests': {ROLE_SUPER_ADMIN, ROLE_ADMIN}, + 'manage_integrations': {ROLE_SUPER_ADMIN, ROLE_ADMIN}, + 'manage_welcome_emails': {ROLE_SUPER_ADMIN, ROLE_ADMIN}, + 'manage_builders': {ROLE_SUPER_ADMIN, ROLE_ADMIN}, + 'view_audit_log': {ROLE_SUPER_ADMIN, ROLE_ADMIN}, + 'manage_backups': {ROLE_SUPER_ADMIN, ROLE_ADMIN}, + 'view_docs': {ROLE_SUPER_ADMIN, ROLE_ADMIN}, + 'access_django_admin_link': {ROLE_SUPER_ADMIN}, +} + + +def ensure_role_groups() -> None: + for name in ROLE_GROUP_NAMES.values(): + Group.objects.get_or_create(name=name) + + +def assign_user_role(user, role_key: str) -> None: + ensure_role_groups() + if role_key not in ROLE_GROUP_NAMES: + raise ValueError(f'Unknown role: {role_key}') + + role_groups = Group.objects.filter(name__in=ROLE_GROUP_NAMES.values()) + user.groups.remove(*role_groups) + user.groups.add(Group.objects.get(name=ROLE_GROUP_NAMES[role_key])) + + is_super_admin = role_key == ROLE_SUPER_ADMIN + user.is_staff = is_super_admin + user.is_superuser = is_super_admin + user.save(update_fields=['is_staff', 'is_superuser']) + + +def ensure_bootstrap_role_assignments() -> None: + user_model = get_user_model() + bootstrap_roles = { + 'admin_test': ROLE_SUPER_ADMIN, + 'user_test': ROLE_STAFF, + } + role_group_names = set(ROLE_GROUP_NAMES.values()) + for username, role_key in bootstrap_roles.items(): + try: + user = user_model.objects.get(username=username) + except user_model.DoesNotExist: + continue + if user.groups.filter(name__in=role_group_names).exists(): + continue + assign_user_role(user, role_key) + + +def get_user_role_key(user) -> str: + if not getattr(user, 'is_authenticated', False): + return ROLE_STAFF + if getattr(user, 'is_superuser', False): + return ROLE_SUPER_ADMIN + + group_names = set(user.groups.values_list('name', flat=True)) + for role_key in (ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF, ROLE_STAFF): + if ROLE_GROUP_NAMES[role_key] in group_names: + return role_key + + if getattr(user, 'is_staff', False): + return ROLE_ADMIN + return ROLE_STAFF + + +def get_user_role_label(user) -> str: + return str(ROLE_LABELS[get_user_role_key(user)]) + + +def user_has_capability(user, capability: str) -> bool: + if not getattr(user, 'is_authenticated', False): + return False + if getattr(user, 'is_superuser', False): + return True + allowed_roles = CAPABILITIES.get(capability, set()) + return get_user_role_key(user) in allowed_roles + + +def template_role_context(user) -> dict[str, object]: + role_key = get_user_role_key(user) + return { + 'role_key': role_key, + 'role_label': str(ROLE_LABELS[role_key]), + 'can_manage_users': user_has_capability(user, 'manage_users'), + 'can_access_requests_dashboard': user_has_capability(user, 'access_requests_dashboard'), + 'can_run_intro_session': user_has_capability(user, 'run_intro_session'), + 'can_generate_intro_pdfs': user_has_capability(user, 'generate_intro_pdfs'), + 'can_retry_requests': user_has_capability(user, 'retry_requests'), + 'can_delete_requests': user_has_capability(user, 'delete_requests'), + 'can_manage_integrations': user_has_capability(user, 'manage_integrations'), + 'can_manage_welcome_emails': user_has_capability(user, 'manage_welcome_emails'), + 'can_manage_builders': user_has_capability(user, 'manage_builders'), + 'can_view_audit_log': user_has_capability(user, 'view_audit_log'), + 'can_manage_backups': user_has_capability(user, 'manage_backups'), + 'can_view_docs': user_has_capability(user, 'view_docs'), + 'can_access_django_admin_link': user_has_capability(user, 'access_django_admin_link'), + } diff --git a/backend/workflows/signals.py b/backend/workflows/signals.py new file mode 100644 index 0000000..d761dbb --- /dev/null +++ b/backend/workflows/signals.py @@ -0,0 +1,12 @@ +from django.db.models.signals import post_migrate +from django.dispatch import receiver + +from .roles import ensure_bootstrap_role_assignments, ensure_role_groups + + +@receiver(post_migrate) +def workflows_post_migrate(sender, **kwargs): + if getattr(sender, 'name', '') != 'workflows': + return + ensure_role_groups() + ensure_bootstrap_role_assignments() diff --git a/backend/workflows/static/workflows/css/admin_tools.css b/backend/workflows/static/workflows/css/admin_tools.css index 99d5c76..014198d 100644 --- a/backend/workflows/static/workflows/css/admin_tools.css +++ b/backend/workflows/static/workflows/css/admin_tools.css @@ -1,5 +1,6 @@ body { margin: 0; font-family: Arial, sans-serif; background: #f4f8ff; color: #0f172a; padding: 20px; } [hidden] { display: none !important; } +.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } .shell { max-width: 1100px; margin: 0 auto; background: #fff; border: 1px solid #d8e3f0; border-radius: 14px; padding: 16px; } h1 { margin: 12px 0 6px; color: #000078; } .sub { margin: 0 0 12px; color: #54657c; } diff --git a/backend/workflows/templates/workflows/developer_handbook.html b/backend/workflows/templates/workflows/developer_handbook.html index 0f13616..0e94ede 100644 --- a/backend/workflows/templates/workflows/developer_handbook.html +++ b/backend/workflows/templates/workflows/developer_handbook.html @@ -52,6 +52,7 @@
  • /backend/config/: Django settings, WSGI, URL config
  • /backend/workflows/: application logic, views, models, tasks, templates, static assets
  • /backend/workflows/templates/workflows/base_shell.html: standard page shell for new staff-facing pages
  • +
  • /backend/workflows/roles.py: centralized role names, capability matrix, and template permission helpers
  • Rule: all interactive app pages should extend base_shell.html; do not rebuild topbar/frame logic in page-local templates.
  • /backend/media/templates/: PDF HTML templates and letterhead source files
  • /backend/media/pdfs/: generated PDF outputs on host volume
  • @@ -99,6 +100,19 @@ docker compose exec -T web python manage.py check
  • Fresh boot sequence runs migrations automatically in entrypoint-web.sh.
  • +

    Role and Permission Model

    + +

    6) Translation Workflow

    Standard Django i18n path

    make i18n-update-en
    diff --git a/backend/workflows/templates/workflows/home.html b/backend/workflows/templates/workflows/home.html
    index dd98d39..568ed9a 100644
    --- a/backend/workflows/templates/workflows/home.html
    +++ b/backend/workflows/templates/workflows/home.html
    @@ -35,7 +35,7 @@
     

    {% trans "TUBCO Onboarding & Offboarding Portal" %}

    {% trans "Zentrale Arbeitsfläche für Anfragen, PDF-Generierung, E-Mail-Workflows und Ablage in Nextcloud." %}

    - {% trans "Rolle:" %} {% if request.user.is_staff %}{% trans "Admin" %}{% else %}{% trans "Mitarbeiter" %}{% endif %} + {% trans "Rolle:" %} {{ role_label }} {% trans "Nextcloud:" %} {% if nextcloud_enabled %}{% trans "aktiv" %}{% else %}{% trans "inaktiv" %}{% endif %} @@ -88,6 +88,7 @@
    + {% if can_access_requests_dashboard %}
    APP
    @@ -103,34 +104,51 @@ {% trans "Dashboard öffnen" %}
    + {% endif %} - {% if request.user.is_staff %} + {% if can_manage_users or can_manage_integrations or can_view_audit_log or can_manage_backups or can_manage_welcome_emails or can_manage_builders or can_view_docs or can_access_django_admin_link %}

    {% trans "Admin Apps" %}

    {% trans "Konfiguration, Tests und Steuerung." %}

    + {% if can_manage_integrations %}

    {% trans "Integrationen" %}

    {% trans "Nextcloud- und E-Mail-Setup." %}

    {% trans "Öffnen" %}
    + {% endif %} + {% if can_manage_users %} +
    +

    {% trans "Benutzer & Rollen" %}

    +

    {% trans "Benutzer anlegen, Rollen zuweisen und Zugriffe steuern." %}

    +{% trans "Öffnen" %} +
    + {% endif %} + {% if can_view_audit_log %}

    {% trans "Audit Log" %}

    {% trans "Wichtige Admin-Aktionen nachvollziehen und prüfen." %}

    {% trans "Öffnen" %}
    + {% endif %} + {% if can_manage_backups %}

    {% trans "Backup & Recovery" %}

    {% trans "Backups erstellen und sicher verifizieren." %}

    {% trans "Öffnen" %}
    + {% endif %} + {% if can_manage_welcome_emails %}

    {% trans "Welcome E-Mails" %}

    {% trans "Geplante Welcome Mails verwalten." %}

    {% trans "Öffnen" %}
    + {% endif %} + {% if can_manage_builders %}

    {% trans "Form Builder" %}

    {% trans "Felder, Schritte und Optionen verwalten." %}

    @@ -141,16 +159,21 @@

    {% trans "Checklistenpunkte für das Einweisungsprotokoll konfigurieren." %}

    {% trans "Öffnen" %}
    + {% endif %} + {% if can_view_docs %}

    {% trans "Handbook" %}

    {% trans "Project wiki and developer documentation in one place." %}

    {% trans "Öffnen" %}
    + {% endif %} + {% if can_access_django_admin_link %}

    {% trans "Django Admin" %}

    {% trans "Vollständige Datenverwaltung." %}

    {% trans "Öffnen" %}
    + {% endif %}
    {% endif %} diff --git a/backend/workflows/templates/workflows/project_wiki.html b/backend/workflows/templates/workflows/project_wiki.html index 6478a96..add71c6 100644 --- a/backend/workflows/templates/workflows/project_wiki.html +++ b/backend/workflows/templates/workflows/project_wiki.html @@ -172,9 +172,13 @@

    9) Admin Apps (Home)