Sfoglia il codice sorgente

Adds multifilesystem storage backend (See #30)

Jean-Marc Martins 12 anni fa
parent
commit
6e98b1b3e8
3 ha cambiato i file con 104 aggiunte e 7 eliminazioni
  1. 1 1
      config
  2. 9 6
      radicale/ical.py
  3. 94 0
      radicale/storage/multifilesystem.py

+ 1 - 1
config

@@ -105,7 +105,7 @@ file = ~/.config/radicale/rights
 
 [storage]
 # Storage backend
-# Value: filesystem | database
+# Value: filesystem | multifilesystem | database
 type = filesystem
 
 # Folder for storing local collections, created if not present

+ 9 - 6
radicale/ical.py

@@ -107,6 +107,12 @@ class Item(object):
             self.text = self.text.replace(
                 "\nEND:", "\nX-RADICALE-NAME:%s\nEND:" % self._name)
 
+    def __hash__(self):
+        return hash(self.text)
+
+    def __eq__(self, item):
+        return isinstance(item, Item) and self.text == item.text
+
     @property
     def etag(self):
         """Item etag.
@@ -114,7 +120,7 @@ class Item(object):
         Etag is mainly used to know if an item has changed.
 
         """
-        return '"%s"' % hash(self.text)
+        return '"%s"' % hash(self)
 
     @property
     def name(self):
@@ -487,7 +493,7 @@ class Collection(object):
 
     @property
     def timezones(self):
-        """Get list of ``Timezome`` items in calendar."""
+        """Get list of ``Timezone`` items in calendar."""
         return self._parse(self.text, (Timezone,))
 
     @property
@@ -498,10 +504,7 @@ class Collection(object):
     @property
     def owner_url(self):
         """Get the collection URL according to its owner."""
-        if self.owner:
-            return "/%s/" % self.owner
-        else:
-            return None
+        return "/%s/" % self.owner if self.owner else None
 
     @property
     def url(self):

+ 94 - 0
radicale/storage/multifilesystem.py

@@ -0,0 +1,94 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of Radicale Server - Calendar Server
+# Copyright © 2013 Guillaume Ayoub
+# Copyright © 2013 Jean-Marc Martins
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Multi files per calendar filesystem storage backend.
+
+"""
+
+import os
+import shutil
+import time
+
+from . import filesystem
+from .. import ical
+
+
+class Collection(filesystem.Collection):
+    """Collection stored in several files per calendar."""
+    def _create_dirs(self):
+        if not os.path.exists(self._path):
+            os.makedirs(self._path)
+
+    @property
+    def headers(self):
+        return (
+            ical.Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"),
+            ical.Header("VERSION:%s" % self.version))
+
+    def write(self, headers=None, items=None):
+        self._create_dirs()
+        headers = headers or self.headers
+        items = items if items is not None else self.items
+        timezones = list(set(i for i in items if isinstance(i, ical.Timezone)))
+        components = [i for i in items if isinstance(i, ical.Component)]
+        for component in components:
+            text = ical.serialize(self.tag, headers, [component] + timezones)
+            path = os.path.join(self._path, component.name)
+            with filesystem.open(path, "w") as fd:
+                fd.write(text)
+
+    def delete(self):
+        shutil.rmtree(self._path)
+
+    def remove(self, name):
+        if os.path.exists(os.path.join(self._path, name)):
+            os.remove(os.path.join(self._path, name))
+
+    @property
+    def text(self):
+        components = (
+            ical.Timezone, ical.Event, ical.Todo, ical.Journal, ical.Card)
+        items = set()
+        try:
+            for filename in os.listdir(self._path):
+                with filesystem.open(os.path.join(self._path, filename)) as fd:
+                    items.update(self._parse(fd.read(), components))
+        except IOError:
+            return ""
+        else:
+            return ical.serialize(
+                self.tag, self.headers, sorted(items, key=lambda x: x.name))
+
+    @classmethod
+    def is_node(cls, path):
+        path = os.path.join(filesystem.FOLDER, path.replace("/", os.sep))
+        return os.path.isdir(path) and not os.path.exists(path + ".props")
+
+    @classmethod
+    def is_leaf(cls, path):
+        path = os.path.join(filesystem.FOLDER, path.replace("/", os.sep))
+        return os.path.isdir(path) and os.path.exists(path + ".props")
+
+    @property
+    def last_modified(self):
+        last = max([
+            os.path.getmtime(os.path.join(self._path, filename))
+            for filename in os.listdir(self._path)] or [0])
+        return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(last))