sharing/bday/templating: default config rework
This commit is contained in:
@@ -595,11 +595,11 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
|
|||||||
"help": "default permissions for map-based sharing",
|
"help": "default permissions for map-based sharing",
|
||||||
"type": rights_permission}),
|
"type": rights_permission}),
|
||||||
("conversion_bday_summary_template", {
|
("conversion_bday_summary_template", {
|
||||||
"value": "[{n:f} {n:g} {n:a}|{fn}|{nickname}] (BDAY)",
|
"value": sharing.SHARING_BDAY_SUMMARY_TEMPLATE_DEFAULT,
|
||||||
"help": "conversion bday summary template",
|
"help": "conversion bday summary template",
|
||||||
"type": sharing.check_template}),
|
"type": sharing.check_template}),
|
||||||
("conversion_bday_description_template", {
|
("conversion_bday_description_template", {
|
||||||
"value": "BDAY={year}-{month}-{day}",
|
"value": sharing.SHARING_BDAY_DESCRIPTION_TEMPLATE_DEFAULT,
|
||||||
"help": "conversion bday description template",
|
"help": "conversion bday description template",
|
||||||
"type": sharing.check_template}),
|
"type": sharing.check_template}),
|
||||||
("conversion_bday_alarm_trigger_template", {
|
("conversion_bday_alarm_trigger_template", {
|
||||||
@@ -607,11 +607,11 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
|
|||||||
"help": "conversion bday alarm trigger template",
|
"help": "conversion bday alarm trigger template",
|
||||||
"type": sharing.check_template_alarm_trigger}),
|
"type": sharing.check_template_alarm_trigger}),
|
||||||
("conversion_bday_categories", {
|
("conversion_bday_categories", {
|
||||||
"value": "Birthday",
|
"value": sharing.SHARING_BDAY_CATEGORIES_DEFAULT,
|
||||||
"help": "conversion bday categories",
|
"help": "conversion bday categories",
|
||||||
"type": str}),
|
"type": str}),
|
||||||
("conversion_bday_age_max", {
|
("conversion_bday_age_max", {
|
||||||
"value": "99",
|
"value": str(sharing.SHARING_BDAY_AGE_MAX_DEFAULT),
|
||||||
"help": "conversion bday age max",
|
"help": "conversion bday age max",
|
||||||
"type": sharing.check_bday_max_age}),
|
"type": sharing.check_bday_max_age}),
|
||||||
])),
|
])),
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ from typing import (Any, Callable, List, MutableMapping, Optional, Sequence,
|
|||||||
import vobject
|
import vobject
|
||||||
|
|
||||||
from radicale import storage # noqa:F401
|
from radicale import storage # noqa:F401
|
||||||
from radicale import pathutils, utils
|
from radicale import pathutils, sharing, utils
|
||||||
from radicale.item import filter as radicale_filter
|
from radicale.item import filter as radicale_filter
|
||||||
from radicale.log import logger
|
from radicale.log import logger
|
||||||
|
|
||||||
@@ -594,7 +594,6 @@ class Item:
|
|||||||
placeholder_mapping: dict = {}
|
placeholder_mapping: dict = {}
|
||||||
|
|
||||||
bdayS = match[1] + match[2] + match[3]
|
bdayS = match[1] + match[2] + match[3]
|
||||||
bdaySdesc = match[1] + "-" + match[2] + "-" + match[3]
|
|
||||||
bdayY = int(match[1])
|
bdayY = int(match[1])
|
||||||
bdayM = int(match[2])
|
bdayM = int(match[2])
|
||||||
bdayD = int(match[3])
|
bdayD = int(match[3])
|
||||||
@@ -649,37 +648,49 @@ class Item:
|
|||||||
else:
|
else:
|
||||||
item_ics.add('prodid').value = PRODID_CONVERTED
|
item_ics.add('prodid').value = PRODID_CONVERTED
|
||||||
|
|
||||||
# create SUMMARY
|
# prepare SUMMARY
|
||||||
summary = name + " (BDAY)" # default
|
if ShareActions is not None and 'config' in ShareActions and 'conversion_bday_summary_template' in ShareActions['config']:
|
||||||
if ShareActions is not None and 'config' in ShareActions:
|
summary = ShareActions['config']['conversion_bday_summary_template']
|
||||||
if 'conversion_bday_summary_template' in ShareActions['config']:
|
elif ShareActions is not None and 'config_default' in ShareActions and 'conversion_bday_summary_template' in ShareActions['config_default']:
|
||||||
summary = ShareActions['config']['conversion_bday_summary_template']
|
summary = ShareActions['config_default']['conversion_bday_summary_template']
|
||||||
summary = replace_placeholders(summary, placeholder_mapping)
|
else:
|
||||||
|
summary = sharing.SHARING_BDAY_SUMMARY_TEMPLATE_DEFAULT # fallback
|
||||||
|
summary = replace_placeholders(summary, placeholder_mapping)
|
||||||
|
|
||||||
# create DESCRIPTION
|
# prepare DESCRIPTION
|
||||||
description = "BDAY=" + bdaySdesc # default
|
if ShareActions is not None and 'config' in ShareActions and 'conversion_bday_description_template' in ShareActions['config']:
|
||||||
if ShareActions is not None and 'config' in ShareActions:
|
description = ShareActions['config']['conversion_bday_description_template']
|
||||||
if 'conversion_bday_description_template' in ShareActions['config']:
|
elif ShareActions is not None and 'config_default' in ShareActions and 'conversion_bday_description_template' in ShareActions['config_default']:
|
||||||
description = ShareActions['config']['conversion_bday_description_template']
|
description = ShareActions['config_default']['conversion_bday_description_template']
|
||||||
description = replace_placeholders(description, placeholder_mapping)
|
else:
|
||||||
|
description = sharing.SHARING_BDAY_DESCRIPTION_TEMPLATE_DEFAULT # fallback
|
||||||
|
description = replace_placeholders(description, placeholder_mapping)
|
||||||
|
|
||||||
# create CATEGORIES
|
# create CATEGORIES
|
||||||
categories: list = ["Birthday"] # default
|
if ShareActions is not None and 'config' in ShareActions and 'conversion_bday_categories' in ShareActions['config']:
|
||||||
if ShareActions is not None and 'config' in ShareActions:
|
categories = ShareActions['config']['conversion_bday_categories'].split(',')
|
||||||
if 'conversion_bday_categories' in ShareActions['config']:
|
elif ShareActions is not None and 'config_default' in ShareActions and 'conversion_bday_categories' in ShareActions['config_default']:
|
||||||
categories = ShareActions['config']['conversion_bday_categories'].split(',')
|
categories = ShareActions['config_default']['conversion_bday_categories'].split(',')
|
||||||
|
else:
|
||||||
|
categories = sharing.SHARING_BDAY_CATEGORIES_DEFAULT.split(',') # fallback
|
||||||
|
|
||||||
# check ALARM
|
# check ALARM
|
||||||
alarm_trigger = "" # default
|
if ShareActions is not None and 'config' in ShareActions and 'conversion_bday_alarm_trigger_template' in ShareActions['config']:
|
||||||
if ShareActions is not None and 'config' in ShareActions:
|
|
||||||
alarm_trigger = ShareActions['config']['conversion_bday_alarm_trigger_template']
|
alarm_trigger = ShareActions['config']['conversion_bday_alarm_trigger_template']
|
||||||
|
elif ShareActions is not None and 'config_default' in ShareActions and 'conversion_bday_alarm_trigger_template' in ShareActions['config_default']:
|
||||||
|
alarm_trigger = ShareActions['config_default']['conversion_bday_alarm_trigger_template']
|
||||||
|
else:
|
||||||
|
alarm_trigger = "" # default
|
||||||
|
|
||||||
vevent_enable_age = False
|
vevent_enable_age = False
|
||||||
age_max = 0
|
age_max = 0
|
||||||
if "{age}" in summary or "{age}" in description or "age" in alarm_trigger:
|
if "{age}" in summary or "{age}" in description or "age" in alarm_trigger:
|
||||||
if ShareActions is not None and 'config' in ShareActions:
|
if ShareActions is not None and 'config' in ShareActions and 'conversion_bday_age_max' in ShareActions['config']:
|
||||||
if 'conversion_bday_age_max' in ShareActions['config']:
|
age_max = ShareActions['config']['conversion_bday_age_max']
|
||||||
age_max = ShareActions['config']['conversion_bday_age_max']
|
elif ShareActions is not None and 'config_default' in ShareActions and 'conversion_bday_age_max' in ShareActions['config_default']:
|
||||||
|
age_max = ShareActions['config_default']['conversion_bday_age_max']
|
||||||
|
else:
|
||||||
|
age_max = sharing.SHARING_BDAY_AGE_MAX_DEFAULT # fallback
|
||||||
vevent_enable_age = True
|
vevent_enable_age = True
|
||||||
|
|
||||||
# create UID
|
# create UID
|
||||||
|
|||||||
@@ -123,15 +123,19 @@ TOKEN_PATTERN_V1: str = "v1/[a-zA-Z0-9_\\-]{44}"
|
|||||||
|
|
||||||
OVERLAY_PROPERTIES_WHITELIST: Sequence[str] = ("C:calendar-description", "ICAL:calendar-color", "CR:addressbook-description", "INF:addressbook-color", "D:displayname", "ICAL:calendar-order")
|
OVERLAY_PROPERTIES_WHITELIST: Sequence[str] = ("C:calendar-description", "ICAL:calendar-color", "CR:addressbook-description", "INF:addressbook-color", "D:displayname", "ICAL:calendar-order")
|
||||||
|
|
||||||
SHARING_BDAY_AGE_MAX: int = 199 # maximum age to prevent unexpected DoS by config
|
SHARING_BDAY_AGE_MAX_LIMIT: int = 199 # maximum age to prevent unexpected DoS by config
|
||||||
|
SHARING_BDAY_AGE_MAX_DEFAULT: int = 99
|
||||||
|
SHARING_BDAY_SUMMARY_TEMPLATE_DEFAULT: str = "[{n:f} {n:g} {n:a}|{fn}|{nickname}] ({year}) (BDAY)"
|
||||||
|
SHARING_BDAY_DESCRIPTION_TEMPLATE_DEFAULT: str = "BDAY={year}-{month}-{day}"
|
||||||
|
SHARING_BDAY_CATEGORIES_DEFAULT: str = 'Birthday'
|
||||||
|
|
||||||
|
|
||||||
def check_bday_max_age(data: Any) -> int:
|
def check_bday_max_age(data: Any) -> int:
|
||||||
value = int(data)
|
value = int(data)
|
||||||
if value < 0:
|
if value < 0:
|
||||||
raise ValueError("value is negative: %d" % value)
|
raise ValueError("value is negative: %d" % value)
|
||||||
if value > SHARING_BDAY_AGE_MAX:
|
if value > SHARING_BDAY_AGE_MAX_LIMIT:
|
||||||
raise ValueError("value exceeds maximum (%d): %d" % (SHARING_BDAY_AGE_MAX, value))
|
raise ValueError("value exceeds maximum (%d): %d" % (SHARING_BDAY_AGE_MAX_LIMIT, value))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
@@ -478,7 +482,7 @@ class BaseSharing:
|
|||||||
# autogenerate Actions if not existing
|
# autogenerate Actions if not existing
|
||||||
if share['Actions'] is None or 'config' not in share['Actions']:
|
if share['Actions'] is None or 'config' not in share['Actions']:
|
||||||
share['Actions'] = {
|
share['Actions'] = {
|
||||||
'config': {
|
'config_default': {
|
||||||
'conversion_bday_summary_template': self.conversion_bday_summary_template,
|
'conversion_bday_summary_template': self.conversion_bday_summary_template,
|
||||||
'conversion_bday_description_template': self.conversion_bday_description_template,
|
'conversion_bday_description_template': self.conversion_bday_description_template,
|
||||||
'conversion_bday_alarm_trigger_template': self.conversion_bday_alarm_trigger_template,
|
'conversion_bday_alarm_trigger_template': self.conversion_bday_alarm_trigger_template,
|
||||||
@@ -492,7 +496,9 @@ class BaseSharing:
|
|||||||
# nothing to do
|
# nothing to do
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
share['Actions']['config'].update(
|
if 'config_default' not in share['Actions']:
|
||||||
|
share['Actions'].update({'config_default': {}})
|
||||||
|
share['Actions']['config_default'].update(
|
||||||
{'conversion_bday_summary_template': self.conversion_bday_summary_template}
|
{'conversion_bday_summary_template': self.conversion_bday_summary_template}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -500,7 +506,9 @@ class BaseSharing:
|
|||||||
# nothing to do
|
# nothing to do
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
share['Actions']['config'].update(
|
if 'config_default' not in share['Actions']:
|
||||||
|
share['Actions'].update({'config_default': {}})
|
||||||
|
share['Actions']['config_default'].update(
|
||||||
{'conversion_bday_description_template': self.conversion_bday_description_template}
|
{'conversion_bday_description_template': self.conversion_bday_description_template}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -508,7 +516,9 @@ class BaseSharing:
|
|||||||
# nothing to do
|
# nothing to do
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
share['Actions']['config'].update(
|
if 'config_default' not in share['Actions']:
|
||||||
|
share['Actions'].update({'config_default': {}})
|
||||||
|
share['Actions']['config_default'].update(
|
||||||
{'conversion_bday_alarm_trigger_template': self.conversion_bday_alarm_trigger_template}
|
{'conversion_bday_alarm_trigger_template': self.conversion_bday_alarm_trigger_template}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -516,7 +526,9 @@ class BaseSharing:
|
|||||||
# nothing to do
|
# nothing to do
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
share['Actions']['config'].update(
|
if 'config_default' not in share['Actions']:
|
||||||
|
share['Actions'].update({'config_default': {}})
|
||||||
|
share['Actions']['config_default'].update(
|
||||||
{'conversion_bday_categories': self.conversion_bday_categories}
|
{'conversion_bday_categories': self.conversion_bday_categories}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -524,7 +536,9 @@ class BaseSharing:
|
|||||||
# nothing to do
|
# nothing to do
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
share['Actions']['config'].update(
|
if 'config_default' not in share['Actions']:
|
||||||
|
share['Actions'].update({'config_default': {}})
|
||||||
|
share['Actions']['config_default'].update(
|
||||||
{'conversion_bday_age_max': self.conversion_bday_age_max}
|
{'conversion_bday_age_max': self.conversion_bday_age_max}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -5008,7 +5008,7 @@ permissions: RrWw""")
|
|||||||
assert "SUMMARY:Test-FN (BDAY)" in answer
|
assert "SUMMARY:Test-FN (BDAY)" in answer
|
||||||
|
|
||||||
self.configure({"sharing": {"conversion_bday_summary_template": "[{fn}|{n:f} {n:g} {n:a}|{nickname}] (Birthday)"}})
|
self.configure({"sharing": {"conversion_bday_summary_template": "[{fn}|{n:f} {n:g} {n:a}|{nickname}] (Birthday)"}})
|
||||||
logging.info("\n*** GET collection user format:text -> ok")
|
logging.info("\n*** GET collection user format with fn+Birthday -> ok")
|
||||||
_, headers, answer = self.request("GET", path_shared_2, login="user:userpw")
|
_, headers, answer = self.request("GET", path_shared_2, login="user:userpw")
|
||||||
assert "SUMMARY:Test-FN (Birthday)" in answer
|
assert "SUMMARY:Test-FN (Birthday)" in answer
|
||||||
|
|
||||||
@@ -5244,7 +5244,7 @@ permissions: RrWw""")
|
|||||||
logging.info("\n*** configuration test: conversion_bday_age_max > MAX")
|
logging.info("\n*** configuration test: conversion_bday_age_max > MAX")
|
||||||
try:
|
try:
|
||||||
self.configure({"sharing": {
|
self.configure({"sharing": {
|
||||||
"conversion_bday_age_max": (sharing.SHARING_BDAY_AGE_MAX + 1),
|
"conversion_bday_age_max": (sharing.SHARING_BDAY_AGE_MAX_LIMIT + 1),
|
||||||
}})
|
}})
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
@@ -5349,7 +5349,7 @@ permissions: RrWw""")
|
|||||||
json_dict['PathMapped'] = path_mapped
|
json_dict['PathMapped'] = path_mapped
|
||||||
json_dict['PathOrToken'] = path_shared_r
|
json_dict['PathOrToken'] = path_shared_r
|
||||||
json_dict['Actions'] = {"config": {
|
json_dict['Actions'] = {"config": {
|
||||||
"conversion_bday_age_max": (sharing.SHARING_BDAY_AGE_MAX + 1),
|
"conversion_bday_age_max": (sharing.SHARING_BDAY_AGE_MAX_LIMIT + 1),
|
||||||
}}
|
}}
|
||||||
_, headers, answer = self._sharing_api_json("map", "update", check=400, login="owner:ownerpw", json_dict=json_dict)
|
_, headers, answer = self._sharing_api_json("map", "update", check=400, login="owner:ownerpw", json_dict=json_dict)
|
||||||
|
|
||||||
@@ -5371,7 +5371,7 @@ permissions: RrWw""")
|
|||||||
json_dict['PathMapped'] = path_mapped
|
json_dict['PathMapped'] = path_mapped
|
||||||
json_dict['PathOrToken'] = path_shared_r
|
json_dict['PathOrToken'] = path_shared_r
|
||||||
json_dict['Actions'] = {"config": {
|
json_dict['Actions'] = {"config": {
|
||||||
"conversion_bday_age_max": sharing.SHARING_BDAY_AGE_MAX,
|
"conversion_bday_age_max": sharing.SHARING_BDAY_AGE_MAX_LIMIT,
|
||||||
}}
|
}}
|
||||||
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
|
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
|
||||||
|
|
||||||
@@ -5952,8 +5952,8 @@ permissions: RrWw""")
|
|||||||
logging.info("\n*** GET bday with token")
|
logging.info("\n*** GET bday with token")
|
||||||
_, answer = self.get(path_shared)
|
_, answer = self.get(path_shared)
|
||||||
assert "VCARD" not in answer
|
assert "VCARD" not in answer
|
||||||
assert "Test-FN-C3 (BDAY)" in answer
|
assert "Test-FN-C3 (BDAY)" in answer # contact2
|
||||||
assert "Test-FN (BDAY)" in answer
|
assert "Test-FN (BDAY)" in answer # contact1
|
||||||
|
|
||||||
# verify content as owner
|
# verify content as owner
|
||||||
logging.info("\n*** GET collection owner -> ok")
|
logging.info("\n*** GET collection owner -> ok")
|
||||||
|
|||||||
Reference in New Issue
Block a user