Просмотр исходного кода

Merge pull request #1473 from itglob/master

Ability to create predefined calendar or(and) addressbook for new user
Peter Bieringer 1 год назад
Родитель
Сommit
e9c7d4a671
4 измененных файлов с 63 добавлено и 2 удалено
  1. 20 0
      DOCUMENTATION.md
  2. 18 0
      config
  3. 8 1
      radicale/app/__init__.py
  4. 17 1
      radicale/config.py

+ 20 - 0
DOCUMENTATION.md

@@ -878,6 +878,26 @@ Command that is run after changes to storage. Take a look at the
 
 
 Default:
 Default:
 
 
+##### predefined_collections
+
+Create predefined user collections
+
+ Example:
+
+     {
+       "def-addressbook": {
+          "D:displayname": "Personal Address Book",
+          "tag": "VADDRESSBOOK"
+       },
+       "def-calendar": {
+          "C:supported-calendar-component-set": "VEVENT,VJOURNAL,VTODO",
+          "D:displayname": "Personal Calendar",
+          "tag": "VCALENDAR"
+       }
+     }
+
+Default:
+
 #### web
 #### web
 
 
 ##### type
 ##### type

+ 18 - 0
config

@@ -106,6 +106,24 @@
 # Example: ([ -d .git ] || git init) && git add -A && (git diff --cached --quiet || git commit -m "Changes by \"%(user)s\"")
 # Example: ([ -d .git ] || git init) && git add -A && (git diff --cached --quiet || git commit -m "Changes by \"%(user)s\"")
 #hook =
 #hook =
 
 
+# Create predefined user collections
+#
+# json format:
+#
+#  {
+#    "def-addressbook": {
+#       "D:displayname": "Personal Address Book",
+#       "tag": "VADDRESSBOOK"
+#    },
+#    "def-calendar": {
+#       "C:supported-calendar-component-set": "VEVENT,VJOURNAL,VTODO",
+#       "D:displayname": "Personal Calendar",
+#       "tag": "VCALENDAR"
+#    }
+#  }
+#
+#predefined_collections =
+
 
 
 [web]
 [web]
 
 

+ 8 - 1
radicale/app/__init__.py

@@ -274,7 +274,14 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
                 if "W" in self._rights.authorization(user, principal_path):
                 if "W" in self._rights.authorization(user, principal_path):
                     with self._storage.acquire_lock("w", user):
                     with self._storage.acquire_lock("w", user):
                         try:
                         try:
-                            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():
+                                    try:
+                                        self._storage.create_collection(principal_path + name_coll, props=props)
+                                    except ValueError as e:
+                                        logger.warning("Failed to create predefined collection %r: %s", name_coll, e)
                         except ValueError as e:
                         except ValueError as e:
                             logger.warning("Failed to create principal "
                             logger.warning("Failed to create principal "
                                            "collection %r: %s", user, e)
                                            "collection %r: %s", user, e)

+ 17 - 1
radicale/config.py

@@ -27,6 +27,7 @@ Use ``load()`` to obtain an instance of ``Configuration`` for use with
 """
 """
 
 
 import contextlib
 import contextlib
+import json
 import math
 import math
 import os
 import os
 import string
 import string
@@ -37,6 +38,7 @@ from typing import (Any, Callable, ClassVar, Iterable, List, Optional,
                     Sequence, Tuple, TypeVar, Union)
                     Sequence, Tuple, TypeVar, Union)
 
 
 from radicale import auth, hook, rights, storage, types, web
 from radicale import auth, hook, rights, storage, types, web
+from radicale.item import check_and_sanitize_props
 
 
 DEFAULT_CONFIG_PATH: str = os.pathsep.join([
 DEFAULT_CONFIG_PATH: str = os.pathsep.join([
     "?/etc/radicale/config",
     "?/etc/radicale/config",
@@ -102,6 +104,16 @@ def _convert_to_bool(value: Any) -> bool:
     return RawConfigParser.BOOLEAN_STATES[value.lower()]
     return RawConfigParser.BOOLEAN_STATES[value.lower()]
 
 
 
 
+def json_str(value: Any) -> dict:
+    if not value:
+        return {}
+    ret = json.loads(value)
+    for (name_coll, props) in ret.items():
+        checked_props = check_and_sanitize_props(props)
+        ret[name_coll] = checked_props
+    return ret
+
+
 INTERNAL_OPTIONS: Sequence[str] = ("_allow_extra",)
 INTERNAL_OPTIONS: Sequence[str] = ("_allow_extra",)
 # Default configuration
 # Default configuration
 DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
 DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
@@ -222,7 +234,11 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
         ("_filesystem_fsync", {
         ("_filesystem_fsync", {
             "value": "True",
             "value": "True",
             "help": "sync all changes to filesystem during requests",
             "help": "sync all changes to filesystem during requests",
-            "type": bool})])),
+            "type": bool}),
+        ("predefined_collections", {
+            "value": "",
+            "help": "predefined user collections",
+            "type": json_str})])),
     ("hook", OrderedDict([
     ("hook", OrderedDict([
         ("type", {
         ("type", {
             "value": "none",
             "value": "none",