from __future__ import annotations from govoplan_core.core.modules import with_documentation_structured_translations from govoplan_quick_access.backend.german_structured_documentation import GERMAN_STRUCTURED_TRANSLATIONS from pathlib import Path from govoplan_core.core.access import ( CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER, ) from govoplan_core.core.module_guards import ( drop_table_retirement_provider, persistent_table_uninstall_guard, ) from govoplan_core.core.modules import ( CapabilityDocumentation, DocumentationCondition, DocumentationLink, DocumentationTopic, FrontendModule, MigrationSpec, ModuleContext, ModuleInterfaceProvider, ModuleManifest, PermissionDefinition, RoleTemplate, ) from govoplan_core.core.provider_governance import declared_module_architecture from govoplan_core.core.views import ViewSurface from govoplan_core.db.base import Base from govoplan_quick_access.backend.db import models as quick_access_models from govoplan_quick_access.backend.dsar_provider import ( QUICK_ACCESS_DSAR_CAPABILITY, QuickAccessDsarProvider, ) MODULE_ID = "quick_access" MODULE_NAME = "Quick Access" MODULE_VERSION = "0.1.21" READ_SCOPE = "quick_access:profile:read" WRITE_SCOPE = "quick_access:profile:write" TENANT_ADMIN_SCOPE = "quick_access:profile:admin" SYSTEM_ADMIN_SCOPE = "quick_access:system:admin" def _permission( scope: str, label: str, description: str, *, level: str = "tenant", ) -> PermissionDefinition: module_id, resource, action = scope.split(":", 2) return PermissionDefinition( scope=scope, label=label, description=description, category="Quick Access", level=level, module_id=module_id, resource=resource, action=action, ) PERMISSIONS = ( _permission( READ_SCOPE, "Use Quick Access", "Read the effective Quick Access catalogue and preferences.", ), _permission( WRITE_SCOPE, "Configure personal Quick Access", "Enable, disable, and order available Quick Access categories and tools.", ), _permission( TENANT_ADMIN_SCOPE, "Manage tenant Quick Access", "Set tenant availability, forced items, and default ordering.", ), _permission( SYSTEM_ADMIN_SCOPE, "Manage system Quick Access", "Set system-wide availability, forced items, and default ordering.", level="system", ), ) ROLE_TEMPLATES = ( RoleTemplate( slug="quick_access_user", name="Quick Access user", description="Use and arrange the Quick Access rail.", permissions=(READ_SCOPE, WRITE_SCOPE), default_authenticated=True, ), RoleTemplate( slug="quick_access_manager", name="Quick Access manager", description="Manage tenant Quick Access policy and defaults.", permissions=(READ_SCOPE, WRITE_SCOPE, TENANT_ADMIN_SCOPE), ), RoleTemplate( slug="quick_access_system_manager", name="Quick Access system manager", description="Manage system-wide Quick Access policy and defaults.", permissions=(SYSTEM_ADMIN_SCOPE,), level="system", ), ) def _router(context: ModuleContext): from govoplan_quick_access.backend.router import create_router return create_router(context.registry) def _dsar_provider(_context: ModuleContext) -> QuickAccessDsarProvider: return QuickAccessDsarProvider() DOCUMENTATION = ( DocumentationTopic( id="quick-access.data-subject-requests", title="Quick Access data-subject requests", summary=( "Export or delete personal rail preferences while retaining " "institutional Quick Access policy." ), body=( "Quick Access contributes the user profile owned by the exact account " "in the active tenant. The access package contains bounded category and " "tool availability and ordering preferences but never follows a tool " "into Mail, Postbox, Tasks, Files, or another owner module. Tenant policy " "records are included only as minimized creation or update attribution " "when the subject account performed that action; their preference payload " "is excluded and their attribution is retained as institutional evidence. " "System-wide policy is outside tenant-scoped requests. Erasure deletes " "only the selected personal profile after owner and revision checks. The " "effective rail then falls back to current module, system, and tenant " "defaults, including all locked or forced items. A repeated execution is " "unchanged and no tool or domain data is modified." ), layer="configured", documentation_types=("admin", "user"), audience=("user", "tenant_admin", "operator", "auditor"), translations={ "de": { "title": "Datenschutzanfragen für Schnellzugriff", "summary": "Persönliche Leisteneinstellungen exportieren oder löschen, während institutionelle Schnellzugriffsrichtlinien erhalten bleiben.", "body": ( "Schnellzugriff trägt das Benutzerprofil bei, das dem genauen Konto im aktiven Mandanten gehört. Das Auskunftspaket enthält begrenzte Einstellungen zu Verfügbarkeit und Reihenfolge von Kategorien und Werkzeugen, folgt einem Werkzeug jedoch niemals in Mail, Postbox, Tasks, Files oder ein anderes Besitzermodul. " "Mandantenrichtlinien werden nur mit minimierter Erstellungs- oder Änderungszuordnung aufgenommen, wenn das betroffene Konto die Handlung ausgeführt hat; ihr Einstellungsinhalt bleibt ausgeschlossen und die Zuordnung bleibt als institutioneller Nachweis erhalten. Systemweite Richtlinien liegen außerhalb mandantenbezogener Anfragen. " "Eine Löschung entfernt nach Eigentums- und Revisionsprüfung ausschließlich das ausgewählte persönliche Profil. Die wirksame Leiste fällt anschließend auf die aktuellen Modul-, System- und Mandantenvorgaben einschließlich aller gesperrten oder erzwungenen Einträge zurück. Wiederholte Ausführung verändert nichts und keine Werkzeug- oder Fachdaten werden geändert." ), } }, related_modules=("core", "access", "views"), metadata={ "kind": "reference", "help_contexts": [ "quick_access.rail", "quick_access.settings.personal", "quick_access.admin.tenant", "privacy.data-subject-requests", ], "consequence_classes": { "export_personal_profile": ( "Returns bounded rail preferences, never tool-owned data." ), "delete_personal_profile": ( "Removes personal overrides; governed defaults apply again." ), "retain_tenant_attribution": ( "Preserves minimized tenant-policy accountability evidence." ), }, }, ), DocumentationTopic( id="quick-access.user", title="Quick Access rail", summary="Keep selected work, calendar, message, and file tools available beside the current page.", body=( "Documentation books sit immediately beside the visible heading or contextual label for the " "displayed Quick Access tool, not among operational action buttons. Field help remains beside " "its label. " "Open a category on the right rail to use compact tools without leaving the current task. " "Messages combines enabled Mail, Postbox, and future chat contributions in one overlay. " "Personal settings can reorder or hide items that remain available under system, tenant, " "permission, and View policy. Every item retains a link to its complete owning page. " "When a View focuses the rail, the rail names that View and offers a temporary All available tools escape that " "shows the complete permission-derived catalogue without changing the View or personal settings. If none of a " "View's focused tools is currently available, Quick Access explains the stale focus and falls back to that catalogue. " "Launch-context version 2 carries only versioned, bounded references to the current object, acting assignment, " "temporal selection, exact View revision, and return location. Owner modules recheck access when a tool opens and " "before each effect. A tool returns either an explicit version-1 completion with a typed owner reference or an " "explicit cancellation; closing the drawer does not imply success. The overlay preserves unsaved host-page work, " "and the complete owning page remains the fallback for work that exceeds the compact surface." ), layer="configured", documentation_types=("user",), audience=("user",), conditions=( DocumentationCondition( required_modules=("quick_access", "access"), required_scopes=(READ_SCOPE,), ), ), links=( DocumentationLink( label="Quick Access architecture", href="govoplan-quick-access/docs/QUICK_ACCESS.md", kind="repository", ), ), translations={ "de": { "title": "Schnellzugriffsleiste", "summary": "Ausgewaehlte Werkzeuge fuer Arbeit, Kalender, Nachrichten und Dateien neben der aktuellen Seite verwenden.", "body": ( "Dokumentationsbücher stehen unmittelbar neben der sichtbaren Überschrift oder " "Kontextbezeichnung für das angezeigte Schnellzugriffswerkzeug, nicht zwischen ausführbaren " "Aktionsschaltflächen. Feldhilfe bleibt neben der Feldbezeichnung. " "Eine Kategorie in der rechten Leiste oeffnet kompakte Werkzeuge, ohne die aktuelle Aufgabe zu verlassen. " "Nachrichten fuehrt Beitraege aus Mail, Postfach und kuenftigen Chat-Modulen in einer Einblendung zusammen. " "Persoenliche Einstellungen koennen alle durch System, Mandant, Berechtigungen und Ansicht zugelassenen Eintraege ordnen oder ausblenden. " "Wenn eine Ansicht die Leiste fokussiert, benennt die Leiste diese Ansicht und bietet voruebergehend alle " "verfuegbaren Werkzeuge an, ohne Ansicht oder persoenliche Einstellungen zu aendern. Ist keines der fokussierten " "Werkzeuge verfuegbar, wird der veraltete Fokus erklaert und der berechtigungsabgeleitete Katalog angezeigt. " "Startkontext Version 2 uebergibt nur versionierte, begrenzte Verweise auf Objekt, handelnde Zuordnung, " "Zeitbezug, genaue Ansichtsversion und Ruecksprungort. Das besitzende Modul prueft den Zugriff beim Oeffnen " "und vor jeder Wirkung erneut. Ein Werkzeug meldet entweder einen ausdruecklichen Abschluss mit typisiertem " "Besitzerverweis oder einen ausdruecklichen Abbruch; das Schliessen gilt nicht als Erfolg. Die Einblendung " "erhaelt ungespeicherte Arbeit auf der Ausgangsseite, die vollstaendige Besitzerseite bleibt das Ausweichziel." ), } }, metadata={ "kind": "workflow", "help_contexts": [ "quick_access.rail", "quick_access.drawer", "quick_access.settings.personal", ], "steps": [ "Open a category on the right rail and choose an available compact tool.", "Complete or explicitly cancel the bounded owner-module action.", "Follow the owning-page link when the task exceeds the compact surface.", ], }, ), DocumentationTopic( id="quick-access.admin", title="Quick Access policy", summary="Govern which registered compact tools lower scopes may use and how they are ordered by default.", body=( "Documentation books sit immediately beside the visible heading or contextual label for the " "displayed Quick Access tool, not among operational action buttons. Field help remains beside " "its label. " "The catalogue follows installed module registrations. System settings constrain tenants; " "tenant settings constrain users. An item may remain available, be blocked, or be forced. " "Effective entries identify the system, tenant, user, or module source of availability and ordering; " "preferences for retired entries retain their scope provenance. " "Views and permissions form additional ceilings and Quick Access never grants access to domain data. " "A View may recommend tools or focus the rail to a subset, but only currently enabled, context-compatible, authorized " "tools participate. The All available tools escape only restores that permission-derived set for the current session; " "it never broadens authorization or persists an override. Workflow receives that same presentation from the exact resolved View revision. Launch-context " "version 2, reference contract version 1, and result contract version 1 fail closed on unknown versions; cross-tenant " "active-object and result references are rejected." ), layer="configured", documentation_types=("admin",), audience=("system_admin", "tenant_admin", "module_admin"), translations={ "de": { "title": "Richtlinien fuer den Schnellzugriff", "summary": "Verfuegbarkeit und Standardreihenfolge registrierter kompakter Werkzeuge steuern.", "body": ( "Dokumentationsbücher stehen unmittelbar neben der sichtbaren Überschrift oder " "Kontextbezeichnung für das angezeigte Schnellzugriffswerkzeug, nicht zwischen ausführbaren " "Aktionsschaltflächen. Feldhilfe bleibt neben der Feldbezeichnung. " "Der Katalog folgt den Registrierungen installierter Module. Systemeinstellungen begrenzen Mandanten, " "Mandanteneinstellungen begrenzen Benutzer. Ein Eintrag kann verfuegbar, gesperrt oder erzwungen sein. " "Effektive Eintraege nennen System, Mandant, Benutzer oder Modul als Quelle fuer Verfuegbarkeit und Reihenfolge; " "Einstellungen fuer entfernte Eintraege behalten ihren Ebenennachweis. " "Ansichten und Berechtigungen bilden weitere Grenzen; Schnellzugriff erteilt selbst keinen Datenzugriff. " "Eine Ansicht darf Werkzeuge empfehlen oder die Leiste auf eine Teilmenge fokussieren, jedoch nur innerhalb " "der aktivierten, kontextgeeigneten und berechtigten Werkzeuge. Alle verfuegbaren Werkzeuge stellt nur diese " "berechtigungsabgeleitete Menge fuer die laufende Sitzung wieder her und speichert keine Umgehung. Workflow verwendet dieselbe Darstellung aus " "der genau aufgeloesten Ansichtsversion. Startkontext Version 2 sowie Verweis- und Ergebniskontrakt Version 1 " "lehnen unbekannte Versionen ab; mandantenfremde Objekt- und Ergebnisverweise werden verworfen." ), } }, metadata={ "help_contexts": [ "quick_access.admin.system", "quick_access.admin.tenant", "quick_access.field.availability", "quick_access.field.order", ] }, ), ) manifest = ModuleManifest( id=MODULE_ID, name=MODULE_NAME, version=MODULE_VERSION, dependencies=("access",), optional_dependencies=("views", "policy"), required_capabilities=( CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR, ), provides_interfaces=( ModuleInterfaceProvider(name="quick_access.runtime", version="1.0.0"), ModuleInterfaceProvider(name="quick_access.preferences", version="1.0.0"), ModuleInterfaceProvider( name=QUICK_ACCESS_DSAR_CAPABILITY, version="0.1.0", ), ), permissions=PERMISSIONS, role_templates=ROLE_TEMPLATES, route_factory=_router, capability_factories={QUICK_ACCESS_DSAR_CAPABILITY: _dsar_provider}, capability_documentation={ QUICK_ACCESS_DSAR_CAPABILITY: CapabilityDocumentation( label="Quick Access data-subject request provider", summary=( "Exports and deletes personal Quick Access preferences while " "retaining minimized tenant-policy attribution." ), contract_version="0.1.0", ), }, frontend=FrontendModule( module_id=MODULE_ID, package_name="@govoplan/quick-access-webui", view_surfaces=( ViewSurface( id="quick_access.rail", module_id=MODULE_ID, kind="quick_access", label="Quick Access rail", description="Optional right-side rail and overlay host.", order=5, required=True, ), ViewSurface( id="quick_access.drawer", module_id=MODULE_ID, kind="quick_access", label="Quick Access drawer", parent_id="quick_access.rail", order=10, required=True, ), ViewSurface( id="quick_access.settings.personal", module_id=MODULE_ID, kind="section", label="Personal Quick Access settings", order=20, ), ViewSurface( id="quick_access.admin.tenant", module_id=MODULE_ID, kind="section", label="Tenant Quick Access policy", order=30, ), ViewSurface( id="quick_access.admin.system", module_id=MODULE_ID, kind="section", label="System Quick Access policy", order=40, ), ), ), migration_spec=MigrationSpec( module_id=MODULE_ID, metadata=Base.metadata, script_location=str(Path(__file__).with_name("migrations") / "versions"), retirement_supported=True, retirement_provider=drop_table_retirement_provider( quick_access_models.QuickAccessProfile, label="Quick Access preferences", ), retirement_notes=( "Destructive retirement removes presentation preferences only; " "contributing module data and full-page routes remain unchanged." ), ), uninstall_guard_providers=( persistent_table_uninstall_guard( quick_access_models.QuickAccessProfile, label="Quick Access preferences", ), ), documentation=DOCUMENTATION, architecture=declared_module_architecture( layer="runtime_meta", kind="presentation", maturity="vertical_slice", documentation_ref="docs/QUICK_ACCESS.md", test_ref="tests/test_quick_access.py", known_limits=( "The first slice provides four stable categories; administrators cannot yet define additional category identities.", ), supported_authority_modes=("native_authoritative",), owned_concepts=( "Quick Access profile", "Quick Access rail", "Quick Access category ordering", ), non_owned_concepts=( "task", "calendar event", "mail message", "postbox message", "file", "authorization decision", ), reference_packages=("product.task-focused-workspace",), migration_docs=("docs/QUICK_ACCESS.md",), recovery_docs=("docs/QUICK_ACCESS.md",), security_docs=("docs/QUICK_ACCESS.md",), operations_docs=("docs/QUICK_ACCESS.md",), ), ) manifest = with_documentation_structured_translations( manifest, locale="de", translations=GERMAN_STRUCTURED_TRANSLATIONS ) def get_manifest() -> ModuleManifest: return manifest __all__ = [ "MODULE_ID", "MODULE_VERSION", "READ_SCOPE", "SYSTEM_ADMIN_SCOPE", "TENANT_ADMIN_SCOPE", "WRITE_SCOPE", "get_manifest", "manifest", ]