From 70ec04342338abd59dca8ceb9c911cec2cf39db5 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sat, 30 May 2026 18:20:35 +0200 Subject: [PATCH] sharing/bday/add support for custom categories --- CHANGELOG.md | 1 + DOCUMENTATION.md | 8 ++++++++ config | 3 +++ radicale/config.py | 4 ++++ radicale/item/__init__.py | 10 ++++++++++ radicale/sharing/__init__.py | 12 ++++++++++++ radicale/tests/test_sharing.py | 9 +++++++++ 7 files changed, 47 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7254d93c..36f62c2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Add: [sharing] conversion_bday_summary_template (customize summary) * Add: [sharing] conversion_bday_description_template (customize description) * Add: [sharing] conversion_bday_alarm_trigger_template (customize alarms) +* Add: [sharing] conversion_bday_categories (customize) * Add: [sharing] conversion_bday_age_max (limit in case of "age" placeholder is used which blocks using RRULE) ## 3.7.4 diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 6275d587..2e6fccbd 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -2352,6 +2352,14 @@ Supported format for `TIMEDELTA`: `[+-]?[0-9]+[WDHM]` Supported placeholders for `DESCRIPTION` see `conversion_bday_summary_template` +##### conversion_bday_categories + +_(>= 3.7.5)_ + +Global categories of conversion "bday", separated by `,` + +Default: `Birthday` + ##### conversion_bday_age_max _(>= 3.7.5)_ diff --git a/config b/config index 19aab2b7..1876b629 100644 --- a/config +++ b/config @@ -366,6 +366,9 @@ # Example: "-15H;BDAY tomorrow|9H;BDAY today" #conversion_bday_alarm_trigger_template = "" +# Global categories of conversion "bday" +#conversion_bday_categories = "Birthday" + # Global max limit of "bday" age # Only active in case of {age} is used as placeholder #conversion_bday_age_max = 99 diff --git a/radicale/config.py b/radicale/config.py index 9679c975..27bba0d2 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -606,6 +606,10 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([ "value": "", "help": "conversion bday alarm trigger template", "type": sharing.check_template_alarm_trigger}), + ("conversion_bday_categories", { + "value": "Birthday", + "help": "conversion bday categories", + "type": str}), ("conversion_bday_age_max", { "value": "99", "help": "conversion bday age max", diff --git a/radicale/item/__init__.py b/radicale/item/__init__.py index 2e8a6ef4..3e0fb599 100644 --- a/radicale/item/__init__.py +++ b/radicale/item/__init__.py @@ -663,6 +663,12 @@ class Item: description = ShareActions['config']['conversion_bday_description_template'] description = replace_placeholders(description, placeholder_mapping) + # create CATEGORIES + categories: list = ["Birthday"] # default + if ShareActions is not None and 'config' in ShareActions: + if 'conversion_bday_categories' in ShareActions['config']: + categories = ShareActions['config']['conversion_bday_categories'].split(',') + # check ALARM alarm_trigger = "" # default if ShareActions is not None and 'config' in ShareActions: @@ -718,6 +724,10 @@ class Item: summary_value = summary vevent.add('summary').value = summary_value + # set CATEGORIES + if categories is not None and categories != []: + vevent.add('categories').value = categories + # set VALARM if alarm_trigger is not None and alarm_trigger != "": for entry in alarm_trigger.split('|'): diff --git a/radicale/sharing/__init__.py b/radicale/sharing/__init__.py index dca7a89d..6356fd86 100644 --- a/radicale/sharing/__init__.py +++ b/radicale/sharing/__init__.py @@ -178,6 +178,7 @@ ACTIONS_WHITELIST: dict = { 'conversion_bday_summary_template': check_template, 'conversion_bday_description_template': check_template, 'conversion_bday_alarm_trigger_template': check_template_alarm_trigger, + 'conversion_bday_categories': str, 'conversion_bday_age_max': check_bday_max_age, }, } @@ -228,6 +229,7 @@ class BaseSharing: self.conversion_bday_summary_template = configuration.get("sharing", "conversion_bday_summary_template") self.conversion_bday_description_template = configuration.get("sharing", "conversion_bday_description_template") self.conversion_bday_alarm_trigger_template = configuration.get("sharing", "conversion_bday_alarm_trigger_template") + self.conversion_bday_categories = configuration.get("sharing", "conversion_bday_categories") self.conversion_bday_age_max = configuration.get("sharing", "conversion_bday_age_max") logger.info("sharing.collection_by_map : %s", self.sharing_collection_by_map) @@ -241,6 +243,7 @@ class BaseSharing: logger.info("sharing.conversion_bday_summary_template: %s", self.conversion_bday_summary_template) logger.info("sharing.conversion_bday_description_template: %s", self.conversion_bday_description_template) logger.info("sharing.conversion_bday_alarm_trigger_template: %s", self.conversion_bday_alarm_trigger_template) + logger.info("sharing.conversion_bday_categories: %s", self.conversion_bday_categories) logger.info("sharing.conversion_bday_age_max: %s", self.conversion_bday_age_max) # database tasks @@ -479,6 +482,7 @@ class BaseSharing: 'conversion_bday_summary_template': self.conversion_bday_summary_template, 'conversion_bday_description_template': self.conversion_bday_description_template, 'conversion_bday_alarm_trigger_template': self.conversion_bday_alarm_trigger_template, + 'conversion_bday_categories': self.conversion_bday_categories, 'conversion_bday_age_max': self.conversion_bday_age_max, }, } @@ -508,6 +512,14 @@ class BaseSharing: {'conversion_bday_alarm_trigger_template': self.conversion_bday_alarm_trigger_template} ) + if 'conversion_bday_categories' in share['Actions']['config']: + # nothing to do + pass + else: + share['Actions']['config'].update( + {'conversion_bday_categories': self.conversion_bday_categories} + ) + if 'conversion_bday_age_max' in share['Actions']['config']: # nothing to do pass diff --git a/radicale/tests/test_sharing.py b/radicale/tests/test_sharing.py index 71756e18..be44d0e8 100644 --- a/radicale/tests/test_sharing.py +++ b/radicale/tests/test_sharing.py @@ -4830,6 +4830,7 @@ permissions: RrWw""") assert "DTEND;VALUE=DATE:19700102" in answer assert "TRANSP:TRANSPARENT" in answer assert "DESCRIPTION:BDAY=1970-01-01" in answer + assert "CATEGORIES:Birthday" in answer # content type must be adjusted assert 'Content-Type' in headers assert 'text/calendar' in headers['Content-Type'] @@ -4879,6 +4880,7 @@ permissions: RrWw""") assert "DTEND;VALUE=DATE:19700102" in answer assert "TRANSP:TRANSPARENT" in answer assert "DESCRIPTION:BDAY=1970-01-01" in answer + assert "CATEGORIES:Birthday" in answer # content type must be adjusted assert 'Content-Type' in headers assert 'text/calendar' in headers['Content-Type'] @@ -5068,6 +5070,13 @@ permissions: RrWw""") assert "TRIGGER:-PT12H" in answer assert "TRIGGER:PT12H" in answer + self.configure({"sharing": {"conversion_bday_categories": "Birthday,Geburtstag"}}) + logging.info("\n*** GET collection user format: description -> ok") + _, headers, answer = self.request("GET", path_shared_3, login="user:userpw") + assert "DESCRIPTION:Birthday tomorrow of Test-FN-C3" in answer + assert "DESCRIPTION:Birthday today of Given3Test Family3Test" in answer + assert "CATEGORIES:Birthday,Geburtstag" in answer + logging.info("\n*** configuration test: conversion_bday_summary_template not supported") try: self.configure({"sharing": {