LH Calendars is a CalDAV provider for LH DAV, which owns the protocol, login (application passwords), sync tokens and XML. LH Calendars registers two providers on lh_dav_collection_providers; both serve calendars/{user ID}/{collection}/ on the user’s home site only.
Home site
LH_Calendars::return_home_site_id(): the main site for super admins, otherwise the user’s primary site if they still belong to it, else their first site. Serving only there means someone who adds accounts for several sites does not see every calendar twice. LH DAV’s setup profile leaves out CalDAV on other sites, and the profile screen there links to the home site.
Personal calendars (LH_Calendars_Personal_Provider)
- Writable, owned by the user. Implements
LH_DAV_Collection_Manager, so devices make (MKCALENDAR or extended MKCOL), rename, recolour, reorder and delete them. Ids beginningorg-are reserved; at most 50 per person. - A default calendar,
personal/, is made the first time a user’s calendars are listed; network user metalh_calendars-default_calendar_madestops a deleted default coming back. - Stored in two network tables:
{base_prefix}lh_calendars_calendars(uri, name, description, colour, components, order) and{base_prefix}lh_calendars_objects(one row per entry: the iCalendar exactly as sent, with UID, component, and start and end in UTC copied out for queries). Keyed on the site optionlh_calendars-schema_version. - An entry is one VCALENDAR with one event, to-do or journal entry and its recurrence exceptions. A calendar takes only the components it was made for; two entries may not share a UID (409).
- Changes are logged in LH DAV’s change log against the main site (the collection’s
site_id), since the data is network-wide. Deleting a user deletes their calendars.
Organisation calendars (LH_Calendars_Organisations_Provider)
- One read-only collection,
org-{site ID}, per site the user belongs to (get_blogs_of_user()), can read on, and where GatherPress is active. - Holds that site’s
gatherpress_eventposts starting from 90 days ago, in the statuses the user may see, decided on the event’s own site withuser_can(): publish for everyone; private with the post type’sread_private_posts; LH Membership’s statuses with the capabilityLH_Membership_access_control_class::return_view_capability_for_status()names (read_restricted_posts,read_confidential_posts), or signed-in forlogged_in. - Events are written as iCalendar on their own site (
LH_Calendars_Ical::build_event()): UTC times, title, plain-text excerpt or content, venue name, address and GEO, online link, topics as CATEGORIES, permalink. No attendees or organiser email. UIDgatherpress-event-{ID}@{site host}; resourceevent-{ID}.ics. LH_Calendars_Event_Trackerlogs saves (wp_after_insert_post), trashing, deletion and date meta changes on the event’s site againstcalendar/0/org-{site ID}, so every member’s sync sees them wherever their home site is. Whether a member still sees a changed event is worked out at sync time.- The collection state carries the statuses the user may see and the month, so a change in access, or events leaving the window, restarts their sync. Venue edits reach phones when the event is next edited.
Profile and download
LH_Calendars_Profile adds “Your calendars” to the member’s own profile screen, and serves GET /wp-json/lh-calendars/v1/export/{calendar}: one .ics file of a personal calendar, timezones merged, for the signed-in user only.
iCalendar is read and written with sabre/vobject, installed with Composer into vendor/.