CalDAV account connect (Settings → Calendars) with RFC 6764 discovery,
per-calendar vaulted credentials, and full two-way sync verified live
against NextCloud:
- caldav.py: discovery, calendar-query REPORT (pull), PUT/DELETE with
quoted ETags (If-Match/If-None-Match), iCloud partition-redirect
handling, ICS build/parse; clear error codes (auth/private-host/
unreachable) surfaced to the connect UI.
- sync.py: ctag-gated pull + push of local changes; dirty rows protected
from being clobbered; 15-min cron + immediate enqueue on every
create/edit/delete so changes reach the server in ~1s.
- Dirty tracking via a BEFORE INSERT/UPDATE trigger (migration 0007),
suppressed during sync with a txn-local GUC; deletes tombstone then
DELETE server-side. Writability now driven by server privileges.
- migration 0006: event uniqueness NULLS NOT DISTINCT — fixes duplicate
events on every full re-pull (NULL recurrence_id never matched ON CONFLICT).
- 5 CalDAV parsing unit tests.
Calendar UI (page.js): all-day events now render in Day/3-days/Week views
(were month-only). They share the timed-event column layout so overlapping
events sit side-by-side, fill the day column vertically with a sticky label,
and are matched by start day (fixes UTC-midnight events spilling into the
next local day).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Calendar plugin (webmail_plugin_calendar, non-removable core plugin):
- Local calendars/events CRUD with RLS isolation; recurrence expanded at
query time (DST-correct), EXDATE + detached-override merge.
- Calendar app bundle: month/week/day/3-day views, mini-month, event form
(color, show-as, timezone combobox, repeat, reminders, guests w/ recipient
autocomplete, Jitsi video call), event popover with RSVP badges, resizable/
collapsible left panel + theme toggle, overlap layout.
- iMIP invites: sdk.mail sends REQUEST from the organizer's mailbox (HTML +
text/calendar + invite.ics); public /rsvp endpoint (vault-sealed token)
records accepted/declined/tentative per attendee.
- "This and following" edit + scoped delete (this / this-and-following), with
a delete confirmation dialog for recurring events.
Core:
- Settings: time_format (12/24h, default 24h) + jitsi_server; new Integrations
section; date order follows time format (en-GB vs en-US) across Mail.
- Plugin SDK: mail (send from a user's account), vault (envelope encrypt),
multi-method SSRF-guarded http; AppSwitcher product switcher; full-bleed
PluginPage.
Mail HTML rendering:
- Sanitizer allows <style> blocks + class (CSS sanitized: drop script-like,
strip @import/url() when remote images blocked); SANITIZER_VERSION 3 so
already-indexed mail re-sanitizes on view.
- Attachments: a Content-Disposition: attachment part is a real attachment
even when it also carries a Content-ID (fixes hidden invite.ics).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>