Add Ui fix. Add Links in Notifications
This commit is contained in:
parent
77f08ed440
commit
c25402bc98
13 changed files with 315 additions and 222 deletions
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
|
@ -2,7 +2,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: wira-risk-management\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-09-15 11:24+0200\n"
|
||||
"POT-Creation-Date: 2025-09-16 14:08+0200\n"
|
||||
"PO-Revision-Date: 2025-09-09 13:45+0200\n"
|
||||
"Last-Translator: Kevin Heyer <kevin@example.com>\n"
|
||||
"Language-Team: German\n"
|
||||
|
@ -25,7 +25,7 @@ msgstr "Admin"
|
|||
msgid "Risks"
|
||||
msgstr "Risiken"
|
||||
|
||||
#: risks/admin.py:34 risks/models.py:184 templates/base.html:54
|
||||
#: risks/admin.py:34 risks/models.py:191 templates/base.html:54
|
||||
#: templates/risks/item_control.html:5 templates/risks/list_controls.html:5
|
||||
msgid "Controls"
|
||||
msgstr "Maßnahmen"
|
||||
|
@ -38,7 +38,7 @@ msgstr "Restrisiken"
|
|||
msgid "Reviews"
|
||||
msgstr "Prüfung"
|
||||
|
||||
#: risks/admin.py:37 risks/models.py:255 templates/base.html:55
|
||||
#: risks/admin.py:37 risks/models.py:262 templates/base.html:55
|
||||
#: templates/risks/item_incident.html:5 templates/risks/item_risk.html:14
|
||||
#: templates/risks/list_incidents.html:5 templates/risks/list_incidents.html:18
|
||||
msgid "Incidents"
|
||||
|
@ -48,7 +48,7 @@ msgstr "Vorfälle"
|
|||
msgid "Users"
|
||||
msgstr "Benutzer"
|
||||
|
||||
#: risks/admin.py:166 risks/models.py:362 templates/risks/item_control.html:96
|
||||
#: risks/admin.py:166 risks/models.py:370 templates/risks/item_control.html:96
|
||||
#: templates/risks/item_incident.html:88 templates/risks/item_risk.html:245
|
||||
msgid "User"
|
||||
msgstr "Benutzer"
|
||||
|
@ -137,11 +137,11 @@ msgstr "Risiko"
|
|||
msgid "Open"
|
||||
msgstr "Offen"
|
||||
|
||||
#: risks/models.py:54 risks/models.py:259
|
||||
#: risks/models.py:54 risks/models.py:266
|
||||
msgid "In Progress"
|
||||
msgstr "In Bearbeitung"
|
||||
|
||||
#: risks/models.py:55 risks/models.py:260
|
||||
#: risks/models.py:55 risks/models.py:267
|
||||
msgid "Closed"
|
||||
msgstr "Geschlossen"
|
||||
|
||||
|
@ -182,26 +182,26 @@ msgid "Critical (> 100,000 € – existential threat)"
|
|||
msgstr "Kritisch (> 100.000 € – existenzielle Bedrohung)"
|
||||
|
||||
#: risks/models.py:72 templates/risks/dashboard.html:57
|
||||
#: templates/risks/list_risks.html:112
|
||||
#: templates/risks/list_risks.html:112 templates/risks/list_risks.html:143
|
||||
msgid "Confidentiality"
|
||||
msgstr "Vertraulichkeit"
|
||||
|
||||
#: risks/models.py:73 templates/risks/dashboard.html:63
|
||||
#: templates/risks/list_risks.html:113
|
||||
#: templates/risks/list_risks.html:113 templates/risks/list_risks.html:150
|
||||
msgid "Integrity"
|
||||
msgstr "Integrität"
|
||||
|
||||
#: risks/models.py:74 templates/risks/dashboard.html:69
|
||||
#: templates/risks/list_risks.html:114
|
||||
#: templates/risks/list_risks.html:114 templates/risks/list_risks.html:157
|
||||
msgid "Availability"
|
||||
msgstr "Verfügbarkeit"
|
||||
|
||||
#: risks/models.py:78 risks/models.py:194 risks/models.py:263
|
||||
#: risks/models.py:78 risks/models.py:201 risks/models.py:270
|
||||
#: templates/risks/item_risk.html:188
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: risks/models.py:79 risks/models.py:264 templates/risks/item_control.html:52
|
||||
#: risks/models.py:79 risks/models.py:271 templates/risks/item_control.html:52
|
||||
#: templates/risks/item_incident.html:44
|
||||
msgid "Description"
|
||||
msgstr "Beschreibung"
|
||||
|
@ -238,176 +238,176 @@ msgstr "Aktualisiert am"
|
|||
msgid "Effects"
|
||||
msgstr "Auswirkungen"
|
||||
|
||||
#: risks/models.py:140
|
||||
#: risks/models.py:147
|
||||
msgid "Residual Risk"
|
||||
msgstr "Restrisiko"
|
||||
|
||||
#: risks/models.py:141
|
||||
#: risks/models.py:148
|
||||
msgid "Residual Risks"
|
||||
msgstr "Restrisiken"
|
||||
|
||||
#: risks/models.py:183 templates/risks/item_control.html:33
|
||||
#: risks/models.py:190 templates/risks/item_control.html:33
|
||||
#: templates/risks/list_controls.html:18 templates/risks/list_controls.html:97
|
||||
msgid "Control"
|
||||
msgstr "Maßnahme"
|
||||
|
||||
#: risks/models.py:187
|
||||
#: risks/models.py:194
|
||||
msgid "Planned"
|
||||
msgstr "Geplant"
|
||||
|
||||
#: risks/models.py:188
|
||||
#: risks/models.py:195
|
||||
msgid "In progress"
|
||||
msgstr "In Bearbeitung"
|
||||
|
||||
#: risks/models.py:189
|
||||
#: risks/models.py:196
|
||||
msgid "Completed"
|
||||
msgstr "Abgeschlossen"
|
||||
|
||||
#: risks/models.py:190
|
||||
#: risks/models.py:197
|
||||
msgid "Verified"
|
||||
msgstr "Verifiziert"
|
||||
|
||||
#: risks/models.py:191
|
||||
#: risks/models.py:198
|
||||
msgid "Rejected"
|
||||
msgstr "Abgelehnt"
|
||||
|
||||
#: risks/models.py:222
|
||||
#: risks/models.py:229
|
||||
msgid "Auditlog"
|
||||
msgstr "Audit-Log"
|
||||
|
||||
#: risks/models.py:223
|
||||
#: risks/models.py:230
|
||||
msgid "Auditlogs"
|
||||
msgstr "Audit-Logs"
|
||||
|
||||
#: risks/models.py:254 templates/risks/item_incident.html:33
|
||||
#: risks/models.py:261 templates/risks/item_incident.html:33
|
||||
#: templates/risks/item_risk.html:218 templates/risks/list_incidents.html:97
|
||||
msgid "Incident"
|
||||
msgstr "Vorfall"
|
||||
|
||||
#: risks/models.py:258
|
||||
#: risks/models.py:265
|
||||
msgid "Opened"
|
||||
msgstr "Eröffnet"
|
||||
|
||||
#: risks/models.py:265
|
||||
#: risks/models.py:272
|
||||
msgid "Date reported"
|
||||
msgstr "Meldedatum"
|
||||
|
||||
#: risks/models.py:268 templates/risks/item_incident.html:34
|
||||
#: risks/models.py:275 templates/risks/item_incident.html:34
|
||||
#: templates/risks/list_incidents.html:63
|
||||
#: templates/risks/list_incidents.html:101
|
||||
msgid "Reported by"
|
||||
msgstr "Gemeldet von"
|
||||
|
||||
#: risks/models.py:284
|
||||
#: risks/models.py:291
|
||||
msgid "Risk created"
|
||||
msgstr "Risiko erstellt"
|
||||
|
||||
#: risks/models.py:285
|
||||
#: risks/models.py:292
|
||||
msgid "Risk updated"
|
||||
msgstr "Risiko Aktualisiert"
|
||||
|
||||
#: risks/models.py:286
|
||||
#: risks/models.py:293
|
||||
msgid "Risk deleted"
|
||||
msgstr "Risiko gelöscht"
|
||||
|
||||
#: risks/models.py:287
|
||||
#: risks/models.py:294
|
||||
msgid "Risk review required"
|
||||
msgstr "Risikoprüfung nötig"
|
||||
|
||||
#: risks/models.py:288
|
||||
#: risks/models.py:295
|
||||
msgid "Risk review completed"
|
||||
msgstr "Risikoprüfung Abgeschlossen"
|
||||
|
||||
#: risks/models.py:290
|
||||
#: risks/models.py:297
|
||||
msgid "Control created"
|
||||
msgstr "Maßnahme erstellt"
|
||||
|
||||
#: risks/models.py:291
|
||||
#: risks/models.py:298
|
||||
msgid "Control updated"
|
||||
msgstr "Maßnahme Aktualisiert"
|
||||
|
||||
#: risks/models.py:292
|
||||
#: risks/models.py:299
|
||||
msgid "Control deleted"
|
||||
msgstr "Maßnahme '{title}' gelöscht"
|
||||
|
||||
#: risks/models.py:294
|
||||
#: risks/models.py:301
|
||||
msgid "Residual created"
|
||||
msgstr "Restrisiko erstellt"
|
||||
|
||||
#: risks/models.py:295
|
||||
#: risks/models.py:302
|
||||
msgid "Residual updated"
|
||||
msgstr "Restrisiko Aktualisiert"
|
||||
|
||||
#: risks/models.py:296
|
||||
#: risks/models.py:303
|
||||
msgid "Residual deleted"
|
||||
msgstr "Restrisiko gelöscht"
|
||||
|
||||
#: risks/models.py:297
|
||||
#: risks/models.py:304
|
||||
msgid "Residual review required"
|
||||
msgstr "Restrisikoprüfung nötig"
|
||||
|
||||
#: risks/models.py:298
|
||||
#: risks/models.py:305
|
||||
msgid "Residual review completed"
|
||||
msgstr "Restrisiko geprüft"
|
||||
|
||||
#: risks/models.py:300
|
||||
#: risks/models.py:307
|
||||
msgid "Incident created"
|
||||
msgstr "Vorfall erstellt"
|
||||
|
||||
#: risks/models.py:301
|
||||
#: risks/models.py:308
|
||||
msgid "Incident updated"
|
||||
msgstr "Vorfall Aktualisiert"
|
||||
|
||||
#: risks/models.py:302
|
||||
#: risks/models.py:309
|
||||
msgid "Incident deleted"
|
||||
msgstr "Vorfall gelöscht"
|
||||
|
||||
#: risks/models.py:304
|
||||
#: risks/models.py:311
|
||||
msgid "User created"
|
||||
msgstr "Benutzer erstellt"
|
||||
|
||||
#: risks/models.py:305
|
||||
#: risks/models.py:312
|
||||
msgid "User deleted"
|
||||
msgstr "Benutzer gelöscht"
|
||||
|
||||
#: risks/models.py:314 risks/utils.py:142
|
||||
#: risks/models.py:321 risks/utils.py:79
|
||||
msgid "Notification"
|
||||
msgstr "Nachricht"
|
||||
|
||||
#: risks/models.py:315 templates/base.html:96
|
||||
#: risks/models.py:322 templates/base.html:96
|
||||
#: templates/risks/notifications.html:4
|
||||
msgid "Notifications"
|
||||
msgstr "Nachrichten"
|
||||
|
||||
#: risks/models.py:411
|
||||
#: risks/models.py:419
|
||||
msgid "Notification rule"
|
||||
msgstr "Benachrichtigungsregel"
|
||||
|
||||
#: risks/models.py:412
|
||||
#: risks/models.py:420
|
||||
msgid "Notification rules"
|
||||
msgstr "Benachrichtigungsregeln"
|
||||
|
||||
#: risks/models.py:415
|
||||
#: risks/models.py:423
|
||||
msgid "Event"
|
||||
msgstr "Aktion"
|
||||
|
||||
#: risks/models.py:420
|
||||
#: risks/models.py:428
|
||||
msgid "Show in app"
|
||||
msgstr "Zeige in der WebApp"
|
||||
|
||||
#: risks/models.py:421
|
||||
#: risks/models.py:429
|
||||
msgid "Send via email"
|
||||
msgstr "Sende via E-Mail"
|
||||
|
||||
#: risks/models.py:425
|
||||
#: risks/models.py:433
|
||||
msgid "Send to owner/responsible/reporter (if available)"
|
||||
msgstr "Sende an Risikoeigner/Verantwortliche/Melder (Wenn vorhanden)"
|
||||
|
||||
#: risks/models.py:428
|
||||
#: risks/models.py:436
|
||||
msgid "Send to all staff"
|
||||
msgstr "Sende an alle App-Mitarbeiter"
|
||||
|
||||
#: risks/models.py:430
|
||||
#: risks/models.py:438
|
||||
msgid "Extra recipients (emails, comma or newline separated)"
|
||||
msgstr "Zusätzliche Empfänger (E-Mails, durch Komma oder Zeilenumbruch getrennt)"
|
||||
|
||||
|
@ -426,108 +426,108 @@ msgstr "Benutzer '{u}' löschte"
|
|||
msgid "Risk created: {t}"
|
||||
msgstr "Risiko erstellt: {t}"
|
||||
|
||||
#: risks/signals.py:121
|
||||
#: risks/signals.py:122
|
||||
#, python-brace-format
|
||||
msgid "Risk updated: {t}"
|
||||
msgstr "Risiko Aktualisiert: {t}"
|
||||
|
||||
#: risks/signals.py:134
|
||||
#: risks/signals.py:136
|
||||
#, python-brace-format
|
||||
msgid "Risk deleted: {t}"
|
||||
msgstr "Risiko gelöscht: {t}"
|
||||
|
||||
#: risks/signals.py:181
|
||||
#: risks/signals.py:184
|
||||
#, python-brace-format
|
||||
msgid "Control {e}: {t}"
|
||||
msgstr "Maßnahme {e}: {t}"
|
||||
|
||||
#: risks/signals.py:182 risks/signals.py:317
|
||||
#: risks/signals.py:185 risks/signals.py:326
|
||||
msgid "created"
|
||||
msgstr "erstellt"
|
||||
|
||||
#: risks/signals.py:182 risks/signals.py:317
|
||||
#: risks/signals.py:185 risks/signals.py:326
|
||||
msgid "updated"
|
||||
msgstr "Aktualisiert"
|
||||
|
||||
#: risks/signals.py:196
|
||||
#: risks/signals.py:200
|
||||
#, python-brace-format
|
||||
msgid "Control deleted: {t}"
|
||||
msgstr "Maßnahme gelöscht: {t}"
|
||||
|
||||
#: risks/signals.py:213
|
||||
#: risks/signals.py:218
|
||||
#, python-brace-format
|
||||
msgid "Residual review required for risk '{t}' due to control change"
|
||||
msgstr "Restrisikoprüfung nötig für das Risiko: '{t}', da Maßnahmen geändert wurden"
|
||||
|
||||
#: risks/signals.py:239
|
||||
#: risks/signals.py:245
|
||||
#, python-brace-format
|
||||
msgid "Residual created for risk: {t}"
|
||||
msgstr "Restrisiko erstellt für das Risiko: {t}"
|
||||
|
||||
#: risks/signals.py:257
|
||||
#: risks/signals.py:264
|
||||
#, python-brace-format
|
||||
msgid "Residual review required for risk: {t}"
|
||||
msgstr "Restrisikoprüfung benötigt für Risiko {t}"
|
||||
|
||||
#: risks/signals.py:260
|
||||
#: risks/signals.py:267
|
||||
#, python-brace-format
|
||||
msgid "Residual review completed for risk: {t}"
|
||||
msgstr "Restrisikoprüfung Abgeschlossen für Risiko {t}"
|
||||
|
||||
#: risks/signals.py:263
|
||||
#: risks/signals.py:270
|
||||
#, python-brace-format
|
||||
msgid "Residual updated for risk: {t}"
|
||||
msgstr "Restrisiko Aktualisiert für das Risiko: {t}"
|
||||
|
||||
#: risks/signals.py:280
|
||||
#: risks/signals.py:288
|
||||
#, python-brace-format
|
||||
msgid "Residual deleted for risk: {t}"
|
||||
msgstr "Restrisiko gelöscht für das Risiko: {t}"
|
||||
|
||||
#: risks/signals.py:316
|
||||
#: risks/signals.py:325
|
||||
#, python-brace-format
|
||||
msgid "Incident {e}: {t}"
|
||||
msgstr "Vorfall {e}: {t}"
|
||||
|
||||
#: risks/signals.py:331
|
||||
#: risks/signals.py:341
|
||||
#, python-brace-format
|
||||
msgid "Incident deleted: {t}"
|
||||
msgstr "Vorfall gelöscht: {t}"
|
||||
|
||||
#: risks/utils.py:66
|
||||
#: risks/utils.py:135
|
||||
#, python-brace-format
|
||||
msgid "Follow-up reached: review required for risk '{t}'"
|
||||
msgstr "Wiedervorlagedatum erreicht: Prüfung nötig für Risiko '{t}'"
|
||||
|
||||
#: risks/views.py:208
|
||||
#: risks/views.py:218
|
||||
msgid "Risk has been marked as reviewed and closed."
|
||||
msgstr "Das Risiko wurde geprüft und als geschlossen markiert"
|
||||
|
||||
#: risks/views.py:210
|
||||
#: risks/views.py:220
|
||||
msgid "Not all controls are completed. Risk cannot be closed yet."
|
||||
msgstr "Nicht alle Maßnhamen sind abgeschlossen, das Risiko kann nicht geschlossen werden."
|
||||
|
||||
#: risks/views.py:368
|
||||
#: risks/views.py:378
|
||||
msgid "Notification marked as read."
|
||||
msgstr "Nachricht als gelesen markiert"
|
||||
|
||||
#: risks/views.py:378
|
||||
#: risks/views.py:388
|
||||
msgid "All notifications marked as read."
|
||||
msgstr "Alle Benachrichtigungen wurden als gelesen Markiert"
|
||||
|
||||
#: risks/views.py:397
|
||||
#: risks/views.py:407
|
||||
msgid "Risk status updated."
|
||||
msgstr "Risikostatus Aktualisiert"
|
||||
|
||||
#: risks/views.py:413
|
||||
#: risks/views.py:423
|
||||
msgid "Control status updated."
|
||||
msgstr "Maßnahmenstatus Aktualisiert"
|
||||
|
||||
#: risks/views.py:429
|
||||
#: risks/views.py:439
|
||||
msgid "Incident status updated."
|
||||
msgstr "Vorfallstatus Aktualisiert"
|
||||
|
||||
#: risks/views.py:446
|
||||
#: risks/views.py:456
|
||||
msgid "Residual review flag updated."
|
||||
msgstr "Restrisiko geprüft"
|
||||
|
||||
|
@ -790,11 +790,11 @@ msgstr "Nr."
|
|||
msgid "Related Risk"
|
||||
msgstr "Zugeordnete Risiken"
|
||||
|
||||
#: templates/risks/list_controls.html:144
|
||||
#: templates/risks/list_controls.html:164
|
||||
msgid "No controls found."
|
||||
msgstr "Keine Maßnahmen vorhanden"
|
||||
|
||||
#: templates/risks/list_incidents.html:125
|
||||
#: templates/risks/list_incidents.html:137
|
||||
msgid "No incidents found."
|
||||
msgstr "Keine Vorfälle gefunden."
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-09-15 11:24+0200\n"
|
||||
"POT-Creation-Date: 2025-09-16 14:08+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -31,7 +31,7 @@ msgstr ""
|
|||
msgid "Risks"
|
||||
msgstr ""
|
||||
|
||||
#: risks/admin.py:34 risks/models.py:184 templates/base.html:54
|
||||
#: risks/admin.py:34 risks/models.py:191 templates/base.html:54
|
||||
#: templates/risks/item_control.html:5 templates/risks/list_controls.html:5
|
||||
msgid "Controls"
|
||||
msgstr ""
|
||||
|
@ -44,7 +44,7 @@ msgstr ""
|
|||
msgid "Reviews"
|
||||
msgstr ""
|
||||
|
||||
#: risks/admin.py:37 risks/models.py:255 templates/base.html:55
|
||||
#: risks/admin.py:37 risks/models.py:262 templates/base.html:55
|
||||
#: templates/risks/item_incident.html:5 templates/risks/item_risk.html:14
|
||||
#: templates/risks/list_incidents.html:5 templates/risks/list_incidents.html:18
|
||||
msgid "Incidents"
|
||||
|
@ -54,7 +54,7 @@ msgstr ""
|
|||
msgid "Users"
|
||||
msgstr ""
|
||||
|
||||
#: risks/admin.py:166 risks/models.py:362 templates/risks/item_control.html:96
|
||||
#: risks/admin.py:166 risks/models.py:370 templates/risks/item_control.html:96
|
||||
#: templates/risks/item_incident.html:88 templates/risks/item_risk.html:245
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
@ -143,11 +143,11 @@ msgstr ""
|
|||
msgid "Open"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:54 risks/models.py:259
|
||||
#: risks/models.py:54 risks/models.py:266
|
||||
msgid "In Progress"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:55 risks/models.py:260
|
||||
#: risks/models.py:55 risks/models.py:267
|
||||
msgid "Closed"
|
||||
msgstr ""
|
||||
|
||||
|
@ -188,26 +188,26 @@ msgid "Critical (> 100,000 € – existential threat)"
|
|||
msgstr ""
|
||||
|
||||
#: risks/models.py:72 templates/risks/dashboard.html:57
|
||||
#: templates/risks/list_risks.html:112
|
||||
#: templates/risks/list_risks.html:112 templates/risks/list_risks.html:143
|
||||
msgid "Confidentiality"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:73 templates/risks/dashboard.html:63
|
||||
#: templates/risks/list_risks.html:113
|
||||
#: templates/risks/list_risks.html:113 templates/risks/list_risks.html:150
|
||||
msgid "Integrity"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:74 templates/risks/dashboard.html:69
|
||||
#: templates/risks/list_risks.html:114
|
||||
#: templates/risks/list_risks.html:114 templates/risks/list_risks.html:157
|
||||
msgid "Availability"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:78 risks/models.py:194 risks/models.py:263
|
||||
#: risks/models.py:78 risks/models.py:201 risks/models.py:270
|
||||
#: templates/risks/item_risk.html:188
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:79 risks/models.py:264 templates/risks/item_control.html:52
|
||||
#: risks/models.py:79 risks/models.py:271 templates/risks/item_control.html:52
|
||||
#: templates/risks/item_incident.html:44
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
@ -244,176 +244,176 @@ msgstr ""
|
|||
msgid "Effects"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:140
|
||||
#: risks/models.py:147
|
||||
msgid "Residual Risk"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:141
|
||||
#: risks/models.py:148
|
||||
msgid "Residual Risks"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:183 templates/risks/item_control.html:33
|
||||
#: risks/models.py:190 templates/risks/item_control.html:33
|
||||
#: templates/risks/list_controls.html:18 templates/risks/list_controls.html:97
|
||||
msgid "Control"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:187
|
||||
#: risks/models.py:194
|
||||
msgid "Planned"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:188
|
||||
#: risks/models.py:195
|
||||
msgid "In progress"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:189
|
||||
#: risks/models.py:196
|
||||
msgid "Completed"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:190
|
||||
#: risks/models.py:197
|
||||
msgid "Verified"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:191
|
||||
#: risks/models.py:198
|
||||
msgid "Rejected"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:222
|
||||
#: risks/models.py:229
|
||||
msgid "Auditlog"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:223
|
||||
#: risks/models.py:230
|
||||
msgid "Auditlogs"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:254 templates/risks/item_incident.html:33
|
||||
#: risks/models.py:261 templates/risks/item_incident.html:33
|
||||
#: templates/risks/item_risk.html:218 templates/risks/list_incidents.html:97
|
||||
msgid "Incident"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:258
|
||||
#: risks/models.py:265
|
||||
msgid "Opened"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:265
|
||||
#: risks/models.py:272
|
||||
msgid "Date reported"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:268 templates/risks/item_incident.html:34
|
||||
#: risks/models.py:275 templates/risks/item_incident.html:34
|
||||
#: templates/risks/list_incidents.html:63
|
||||
#: templates/risks/list_incidents.html:101
|
||||
msgid "Reported by"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:284
|
||||
#: risks/models.py:291
|
||||
msgid "Risk created"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:285
|
||||
#: risks/models.py:292
|
||||
msgid "Risk updated"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:286
|
||||
#: risks/models.py:293
|
||||
msgid "Risk deleted"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:287
|
||||
#: risks/models.py:294
|
||||
msgid "Risk review required"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:288
|
||||
#: risks/models.py:295
|
||||
msgid "Risk review completed"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:290
|
||||
#: risks/models.py:297
|
||||
msgid "Control created"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:291
|
||||
#: risks/models.py:298
|
||||
msgid "Control updated"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:292
|
||||
#: risks/models.py:299
|
||||
msgid "Control deleted"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:294
|
||||
#: risks/models.py:301
|
||||
msgid "Residual created"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:295
|
||||
#: risks/models.py:302
|
||||
msgid "Residual updated"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:296
|
||||
#: risks/models.py:303
|
||||
msgid "Residual deleted"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:297
|
||||
#: risks/models.py:304
|
||||
msgid "Residual review required"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:298
|
||||
#: risks/models.py:305
|
||||
msgid "Residual review completed"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:300
|
||||
#: risks/models.py:307
|
||||
msgid "Incident created"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:301
|
||||
#: risks/models.py:308
|
||||
msgid "Incident updated"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:302
|
||||
#: risks/models.py:309
|
||||
msgid "Incident deleted"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:304
|
||||
#: risks/models.py:311
|
||||
msgid "User created"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:305
|
||||
#: risks/models.py:312
|
||||
msgid "User deleted"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:314 risks/utils.py:142
|
||||
#: risks/models.py:321 risks/utils.py:79
|
||||
msgid "Notification"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:315 templates/base.html:96
|
||||
#: risks/models.py:322 templates/base.html:96
|
||||
#: templates/risks/notifications.html:4
|
||||
msgid "Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:411
|
||||
#: risks/models.py:419
|
||||
msgid "Notification rule"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:412
|
||||
#: risks/models.py:420
|
||||
msgid "Notification rules"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:415
|
||||
#: risks/models.py:423
|
||||
msgid "Event"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:420
|
||||
#: risks/models.py:428
|
||||
msgid "Show in app"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:421
|
||||
#: risks/models.py:429
|
||||
msgid "Send via email"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:425
|
||||
#: risks/models.py:433
|
||||
msgid "Send to owner/responsible/reporter (if available)"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:428
|
||||
#: risks/models.py:436
|
||||
msgid "Send to all staff"
|
||||
msgstr ""
|
||||
|
||||
#: risks/models.py:430
|
||||
#: risks/models.py:438
|
||||
msgid "Extra recipients (emails, comma or newline separated)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -432,108 +432,108 @@ msgstr ""
|
|||
msgid "Risk created: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:121
|
||||
#: risks/signals.py:122
|
||||
#, python-brace-format
|
||||
msgid "Risk updated: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:134
|
||||
#: risks/signals.py:136
|
||||
#, python-brace-format
|
||||
msgid "Risk deleted: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:181
|
||||
#: risks/signals.py:184
|
||||
#, python-brace-format
|
||||
msgid "Control {e}: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:182 risks/signals.py:317
|
||||
#: risks/signals.py:185 risks/signals.py:326
|
||||
msgid "created"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:182 risks/signals.py:317
|
||||
#: risks/signals.py:185 risks/signals.py:326
|
||||
msgid "updated"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:196
|
||||
#: risks/signals.py:200
|
||||
#, python-brace-format
|
||||
msgid "Control deleted: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:213
|
||||
#: risks/signals.py:218
|
||||
#, python-brace-format
|
||||
msgid "Residual review required for risk '{t}' due to control change"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:239
|
||||
#: risks/signals.py:245
|
||||
#, python-brace-format
|
||||
msgid "Residual created for risk: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:257
|
||||
#: risks/signals.py:264
|
||||
#, python-brace-format
|
||||
msgid "Residual review required for risk: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:260
|
||||
#: risks/signals.py:267
|
||||
#, python-brace-format
|
||||
msgid "Residual review completed for risk: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:263
|
||||
#: risks/signals.py:270
|
||||
#, python-brace-format
|
||||
msgid "Residual updated for risk: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:280
|
||||
#: risks/signals.py:288
|
||||
#, python-brace-format
|
||||
msgid "Residual deleted for risk: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:316
|
||||
#: risks/signals.py:325
|
||||
#, python-brace-format
|
||||
msgid "Incident {e}: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/signals.py:331
|
||||
#: risks/signals.py:341
|
||||
#, python-brace-format
|
||||
msgid "Incident deleted: {t}"
|
||||
msgstr ""
|
||||
|
||||
#: risks/utils.py:66
|
||||
#: risks/utils.py:135
|
||||
#, python-brace-format
|
||||
msgid "Follow-up reached: review required for risk '{t}'"
|
||||
msgstr ""
|
||||
|
||||
#: risks/views.py:208
|
||||
#: risks/views.py:218
|
||||
msgid "Risk has been marked as reviewed and closed."
|
||||
msgstr ""
|
||||
|
||||
#: risks/views.py:210
|
||||
#: risks/views.py:220
|
||||
msgid "Not all controls are completed. Risk cannot be closed yet."
|
||||
msgstr ""
|
||||
|
||||
#: risks/views.py:368
|
||||
#: risks/views.py:378
|
||||
msgid "Notification marked as read."
|
||||
msgstr ""
|
||||
|
||||
#: risks/views.py:378
|
||||
#: risks/views.py:388
|
||||
msgid "All notifications marked as read."
|
||||
msgstr ""
|
||||
|
||||
#: risks/views.py:397
|
||||
#: risks/views.py:407
|
||||
msgid "Risk status updated."
|
||||
msgstr ""
|
||||
|
||||
#: risks/views.py:413
|
||||
#: risks/views.py:423
|
||||
msgid "Control status updated."
|
||||
msgstr ""
|
||||
|
||||
#: risks/views.py:429
|
||||
#: risks/views.py:439
|
||||
msgid "Incident status updated."
|
||||
msgstr ""
|
||||
|
||||
#: risks/views.py:446
|
||||
#: risks/views.py:456
|
||||
msgid "Residual review flag updated."
|
||||
msgstr ""
|
||||
|
||||
|
@ -796,11 +796,11 @@ msgstr ""
|
|||
msgid "Related Risk"
|
||||
msgstr ""
|
||||
|
||||
#: templates/risks/list_controls.html:144
|
||||
#: templates/risks/list_controls.html:164
|
||||
msgid "No controls found."
|
||||
msgstr ""
|
||||
|
||||
#: templates/risks/list_incidents.html:125
|
||||
#: templates/risks/list_incidents.html:137
|
||||
msgid "No incidents found."
|
||||
msgstr ""
|
||||
|
||||
|
|
17
risks/migrations/0028_notification_target_url.py
Normal file
17
risks/migrations/0028_notification_target_url.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 5.2.6 on 2025-09-16 12:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("risks", "0027_notification_content_type_notification_kind_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="notification",
|
||||
name="target_url",
|
||||
field=models.CharField(blank=True, max_length=500, null=True),
|
||||
),
|
||||
]
|
|
@ -337,6 +337,7 @@ class Notification(models.Model):
|
|||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True)
|
||||
object_id = models.PositiveIntegerField(null=True, blank=True)
|
||||
related_object = GenericForeignKey("content_type", "object_id")
|
||||
target_url = models.CharField(max_length=500, blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
user_display = self.user.username if self.user else "System"
|
||||
|
|
|
@ -101,6 +101,7 @@ def risk_saved(sender, instance: Risk, created, **kwargs):
|
|||
NotificationKind.RISK_CREATED,
|
||||
message=_("Risk created: {t}").format(t=instance.title),
|
||||
users=[instance.owner] if instance.owner_id else None,
|
||||
obj=instance,
|
||||
)
|
||||
else:
|
||||
# Diff audit log
|
||||
|
@ -120,6 +121,7 @@ def risk_saved(sender, instance: Risk, created, **kwargs):
|
|||
NotificationKind.RISK_UPDATED,
|
||||
message=_("Risk updated: {t}").format(t=instance.title),
|
||||
users=[instance.owner] if instance.owner_id else None,
|
||||
obj=instance,
|
||||
)
|
||||
|
||||
|
||||
|
@ -133,6 +135,7 @@ def risk_deleted(sender, instance: Risk, **kwargs):
|
|||
NotificationKind.RISK_DELETED,
|
||||
message=_("Risk deleted: {t}").format(t=instance.title),
|
||||
users=[instance.owner] if instance.owner_id else None,
|
||||
obj=instance,
|
||||
)
|
||||
|
||||
|
||||
|
@ -182,6 +185,7 @@ def control_saved(sender, instance: Control, created, **kwargs):
|
|||
e=_("created") if created else _("updated"), t=instance.title
|
||||
),
|
||||
users=[instance.responsible] if instance.responsible_id else None,
|
||||
obj=instance,
|
||||
)
|
||||
|
||||
|
||||
|
@ -195,6 +199,7 @@ def control_deleted(sender, instance: Control, **kwargs):
|
|||
NotificationKind.CONTROL_DELETED,
|
||||
message=_("Control deleted: {t}").format(t=instance.title),
|
||||
users=[instance.responsible] if instance.responsible_id else None,
|
||||
obj=instance,
|
||||
)
|
||||
|
||||
|
||||
|
@ -211,7 +216,8 @@ def control_risks_changed(sender, instance: Control, action, **kwargs):
|
|||
notify_event(
|
||||
NotificationKind.RESIDUAL_REVIEW_REQUIRED,
|
||||
message=_("Residual review required for risk '{t}' due to control change").format(t=risk.title),
|
||||
users=_risk_stakeholders(risk)
|
||||
users=_risk_stakeholders(risk),
|
||||
obj=instance,
|
||||
)
|
||||
|
||||
|
||||
|
@ -238,6 +244,7 @@ def residual_saved(sender, instance: ResidualRisk, created, **kwargs):
|
|||
NotificationKind.RESIDUAL_CREATED,
|
||||
message=_("Residual created for risk: {t}").format(t=instance.risk.title),
|
||||
users=[instance.risk.owner] if instance.risk.owner_id else None,
|
||||
obj=instance,
|
||||
)
|
||||
else:
|
||||
old = ResidualRisk.objects.get(pk=instance.pk)
|
||||
|
@ -266,6 +273,7 @@ def residual_saved(sender, instance: ResidualRisk, created, **kwargs):
|
|||
kind,
|
||||
message=msg.format(t=instance.risk.title),
|
||||
users=[instance.risk.owner] if instance.risk.owner_id else None,
|
||||
obj=instance,
|
||||
)
|
||||
|
||||
|
||||
|
@ -279,6 +287,7 @@ def residual_deleted(sender, instance: ResidualRisk, **kwargs):
|
|||
NotificationKind.RESIDUAL_DELETED,
|
||||
message=_("Residual deleted for risk: {t}").format(t=instance.risk.title),
|
||||
users=[instance.risk.owner] if instance.risk.owner_id else None,
|
||||
obj=instance,
|
||||
)
|
||||
|
||||
|
||||
|
@ -317,6 +326,7 @@ def incident_saved(sender, instance: Incident, created, **kwargs):
|
|||
e=_("created") if created else _("updated"), t=instance.title
|
||||
),
|
||||
users=[instance.reported_by] if instance.reported_by_id else None,
|
||||
obj=instance,
|
||||
)
|
||||
|
||||
|
||||
|
@ -330,6 +340,7 @@ def incident_deleted(sender, instance: Incident, **kwargs):
|
|||
NotificationKind.INCIDENT_DELETED,
|
||||
message=_("Incident deleted: {t}").format(t=instance.title),
|
||||
users=[instance.reported_by] if instance.reported_by_id else None,
|
||||
obj=instance,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ urlpatterns = [
|
|||
path("risks/risks/<int:id>", views.show_risk, name="show_risk"),
|
||||
path("risks/risk_matrix", views.risk_matrix, name="risk_matrix"),
|
||||
path("risks/<int:id>/status", views.update_risk_status, name="update_risk_status"),
|
||||
path("risks/<int:pk>/mark_reviewed/", views.mark_risk_reviewed, name="mark_risk_reviewed"),
|
||||
path("risks/<int:pk>/mark_risk_reviewed/", views.mark_risk_reviewed, name="mark_risk_reviewed"),
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Controls
|
||||
|
|
120
risks/utils.py
120
risks/utils.py
|
@ -1,9 +1,10 @@
|
|||
from datetime import date, datetime
|
||||
from typing import Iterable, Optional
|
||||
from typing import Any, Iterable, Optional
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.mail import send_mail
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
@ -14,6 +15,74 @@ from .models import (
|
|||
|
||||
User = get_user_model()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# notify_event()
|
||||
# ---------------------------------------------------------------------------
|
||||
def get_entity_url(obj):
|
||||
if obj is None:
|
||||
return None
|
||||
model_name = obj.__class__.__name__.lower()
|
||||
|
||||
mapping = {
|
||||
"risk": "risks:show_risk",
|
||||
"control": "risks:show_control",
|
||||
"residualrisk": "risks:show_risk",
|
||||
"incident": "risks:show_incident",
|
||||
"user": "admin:user_detail",
|
||||
}
|
||||
|
||||
view_name = mapping.get(model_name)
|
||||
if view_name:
|
||||
return reverse(view_name, args=[obj.pk])
|
||||
return None
|
||||
|
||||
def notify_event(kind: str, *, message: str, users: Optional[Iterable[Any]] = None, obj=None):
|
||||
"""
|
||||
Generates in-app notifications and/or emails depending on the NotificationRule.
|
||||
- users: Basic recipients (owner/responsible/reporter) – can be None.
|
||||
- staff/extra recipients are added from the rule.
|
||||
"""
|
||||
rule = NotificationRule.objects.filter(kind=kind).first()
|
||||
|
||||
# Defaults (no rule → in-app only)
|
||||
enabled_in_app = True
|
||||
enabled_email = False
|
||||
recipients_users = set()
|
||||
extra_emails = []
|
||||
|
||||
# Base recipients
|
||||
if users:
|
||||
recipients_users.update(u for u in users if u and getattr(u, "is_active", False))
|
||||
|
||||
# Rule overrides
|
||||
if rule:
|
||||
enabled_in_app = rule.enabled_in_app
|
||||
enabled_email = rule.enabled_email
|
||||
if rule.to_staff:
|
||||
recipients_users.update(User.objects.filter(is_staff=True, is_active=True))
|
||||
extra_emails = _split_emails(rule.extra_recipients)
|
||||
|
||||
url = get_entity_url(obj)
|
||||
|
||||
# In-App Notifications
|
||||
if enabled_in_app:
|
||||
for u in recipients_users:
|
||||
Notification.objects.create(user=u, message=message, target_url=url)
|
||||
|
||||
# Email Notifications
|
||||
if enabled_email:
|
||||
emails = [u.email for u in recipients_users if u and u.email] + extra_emails
|
||||
emails = list(dict.fromkeys(emails))
|
||||
if emails:
|
||||
body = f"{message}\n\n{url}" if url else message
|
||||
send_mail(
|
||||
_("Notification"),
|
||||
body,
|
||||
getattr(settings, "DEFAULT_FROM_EMAIL", "webmaster@localhost"),
|
||||
emails,
|
||||
fail_silently=True,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# model_diff()
|
||||
|
@ -85,6 +154,7 @@ def check_risk_followups():
|
|||
NotificationKind.RISK_REVIEW_REQUIRED,
|
||||
message=message,
|
||||
users=[risk.owner] if risk.owner_id else None,
|
||||
obj=risk,
|
||||
)
|
||||
|
||||
|
||||
|
@ -97,51 +167,3 @@ def _split_emails(value: str) -> list[str]:
|
|||
return []
|
||||
raw = value.replace("\n", ",").split(",")
|
||||
return [e.strip() for e in raw if "@" in e and e.strip()]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# notify_event()
|
||||
# ---------------------------------------------------------------------------
|
||||
def notify_event(kind: str, *, message: str, users: Optional[Iterable[User]] = None):
|
||||
"""
|
||||
Generates in-app notifications and/or emails depending on the NotificationRule.
|
||||
- users: Basic recipients (owner/responsible/reporter) – can be None.
|
||||
- staff/extra recipients are added from the rule.
|
||||
"""
|
||||
rule = NotificationRule.objects.filter(kind=kind).first()
|
||||
|
||||
# Defaults (no rule → in-app only)
|
||||
enabled_in_app = True
|
||||
enabled_email = False
|
||||
recipients_users = set()
|
||||
extra_emails = []
|
||||
|
||||
# Base recipients
|
||||
if users:
|
||||
recipients_users.update(u for u in users if u and getattr(u, "is_active", False))
|
||||
|
||||
# Rule overrides
|
||||
if rule:
|
||||
enabled_in_app = rule.enabled_in_app
|
||||
enabled_email = rule.enabled_email
|
||||
if rule.to_staff:
|
||||
recipients_users.update(User.objects.filter(is_staff=True, is_active=True))
|
||||
extra_emails = _split_emails(rule.extra_recipients)
|
||||
|
||||
# In-App Notifications
|
||||
if enabled_in_app:
|
||||
for u in recipients_users:
|
||||
Notification.objects.create(user=u, message=message)
|
||||
|
||||
# Email Notifications
|
||||
if enabled_email:
|
||||
emails = [u.email for u in recipients_users if u and u.email] + extra_emails
|
||||
emails = list(dict.fromkeys(emails)) # de-dupe, preserve order
|
||||
if emails:
|
||||
send_mail(
|
||||
_("Notification"),
|
||||
message,
|
||||
getattr(settings, "DEFAULT_FROM_EMAIL", "webmaster@localhost"),
|
||||
emails,
|
||||
fail_silently=True, # don’t crash on mail error
|
||||
)
|
||||
|
|
|
@ -13,7 +13,7 @@ from rest_framework import viewsets
|
|||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from .forms import RiskStatusForm, ControlStatusForm, IncidentStatusForm, ResidualReviewForm
|
||||
from .models import Risk, Control, ResidualRisk, AuditLog, Incident, Notification
|
||||
from .models import AuditLog, Risk, Control, ResidualRisk, AuditLog, Incident, Notification
|
||||
from .serializers import (
|
||||
ControlSerializer, RiskSerializer, ResidualRiskSerializer,
|
||||
UserSerializer, AuditSerializer, IncidentSerializer,
|
||||
|
@ -205,6 +205,16 @@ def mark_risk_reviewed(request, pk):
|
|||
risk.status = "closed"
|
||||
risk._changed_by = request.user
|
||||
risk.save(update_fields=["status", "updated_at"])
|
||||
|
||||
# ➜ AuditLog schreiben
|
||||
AuditLog.objects.create(
|
||||
user=request.user,
|
||||
action="reviewed",
|
||||
model="Risk",
|
||||
object_id=risk.pk,
|
||||
changes={"status": {"old": "review_required", "new": "closed"}},
|
||||
)
|
||||
|
||||
messages.success(request, _("Risk has been marked as reviewed and closed."))
|
||||
else:
|
||||
messages.error(request, _("Not all controls are completed. Risk cannot be closed yet."))
|
||||
|
|
|
@ -108,12 +108,18 @@
|
|||
<td class="has-text-centered">{{ c.id }}</td>
|
||||
<td>{{ c.title }}</td>
|
||||
<td>
|
||||
{% if c.risk %}
|
||||
<a href="{% url 'risks:show_risk' c.risk.id %}" onclick="event.stopPropagation();">
|
||||
{{ c.risk.title }}
|
||||
{% if c.risks.all %}
|
||||
<ul>
|
||||
{% for r in c.risks.all %}
|
||||
<li>
|
||||
<a href="{% url 'risks:show_risk' r.id %}" onclick="event.stopPropagation();">
|
||||
{{ r.title }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
–
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="has-text-centered">
|
||||
|
@ -123,7 +129,21 @@
|
|||
–
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="has-text-centered">{{ c.get_status_display }}</td>
|
||||
<td class="has-text-centered">
|
||||
<div class="tag
|
||||
{% if c.status == 'planned' %}
|
||||
is-info
|
||||
{% elif c.status == 'completed' %}
|
||||
is-success
|
||||
{% elif c.status == 'verified' %}
|
||||
is-success
|
||||
{% elif c.status == 'in_progress' %}
|
||||
is-link is-light
|
||||
{% endif %}
|
||||
">
|
||||
{{ c.get_status_display }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="has-text-centered">
|
||||
{% if c.due_date %}
|
||||
{{ c.due_date|date:"d.m.Y" }}
|
||||
|
|
|
@ -117,9 +117,21 @@
|
|||
<span class="has-text-grey">–</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ i.get_status_display }}</td>
|
||||
<td>{{ i.date_reported|date:"d.m.Y" }}</td>
|
||||
<td>{{ i.reported_by|default:"–" }}</td>
|
||||
<td class="has-text-centered">
|
||||
<div class="tag
|
||||
{% if i.status == 'open' %}
|
||||
is-info
|
||||
{% elif i.status == 'closed' %}
|
||||
is-success
|
||||
{% elif i.status == 'in_progress' %}
|
||||
is-link is-light
|
||||
{% endif %}
|
||||
">
|
||||
{{ i.get_status_display }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="has-text-centered">{{ i.date_reported|date:"d.m.Y" }}</td>
|
||||
<td class="has-text-centered">{{ i.reported_by|default:"–" }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="6" class="has-text-grey has-text-centered">{% trans "No incidents found." %}</td></tr>
|
||||
|
|
|
@ -140,21 +140,21 @@
|
|||
<td class="has-text-centered">
|
||||
{% if "1" in r.cia %}
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-circle" aria-hidden="true" style="color: grey"></i>
|
||||
<abbr title="{% trans 'Confidentiality' %}"><i class="fas fa-circle" aria-hidden="true" style="color: grey"></i></abbr>
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="has-text-centered">
|
||||
{% if "2" in r.cia %}
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-circle" aria-hidden="true" style="color: grey"></i>
|
||||
<abbr title="{% trans 'Integrity' %}"><i class="fas fa-circle" aria-hidden="true" style="color: grey"></i></abbr>
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="has-text-centered">
|
||||
{% if "3" in r.cia %}
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-circle" aria-hidden="true" style="color: grey"></i>
|
||||
<abbr title="{% trans 'Availability' %}"><i class="fas fa-circle" aria-hidden="true" style="color: grey"></i></abbr>
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
{% trans "New" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if n.get_link %}
|
||||
<a href="{{ n.get_link }}">{{ n.message }}</a>
|
||||
{% if n.target_url %}
|
||||
<a href="{{ n.target_url }}">{{ n.message }}</a>
|
||||
{% else %}
|
||||
{{ n.message }}
|
||||
{% endif %}
|
||||
|
|
Loading…
Add table
Reference in a new issue