diff --git a/package.json b/package.json index a44e92b..fbba8aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/forms-runtime", - "version": "0.1.18", + "version": "0.1.19", "private": true, "description": "Definition-aware form submissions and service launch for GovOPlaN.", "type": "module", diff --git a/pyproject.toml b/pyproject.toml index 7cd7aa1..e400926 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "govoplan-forms-runtime" -version = "0.1.18" +version = "0.1.19" description = "Definition-aware form submissions and service launch for GovOPlaN." readme = "README.md" requires-python = ">=3.12" diff --git a/src/govoplan_forms_runtime/backend/manifest.py b/src/govoplan_forms_runtime/backend/manifest.py index 11879a9..6655033 100644 --- a/src/govoplan_forms_runtime/backend/manifest.py +++ b/src/govoplan_forms_runtime/backend/manifest.py @@ -21,6 +21,7 @@ from govoplan_core.core.module_guards import ( from govoplan_core.core.notifications import CAPABILITY_NOTIFICATIONS_DISPATCH from govoplan_core.core.modules import ( CapabilityDocumentation, + DocumentationCondition, DocumentationLink, DocumentationTopic, FrontendModule, @@ -64,7 +65,7 @@ from govoplan_forms_runtime.backend.status_access import FormStatusAccessService MODULE_ID = "forms_runtime" MODULE_NAME = "Forms Runtime" -MODULE_VERSION = "0.1.18" +MODULE_VERSION = "0.1.19" PARTICIPATE_SCOPE = "forms_runtime:submission:participate" ASSIST_SCOPE = "forms_runtime:submission:assist" READ_SCOPE = "forms_runtime:workspace:read" @@ -537,6 +538,30 @@ manifest = ModuleManifest( kind="repository", ), ), + metadata={"kind": "reference"}, + translations={ + "de": { + "title": "Datenschutzanfragen zu Formulareinreichungen", + "summary": ( + "Personenbezogene Formularwerte und gesteuerte Lebenszyklusnachweise ausgeben, ohne Zugangsdaten oder fremde Daten einer assistierten Erfassung offenzulegen." + ), + "body": ( + "Der DSAR-Anbieter von Forms Runtime gleicht kanonische Akteure im exakten Mandanten, normalisierte " + "E-Mail-Statusfreigaben, Bestätigungen und Kenntnisnahmen von Antragstellenden sowie ausdrückliche " + "Runtime-Verweise ab. Assistierende Bearbeitende werden als minimierte Zuordnung erfasst, gelten aber " + "nicht als Antragsteller, wenn die assistierte Sitzung eine andere Person bezeichnet. Bestätigte " + "Instanzen geben begrenzte Formularwerte und typisierte Fakten zu Definition, Status, Erfassung, " + "Kenntnisnahme, Statuszugriff und Übergabe aus. Zugangsdatenähnliche Formularschlüssel werden geschwärzt; " + "Token- und E-Mail-Prüfsummen, Idempotenzschlüssel, Anfrage- und Nutzdatenprüfsummen, undurchsichtige " + "Metadaten und Details, Validierungsinterna, Nachweiskennungen, Anbieterwiederholungsschlüssel, Fehler und " + "fremde Einreichungen bleiben ausgeschlossen. Unveränderliche Revisionen, Ereignisse, Übergaben, " + "Bestätigungen, Kenntnisnahmen, Erfassungsnachweise und minimierte Token-Lebenszyklen erhalten " + "Aufbewahrungsaktionen. Aktuelle Entwürfe und aktive Statusfreigaben erfordern eine autorisierte manuelle " + "Prüfung; Forms Runtime veröffentlicht keine automatische DSAR-Änderung. Forms führt Definitionen, Portal " + "zeigt öffentliche und Statusrouten, und Cases beziehungsweise Workflow Engine führen nachgelagerte Ziele." + ), + } + }, ), DocumentationTopic( id="forms_runtime.submissions", @@ -550,6 +575,16 @@ manifest = ModuleManifest( layer="configured", documentation_types=("admin", "user"), audience=("user", "operator", "module_admin", "product_owner"), + conditions=( + DocumentationCondition( + any_scopes=( + PARTICIPATE_SCOPE, + ASSIST_SCOPE, + READ_SCOPE, + WRITE_SCOPE, + ) + ), + ), links=( DocumentationLink( label="Forms Runtime security and recovery", @@ -559,6 +594,7 @@ manifest = ModuleManifest( ), metadata={ "seed": True, + "kind": "workflow", "help_contexts": [ "forms_runtime.navigation", "forms_runtime.workspace", @@ -582,6 +618,49 @@ manifest = ModuleManifest( "A permanent status URL is a non-expiring bearer link. Anyone holding it can see the bounded status projection until an administrator disables the exact Form policy or the grant is revoked.", ], }, + translations={ + "de": { + "title": "Formulare ausfüllen und verwalten", + "summary": ( + "Zulässige Entwürfe speichern, validierte Werte einreichen und exakte Definitions- und Übergabenachweise bewahren." + ), + "body": ( + "Jede Instanz löst genau eine unveränderliche veröffentlichte Formularrevision auf. Entwurfs- und Endwerte " + "werden auf dem Server validiert; die endgültige Einreichung erzwingt zusätzlich Anforderungen an Anlagen, " + "Signaturen und Regeln. Service-Aufrufe bewahren den exakten Service und seine Bindung. Native Übergaben an " + "Cases und Workflow speichern die Absicht vor der Ausführung, verwenden Eigentümerfähigkeiten mit stabilen " + "Anbieterschlüsseln und erfordern bei unbekanntem Ergebnis einen Abgleich. Verlauf, Belege und Übergaben sind " + "wiederholungssicher und durch optimistische Nebenläufigkeit geschützt. Einladungen und ausdrücklich aktivierte " + "anonyme Erfassung verwenden nur als Prüfsumme gespeicherte, ablaufende Tokens, begrenzte Raten und isolierte " + "synthetische Akteure. Authentifizierte assistierte Sitzungen bewahren Kanal, betroffene und vertretene Parteien, " + "Befugnis, Zweck, Hinweis, zuständige Funktion, Sprache, Barrierefreiheitsbedarfe und Feldherkunft, ohne die " + "exakten Formularregeln zu umgehen. Die Einreichung erfordert unveränderliche Rücklesenachweise, die an aktuelle " + "Revision, Werte, Anlagen und Signaturen gebunden sind; jede spätere Entwurfsänderung macht sie ungültig. " + "Administratoren konfigurieren den Antragstellerstatus je exakter Formularrevision als nur authentifiziert, " + "kurzlebigen Link an eine passende Formular-E-Mail oder nicht ablaufenden öffentlichen Link. Statusprojektionen " + "zeigen nur eine begrenzte Lebenszykluszeitleiste und Belegreferenz, niemals Formularwerte, Akteure, Nachweise, " + "interne Notizen oder Übergabedetails. Files-Anlagen nutzen einmalige zweckgebundene Freigaben; authentifizierte " + "Kenntnisnahmen binden einen exakten Akteur und eine Nutzdatenprüfsumme, ohne fortgeschrittene oder qualifizierte " + "Signaturzusicherung zu behaupten. Bei aktiviertem Search trägt Forms Runtime eine wiederaufbaubare reine " + "Metadatenprojektion bei; eingereichte Werte und Nachweisinhalte bleiben ausgeschlossen und jeder Treffer wird " + "erneut gegen aktuelle Arbeitsbereichs- oder Teilnahmeberechtigung geprüft." + ), + } + }, + structured_translation_version="1", + structured_translations={ + "de": { + "privacy_notes": [ + "Formularwerte werden nur nach mandantengebundenen Instanzberechtigungen und Eigentumsregeln ausgegeben.", + "Validierungsmeldungen zeigen feldbezogene Diagnosen, ohne fremde Einreichungen offenzulegen.", + "Übergabezeilen bewahren Anbieterverweise und Ergebnisse, umgehen aber nicht die Autorisierung des Zielmoduls.", + "Tokens für öffentliche Erfassung und Files-Uploads werden nur als kryptografische Prüfsummen bewahrt; anonyme Einreichungen können später keiner Identität zugeschrieben werden.", + "Die Herkunft assistierter Sitzungen nennt gesteuerte Partei- und Funktionsverweise sowie den Zweck; Bearbeitende sollen Namen oder Nachweisinhalte nicht zusätzlich in Freitextverweisen und Notizen erfassen.", + "Forms Runtime bewahrt kurzlebige Statusgeheimnisse nur als Prüfsummen. Die zugestellte URL durchläuft notwendigerweise Notifications und Mail unter deren Aufbewahrung; der E-Mail-Vergleich verwendet eine freigabegebundene Prüfsumme und nicht passende Anfragen erhalten dieselbe Antwort.", + "Eine dauerhafte Status-URL ist ein nicht ablaufender Inhaberlink. Wer ihn besitzt, kann die begrenzte Statusprojektion sehen, bis ein Administrator die exakte Formularregel deaktiviert oder die Freigabe widerruft.", + ] + } + }, ), DocumentationTopic( id="forms_runtime.reference.fields-and-consequences", @@ -611,6 +690,7 @@ manifest = ModuleManifest( ), metadata={ "seed": True, + "kind": "reference", "help_contexts": [ "forms_runtime.field.dynamic-value", "forms_runtime.field.change-reason", @@ -647,6 +727,58 @@ manifest = ModuleManifest( "file_submission": "Resolves the exact immutable submission revision under current access and preserves only a digest-bound reference in Records.", }, }, + translations={ + "de": { + "title": "Formularwerte sowie Folgen von Einreichung und Übergabe", + "summary": ( + "Laufzeitverhalten von Feldern, unveränderliche Belege, Entwurfsrevisionen, optionale Nachweise und wiederherstellbare externe Wirkungen." + ), + "body": ( + "Die aktive Instanz löst eine exakte veröffentlichte Formulardefinitionsrevision auf. Sichtbarkeitsbedingungen " + "ändern die Darstellung, nicht Servervalidierung oder Autorisierung. Das Speichern eines zulässigen Entwurfs " + "erzeugt eine neue Revision mit Änderungsgrund. Einreichen validiert Werte, Anlagen, Signaturen und Regeln und " + "erzeugt einen unveränderlichen Beleg; es ist kein bearbeitbares Entwurfsspeichern. Eine Case- oder " + "Workflow-Übergabe speichert die Absicht vor dem Aufruf des optionalen Anbieters und verwendet einen stabilen " + "Idempotenzschlüssel. Abgelehnte Wirkungen können erneut versucht werden. Unbekannte Ergebnisse müssen vor " + "einem erneuten Versuch abgeglichen werden, um doppelte Ziele zu vermeiden. Administrative Kompensation hält " + "die bestätigte Abwesenheit fest und löscht niemals ein entferntes Ziel. Bei aktiviertem Records können nur " + "unveränderliche eingereichte Revisionen für die Ablage aufgelöst werden; der aktuelle Zugriff in Forms Runtime " + "wird erneut geprüft und bearbeitbare Entwürfe werden abgewiesen. Assistierte Erfassung beginnt mit einer " + "authentifizierten zweckgebundenen Sitzung. Bearbeitende erfassen Kanal, Partei- und Vertretungsverweise, " + "Befugnisgrundlage, Hinweis, zuständige Funktion, Sprache, Barrierefreiheit und Quellen je Feld. " + "Rückleseergebnisse werden nur angefügt und binden die exakten aktuellen Nutzdaten. Korrekturen müssen zuerst " + "als neue Entwurfsrevision gespeichert und erneut bestätigt werden; eine nicht mögliche Bestätigung erfordert " + "einen ausdrücklichen Ausnahmevermerk. Regeln für Antragstellerstatus sind an eine exakte veröffentlichte " + "Formularrevision gebunden. Authentifizierter Zugriff ist objektgebunden an das Antragstellerkonto; assistierte " + "Erfassung darf ihn nur aus einem ausdrücklichen Konto-Parteiverweis binden. Der E-Mail-Link-Modus vergleicht " + "die eingereichte E-Mail, ohne die Übereinstimmung offenzulegen, widerruft beim erneuten Senden den früheren " + "Link und übergibt die Zustellung an Notifications. Der Dauerlink-Modus tauscht bewusst Authentifizierung und " + "Ablauf gegen den Besitz einer stabilen hochentropischen URL. Das Deaktivieren einer Regel setzt alle darunter " + "ausgestellten Freigaben sofort aus; Feldänderungen gelten für künftige Einreichungen, während bestehende " + "Freigaben ihr ausgestelltes Zugriffsprofil behalten." + ), + } + }, + structured_translation_version="1", + structured_translations={ + "de": { + "consequence_classes": { + "save_draft": "Erzeugt eine unveränderliche Entwurfsrevision mit Änderungsgrund.", + "submit": "Validiert die exakte Definition und erzeugt einen unveränderlichen Einreichungsbeleg.", + "start_handoff": "Speichert die Absicht vor dem Aufruf eines optionalen Case- oder Workflow-Anbieters.", + "reconcile": "Klärt eine Wirkung mit unbekanntem Ergebnis ohne unsichere doppelte Ausführung.", + "compensate": "Hält administrativ nachgewiesen fest, dass keine Zielwirkung besteht.", + "public_intake": "Startet eine isolierte ablaufende Einladung oder ausdrücklich aktivierte anonyme Einreichung, ohne allgemeinen Plattformzugriff zu gewähren.", + "upload_evidence": "Stellt eine kurzlebige Anbieterfreigabe aus; der unveränderliche Nachweisverweis muss bei der Einreichung erneut vom Eigentümer geprüft werden.", + "acknowledge": "Bindet handelndes Konto, Erklärungsversion, exakte Formularrevision, Werte und Anlagen in einer authentifizierten Kenntnisnahmeprüfsumme.", + "start_assisted_intake": "Erzeugt einen fortsetzbaren authentifizierten Entwurf mit Kanal, Parteien, Befugnis, Zweck, Hinweis, Funktion, Barrierefreiheit und Quellenherkunft.", + "confirm_assisted_readback": "Erzeugt unveränderliche Nachweise für exakte aktuelle Revision und Nutzdaten; spätere Korrekturen erfordern vor Einreichung eine neue Bestätigung.", + "configure_status_access": "Wählt Zugriffs- und Offenlegungsprofil des Antragstellerstatus für künftige Einreichungen einer exakten Formularrevision; Deaktivierung setzt bestehende Freigaben aus.", + "request_status_link": "Gibt eine generische Antwort zurück, begrenzt Versuche und widerruft nur bei passender verknüpfter E-Mail das frühere kurzlebige Geheimnis, bevor Notifications einen neuen Link zustellt.", + "file_submission": "Löst die exakte unveränderliche Einreichungsrevision unter aktuellem Zugriff auf und bewahrt in Records nur einen prüfsummengebundenen Verweis.", + } + } + }, ), ), architecture=declared_module_architecture( diff --git a/tests/test_documentation.py b/tests/test_documentation.py new file mode 100644 index 0000000..eac0de1 --- /dev/null +++ b/tests/test_documentation.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +import unittest + +from govoplan_core.core.modules import ( + documentation_structured_translation_issues, + localizable_documentation_metadata_keys, +) +from govoplan_forms_runtime.backend.manifest import manifest + + +class FormsRuntimeDocumentationTests(unittest.TestCase): + def test_german_reference_documentation_is_complete(self) -> None: + topics = manifest.documentation + self.assertEqual(3, len(topics)) + for topic in topics: + translation = topic.translations.get("de", {}) + self.assertTrue(translation.get("title"), topic.id) + self.assertTrue(translation.get("summary"), topic.id) + self.assertTrue(translation.get("body"), topic.id) + if localizable_documentation_metadata_keys(topic): + self.assertEqual("1", topic.structured_translation_version, topic.id) + self.assertIn("de", topic.structured_translations, topic.id) + self.assertEqual((), documentation_structured_translation_issues(topic)) + + kinds = {topic.metadata.get("kind") for topic in topics} + self.assertIn("workflow", kinds) + self.assertIn("reference", kinds) + workflow = next(topic for topic in topics if topic.metadata.get("kind") == "workflow") + self.assertTrue(workflow.conditions) + + +if __name__ == "__main__": + unittest.main() diff --git a/webui/package.json b/webui/package.json index fcb8f3e..6cdba47 100644 --- a/webui/package.json +++ b/webui/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/forms-runtime-webui", - "version": "0.1.18", + "version": "0.1.19", "private": true, "type": "module", "main": "src/index.ts", diff --git a/webui/src/module.ts b/webui/src/module.ts index ef7a253..2b860d0 100644 --- a/webui/src/module.ts +++ b/webui/src/module.ts @@ -15,7 +15,7 @@ const routeScopes = [ export const formsRuntimeModule: PlatformWebModule = { id: "forms_runtime", label: "i18n:govoplan-forms-runtime.forms", - version: "0.1.18", + version: "0.1.19", dependencies: ["access", "forms"], optionalDependencies: ["files", "approvals", "workflow_engine", "portal", "cases", "records", "policy", "audit"], translations: generatedTranslations,