feat(calendar): migrate iCalendar export to sabre/vobject - #1611
feat(calendar): migrate iCalendar export to sabre/vobject#1611JohnVillalovos wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR migrates LibreBooking’s iCalendar (ICS) export from a Smarty template to programmatic generation using sabre/vobject, improving correctness for escaping, parameters, and nested components while removing the legacy template renderer.
Changes:
- Replace Smarty-templated ICS generation with
Sabre\VObject-basedVCALENDAR/VEVENT/VALARMconstruction. - Stop pre-escaping TEXT fields in
iCalendarReservationViewand rely on Sabre’s serializer for RFC 5545 escaping. - Add/adjust tests for reserved-character escaping and robust parsing of plugin-supplied
ExtraIcalLines, and update PHPStan config to accommodate Sabre’s magic properties.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tpl/Export/ical.tpl | Removes the legacy Smarty ICS template renderer. |
| Pages/Export/CalendarExportDisplay.php | Implements ICS generation via sabre/vobject, including alarms and plugin-supplied fragments. |
| lib/Application/Schedule/iCalendarReservationView.php | Removes RFC 5545 pre-escaping so Sabre handles TEXT serialization. |
| tests/Presenters/CalendarExportPresenterTest.php | Updates/extends tests to validate raw view values and serialized output escaping + fragment parsing behavior. |
| phpstan.neon | Adds Sabre\VObject\Node as a universal object crate to suppress magic-property false positives. |
| phpstan_next.neon | Same PHPStan adjustment for the “next” ruleset. |
Code Review:
|
AI agent/model: Codex (GPT-5)Review of PR 1611Commit reviewed: Findings1. Medium: Decide and enforce the correct iTIP
|
1c7d553 to
ae6272f
Compare
Replace the Smarty-templated ICS renderer with programmatic VCALENDAR/VEVENT/VALARM construction via sabre/vobject and remove the now-unused tpl/Export/ical.tpl. Render() also accepts an optional $calendarName, emitted as NAME/X-WR-CALNAME, for use by subscription feeds. ExtraIcalLines (plugin-supplied raw ICS text) is parsed with Sabre's own Reader, wrapped in a throwaway VCALENDAR/VEVENT shell, instead of being concatenated into the template output verbatim. That preserves property parameters (e.g. ATTENDEE;CN=...), nested components (e.g. BEGIN:VALARM), and RFC 5545 line folding, and it stops a plugin from injecting arbitrary calendar structure. A caught ParseException skips only the malformed fragment so one bad plugin-supplied fragment can't break the whole feed. iCalendarReservationView no longer pre-escapes SUMMARY/DESCRIPTION for RFC 5545 (removes toRfc5545Text): Sabre's serializer already escapes TEXT values, so the old pre-escaping caused reserved characters and newlines to be double-escaped in the output. Assisted-by: Claude:claude-sonnet-5
ae6272f to
2d30a8c
Compare
RFC 5546 §3.2.1: a PUBLISH VEVENT's ATTENDEE list MUST be empty, since PUBLISH doesn't solicit a reply. DetermineMethod() always resolves a multi-event export/subscription feed to PUBLISH, but Render() still emitted ATTENDEE per event whenever a reservation had real attendee data, producing the exact PUBLISH+ATTENDEE combination two independent reviews blocked on in PR LibreBooking#1611. ATTENDEE is only meaningful for a single-event render: a one-to-one scheduling email (REQUEST/CANCEL, always exactly one reservation) or an export of exactly one reservation. Gate ATTENDEE emission on count($reservations) === 1 so multi-event feeds never carry it, while single-reservation renders (email attachments, single-reservation exports) keep their existing, already-tested behavior. Assisted-by: Claude:claude-sonnet-5
Replace the Smarty-templated ICS renderer with programmatic
VCALENDAR/VEVENT/VALARM construction via sabre/vobject and remove the
now-unused tpl/Export/ical.tpl. Render() also accepts an optional
$calendarName, emitted as NAME/X-WR-CALNAME, for use by subscription
feeds.
ExtraIcalLines (plugin-supplied raw ICS text) is parsed with Sabre's own
Reader, wrapped in a throwaway VCALENDAR/VEVENT shell, instead of being
concatenated into the template output verbatim. That preserves property
parameters (e.g. ATTENDEE;CN=...), nested components (e.g.
BEGIN:VALARM), and RFC 5545 line folding, and it stops a plugin from
injecting arbitrary calendar structure. A caught ParseException skips
only the malformed fragment so one bad plugin-supplied fragment can't
break the whole feed.
iCalendarReservationView no longer pre-escapes SUMMARY/DESCRIPTION for
RFC 5545 (removes toRfc5545Text): Sabre's serializer already escapes
TEXT values, so the old pre-escaping caused reserved characters and
newlines to be double-escaped in the output.
Assisted-by: Claude:claude-sonnet-5