266 lines
13 KiB
Markdown
266 lines
13 KiB
Markdown
# GovOPlaN Calendar Integration Concept
|
|
|
|
## Module Boundary
|
|
|
|
`govoplan-calendar` owns calendar primitives:
|
|
|
|
- calendar collections
|
|
- VEVENT storage and iCalendar import/export
|
|
- event recurrence data, recurrence expansion, and recurrence exceptions
|
|
- availability and free/busy semantics
|
|
- resources such as rooms, shared equipment, and service desks
|
|
- groupware calendar adapter boundaries, including CalDAV and Open-Xchange
|
|
|
|
Calendar does not own meeting-poll decisions, bookable service appointments, task lifecycle, mail transport, or document lifecycle. Those modules should reference calendar events through stable IDs, iCalendar UIDs, capabilities, or API contracts.
|
|
|
|
## First Implementation
|
|
|
|
The first standalone module provides:
|
|
|
|
- `CalendarCollection` records for tenant calendars
|
|
- `CalendarEvent` records for VEVENT data
|
|
- normalized query fields: start, end, summary, location, all-day flag, status, transparency, classification, calendar ID, UID, recurrence ID, sequence, source, and ETag
|
|
- iCalendar preservation: raw VEVENT properties, parameters, and generated `text/calendar` export
|
|
- API endpoints for listing calendars, creating/updating/deleting events, importing iCalendar, and exporting event ICS
|
|
- bounded occurrence expansion with RRULE, RDATE, EXDATE, detached overrides,
|
|
instance/series editing, and free/busy reconciliation
|
|
- durable per-user calendar view preferences exposed through the platform
|
|
Settings surface
|
|
- calendar-owned CalDAV sync sources with credential references, scheduled due-sync metadata, full/incremental inbound sync, two-way PUT/DELETE writes, and ETag conflict handling
|
|
- a Calendar-owned durable desired-state outbox for two-way CalDAV writes. Event
|
|
state and the exact external resource snapshot commit atomically; workers use
|
|
deterministic hrefs, conditional requests, expiring leases, bounded
|
|
exponential retry, and semantic GET reconciliation after ambiguous outcomes
|
|
- WebUI views: month, week, workweek, day, and continuous week-row scrolling
|
|
- an Open-Xchange profile over the CalDAV transport, with explicit connector,
|
|
IDM/group-mapping, and resource-calendar references
|
|
|
|
The implementation is not yet a full CalDAV network server. It is the internal
|
|
calendar storage, recurrence, sync, availability, and UI foundation on which
|
|
scheduling inbox/outbox behavior and CalDAV server endpoints can be built.
|
|
|
|
## Open-Xchange profile
|
|
|
|
Open-Xchange is an explicit external profile backed by Calendar's CalDAV
|
|
engine. A profile therefore receives the same bounded discovery, VEVENT
|
|
round-trip, RRULE/RDATE/EXDATE and detached-exception handling, sync-token/ctag
|
|
tracking, per-resource ETag conflict detection, and durable two-way outbox as a
|
|
generic CalDAV source. Calendar's availability capability reads the resulting
|
|
projection, so free/busy and collision checks use the same recurrence-aware
|
|
event set. Availability is current as of the source's visible last successful
|
|
sync; GovOPlaN does not claim live Open-Xchange state while a source is stale.
|
|
|
|
The source metadata retains three optional, non-secret bindings:
|
|
|
|
- `connector_profile_ref` links the endpoint to a Connectors-owned inventory or
|
|
deployment profile.
|
|
- `identity_mapping_ref` links attendee/group identifiers to an IDM- or
|
|
Access-governed mapping without importing either module.
|
|
- `resource_calendar_ref` identifies an Open-Xchange room/equipment calendar;
|
|
the corresponding Calendar collection is owned as a `resource`.
|
|
|
|
The `calendar.externalProfiles` capability lets an optional connector configure
|
|
the same profile through a Core contract. Direct Calendar setup remains
|
|
available when Connectors is absent. The connector never supplies a Calendar
|
|
event model and the references never contain credentials. Open-Xchange
|
|
credentials remain a reusable credential-envelope reference or a
|
|
Calendar-owned encrypted credential. The WebUI exposes Open-Xchange as a source
|
|
type and reuses CalDAV discovery; deployments must enter a DAV endpoint that
|
|
resolves to the intended collection.
|
|
|
|
## Outbound delivery operations
|
|
|
|
No event API or cross-module capability performs CalDAV network I/O inside the
|
|
caller's transaction. A local mutation instead creates a
|
|
`calendar_outbox_operations` row containing the exact desired ICS resource,
|
|
target href, expected ETag, conflict-policy snapshot, and idempotency key.
|
|
|
|
The registered `govoplan.calendar.dispatch_outbox` worker commits an expiring
|
|
lease before doing network I/O and commits each outcome independently. If a
|
|
worker loses the database connection after a successful remote write, the next
|
|
attempt reads the remote object and compares semantic ICS fingerprints. A
|
|
matching PUT or an already absent DELETE completes successfully without a blind
|
|
duplicate write. Pending updates for one href supersede older never-attempted
|
|
rows; attempted predecessors are reconciled first, and updates queued behind an
|
|
in-flight write inherit the ETag produced by that write. Delivery and inbound
|
|
REPORT application take the same source-row lock,
|
|
and only one operation per source is leased at a time. This deliberately trades
|
|
per-source throughput for a simple ordering guarantee: a stale inbound report
|
|
cannot land after a newer outbound result, and queued leases do not expire while
|
|
waiting behind another network request for the same source.
|
|
|
|
Operators open **Outbound changes** from a synchronized calendar's settings to
|
|
inspect a bounded queue and use only the retry, reconcile, or discard actions
|
|
that are valid for the latest resource generation. Dispatch and due-source
|
|
routes are worker-only and reject interactive sessions even when the account is
|
|
an administrator. Terminal ETag
|
|
conflicts and exhausted retries remain the current local desired state and
|
|
shield that resource from inbound overwrite until explicitly resolved.
|
|
Discarding is the administrator's accept-remote transition: it is allowed only
|
|
for the latest generation, atomically cancels its unresolved predecessor chain,
|
|
marks the event projection as discarded, clears the sync token, and schedules a
|
|
full inbound reconciliation so an unchanged remote object is not missed.
|
|
The UI requires a separate destructive confirmation and explains that accepting
|
|
remote may lose the unresolved local desired state. Disabled and inbound-only
|
|
sources still permit discard when the retained source belongs to the tenant,
|
|
but they cannot be retried or reconciled until made writable again. Missing
|
|
sources, active worker leases, stale generations, and already-resolved rows
|
|
expose diagnostics instead of unsafe action buttons.
|
|
|
|
Disabling outbound delivery or switching to inbound-only is rejected while
|
|
unresolved desired state exists; retirement is the explicit exception and
|
|
cancels unresolved work. Endpoint/calendar changes also require no active event
|
|
bindings or unresolved delivery. Credential rotation does not discard committed
|
|
desired state. Public event mutation cannot set sync-owned source hrefs, kinds,
|
|
or ETags. Deleting a synchronized collection or retiring its source is a local
|
|
unlink: it never deletes the remote collection or its remaining remote events.
|
|
The unlink immediately scrubs Calendar-owned credential ciphertext and external
|
|
provider references and audits the deletion. External provider failure blocks
|
|
retirement before queued work is changed; a later retry tolerates a provider
|
|
secret already removed by an earlier attempt whose database transaction rolled
|
|
back. Provider errors and audit details never contain credential values or
|
|
secret references.
|
|
|
|
### Runtime provider state
|
|
|
|
Calendar registers tenant-aware runtime state for
|
|
`calendar.caldav_sync`. Each active CalDAV source is projected by a stable
|
|
`calendar:sync-source:<id>` reference with its effective source-authority mode,
|
|
enabled state, health, freshness, unresolved-conflict state, recovery readiness,
|
|
last successful synchronization, and bounded outbox counts. Collection URLs,
|
|
usernames, credential references, remote resource paths, and error text are not
|
|
included.
|
|
|
|
Docs uses the projection to explain configured availability, Ops aggregates it
|
|
for operator inspection, and configuration-package preflight can require one
|
|
exact source binding. A source with dead or conflicting desired-state work
|
|
requires recovery attention; a never-synchronized source remains unknown rather
|
|
than being reported healthy.
|
|
The current singular ownership model permits one active sync source per
|
|
calendar; multi-source fan-in will require per-event source routing. Celery beat
|
|
triggers recovery every minute, while root-transaction after-commit dispatch
|
|
provides the normal low-latency path.
|
|
|
|
### Collection retirement and bulk event moves
|
|
|
|
Collection deletion accepts two explicit, non-destructive external actions for
|
|
`event_action="move"`:
|
|
|
|
- `detach_keep_remote` applies only when the source collection is synchronized
|
|
and the target is local. It moves the local event projections, clears their
|
|
sync-owned source kind, href, ETag, and CalDAV projection metadata, retires
|
|
the source locally, and cancels undelivered outbox state. An active delivery
|
|
lease blocks the transaction. The action never enqueues a remote DELETE, so
|
|
the remote collection and remote events remain unchanged.
|
|
- `copy_to_remote` applies only when the source is local and the target has an
|
|
active, enabled, two-way CalDAV source. It moves the local events and commits
|
|
destination PUT desired states in the durable outbox in the same database
|
|
transaction. Remote failures therefore remain visible and retryable without
|
|
rolling back or hiding the local move.
|
|
|
|
Local-to-local moves retain their existing behavior and do not accept an
|
|
`external_action`. Implicit or mismatched actions, inbound-only destinations,
|
|
and synchronized-to-synchronized moves are rejected. `remote_move` is a known
|
|
but deliberately unsupported action: implementing it requires a separately
|
|
approved saga for destination reconciliation, conditional source deletion,
|
|
collision handling, and concurrent-edit policy. The WebUI does not offer that
|
|
destructive mode.
|
|
|
|
Resolved terminal rows (`succeeded`, `superseded`, and `cancelled`) are removed
|
|
in bounded batches after `CALENDAR_OUTBOX_TERMINAL_RETENTION_DAYS` (90 days by
|
|
default; `0` disables cleanup). `conflict` and `dead` rows are never removed by
|
|
this cleanup because they still carry unresolved local desired state. Each
|
|
periodic or after-commit dispatch also performs one bounded cleanup batch for
|
|
the same tenant scope.
|
|
|
|
## Integration Points
|
|
|
|
### Scheduling
|
|
|
|
`govoplan-scheduling` should use calendar for:
|
|
|
|
- organizer availability lookup
|
|
- candidate slot conflict checks
|
|
- final event creation after a poll decision
|
|
- participant invitation state as VEVENT attendees when a meeting becomes real
|
|
|
|
Scheduling remains owner of polls, candidate ranking, external participation links, and decision audit.
|
|
|
|
### Appointments
|
|
|
|
`govoplan-appointments` should use calendar for:
|
|
|
|
- confirmed appointment placement
|
|
- staff/resource conflict checks
|
|
- room and desk calendars
|
|
- cancellation or rescheduling events
|
|
|
|
Appointments remains owner of public booking flows, service definitions, capacity rules, queues, and no-show handling.
|
|
|
|
### Tasks And Workflow
|
|
|
|
`govoplan-tasks` and `govoplan-workflow` should use calendar for:
|
|
|
|
- due-date and reminder calendar overlays
|
|
- workflow deadlines
|
|
- escalation dates
|
|
- optional event creation for hearings, reviews, inspections, and internal meetings
|
|
|
|
Tasks/workflow remain owners of assignment, status, SLA logic, and completion semantics.
|
|
|
|
### Mail And Notifications
|
|
|
|
`govoplan-mail` and `govoplan-notifications` should use calendar for:
|
|
|
|
- outbound iCalendar invites and updates
|
|
- inbound invite parsing from messages
|
|
- RSVP state changes
|
|
- reminders and digest notifications
|
|
|
|
Mail remains owner of SMTP/IMAP profiles and mailbox transport. Notifications remains owner of delivery channels and delivery policy.
|
|
|
|
### Documents And DMS
|
|
|
|
`govoplan-dms` can link documents to events for:
|
|
|
|
- agendas
|
|
- minutes
|
|
- attachments
|
|
- legal deadlines
|
|
- retention and audit context
|
|
|
|
DMS remains owner of versions, locks, approvals, collaboration, legal hold, and document retention.
|
|
|
|
### Cases, Campaigns, Forms, And Portal
|
|
|
|
Domain modules may attach calendar references to domain objects:
|
|
|
|
- cases: hearings, inspections, review meetings
|
|
- campaigns: send windows and review deadlines
|
|
- forms: submission windows and office-hour sessions
|
|
- portal: citizen-facing event or appointment visibility
|
|
|
|
Calendar should expose reusable event and availability APIs instead of importing those modules.
|
|
|
|
### Connectors And Public-Sector Groupware
|
|
|
|
`govoplan-connectors` should own generic connector catalogue behavior, connection setup patterns, and external-system inventory. Calendar owns the actual calendar semantics for:
|
|
|
|
- CalDAV
|
|
- Open-Xchange calendar
|
|
- OpenDesk calendar stack integration
|
|
- resource-calendar mapping
|
|
- free/busy sync
|
|
- recurrence and exception mapping
|
|
- conflict handling
|
|
|
|
The connector boundary should hand calendar a configured profile and credentials reference, not a domain-specific event model.
|
|
|
|
## Follow-Up Work
|
|
|
|
- CalDAV server endpoints if GovOPlaN should expose calendars to external clients rather than only syncing remote collections.
|
|
- Open-Xchange adapter that maps OX calendars, attendees, resources, recurrence, and free/busy to the internal calendar model.
|
|
- Attendee RSVP workflow and mail/notification bridge.
|
|
- Resource calendars for rooms, equipment, counters, and service desks.
|
|
- Calendar ACLs beyond tenant-wide permissions.
|