From 2dfd3b928f8d703353e6d69abde15dffa64412b9 Mon Sep 17 00:00:00 2001 From: jorn Date: Mon, 12 Jan 2026 11:44:41 +0100 Subject: [PATCH 01/11] Fix tuple behaviour after radicale 3.5.5 --- radicale_storage_decsync/__init__.py | 40 +++++++++++++++++----------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/radicale_storage_decsync/__init__.py b/radicale_storage_decsync/__init__.py index b5e7ba2..d08397a 100644 --- a/radicale_storage_decsync/__init__.py +++ b/radicale_storage_decsync/__init__.py @@ -94,17 +94,21 @@ def resources_listener(path, datetime, key, value, extra): self.load_hrefs(sync_type) def upload(self, href, orig_item, update_decsync=True): - item = super().upload(href, orig_item) + result = super().upload(href, orig_item) + if isinstance(result, tuple): + item = next((x for x in result if hasattr(x, "uid")), result[1]) + else: + item = result if update_decsync: - tag = self.get_meta("tag") - if tag == "VCALENDAR": - supported_components = (self.get_meta("C:supported-calendar-component-set") or "VEVENT,VTODO,VJOURNAL").split(",") - component_name = item.component_name - if len(supported_components) > 1 and component_name != "VEVENT": - raise RuntimeError("Component " + component_name + " is not supported by old DecSync collections. Create a new collection in Radicale for support.") - self.set_href(item.uid, href) - self.decsync.set_entry(["resources", item.uid], None, item.serialize()) - return item + if hasattr(self, 'decsync'): + tag = self.get_meta("tag") + if tag == "VCALENDAR": + supported = (self.get_meta("C:supported-calendar-component-set") or "VEVENT,VTODO,VJOURNAL").split(",") + if len(supported) > 1 and item.component_name != "VEVENT": + raise RuntimeError("Component not supported by old DecSync.") + self.set_href(item.uid, href) + self.decsync.set_entry(["resources", item.uid], None, item.serialize()) + return result def delete(self, href=None, update_decsync=True): if update_decsync: @@ -157,8 +161,8 @@ def __init__(self, configuration): self.decsync_dir = "" def discover(self, path, depth="0", child_context_manager=( - lambda path, href=None: contextlib.ExitStack())): - collections = list(super().discover(path, depth, child_context_manager)) + lambda path, href=None: contextlib.ExitStack()),user_groups=set()): + collections = list(super().discover(path, depth, child_context_manager, user_groups)) for collection in collections: yield collection @@ -246,12 +250,16 @@ def create_collection(self, href, items=None, props=None): raise RuntimeError("Unknown tag " + tag) if collection.startswith(sync_type + "-"): - path = "/%s/%s/" % (username, collection) + path = href else: path = "/%s/%s-%s/" % (username, sync_type, collection) - col = super().create_collection(path, None, props) + result = super().create_collection(path, None, props) + if isinstance(result, tuple): + col = next((x for x in result if hasattr(x, "get_href")), result[0]) + else: + col = result if items is not None: for item in items: href = col.get_href(item.uid) - col.upload(href, item) - return col + col.upload(href, item) + return result From 3ac9166e4cf2458d3ac351b2054182261e89ac5b Mon Sep 17 00:00:00 2001 From: DiagonalArg <5190243+DiagonalArg@users.noreply.github.com> Date: Wed, 11 Mar 2026 01:51:38 -0700 Subject: [PATCH 02/11] Fix: Stringify structured vCard fields for DecSync compatibility --- radicale_storage_decsync/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radicale_storage_decsync/__init__.py b/radicale_storage_decsync/__init__.py index d08397a..a3d4443 100644 --- a/radicale_storage_decsync/__init__.py +++ b/radicale_storage_decsync/__init__.py @@ -107,6 +107,10 @@ def upload(self, href, orig_item, update_decsync=True): if len(supported) > 1 and item.component_name != "VEVENT": raise RuntimeError("Component not supported by old DecSync.") self.set_href(item.uid, href) + # Ensure structured data (lists/tuples) are stringified before serialization + for component in item.vobject_item.getChildren(): + if isinstance(component.value, (list, tuple)): + component.value = ";".join(str(x) for x in component.value) self.decsync.set_entry(["resources", item.uid], None, item.serialize()) return result From 03540d716ea6d9fd880ed49eeacabec4bd162e29 Mon Sep 17 00:00:00 2001 From: m_hobby <61189-m_hobby@users.noreply.drupalcode.org> Date: Thu, 26 Mar 2026 19:47:04 +0000 Subject: [PATCH 03/11] Fix discover() tuple unpacking for Radicale 3 --- radicale_storage_decsync/__init__.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/radicale_storage_decsync/__init__.py b/radicale_storage_decsync/__init__.py index a3d4443..b86ebd4 100644 --- a/radicale_storage_decsync/__init__.py +++ b/radicale_storage_decsync/__init__.py @@ -179,7 +179,11 @@ def discover(self, path, depth="0", child_context_manager=( return elif len(attributes) == 1: username = attributes[0] - known_paths = [collection.path for collection in collections] + known_paths = [] + for collection in collections: + if isinstance(collection, tuple): + collection = next((x for x in collection if hasattr(x, "path")), collection[0]) + known_paths.append(collection.path) for sync_type in ["contacts", "calendars", "tasks", "memos"]: for collection in Decsync.list_collections(self.decsync_dir, sync_type): child_path = "/%s/%s-%s/" % (username, sync_type, collection) @@ -201,7 +205,11 @@ def discover(self, path, depth="0", child_context_manager=( props["C:supported-calendar-component-set"] = "VJOURNAL" else: raise RuntimeError("Unknown sync type " + sync_type) - child = super().create_collection(child_path, props=props) + result = super().create_collection(child_path, props=props) + if isinstance(result, tuple): + child = next((x for x in result if hasattr(x, "decsync")), result[0]) + else: + child = result child.decsync.init_stored_entries() child.decsync.execute_stored_entries_for_path_exact(["info"], child) child.decsync.execute_stored_entries_for_path_prefix(["resources"], child) From b195fcd5574672bf99b35777e005ccfa166745c1 Mon Sep 17 00:00:00 2001 From: DiagonalArg <5190243+DiagonalArg@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:43:40 -0700 Subject: [PATCH 04/11] Link dependency to fixed libdecsync fork branch --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fc2c387..3398c27 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ packages=["radicale_storage_decsync"], install_requires=[ "radicale>=3", - "libdecsync>=2.0.0" + "libdecsync @ git+https://github.com/DiagonalArg/libdecsync-bindings-python3.git@fix/remove-pkg-resources" ], classifiers=[ "Programming Language :: Python :: 3", From de0b3cfe1d0dc89d547a9ea242cac6e68572eb78 Mon Sep 17 00:00:00 2001 From: DiagonalArg <5190243+DiagonalArg@users.noreply.github.com> Date: Fri, 27 Mar 2026 20:28:50 -0700 Subject: [PATCH 05/11] Update libdecsync dependency to point to master --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3398c27..7d56764 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ packages=["radicale_storage_decsync"], install_requires=[ "radicale>=3", - "libdecsync @ git+https://github.com/DiagonalArg/libdecsync-bindings-python3.git@fix/remove-pkg-resources" + "libdecsync @ git+https://github.com/DiagonalArg/libdecsync-bindings-python3.git@master" ], classifiers=[ "Programming Language :: Python :: 3", From ed8eefd443a0084f1b767204157bd121337a1257 Mon Sep 17 00:00:00 2001 From: DiagonalArg <5190243+DiagonalArg@users.noreply.github.com> Date: Fri, 27 Mar 2026 21:23:29 -0700 Subject: [PATCH 06/11] Update to version 2.1.0+diagonalarg.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7d56764..60ed0d3 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name="radicale_storage_decsync", - version="2.1.0", + version="2.1.0+diagonalarg.1", author="Aldo Gunsing", author_email="dev@aldogunsing.nl", url="https://github.com/39aldo39/Radicale-DecSync", From 01eaf92e5c0b637350978f59ef96be91ff141f6b Mon Sep 17 00:00:00 2001 From: DiagonalArg <5190243+DiagonalArg@users.noreply.github.com> Date: Sat, 30 May 2026 04:43:55 -0700 Subject: [PATCH 07/11] Fix 500 error by safely checking for component value using getattr --- radicale_storage_decsync/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/radicale_storage_decsync/__init__.py b/radicale_storage_decsync/__init__.py index b86ebd4..bc5ab7d 100644 --- a/radicale_storage_decsync/__init__.py +++ b/radicale_storage_decsync/__init__.py @@ -109,6 +109,7 @@ def upload(self, href, orig_item, update_decsync=True): self.set_href(item.uid, href) # Ensure structured data (lists/tuples) are stringified before serialization for component in item.vobject_item.getChildren(): + component_value = getattr(component, 'value', None) if isinstance(component.value, (list, tuple)): component.value = ";".join(str(x) for x in component.value) self.decsync.set_entry(["resources", item.uid], None, item.serialize()) From 1b6a1d52fd733fa8bcbe5c907b7d1210f7e93f31 Mon Sep 17 00:00:00 2001 From: DiagonalArg <5190243+DiagonalArg@users.noreply.github.com> Date: Sat, 30 May 2026 05:02:40 -0700 Subject: [PATCH 08/11] Bump version to 2.1.0+diagonalarg.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 60ed0d3..40773d7 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name="radicale_storage_decsync", - version="2.1.0+diagonalarg.1", + version="2.1.0+diagonalarg.2", author="Aldo Gunsing", author_email="dev@aldogunsing.nl", url="https://github.com/39aldo39/Radicale-DecSync", From 460433b4d760f68a8d69822edd949b3d2f2e5167 Mon Sep 17 00:00:00 2001 From: DiagonalArg <5190243+DiagonalArg@users.noreply.github.com> Date: Sat, 30 May 2026 05:33:15 -0700 Subject: [PATCH 09/11] Update README, clarifying maintenance status. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 56bc98f..6bf4e88 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +> ⚠️ **Maintenance Notice:** This is an actively maintained fork of 39aldo39/Radicale-DecSync, updated for compatibility with Python 3.12+ and Radicale 3.x. +> ⚠️ **Disclaimer:** I am not adding functionality. These are bug fix and compatibility releases. Thank you for any input. + Radicale DecSync ================ From 8a8fb9271a522af394562fb0bc997442fe8788a8 Mon Sep 17 00:00:00 2001 From: DiagonalArg <5190243+DiagonalArg@users.noreply.github.com> Date: Sat, 30 May 2026 05:48:19 -0700 Subject: [PATCH 10/11] Update README, clarifying maintenance status #2. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6bf4e88..63c3b75 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> ⚠️ **Maintenance Notice:** This is an actively maintained fork of 39aldo39/Radicale-DecSync, updated for compatibility with Python 3.12+ and Radicale 3.x. +> ⚠️ **Maintenance Notice:** This is an actively maintained fork of 39aldo39/Radicale-DecSync, updated for compatibility with Python 3.12+ and Radicale 3.x. > ⚠️ **Disclaimer:** I am not adding functionality. These are bug fix and compatibility releases. Thank you for any input. Radicale DecSync From b2e21ca210d018008f79ab9ff249ded2a8ec1cb8 Mon Sep 17 00:00:00 2001 From: DiagonalArg <5190243+DiagonalArg@users.noreply.github.com> Date: Sat, 30 May 2026 07:04:45 -0700 Subject: [PATCH 11/11] Fix typo to properly use safe component_value variable --- radicale_storage_decsync/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radicale_storage_decsync/__init__.py b/radicale_storage_decsync/__init__.py index bc5ab7d..73ea089 100644 --- a/radicale_storage_decsync/__init__.py +++ b/radicale_storage_decsync/__init__.py @@ -110,8 +110,8 @@ def upload(self, href, orig_item, update_decsync=True): # Ensure structured data (lists/tuples) are stringified before serialization for component in item.vobject_item.getChildren(): component_value = getattr(component, 'value', None) - if isinstance(component.value, (list, tuple)): - component.value = ";".join(str(x) for x in component.value) + if isinstance(component_value, (list, tuple)): + component.value = ";".join(str(x) for x in component_value) self.decsync.set_entry(["resources", item.uid], None, item.serialize()) return result