4.6.1 Calendar Downloads and Feeds
App\Services\ICalendarService produces RFC 5545 iCalendar output for one gathering or an iterable feed. GatheringsController::downloadCalendar() serves an individual .ics file and feed() serves a calendar feed.
Responsibilities
generateICalendar()renders one event.generateFeed()renders multiple events with a tenant-specific calendar name.getFilename()creates the download filename.- The controller selects only gatherings the endpoint may expose, supplies the base URL, and returns the correct response headers.
The service escapes iCalendar text, folds long lines, formats dates/times, includes public links where available, and derives a stable event identifier. Preserve these rules by extending the service rather than concatenating .ics text in a controller or template.
Time zones
Gathering times must be interpreted in the gathering/user/application timezone rules provided by TimezoneHelper; do not apply the server timezone implicitly. Test daylight-saving boundaries when changing date serialization.
Privacy and tenancy
Feeds are resolved under the request host’s tenant. Do not include unpublished events, private member details, internal numeric IDs, or records from a platform query. If feed output is cached, use a tenant-aware key and invalidate it when publishable event data changes.
Verification
Cover a single-event download, a multi-event feed, escaping of commas/semicolons/newlines, stable UIDs, date/time zones, cancellation/publication filtering, and tenant separation. Importing a fixture into a calendar client is a useful manual smoke test but does not replace assertions on the generated text.