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",
|
"name": "@govoplan/calendar-webui",
|
||||||
"version": "0.1.18",
|
"version": "0.1.20",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "webui/src/index.ts",
|
"main": "webui/src/index.ts",
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "govoplan-calendar"
|
name = "govoplan-calendar"
|
||||||
version = "0.1.18"
|
version = "0.1.20"
|
||||||
description = "GovOPlaN calendar module with VEVENT storage and WebUI integration."
|
description = "GovOPlaN calendar module with VEVENT storage and WebUI integration."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
__all__ = ["__version__"]
|
__all__ = ["__version__"]
|
||||||
|
|
||||||
__version__ = "0.1.18"
|
__version__ = "0.1.20"
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ from pathlib import Path
|
|||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
|
|
||||||
from govoplan_calendar.backend.db import models as calendar_models # noqa: F401 - populate Calendar ORM metadata
|
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 (
|
from govoplan_core.core.calendar import (
|
||||||
CALENDAR_AVAILABILITY_READ_SCOPE,
|
CALENDAR_AVAILABILITY_READ_SCOPE,
|
||||||
CALENDAR_EVENT_WRITE_SCOPE,
|
CALENDAR_EVENT_WRITE_SCOPE,
|
||||||
@@ -15,7 +18,10 @@ from govoplan_core.core.calendar import (
|
|||||||
CAPABILITY_CALENDAR_OUTBOX,
|
CAPABILITY_CALENDAR_OUTBOX,
|
||||||
CAPABILITY_CALENDAR_SCHEDULING,
|
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 (
|
from govoplan_core.core.modules import (
|
||||||
CapabilityDocumentation,
|
CapabilityDocumentation,
|
||||||
DocumentationCondition,
|
DocumentationCondition,
|
||||||
@@ -110,6 +116,7 @@ CALENDAR_ARCHITECTURE = ModuleArchitectureDeclaration(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _read_only_calendar_provider(
|
def _read_only_calendar_provider(
|
||||||
*,
|
*,
|
||||||
provider_id: str,
|
provider_id: str,
|
||||||
@@ -249,13 +256,27 @@ CALENDAR_EXTERNAL_PROVIDERS = (
|
|||||||
ProviderObjectDeclaration(
|
ProviderObjectDeclaration(
|
||||||
object_type="calendar_collection",
|
object_type="calendar_collection",
|
||||||
field_groups=("identity", "display", "sync_state", "resource_mapping"),
|
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",
|
default_authority_mode="governed_sync",
|
||||||
),
|
),
|
||||||
ProviderObjectDeclaration(
|
ProviderObjectDeclaration(
|
||||||
object_type="calendar_event",
|
object_type="calendar_event",
|
||||||
field_groups=("identity", "schedule", "recurrence", "participants", "content"),
|
field_groups=(
|
||||||
authority_modes=("external_authoritative", "external_mirror", "governed_sync"),
|
"identity",
|
||||||
|
"schedule",
|
||||||
|
"recurrence",
|
||||||
|
"participants",
|
||||||
|
"content",
|
||||||
|
),
|
||||||
|
authority_modes=(
|
||||||
|
"external_authoritative",
|
||||||
|
"external_mirror",
|
||||||
|
"governed_sync",
|
||||||
|
),
|
||||||
default_authority_mode="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="A timed-out remote write is outcome-unknown and is reconciled before retry.",
|
||||||
outcome_unknown_supported=True,
|
outcome_unknown_supported=True,
|
||||||
evidence="The Open-Xchange profile reference, mapping references, operation intent, ETag, and reconciliation result are retained.",
|
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.",
|
correction="A later conditional update or tombstone corrects external state after reconciliation.",
|
||||||
rollback="Remote effects are not treated as transactionally rollback-safe.",
|
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.",
|
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.",
|
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.",
|
outage="The local projection remains available with stale markers and durable pending writes.",
|
||||||
classifications=("internal", "confidential", "restricted"),
|
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.",
|
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.",
|
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),
|
capability_names=(
|
||||||
interface_names=("calendar.external_profiles", "calendar.outbox", "calendar.scheduling"),
|
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",),
|
documentation_topic_ids=("calendar.external-sources-and-sync",),
|
||||||
),
|
),
|
||||||
_read_only_calendar_provider(
|
_read_only_calendar_provider(
|
||||||
@@ -325,10 +364,18 @@ def _calendar_retirement_provider(session: object | None, module_id: str):
|
|||||||
return plan
|
return plan
|
||||||
|
|
||||||
def executor(execute_session: object, execute_module_id: str) -> None:
|
def executor(execute_session: object, execute_module_id: str) -> None:
|
||||||
if not hasattr(execute_session, "get_bind") or not hasattr(execute_session, "query"):
|
if not hasattr(execute_session, "get_bind") or not hasattr(
|
||||||
raise RuntimeError("No database session is available for Calendar credential retirement.")
|
execute_session, "query"
|
||||||
if inspect(execute_session.get_bind()).has_table(calendar_models.CalendarSyncCredential.__tablename__):
|
):
|
||||||
from govoplan_calendar.backend.service import delete_calendar_credentials_for_retirement
|
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)
|
delete_calendar_credentials_for_retirement(execute_session)
|
||||||
base_executor(execute_session, execute_module_id)
|
base_executor(execute_session, execute_module_id)
|
||||||
@@ -358,15 +405,51 @@ def _permission(scope: str, label: str, description: str) -> PermissionDefinitio
|
|||||||
|
|
||||||
|
|
||||||
PERMISSIONS = (
|
PERMISSIONS = (
|
||||||
_permission("calendar:calendar:read", "View calendars", "List tenant calendar collections and metadata."),
|
_permission(
|
||||||
_permission("calendar:calendar:write", "Manage calendars", "Create and edit tenant calendar collections."),
|
"calendar:calendar:read",
|
||||||
_permission("calendar:calendar:admin", "Administer calendars", "Delete calendars and manage tenant-level calendar settings."),
|
"View calendars",
|
||||||
_permission("calendar:event:read", "View calendar events", "List and inspect calendar events."),
|
"List tenant calendar collections and metadata.",
|
||||||
_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(
|
||||||
_permission("calendar:event:import", "Import iCalendar events", "Import VEVENT data from iCalendar sources."),
|
"calendar:calendar:write",
|
||||||
_permission("calendar:event:export", "Export iCalendar events", "Export events as text/calendar VEVENT data."),
|
"Manage calendars",
|
||||||
_permission(CALENDAR_AVAILABILITY_READ_SCOPE, "Read availability", "Read free/busy and availability data for integrations."),
|
"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 = (
|
ROLE_TEMPLATES = (
|
||||||
@@ -410,11 +493,32 @@ def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"calendars": session.query(CalendarCollection).filter(CalendarCollection.tenant_id == tenant_id, CalendarCollection.deleted_at.is_(None)).count(),
|
"calendars": session.query(CalendarCollection)
|
||||||
"calendar_events": session.query(CalendarEvent).filter(CalendarEvent.tenant_id == tenant_id, CalendarEvent.deleted_at.is_(None)).count(),
|
.filter(
|
||||||
"calendar_sync_sources": session.query(CalendarSyncSource).filter(CalendarSyncSource.tenant_id == tenant_id, CalendarSyncSource.deleted_at.is_(None)).count(),
|
CalendarCollection.tenant_id == tenant_id,
|
||||||
"calendar_sync_credentials": session.query(CalendarSyncCredential).filter(CalendarSyncCredential.tenant_id == tenant_id, CalendarSyncCredential.deleted_at.is_(None)).count(),
|
CalendarCollection.deleted_at.is_(None),
|
||||||
"calendar_view_preferences": session.query(CalendarViewPreference).filter(CalendarViewPreference.tenant_id == tenant_id).count(),
|
)
|
||||||
|
.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)
|
"calendar_outbox_pending": session.query(CalendarOutboxOperation)
|
||||||
.filter(
|
.filter(
|
||||||
CalendarOutboxOperation.tenant_id == tenant_id,
|
CalendarOutboxOperation.tenant_id == tenant_id,
|
||||||
@@ -464,7 +568,9 @@ def _calendar_outbox_provider(context: ModuleContext) -> object:
|
|||||||
|
|
||||||
def _calendar_external_profile_provider(context: ModuleContext) -> object:
|
def _calendar_external_profile_provider(context: ModuleContext) -> object:
|
||||||
del context
|
del context
|
||||||
from govoplan_calendar.backend.capabilities import SqlCalendarExternalProfileProvider
|
from govoplan_calendar.backend.capabilities import (
|
||||||
|
SqlCalendarExternalProfileProvider,
|
||||||
|
)
|
||||||
|
|
||||||
return SqlCalendarExternalProfileProvider()
|
return SqlCalendarExternalProfileProvider()
|
||||||
|
|
||||||
@@ -509,9 +615,22 @@ def _open_xchange_provider_states(context):
|
|||||||
manifest = ModuleManifest(
|
manifest = ModuleManifest(
|
||||||
id="calendar",
|
id="calendar",
|
||||||
name="Calendar",
|
name="Calendar",
|
||||||
version="0.1.19",
|
version="0.1.20",
|
||||||
required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR),
|
required_capabilities=(
|
||||||
optional_dependencies=("mail", "tasks", "scheduling", "appointments", "workflow_engine", "notifications", "dms", "connectors", "search"),
|
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
||||||
|
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
||||||
|
),
|
||||||
|
optional_dependencies=(
|
||||||
|
"mail",
|
||||||
|
"tasks",
|
||||||
|
"scheduling",
|
||||||
|
"appointments",
|
||||||
|
"workflow_engine",
|
||||||
|
"notifications",
|
||||||
|
"dms",
|
||||||
|
"connectors",
|
||||||
|
"search",
|
||||||
|
),
|
||||||
provides_interfaces=(
|
provides_interfaces=(
|
||||||
ModuleInterfaceProvider(name="calendar.outbox", version="0.1.8"),
|
ModuleInterfaceProvider(name="calendar.outbox", version="0.1.8"),
|
||||||
ModuleInterfaceProvider(name="calendar.scheduling", version="0.1.9"),
|
ModuleInterfaceProvider(name="calendar.scheduling", version="0.1.9"),
|
||||||
@@ -578,7 +697,12 @@ manifest = ModuleManifest(
|
|||||||
),
|
),
|
||||||
layer="configured",
|
layer="configured",
|
||||||
documentation_types=("admin",),
|
documentation_types=("admin",),
|
||||||
audience=("privacy_officer", "calendar_manager", "records_manager", "operator"),
|
audience=(
|
||||||
|
"privacy_officer",
|
||||||
|
"calendar_manager",
|
||||||
|
"records_manager",
|
||||||
|
"operator",
|
||||||
|
),
|
||||||
order=17,
|
order=17,
|
||||||
conditions=(
|
conditions=(
|
||||||
DocumentationCondition(
|
DocumentationCondition(
|
||||||
@@ -603,6 +727,29 @@ manifest = ModuleManifest(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
related_modules=("access", "audit", "ops", "scheduling"),
|
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={
|
metadata={
|
||||||
"kind": "workflow",
|
"kind": "workflow",
|
||||||
"route": "/admin?section=tenant-data-subject-requests",
|
"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,
|
order=18,
|
||||||
),
|
),
|
||||||
DocumentationTopic(
|
DocumentationTopic(
|
||||||
@@ -670,6 +820,20 @@ manifest = ModuleManifest(
|
|||||||
documentation_types=("admin", "user"),
|
documentation_types=("admin", "user"),
|
||||||
audience=("user", "calendar_manager", "administrator"),
|
audience=("user", "calendar_manager", "administrator"),
|
||||||
related_modules=("search",),
|
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,
|
order=19,
|
||||||
),
|
),
|
||||||
DocumentationTopic(
|
DocumentationTopic(
|
||||||
@@ -680,6 +844,24 @@ manifest = ModuleManifest(
|
|||||||
documentation_types=("user",),
|
documentation_types=("user",),
|
||||||
audience=("user", "calendar_manager"),
|
audience=("user", "calendar_manager"),
|
||||||
related_modules=("scheduling", "notifications"),
|
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={
|
metadata={
|
||||||
"kind": "reference",
|
"kind": "reference",
|
||||||
"help_contexts": [
|
"help_contexts": [
|
||||||
@@ -706,6 +888,29 @@ manifest = ModuleManifest(
|
|||||||
documentation_types=("admin", "user"),
|
documentation_types=("admin", "user"),
|
||||||
audience=("user", "calendar_manager", "operator"),
|
audience=("user", "calendar_manager", "operator"),
|
||||||
related_modules=("connectors", "audit", "ops"),
|
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={
|
metadata={
|
||||||
"kind": "reference",
|
"kind": "reference",
|
||||||
"help_contexts": [
|
"help_contexts": [
|
||||||
@@ -742,6 +947,23 @@ manifest = ModuleManifest(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
related_modules=("campaigns", "mail", "audit"),
|
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"},
|
metadata={"kind": "workflow"},
|
||||||
),
|
),
|
||||||
DocumentationTopic(
|
DocumentationTopic(
|
||||||
@@ -759,6 +981,22 @@ manifest = ModuleManifest(
|
|||||||
documentation_types=("admin", "user"),
|
documentation_types=("admin", "user"),
|
||||||
audience=("calendar_manager", "operator", "tenant_admin"),
|
audience=("calendar_manager", "operator", "tenant_admin"),
|
||||||
related_modules=("ops", "audit"),
|
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={
|
metadata={
|
||||||
"kind": "runbook",
|
"kind": "runbook",
|
||||||
"help_contexts": [
|
"help_contexts": [
|
||||||
@@ -787,7 +1025,15 @@ manifest = ModuleManifest(
|
|||||||
audience=("privacy_officer", "calendar_manager", "records_manager"),
|
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(
|
frontend=FrontendModule(
|
||||||
module_id="calendar",
|
module_id="calendar",
|
||||||
package_name="@govoplan/calendar-webui",
|
package_name="@govoplan/calendar-webui",
|
||||||
@@ -799,7 +1045,15 @@ manifest = ModuleManifest(
|
|||||||
order=55,
|
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=(
|
view_surfaces=(
|
||||||
ViewSurface(
|
ViewSurface(
|
||||||
id="calendar.navigation",
|
id="calendar.navigation",
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ REPO_ROOT = Path(__file__).resolve().parents[1]
|
|||||||
|
|
||||||
|
|
||||||
class CalendarInterfaceDocumentationContractTests(unittest.TestCase):
|
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:
|
def test_backend_surfaces_and_hierarchy_remain_declared(self) -> None:
|
||||||
frontend = get_manifest().frontend
|
frontend = get_manifest().frontend
|
||||||
self.assertIsNotNone(frontend)
|
self.assertIsNotNone(frontend)
|
||||||
@@ -31,13 +39,21 @@ class CalendarInterfaceDocumentationContractTests(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
self.assertEqual(expected, set(surfaces))
|
self.assertEqual(expected, set(surfaces))
|
||||||
self.assertEqual("calendar.page", surfaces["calendar.page.sidebar"].parent_id)
|
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.page.workspace"].parent_id)
|
||||||
self.assertEqual("calendar.page", surfaces["calendar.event-editor"].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(
|
||||||
self.assertEqual("calendar.collection-editor", surfaces["calendar.sync-status"].parent_id)
|
"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.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:
|
def test_help_and_consequence_metadata_remain_published(self) -> None:
|
||||||
topics = {topic.id: topic for topic in get_manifest().documentation}
|
topics = {topic.id: topic for topic in get_manifest().documentation}
|
||||||
@@ -63,9 +79,9 @@ class CalendarInterfaceDocumentationContractTests(unittest.TestCase):
|
|||||||
quick_access = (
|
quick_access = (
|
||||||
REPO_ROOT / "webui/src/features/calendar/CalendarQuickAccess.tsx"
|
REPO_ROOT / "webui/src/features/calendar/CalendarQuickAccess.tsx"
|
||||||
).read_text(encoding="utf-8")
|
).read_text(encoding="utf-8")
|
||||||
page = (
|
page = (REPO_ROOT / "webui/src/features/calendar/CalendarPage.tsx").read_text(
|
||||||
REPO_ROOT / "webui/src/features/calendar/CalendarPage.tsx"
|
encoding="utf-8"
|
||||||
).read_text(encoding="utf-8")
|
)
|
||||||
|
|
||||||
self.assertIn("const AGENDA_LIMIT = 7", quick_access)
|
self.assertIn("const AGENDA_LIMIT = 7", quick_access)
|
||||||
self.assertIn("launchContext.temporalContext", quick_access)
|
self.assertIn("launchContext.temporalContext", quick_access)
|
||||||
@@ -75,13 +91,28 @@ class CalendarInterfaceDocumentationContractTests(unittest.TestCase):
|
|||||||
self.assertIn('parameters.get("quickAction") !== "create-event"', page)
|
self.assertIn('parameters.get("quickAction") !== "create-event"', page)
|
||||||
|
|
||||||
def test_webui_uses_shared_help_guard_and_confirmation_components(self) -> None:
|
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")
|
event_dialog = (
|
||||||
collection_dialog = (REPO_ROOT / "webui/src/features/calendar/CalendarCollectionDialogs.tsx").read_text(encoding="utf-8")
|
REPO_ROOT / "webui/src/features/calendar/CalendarEventDialog.tsx"
|
||||||
settings_panel = (REPO_ROOT / "webui/src/features/calendar/CalendarSettingsPanel.tsx").read_text(encoding="utf-8")
|
).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)
|
self.assertIn(component, event_dialog)
|
||||||
for component in ("ActionBlockerHint", "DocumentationHelpLink", "useUnsavedDraftGuard"):
|
for component in (
|
||||||
|
"ActionBlockerHint",
|
||||||
|
"DocumentationHelpLink",
|
||||||
|
"useUnsavedDraftGuard",
|
||||||
|
):
|
||||||
self.assertIn(component, collection_dialog)
|
self.assertIn(component, collection_dialog)
|
||||||
for component in ("DocumentationHelpLink", "useUnsavedDraftGuard"):
|
for component in ("DocumentationHelpLink", "useUnsavedDraftGuard"):
|
||||||
self.assertIn(component, settings_panel)
|
self.assertIn(component, settings_panel)
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/calendar-webui",
|
"name": "@govoplan/calendar-webui",
|
||||||
"version": "0.1.18",
|
"version": "0.1.20",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
Reference in New Issue
Block a user