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 helpersbase_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 volumeentrypoint-web.sh.Super Admin, Admin, IT Staff, Staff.post_migrate hook in workflows.signals.workflows.roles.CAPABILITIES._require_capability(...) in views instead of flat is_staff checks.workflows.context_processors.role_context./admin-tools/users/ and is the preferred path for normal role assignment, account activation, password-reset mail dispatch, and controlled user deletion.is_staff=True but no explicit role group currently fall back to the Admin capability set.superuser accounts resolve to Super Admin.roles.py, gate the view, and hide the UI affordance when the capability is absent.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)
+ - Rollenmodell: the app now uses four named roles:
Super Admin, Admin, IT Staff, and Staff.
+ - Zugriffslogik: page visibility and critical actions are controlled by capability checks, not only by
is_staff.
+ - Fallback-Verhalten: legacy staff users without an explicit role group currently fall back to
Admin access so existing operations do not break during rollout.
- Form Builder: manage field visibility/order/options.
- Einweisungs-Builder: manage custom checklist items for the intro PDF and live introduction checklist, including section, visibility, and conditional display logic.
- Integrations: Nextcloud, SMTP, default routing addresses, notification rules, workflow rules, and remote backup target settings.
+ - Benutzer & Rollen: super-admin-only page for creating users, assigning roles, activating/deactivating access, sending password-reset links, and deleting accounts when appropriate.
- Welcome Emails: scheduled jobs, pause/resume/cancel/trigger now.
- Audit Log: staff-only trace of important admin changes such as builder edits, settings updates, PDF generation, welcome-email operations, and request deletions. Supports filtering by action, user, and date range.
- Requests Dashboard: search records, open PDFs, delete records (single/bulk for staff).
@@ -223,6 +227,7 @@
11) Security & Reliability Hardening (Current)
+ - Rollen & Berechtigungen: operational areas such as Integrationen, Builders, Audit Log, Backup & Recovery, request delete/retry, and intro-session actions are now protected by capability-based role checks.
- Cookie + header hardening: HTTPOnly cookies, SameSite cookies,
X-Content-Type-Options: nosniff, stricter referrer policy, and frame protection.
- Optional secure-cookie mode: can be enabled via environment for HTTPS deployments.
- Upload guards: server-side upload size limits plus signature image magic-byte validation for PNG/JPEG.
diff --git a/backend/workflows/templates/workflows/requests_dashboard.html b/backend/workflows/templates/workflows/requests_dashboard.html
index 20e6536..ec06e1c 100644
--- a/backend/workflows/templates/workflows/requests_dashboard.html
+++ b/backend/workflows/templates/workflows/requests_dashboard.html
@@ -167,7 +167,7 @@
- {% if request.user.is_staff %}
+ {% if can_delete_requests %}