Commit Graph
2 Commits
Author SHA1 Message Date
thethreemagi 7287f0b515 seeded events are the site's; every write lands in the action log
calendar_write.owns() now accepts both site namespaces: the 32 events the
2026-08-29 seed stamped site73-<sha1>@greenlanescouts73.org and the site's
own @site73.greenlanescouts73.org. The rule exists to keep the site off
@band.us and off anything a person adds in a calendar client, not off its
own data. Seeded objects are written back at the seeder's object name so
an edit replaces in place; proven on a throwaway against the real store
(edit, still 32 objects, restored byte-for-byte).

auth_events becomes the one action log. Every P2 write - nearby create,
update, deactivate; unit meeting edit; setting change; announcement post
and take-down - now records who, when, and a one-line before -> after for
the fields that changed, alongside the login, key and calendar entries
already there. GET /api/admin/history (history:read, admin and above, not
scopable on a key) reads it newest first with a kind prefix filter and
before= paging; rowid breaks second-resolution ties.

tests/smoke_admin.py 102 -> 112.
2026-09-04 18:10:03 -04:00
thethreemagi e52cc60fcf P4: calendar write-back to Radicale, site-owned UIDs only
The site becomes a second writer to scouts/site73, as the scoped scoutsite
principal (rw on that one collection, denied everywhere else by the rights
file). Two rules enforced in calendar_write.py, not left to callers: the
site owns only UIDs ending @site73.greenlanescouts73.org and refuses any
other before a network call, the same shape as band-cal-sync and @band.us;
and with no RADICALE_* configuration every write is a 503, never a silent
no-op.

The VEVENT layout matches seed.py exactly so the feed reverses it into the
row shape the public pages already render: all-day DTEND exclusive, TZID +
VTIMEZONE on timed events, 90-minute default for a timed one-day event,
noon on the end date for a timed multi-day one, CATEGORIES for the unit,
X-SCOUT73-BADGE, 75-octet folding. Reads come from the scout-calendar feed
(now carrying uid and recurring); rows the site created and that are not
part of a series are marked mine. Writes are logged to auth_events and
bust the page cache so a leader sees their event within the feed's minute.

DELETE really deletes - the calendar's history is Radicale's git log.
Endpoints under calendar:write. tests/smoke_admin.py 78 -> 102. Proven
against the real store on a 2036 probe (outside the feed window): create,
read back byte-for-byte, replace, delete, second delete 404, foreign UID
403 with no store call, unconfigured 503.
2026-09-04 17:56:55 -04:00