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
41 changes: 28 additions & 13 deletions caldav/calendarobjectresource.py
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,7 @@ def save(
no_create: bool = False,
obj_type: str | None = None,
increase_seqno: bool = True,
only_this_recurrence: bool = True,
only_this_recurrence: bool | None = True,
all_recurrences: bool = False,
) -> "Self | Coroutine[Any, Any, Self]":
"""Save the object, can be used for creation and update.
Expand Down Expand Up @@ -1318,9 +1318,14 @@ def save(
nature mutually exclusive, but since only_this_recurrence is
True by default, it will be ignored if all_recurrences is set.

If you want to sent the recurrence as it is to the server,
you should set both all_recurrences and only_this_recurrence
to False.
only_this_recurrence is a tristate:
* True (default): fetch master event and merge this recurrence
into it; raise NotFoundError if the master cannot be found.
* None: fetch master event and merge if found; if the master
cannot be found, PUT the fragment as-is (useful when
importing ICS data that may contain orphaned overrides).
* False: skip the merge entirely and PUT whatever is in this
object directly to the server.

Returns:
* self
Expand Down Expand Up @@ -1373,15 +1378,20 @@ def get_self():
## fetched via server-side expand), only_this_recurrence will silently not merge
## it into the parent; the caller must add RECURRENCE-ID from DTSTART first.
if (
only_this_recurrence or all_recurrences
only_this_recurrence is not False or all_recurrences
) and "RECURRENCE-ID" in self.icalendar_component:
from caldav.lib import error

obj = get_self()
if obj is None:
raise error.NotFoundError("Could not find parent recurring event")
self._incorporate_recurrence_into_parent(obj, only_this_recurrence, all_recurrences)
return obj.save(increase_seqno=increase_seqno)
if only_this_recurrence is True:
raise error.NotFoundError("Could not find parent recurring event")
## only_this_recurrence is None: master not found, fall through to PUT as-is
else:
self._incorporate_recurrence_into_parent(
obj, only_this_recurrence is not False, all_recurrences
)
return obj.save(increase_seqno=increase_seqno)

self._maybe_increment_sequence(increase_seqno)
path = self.url.path if self.url else None
Expand Down Expand Up @@ -1467,7 +1477,7 @@ async def _async_save(
no_create: bool = False,
obj_type: str | None = None,
increase_seqno: bool = True,
only_this_recurrence: bool = True,
only_this_recurrence: bool | None = True,
all_recurrences: bool = False,
) -> Self:
"""Async implementation of save() for async clients."""
Expand All @@ -1494,13 +1504,18 @@ async def get_self():
self._validate_save_constraints(existing, uid, no_overwrite, no_create)

if (
only_this_recurrence or all_recurrences
only_this_recurrence is not False or all_recurrences
) and "RECURRENCE-ID" in self.icalendar_component:
obj = await get_self()
if obj is None:
raise error.NotFoundError("Could not find parent recurring event")
self._incorporate_recurrence_into_parent(obj, only_this_recurrence, all_recurrences)
return await obj.save(increase_seqno=increase_seqno)
if only_this_recurrence is True:
raise error.NotFoundError("Could not find parent recurring event")
## only_this_recurrence is None: master not found, fall through to PUT as-is
else:
self._incorporate_recurrence_into_parent(
obj, only_this_recurrence is not False, all_recurrences
)
return await obj.save(increase_seqno=increase_seqno)

self._maybe_increment_sequence(increase_seqno)
path = self.url.path if self.url else None
Expand Down
4 changes: 2 additions & 2 deletions caldav/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ def add_object(
)
if self.is_async_client:
return self._async_add_object_finish(o, no_overwrite=no_overwrite, no_create=no_create)
o = o.save(no_overwrite=no_overwrite, no_create=no_create)
o = o.save(no_overwrite=no_overwrite, no_create=no_create, only_this_recurrence=None)
## TODO: Saving nothing is currently giving an object with None as URL.
## This should probably be changed in some future version to raise an error
## See also CalendarObjectResource.save()
Expand All @@ -1019,7 +1019,7 @@ def add_object(

async def _async_add_object_finish(self, o, no_overwrite=False, no_create=False):
"""Async helper for add_object(): awaits save() then handles reverse relations."""
o = await o.save(no_overwrite=no_overwrite, no_create=no_create)
o = await o.save(no_overwrite=no_overwrite, no_create=no_create, only_this_recurrence=None)
if o.url is not None:
await o._handle_reverse_relations(fix=True)
return o
Expand Down
2 changes: 1 addition & 1 deletion tests/docker-test-servers/ccs/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ services:
- ./conf:/opt/ccs/conf:ro
command: ./bin/run -n -c /opt/ccs/conf/caldavd.plist
tmpfs:
- /opt/ccs/data:size=200m,uid=1001,gid=1001
- /opt/ccs/data:size=200m
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8008/"]
interval: 10s
Expand Down
Loading