1
0
Fork 0
mirror of https://github.com/Kozea/Radicale.git synced 2025-09-15 20:36:55 +00:00

- Capture previous version of event pre-overwrite for use in notification hooks

- Use previous version of event in email hooks to determine added/deleted/updated email type
This commit is contained in:
Nate Harris 2025-07-14 00:16:19 -06:00
parent e4b337d3ff
commit 80dc4995cf
10 changed files with 274 additions and 110 deletions

View file

@ -323,7 +323,7 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
if "W" in self._rights.authorization(user, principal_path):
with self._storage.acquire_lock("w", user):
try:
new_coll = self._storage.create_collection(principal_path)
new_coll, _, _ = self._storage.create_collection(principal_path)
if new_coll:
jsn_coll = self.configuration.get("storage", "predefined_collections")
for (name_coll, props) in jsn_coll.items():

View file

@ -24,7 +24,7 @@ from typing import Optional
from radicale import httputils, storage, types, xmlutils
from radicale.app.base import Access, ApplicationBase
from radicale.hook import DeleteHookNotificationItem
from radicale.hook import HookNotificationItem, HookNotificationItemTypes
from radicale.log import logger
@ -82,10 +82,12 @@ class ApplicationPartDelete(ApplicationBase):
return httputils.NOT_ALLOWED
for i in item.get_all():
hook_notification_item_list.append(
DeleteHookNotificationItem(
access.path,
i.uid,
old_content=item.serialize() # type: ignore
HookNotificationItem(
notification_item_type=HookNotificationItemTypes.DELETE,
path=access.path,
uid=i.uid,
old_content=item.serialize(), # type: ignore
new_content=None
)
)
xml_answer = xml_delete(base_prefix, path, item)
@ -93,10 +95,12 @@ class ApplicationPartDelete(ApplicationBase):
assert item.collection is not None
assert item.href is not None
hook_notification_item_list.append(
DeleteHookNotificationItem(
access.path,
item.uid,
old_content=item.serialize() # type: ignore
HookNotificationItem(
notification_item_type=HookNotificationItemTypes.DELETE,
path=access.path,
uid=item.uid,
old_content=item.serialize(), # type: ignore
new_content=None,
)
)
xml_answer = xml_delete(

View file

@ -102,9 +102,9 @@ class ApplicationPartProppatch(ApplicationBase):
item)
if xml_content is not None:
hook_notification_item = HookNotificationItem(
HookNotificationItemTypes.CPATCH,
access.path,
DefusedET.tostring(
notification_item_type=HookNotificationItemTypes.CPATCH,
path=access.path,
new_content=DefusedET.tostring(
xml_content,
encoding=self._encoding
).decode(encoding=self._encoding)

View file

@ -243,14 +243,27 @@ class ApplicationPartPut(ApplicationBase):
if write_whole_collection:
try:
etag = self._storage.create_collection(
path, prepared_items, props).etag
col, replaced_items, new_item_hrefs = self._storage.create_collection(
href=path,
items=prepared_items,
props=props)
for item in prepared_items:
hook_notification_item = HookNotificationItem(
HookNotificationItemTypes.UPSERT,
access.path,
item.serialize()
)
# Try to grab the previously-existing item by href
existing_item = replaced_items.get(item.href, None)
if existing_item:
hook_notification_item = HookNotificationItem(
notification_item_type=HookNotificationItemTypes.UPSERT,
path=access.path,
old_content=existing_item.serialize(),
new_content=item.serialize()
)
else: # We assume the item is new because it was not in the replaced_items
hook_notification_item = HookNotificationItem(
notification_item_type=HookNotificationItemTypes.UPSERT,
path=access.path,
old_content=None,
new_content=item.serialize()
)
self._hook.notify(hook_notification_item)
except ValueError as e:
logger.warning(
@ -267,12 +280,23 @@ class ApplicationPartPut(ApplicationBase):
href = posixpath.basename(pathutils.strip_path(path))
try:
etag = parent_item.upload(href, prepared_item).etag
hook_notification_item = HookNotificationItem(
HookNotificationItemTypes.UPSERT,
access.path,
prepared_item.serialize()
)
uploaded_item, replaced_item = parent_item.upload(href, prepared_item)
etag = uploaded_item.etag
if replaced_item:
# If the item was replaced, we notify with the old content
hook_notification_item = HookNotificationItem(
notification_item_type=HookNotificationItemTypes.UPSERT,
path=access.path,
old_content=replaced_item.serialize(),
new_content=prepared_item.serialize()
)
else: # If it was a new item, we notify with no old content
hook_notification_item = HookNotificationItem(
notification_item_type=HookNotificationItemTypes.UPSERT,
path=access.path,
old_content=None,
new_content=prepared_item.serialize()
)
self._hook.notify(hook_notification_item)
except ValueError as e:
# return better matching HTTP result in case errno is provided and catched