Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions caldav/calendarobjectresource.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,17 @@


class CalendarObjectResource(DAVObject):
"""
Ref RFC 4791, section 4.1, a "Calendar Object Resource" can be an
"""Ref RFC 4791, section 4.1, a "Calendar Object Resource" can be an
event, a todo-item, a journal entry, or a free/busy entry

As per the RFC, a CalendarObjectResource can at most contain one
calendar component, with the exception of recurrence components.
Meaning that event.data typically contains one VCALENDAR with one
VEVENT and possibly one VTIMEZONE.

In the case of expanded calendar date searches, each recurrence
will (by default) wrapped in a distinct CalendarObjectResource
object. This is a deviation from the definition given in the RFC.
"""

## There is also STARTTOFINISH, STARTTOSTART and FINISHTOFINISH in RFC9253,
Expand Down Expand Up @@ -473,7 +481,7 @@ def _set_icalendar_component(self, value) -> None:
icalendar_component = property(
_get_icalendar_component,
_set_icalendar_component,
doc="icalendar component - should not be used with recurrence sets",
doc="icalendar component - this is the simplest way to access the event/task - it will give you the first component that isn't a timezone component. For recurrence sets, the master component will be returned. For any non-recurring event/task/journal, there should be only one calendar component in the object. For results from an expanded search, there should be only one calendar component in the object",
)

component = icalendar_component
Expand Down
47 changes: 46 additions & 1 deletion tests/test_caldav.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,51 @@ def testSchedulingMailboxes(self):
inbox = self.principal.schedule_inbox()
outbox = self.principal.schedule_outbox()

def testIssue397(self):
cal = self._fixCalendar()
cal.save_event(
"""BEGIN:VCALENDAR
VERSION:2.0
Comment thread
tobixen marked this conversation as resolved.
PRODID:-//PeterB//caldav//en_DK
BEGIN:VEVENT
SUMMARY:recurrence with attendee one single item
DTSTART;TZID=Europe/Zurich:20240101T090000
DTEND;TZID=Europe/Zurich:20240101T180000
UID:test1
DESCRIPTION:this is the recurrent series
TRANSP:OPAQUE
RRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH
END:VEVENT
BEGIN:VEVENT
SUMMARY:single item
DTSTART;TZID=Europe/Zurich:20240605T090000
DTEND;TZID=Europe/Zurich:20240605T170000
UID:test1
DESCRIPTION:this is the single item assigning a attendee to just one event
ATTENDEE:foo.bar@corge.baz
RECURRENCE-ID:20240605T070000Z
END:VEVENT
END:VCALENDAR
"""
)

object_by_id = cal.object_by_uid("test1", comp_class=Event)
instance = object_by_id.icalendar_instance
events = [
event
for event in instance.subcomponents
if isinstance(event, icalendar.Event)
]
assert len(events) == 2
object_by_id = cal.object_by_uid("test1", comp_class=None)
instance = object_by_id.icalendar_instance
events = [
event
for event in instance.subcomponents
if isinstance(event, icalendar.Event)
]
assert len(events) == 2

def testPropfind(self):
"""
Test of the propfind methods. (This is sort of redundant, since
Expand Down Expand Up @@ -3001,7 +3046,7 @@ def search(month):
event=True,
start=datetime(2015, month, 1),
end=datetime(2015, month, 2),
expand="client", ## client will be default from 2.0
expand=True,
)
assert len(recurrence) == 1
return recurrence[0]
Expand Down