docs(calendar): complete German reference coverage
Module Package Release / publish-packages (push) Successful in 12s
Module Package Release / publish-packages (push) Successful in 12s
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/calendar-webui",
|
||||
"version": "0.1.18",
|
||||
"version": "0.1.20",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "webui/src/index.ts",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "govoplan-calendar"
|
||||
version = "0.1.18"
|
||||
version = "0.1.20"
|
||||
description = "GovOPlaN calendar module with VEVENT storage and WebUI integration."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
__all__ = ["__version__"]
|
||||
|
||||
__version__ = "0.1.18"
|
||||
__version__ = "0.1.20"
|
||||
|
||||
@@ -6,7 +6,10 @@ from pathlib import Path
|
||||
from sqlalchemy import inspect
|
||||
|
||||
from govoplan_calendar.backend.db import models as calendar_models # noqa: F401 - populate Calendar ORM metadata
|
||||
from govoplan_core.core.access import CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER
|
||||
from govoplan_core.core.access import (
|
||||
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
||||
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
||||
)
|
||||
from govoplan_core.core.calendar import (
|
||||
CALENDAR_AVAILABILITY_READ_SCOPE,
|
||||
CALENDAR_EVENT_WRITE_SCOPE,
|
||||
@@ -15,7 +18,10 @@ from govoplan_core.core.calendar import (
|
||||
CAPABILITY_CALENDAR_OUTBOX,
|
||||
CAPABILITY_CALENDAR_SCHEDULING,
|
||||
)
|
||||
from govoplan_core.core.module_guards import drop_table_retirement_provider, persistent_table_uninstall_guard
|
||||
from govoplan_core.core.module_guards import (
|
||||
drop_table_retirement_provider,
|
||||
persistent_table_uninstall_guard,
|
||||
)
|
||||
from govoplan_core.core.modules import (
|
||||
CapabilityDocumentation,
|
||||
DocumentationCondition,
|
||||
@@ -110,6 +116,7 @@ CALENDAR_ARCHITECTURE = ModuleArchitectureDeclaration(
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _read_only_calendar_provider(
|
||||
*,
|
||||
provider_id: str,
|
||||
@@ -249,13 +256,27 @@ CALENDAR_EXTERNAL_PROVIDERS = (
|
||||
ProviderObjectDeclaration(
|
||||
object_type="calendar_collection",
|
||||
field_groups=("identity", "display", "sync_state", "resource_mapping"),
|
||||
authority_modes=("external_authoritative", "external_mirror", "governed_sync"),
|
||||
authority_modes=(
|
||||
"external_authoritative",
|
||||
"external_mirror",
|
||||
"governed_sync",
|
||||
),
|
||||
default_authority_mode="governed_sync",
|
||||
),
|
||||
ProviderObjectDeclaration(
|
||||
object_type="calendar_event",
|
||||
field_groups=("identity", "schedule", "recurrence", "participants", "content"),
|
||||
authority_modes=("external_authoritative", "external_mirror", "governed_sync"),
|
||||
field_groups=(
|
||||
"identity",
|
||||
"schedule",
|
||||
"recurrence",
|
||||
"participants",
|
||||
"content",
|
||||
),
|
||||
authority_modes=(
|
||||
"external_authoritative",
|
||||
"external_mirror",
|
||||
"governed_sync",
|
||||
),
|
||||
default_authority_mode="governed_sync",
|
||||
),
|
||||
),
|
||||
@@ -272,19 +293,37 @@ CALENDAR_EXTERNAL_PROVIDERS = (
|
||||
outcome_unknown="A timed-out remote write is outcome-unknown and is reconciled before retry.",
|
||||
outcome_unknown_supported=True,
|
||||
evidence="The Open-Xchange profile reference, mapping references, operation intent, ETag, and reconciliation result are retained.",
|
||||
audit_event_types=("calendar.sync.requested", "calendar.sync.completed", "calendar.sync.conflict", "calendar.sync.reconciled"),
|
||||
audit_event_types=(
|
||||
"calendar.sync.requested",
|
||||
"calendar.sync.completed",
|
||||
"calendar.sync.conflict",
|
||||
"calendar.sync.reconciled",
|
||||
),
|
||||
correction="A later conditional update or tombstone corrects external state after reconciliation.",
|
||||
rollback="Remote effects are not treated as transactionally rollback-safe.",
|
||||
compensation="A compensating event update or delete may be queued after remote state is known.",
|
||||
reconciliation="Read by CalDAV href and VEVENT UID, compare ETag and content, then classify the result.",
|
||||
outage="The local projection remains available with stale markers and durable pending writes.",
|
||||
classifications=("internal", "confidential", "restricted"),
|
||||
purposes=("calendar collaboration", "availability", "resource booking", "meeting coordination"),
|
||||
purposes=(
|
||||
"calendar collaboration",
|
||||
"availability",
|
||||
"resource booking",
|
||||
"meeting coordination",
|
||||
),
|
||||
retention="Calendar event, outbox, audit, profile-binding, and credential retention remain independent policies.",
|
||||
secret_handling="Calendar stores only scoped credential references or encrypted Calendar-owned credentials.",
|
||||
),
|
||||
capability_names=(CAPABILITY_CALENDAR_EXTERNAL_PROFILES, CAPABILITY_CALENDAR_OUTBOX, CAPABILITY_CALENDAR_SCHEDULING),
|
||||
interface_names=("calendar.external_profiles", "calendar.outbox", "calendar.scheduling"),
|
||||
capability_names=(
|
||||
CAPABILITY_CALENDAR_EXTERNAL_PROFILES,
|
||||
CAPABILITY_CALENDAR_OUTBOX,
|
||||
CAPABILITY_CALENDAR_SCHEDULING,
|
||||
),
|
||||
interface_names=(
|
||||
"calendar.external_profiles",
|
||||
"calendar.outbox",
|
||||
"calendar.scheduling",
|
||||
),
|
||||
documentation_topic_ids=("calendar.external-sources-and-sync",),
|
||||
),
|
||||
_read_only_calendar_provider(
|
||||
@@ -325,10 +364,18 @@ def _calendar_retirement_provider(session: object | None, module_id: str):
|
||||
return plan
|
||||
|
||||
def executor(execute_session: object, execute_module_id: str) -> None:
|
||||
if not hasattr(execute_session, "get_bind") or not hasattr(execute_session, "query"):
|
||||
raise RuntimeError("No database session is available for Calendar credential retirement.")
|
||||
if inspect(execute_session.get_bind()).has_table(calendar_models.CalendarSyncCredential.__tablename__):
|
||||
from govoplan_calendar.backend.service import delete_calendar_credentials_for_retirement
|
||||
if not hasattr(execute_session, "get_bind") or not hasattr(
|
||||
execute_session, "query"
|
||||
):
|
||||
raise RuntimeError(
|
||||
"No database session is available for Calendar credential retirement."
|
||||
)
|
||||
if inspect(execute_session.get_bind()).has_table(
|
||||
calendar_models.CalendarSyncCredential.__tablename__
|
||||
):
|
||||
from govoplan_calendar.backend.service import (
|
||||
delete_calendar_credentials_for_retirement,
|
||||
)
|
||||
|
||||
delete_calendar_credentials_for_retirement(execute_session)
|
||||
base_executor(execute_session, execute_module_id)
|
||||
@@ -358,15 +405,51 @@ def _permission(scope: str, label: str, description: str) -> PermissionDefinitio
|
||||
|
||||
|
||||
PERMISSIONS = (
|
||||
_permission("calendar:calendar:read", "View calendars", "List tenant calendar collections and metadata."),
|
||||
_permission("calendar:calendar:write", "Manage calendars", "Create and edit tenant calendar collections."),
|
||||
_permission("calendar:calendar:admin", "Administer calendars", "Delete calendars and manage tenant-level calendar settings."),
|
||||
_permission("calendar:event:read", "View calendar events", "List and inspect calendar events."),
|
||||
_permission(CALENDAR_EVENT_WRITE_SCOPE, "Manage calendar events", "Create and edit calendar events."),
|
||||
_permission("calendar:event:delete", "Delete calendar events", "Delete or cancel calendar events where policy allows it."),
|
||||
_permission("calendar:event:import", "Import iCalendar events", "Import VEVENT data from iCalendar sources."),
|
||||
_permission("calendar:event:export", "Export iCalendar events", "Export events as text/calendar VEVENT data."),
|
||||
_permission(CALENDAR_AVAILABILITY_READ_SCOPE, "Read availability", "Read free/busy and availability data for integrations."),
|
||||
_permission(
|
||||
"calendar:calendar:read",
|
||||
"View calendars",
|
||||
"List tenant calendar collections and metadata.",
|
||||
),
|
||||
_permission(
|
||||
"calendar:calendar:write",
|
||||
"Manage calendars",
|
||||
"Create and edit tenant calendar collections.",
|
||||
),
|
||||
_permission(
|
||||
"calendar:calendar:admin",
|
||||
"Administer calendars",
|
||||
"Delete calendars and manage tenant-level calendar settings.",
|
||||
),
|
||||
_permission(
|
||||
"calendar:event:read",
|
||||
"View calendar events",
|
||||
"List and inspect calendar events.",
|
||||
),
|
||||
_permission(
|
||||
CALENDAR_EVENT_WRITE_SCOPE,
|
||||
"Manage calendar events",
|
||||
"Create and edit calendar events.",
|
||||
),
|
||||
_permission(
|
||||
"calendar:event:delete",
|
||||
"Delete calendar events",
|
||||
"Delete or cancel calendar events where policy allows it.",
|
||||
),
|
||||
_permission(
|
||||
"calendar:event:import",
|
||||
"Import iCalendar events",
|
||||
"Import VEVENT data from iCalendar sources.",
|
||||
),
|
||||
_permission(
|
||||
"calendar:event:export",
|
||||
"Export iCalendar events",
|
||||
"Export events as text/calendar VEVENT data.",
|
||||
),
|
||||
_permission(
|
||||
CALENDAR_AVAILABILITY_READ_SCOPE,
|
||||
"Read availability",
|
||||
"Read free/busy and availability data for integrations.",
|
||||
),
|
||||
)
|
||||
|
||||
ROLE_TEMPLATES = (
|
||||
@@ -410,11 +493,32 @@ def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
|
||||
)
|
||||
|
||||
return {
|
||||
"calendars": session.query(CalendarCollection).filter(CalendarCollection.tenant_id == tenant_id, CalendarCollection.deleted_at.is_(None)).count(),
|
||||
"calendar_events": session.query(CalendarEvent).filter(CalendarEvent.tenant_id == tenant_id, CalendarEvent.deleted_at.is_(None)).count(),
|
||||
"calendar_sync_sources": session.query(CalendarSyncSource).filter(CalendarSyncSource.tenant_id == tenant_id, CalendarSyncSource.deleted_at.is_(None)).count(),
|
||||
"calendar_sync_credentials": session.query(CalendarSyncCredential).filter(CalendarSyncCredential.tenant_id == tenant_id, CalendarSyncCredential.deleted_at.is_(None)).count(),
|
||||
"calendar_view_preferences": session.query(CalendarViewPreference).filter(CalendarViewPreference.tenant_id == tenant_id).count(),
|
||||
"calendars": session.query(CalendarCollection)
|
||||
.filter(
|
||||
CalendarCollection.tenant_id == tenant_id,
|
||||
CalendarCollection.deleted_at.is_(None),
|
||||
)
|
||||
.count(),
|
||||
"calendar_events": session.query(CalendarEvent)
|
||||
.filter(
|
||||
CalendarEvent.tenant_id == tenant_id, CalendarEvent.deleted_at.is_(None)
|
||||
)
|
||||
.count(),
|
||||
"calendar_sync_sources": session.query(CalendarSyncSource)
|
||||
.filter(
|
||||
CalendarSyncSource.tenant_id == tenant_id,
|
||||
CalendarSyncSource.deleted_at.is_(None),
|
||||
)
|
||||
.count(),
|
||||
"calendar_sync_credentials": session.query(CalendarSyncCredential)
|
||||
.filter(
|
||||
CalendarSyncCredential.tenant_id == tenant_id,
|
||||
CalendarSyncCredential.deleted_at.is_(None),
|
||||
)
|
||||
.count(),
|
||||
"calendar_view_preferences": session.query(CalendarViewPreference)
|
||||
.filter(CalendarViewPreference.tenant_id == tenant_id)
|
||||
.count(),
|
||||
"calendar_outbox_pending": session.query(CalendarOutboxOperation)
|
||||
.filter(
|
||||
CalendarOutboxOperation.tenant_id == tenant_id,
|
||||
@@ -464,7 +568,9 @@ def _calendar_outbox_provider(context: ModuleContext) -> object:
|
||||
|
||||
def _calendar_external_profile_provider(context: ModuleContext) -> object:
|
||||
del context
|
||||
from govoplan_calendar.backend.capabilities import SqlCalendarExternalProfileProvider
|
||||
from govoplan_calendar.backend.capabilities import (
|
||||
SqlCalendarExternalProfileProvider,
|
||||
)
|
||||
|
||||
return SqlCalendarExternalProfileProvider()
|
||||
|
||||
@@ -509,9 +615,22 @@ def _open_xchange_provider_states(context):
|
||||
manifest = ModuleManifest(
|
||||
id="calendar",
|
||||
name="Calendar",
|
||||
version="0.1.19",
|
||||
required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR),
|
||||
optional_dependencies=("mail", "tasks", "scheduling", "appointments", "workflow_engine", "notifications", "dms", "connectors", "search"),
|
||||
version="0.1.20",
|
||||
required_capabilities=(
|
||||
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
||||
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
||||
),
|
||||
optional_dependencies=(
|
||||
"mail",
|
||||
"tasks",
|
||||
"scheduling",
|
||||
"appointments",
|
||||
"workflow_engine",
|
||||
"notifications",
|
||||
"dms",
|
||||
"connectors",
|
||||
"search",
|
||||
),
|
||||
provides_interfaces=(
|
||||
ModuleInterfaceProvider(name="calendar.outbox", version="0.1.8"),
|
||||
ModuleInterfaceProvider(name="calendar.scheduling", version="0.1.9"),
|
||||
@@ -578,7 +697,12 @@ manifest = ModuleManifest(
|
||||
),
|
||||
layer="configured",
|
||||
documentation_types=("admin",),
|
||||
audience=("privacy_officer", "calendar_manager", "records_manager", "operator"),
|
||||
audience=(
|
||||
"privacy_officer",
|
||||
"calendar_manager",
|
||||
"records_manager",
|
||||
"operator",
|
||||
),
|
||||
order=17,
|
||||
conditions=(
|
||||
DocumentationCondition(
|
||||
@@ -603,6 +727,29 @@ manifest = ModuleManifest(
|
||||
),
|
||||
),
|
||||
related_modules=("access", "audit", "ops", "scheduling"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Calendar-Daten in einer Betroffenenanfrage prüfen",
|
||||
"summary": (
|
||||
"Mandantenbezogene Ereignis-, Teilnahme-, Präferenz-, Synchronisations- und Migrationsmetadaten erfassen, ohne "
|
||||
"Connector-Geheimnisse offenzulegen."
|
||||
),
|
||||
"body": (
|
||||
"Der DSAR-Provider von Calendar durchsucht den wirksamen Mandanten anhand normalisierter E-Mail-Adressen von "
|
||||
"Organisierenden oder Teilnehmenden, direkter Mitgliedschaftsverweise und namensraumgebundener Calendar-Sammlungs- oder "
|
||||
"Ereignisverweise. Er liefert nur den passenden Organisierenden- oder Teilnehmendenausschnitt zusammen mit berechtigtem "
|
||||
"Ereignis- und Sammlungskontext, persönlichen Ansichtspräferenzen, eigener Synchronisationskonfiguration, dauerhaften "
|
||||
"Outbox-Ergebnissen und Migrationsnachweisen. Ausgeschlossen sind rohe ICS- und vollständige iCalendar-Daten, "
|
||||
"Sammlungs-URLs, entfernte hrefs und ETags, Synchronisationstoken, Zugangsdaten, verschlüsselte Geheimnisse, Worker-Leases, "
|
||||
"Idempotenzmaterial, Provider-Fehlertexte sowie unbeteiligte Personen oder Ereignisse. Synchronisierte, korrelierte, "
|
||||
"gelöschte, eingereihte und migrierte Zustände bleiben mit Begründung erhalten, weil sie institutionelle Liefer-, Abgleich- "
|
||||
"oder Wiederherstellungsnachweise sind. Lokale Ereignis-, Sammlungs- und aktive Zugangsdatenmetadaten erfordern eine "
|
||||
"koordinierte manuelle Prüfung. Nach erneuter Prüfung von Mandant und Eigentum darf der Provider die persönliche "
|
||||
"Calendar-Ansichtspräferenz idempotent löschen; Ereignisse, Teilnehmerlisten, synchronisierte Ressourcen, Zugangsdaten "
|
||||
"oder Migrationsdatensätze verändert er niemals direkt."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/admin?section=tenant-data-subject-requests",
|
||||
@@ -653,7 +800,10 @@ manifest = ModuleManifest(
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={"kind": "reference", "help_contexts": ["calendar.quick_access.agenda"]},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"help_contexts": ["calendar.quick_access.agenda"],
|
||||
},
|
||||
order=18,
|
||||
),
|
||||
DocumentationTopic(
|
||||
@@ -670,6 +820,20 @@ manifest = ModuleManifest(
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("user", "calendar_manager", "administrator"),
|
||||
related_modules=("search",),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Berechtigte Kalenderereignisse durchsuchen",
|
||||
"summary": (
|
||||
"Ereignistitel, Zeitpläne, Orte und Beschreibungen für die berechtigungsbewusste Plattform-Suche bereitstellen."
|
||||
),
|
||||
"body": (
|
||||
"Ist Search installiert, liefert Calendar nicht gelöschte Ereignisse aus sichtbaren Kalendern. Jedes Ergebnis bleibt "
|
||||
"mandantengebunden und prüft vor der Ausgabe erneut die aktuelle Leseberechtigung für Ereignisse sowie bei privaten "
|
||||
"Kalendern Eigentum oder Gruppenmitgliedschaft. Festgeschriebene Ereignisänderungen aktualisieren den abgeleiteten Index "
|
||||
"über die Plattform-Event-Outbox; ein Neuaufbau von Search verändert niemals Calendar-Daten."
|
||||
),
|
||||
}
|
||||
},
|
||||
order=19,
|
||||
),
|
||||
DocumentationTopic(
|
||||
@@ -680,6 +844,24 @@ manifest = ModuleManifest(
|
||||
documentation_types=("user",),
|
||||
audience=("user", "calendar_manager"),
|
||||
related_modules=("scheduling", "notifications"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Kalender und Ereignisse verwenden",
|
||||
"summary": (
|
||||
"Kalendersammlungen anlegen und ganztägige oder zeitgebundene Ereignisse in fortlaufender, Monats-, Wochen-, "
|
||||
"Arbeitswochen- und Tagesansicht bearbeiten."
|
||||
),
|
||||
"body": (
|
||||
"Calendar merkt sich gewählte Ansicht und Präferenzen. Ereignisse können mit der entsprechenden Berechtigung angelegt, "
|
||||
"bearbeitet, verschoben, in ihrer Dauer geändert, wiederholt, importiert, exportiert oder gelöscht werden. Ganztägige "
|
||||
"Ereignisse verwenden Datumswerte statt lokaler Uhrzeiten; zeitgebundene Ereignisse bewahren zeitzonenbezogene Start- und "
|
||||
"Endwerte. Scheduling darf eine ausgewählte vorläufige Vormerkung direkt bestätigen und nicht verwendete Vormerkungen "
|
||||
"über die neutrale Calendar-Fähigkeit idempotent freigeben. Calendar besitzt den entstehenden lokalen Löschmarker, die "
|
||||
"synchronisierte Outbox-Operation sowie Wiederholungs- und Abgleichsnachweise; ein bereits freigegebenes oder fehlendes "
|
||||
"Ereignis gilt bei Wiederholung als akzeptiert und nicht als doppelter Fehler."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"help_contexts": [
|
||||
@@ -706,6 +888,29 @@ manifest = ModuleManifest(
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("user", "calendar_manager", "operator"),
|
||||
related_modules=("connectors", "audit", "ops"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Externe Kalender verbinden und synchronisieren",
|
||||
"summary": (
|
||||
"Calendar unterstützt lokale Sammlungen, bidirektionale CalDAV- und Open-Xchange-Profile sowie schreibgeschützte "
|
||||
"ICS-/webcal-, Microsoft-Graph- und Exchange-Web-Services-Quellen."
|
||||
),
|
||||
"body": (
|
||||
"Jede externe Quelle hält URL, Synchronisationsrichtung, Status und Zugangsdatenverweis gemeinsam mit der "
|
||||
"Calendar-Sammlung. Open-Xchange verwendet den bewährten CalDAV-Transport und bewahrt dabei Connector-Profil-, "
|
||||
"Identitäts-/Gruppenzuordnungs- und Ressourcenkalenderverweise. Manuelle und geplante Synchronisation zeichnet begrenzte "
|
||||
"Ergebnisse auf. Geplante Quellen- und Outbox-Worker teilen Arbeit nach Mandantenberechtigung; wird Calendar deaktiviert, "
|
||||
"bleiben angenommene Vorgänge erhalten und verlangen Betriebshandeln, statt einen entfernten Provider anzusprechen. "
|
||||
"CalDAV-Schreibvorgänge verwenden bedingte Anfragen und dauerhaften Outbox-Zustand. Konflikte und unbekannte Ergebnisse "
|
||||
"erfordern Synchronisation oder ausdrücklichen Abgleich statt blinder Wiederholung. Das Verschieben zwischen zwei "
|
||||
"bidirektionalen CalDAV-Kalendern ist ein administrativ genehmigter Migrationsstapel: Alle Zielressourcen müssen kopiert "
|
||||
"sein, bevor eine Quellressource bedingt mit ihrem aufgezeichneten ETag gelöscht wird. Änderungen an Kalender und "
|
||||
"Ereignissen bleiben gesperrt, während Fortschritt, Konflikte, Abbruchmöglichkeit und Nachweise sichtbar sind. Das "
|
||||
"Entfernen einer externen Quelle entfernt die Verbindung; das Löschen eines lokalen Kalenders löscht dessen Ereignisse "
|
||||
"nach Bestätigung oder Übertragung."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"help_contexts": [
|
||||
@@ -742,6 +947,23 @@ manifest = ModuleManifest(
|
||||
),
|
||||
),
|
||||
related_modules=("campaigns", "mail", "audit"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Campaign-Einladungen und Antworten nachverfolgen",
|
||||
"summary": (
|
||||
"Akzeptierte Campaign-Einladungslieferungen als korrelierte VEVENTs spiegeln und den Teilnahmestatus in Calendar "
|
||||
"maßgeblich halten."
|
||||
),
|
||||
"body": (
|
||||
"Campaign kann vor der Zustellung einen METHOD:REQUEST-Anhang erzeugen und das korrelierte Calendar-Ereignis erst nach "
|
||||
"Annahme der Lieferung anlegen oder aktualisieren. Calendar speichert Korrelation, PARTSTAT der Teilnehmenden, "
|
||||
"Antwortzeit, begrenzte Nachweise, Synchronisationszustand und etwaiges eingeschränktes Verhalten. Beobachtet Mail einen "
|
||||
"berechtigten IMAP-Ordner, werden METHOD:REPLY-Teile idempotent an Calendar weitergeleitet. Campaign-Berichte fragen den "
|
||||
"aktuellen Zustand gesammelt ab, statt ihn in Campaign-Datensätze zu kopieren. Wiederkehrende Campaign-Einladungsserien "
|
||||
"bleiben ein eigener Ablauf."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={"kind": "workflow"},
|
||||
),
|
||||
DocumentationTopic(
|
||||
@@ -759,6 +981,22 @@ manifest = ModuleManifest(
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("calendar_manager", "operator", "tenant_admin"),
|
||||
related_modules=("ops", "audit"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Synchronisierte Kalenderschreibvorgänge wiederherstellen",
|
||||
"summary": (
|
||||
"Ungelöste CalDAV-Schreibvorgänge prüfen und ausschließlich die neueste sichere Ressourcengeneration wiederherstellen."
|
||||
),
|
||||
"body": (
|
||||
"Calendar-Administrierende öffnen Ausgehende Änderungen in den Einstellungen eines synchronisierten Kalenders. Die "
|
||||
"begrenzte Historie erläutert Versuche, Konflikte, endgültig fehlgeschlagene Arbeit, veraltete Generationen, deaktivierte "
|
||||
"Quellen und aktive Worker-Leases. Wiederholen reiht einen fehlgeschlagenen Sollzustand erneut ein, Abgleichen vergleicht "
|
||||
"ihn mit der entfernten Ressource und Verwerfen gibt den lokalen Sollzustand nach einer getrennten Warnung auf, sodass die "
|
||||
"nächste vollständige Synchronisation den entfernten Zustand übernehmen kann. Automatische Auslieferung und fällige "
|
||||
"Quellenausführung bleiben reine Worker-Operationen für Dienstkonten und werden nicht als interaktive Steuerungen angeboten."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "runbook",
|
||||
"help_contexts": [
|
||||
@@ -787,7 +1025,15 @@ manifest = ModuleManifest(
|
||||
audience=("privacy_officer", "calendar_manager", "records_manager"),
|
||||
),
|
||||
},
|
||||
nav_items=(NavItem(path="/calendar", label="Calendar", icon="calendar", required_any=("calendar:event:read",), order=55),),
|
||||
nav_items=(
|
||||
NavItem(
|
||||
path="/calendar",
|
||||
label="Calendar",
|
||||
icon="calendar",
|
||||
required_any=("calendar:event:read",),
|
||||
order=55,
|
||||
),
|
||||
),
|
||||
frontend=FrontendModule(
|
||||
module_id="calendar",
|
||||
package_name="@govoplan/calendar-webui",
|
||||
@@ -799,7 +1045,15 @@ manifest = ModuleManifest(
|
||||
order=55,
|
||||
),
|
||||
),
|
||||
nav_items=(NavItem(path="/calendar", label="Calendar", icon="calendar", required_any=("calendar:event:read",), order=55),),
|
||||
nav_items=(
|
||||
NavItem(
|
||||
path="/calendar",
|
||||
label="Calendar",
|
||||
icon="calendar",
|
||||
required_any=("calendar:event:read",),
|
||||
order=55,
|
||||
),
|
||||
),
|
||||
view_surfaces=(
|
||||
ViewSurface(
|
||||
id="calendar.navigation",
|
||||
|
||||
@@ -10,6 +10,14 @@ REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
class CalendarInterfaceDocumentationContractTests(unittest.TestCase):
|
||||
def test_all_static_topics_have_complete_german_content(self) -> None:
|
||||
for topic in get_manifest().documentation:
|
||||
german = (topic.translations or {}).get("de", {})
|
||||
self.assertEqual({"title", "summary", "body"}, set(german), topic.id)
|
||||
self.assertTrue(
|
||||
all(str(value).strip() for value in german.values()), topic.id
|
||||
)
|
||||
|
||||
def test_backend_surfaces_and_hierarchy_remain_declared(self) -> None:
|
||||
frontend = get_manifest().frontend
|
||||
self.assertIsNotNone(frontend)
|
||||
@@ -31,13 +39,21 @@ class CalendarInterfaceDocumentationContractTests(unittest.TestCase):
|
||||
}
|
||||
self.assertEqual(expected, set(surfaces))
|
||||
self.assertEqual("calendar.page", surfaces["calendar.page.sidebar"].parent_id)
|
||||
self.assertEqual("calendar.page.sidebar", surfaces["calendar.page.agenda"].parent_id)
|
||||
self.assertEqual(
|
||||
"calendar.page.sidebar", surfaces["calendar.page.agenda"].parent_id
|
||||
)
|
||||
self.assertEqual("calendar.page", surfaces["calendar.page.workspace"].parent_id)
|
||||
self.assertEqual("calendar.page", surfaces["calendar.event-editor"].parent_id)
|
||||
self.assertEqual("calendar.page.sidebar", surfaces["calendar.collection-editor"].parent_id)
|
||||
self.assertEqual("calendar.collection-editor", surfaces["calendar.sync-status"].parent_id)
|
||||
self.assertEqual(
|
||||
"calendar.page.sidebar", surfaces["calendar.collection-editor"].parent_id
|
||||
)
|
||||
self.assertEqual(
|
||||
"calendar.collection-editor", surfaces["calendar.sync-status"].parent_id
|
||||
)
|
||||
self.assertEqual("calendar.sync-status", surfaces["calendar.outbox"].parent_id)
|
||||
self.assertEqual("calendar.sync-status", surfaces["calendar.migration"].parent_id)
|
||||
self.assertEqual(
|
||||
"calendar.sync-status", surfaces["calendar.migration"].parent_id
|
||||
)
|
||||
|
||||
def test_help_and_consequence_metadata_remain_published(self) -> None:
|
||||
topics = {topic.id: topic for topic in get_manifest().documentation}
|
||||
@@ -63,9 +79,9 @@ class CalendarInterfaceDocumentationContractTests(unittest.TestCase):
|
||||
quick_access = (
|
||||
REPO_ROOT / "webui/src/features/calendar/CalendarQuickAccess.tsx"
|
||||
).read_text(encoding="utf-8")
|
||||
page = (
|
||||
REPO_ROOT / "webui/src/features/calendar/CalendarPage.tsx"
|
||||
).read_text(encoding="utf-8")
|
||||
page = (REPO_ROOT / "webui/src/features/calendar/CalendarPage.tsx").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
self.assertIn("const AGENDA_LIMIT = 7", quick_access)
|
||||
self.assertIn("launchContext.temporalContext", quick_access)
|
||||
@@ -75,13 +91,28 @@ class CalendarInterfaceDocumentationContractTests(unittest.TestCase):
|
||||
self.assertIn('parameters.get("quickAction") !== "create-event"', page)
|
||||
|
||||
def test_webui_uses_shared_help_guard_and_confirmation_components(self) -> None:
|
||||
event_dialog = (REPO_ROOT / "webui/src/features/calendar/CalendarEventDialog.tsx").read_text(encoding="utf-8")
|
||||
collection_dialog = (REPO_ROOT / "webui/src/features/calendar/CalendarCollectionDialogs.tsx").read_text(encoding="utf-8")
|
||||
settings_panel = (REPO_ROOT / "webui/src/features/calendar/CalendarSettingsPanel.tsx").read_text(encoding="utf-8")
|
||||
event_dialog = (
|
||||
REPO_ROOT / "webui/src/features/calendar/CalendarEventDialog.tsx"
|
||||
).read_text(encoding="utf-8")
|
||||
collection_dialog = (
|
||||
REPO_ROOT / "webui/src/features/calendar/CalendarCollectionDialogs.tsx"
|
||||
).read_text(encoding="utf-8")
|
||||
settings_panel = (
|
||||
REPO_ROOT / "webui/src/features/calendar/CalendarSettingsPanel.tsx"
|
||||
).read_text(encoding="utf-8")
|
||||
|
||||
for component in ("ActionBlockerHint", "ConfirmDialog", "DocumentationHelpLink", "useUnsavedDraftGuard"):
|
||||
for component in (
|
||||
"ActionBlockerHint",
|
||||
"ConfirmDialog",
|
||||
"DocumentationHelpLink",
|
||||
"useUnsavedDraftGuard",
|
||||
):
|
||||
self.assertIn(component, event_dialog)
|
||||
for component in ("ActionBlockerHint", "DocumentationHelpLink", "useUnsavedDraftGuard"):
|
||||
for component in (
|
||||
"ActionBlockerHint",
|
||||
"DocumentationHelpLink",
|
||||
"useUnsavedDraftGuard",
|
||||
):
|
||||
self.assertIn(component, collection_dialog)
|
||||
for component in ("DocumentationHelpLink", "useUnsavedDraftGuard"):
|
||||
self.assertIn(component, settings_panel)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/calendar-webui",
|
||||
"version": "0.1.18",
|
||||
"version": "0.1.20",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
Reference in New Issue
Block a user