Fix: expand REPORT leaves recurrence properties on expanded instances

_strip_single_event() removed the recurrence-defining properties (RRULE,
EXDATE, EXRULE, RDATE) with a single try/except around sequential
delattr() calls. When one of them was absent (e.g. an event with no
EXDATE), the AttributeError aborted the whole block and the following
properties -- notably RDATE -- were left on the expanded
single-occurrence VEVENTs returned by a calendar-data expand REPORT.

Remove each property independently so a missing one no longer prevents
removal of the others. Add a regression test and fixture.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
TowyTowy
2026-07-12 22:30:47 +02:00
parent ffb90de841
commit 9d46d5b63d
4 changed files with 53 additions and 7 deletions

View File

@@ -2,6 +2,7 @@
## 3.7.7.dev
* Fix: time-range filter treated a VEVENT with a whole-day DURATION (e.g. P1D, P2D) as zero-length (timedelta.seconds instead of total_seconds), so such events were missing from calendar-query REPORT results
* Fix: calendar-data expand (REPORT) left recurrence properties (e.g. RDATE) on the expanded single-occurrence VEVENTs; a single try/except around the sequential delattr() calls stopped at the first absent property (e.g. missing EXDATE), so later ones were never removed
## 3.7.6
* Extension: item verification on commandline

View File

@@ -638,13 +638,15 @@ def _strip_single_event(
_convert_to_utc(vevent, 'dtend', dt_format)
_convert_to_utc(vevent, 'recurrence_id', dt_format)
try:
delattr(vevent, 'rrule')
delattr(vevent, 'exdate')
delattr(vevent, 'exrule')
delattr(vevent, 'rdate')
except AttributeError:
pass
# Remove every recurrence-defining property independently: a single
# try/except around the sequential delattr() calls stopped at the first
# property that was absent, leaving the following ones (e.g. RDATE) on the
# expanded single-occurrence instance.
for prop in ('rrule', 'exdate', 'exrule', 'rdate'):
try:
delattr(vevent, prop)
except AttributeError:
pass
def _strip_component(vevent: vobject.base.Component) -> None:

View File

@@ -0,0 +1,12 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Radicale tests//NONSGML test//EN
BEGIN:VEVENT
UID:event_rrule_rdate
DTSTART:20060102T170000Z
DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=3
RDATE:20060110T170000Z
SUMMARY:Daily recurring meeting
END:VEVENT
END:VCALENDAR

View File

@@ -750,3 +750,34 @@ permissions: RrWw""")
ONLY_DATES,
2
)
def test_report_with_expand_property_strips_recurrence_properties(self) -> None:
"""Expanded instances must not retain recurrence properties.
Regression test: the stored event has an RRULE and an RDATE but no
EXDATE/EXRULE. Expanded VEVENT instances represent a single
occurrence (identified by RECURRENCE-ID) and must not carry any
recurrence-defining property (RRULE, RDATE, EXRULE, EXDATE).
"""
uid = "event_rrule_rdate"
start = "20060102T000000Z"
end = "20060105T000000Z"
# Baseline (no expand): the stored event still holds RRULE and RDATE.
_, responses = self.report(
"/calendar.ics/", self._req_without_expand(uid, start, end))
status, element = responses[
f"/calendar.ics/{uid}.ics"]["C:calendar-data"]
assert status == 200 and element.text
assert "RRULE" in element.text
assert "RDATE" in element.text
# Expanded: individual instances must not carry recurrence properties.
_, responses = self.report(
"/calendar.ics/", self._req_with_expand(uid, start, end))
status, element = responses[
f"/calendar.ics/{uid}.ics"]["C:calendar-data"]
assert status == 200 and element.text
assert "RECURRENCE-ID" in element.text
assert "RRULE" not in element.text
assert "RDATE" not in element.text