diff --git a/radicale/__init__.py b/radicale/__init__.py index 6b86ff7..7100c05 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -269,7 +269,7 @@ class Application(object): """Manage DELETE request.""" calendar = calendars[0] - if calendar.local_path == environ["PATH_INFO"].strip("/"): + if calendar.path == environ["PATH_INFO"].strip("/"): # Path matching the calendar, the item to delete is the calendar item = calendar else: diff --git a/radicale/ical.py b/radicale/ical.py index 81ac38f..1692a05 100644 --- a/radicale/ical.py +++ b/radicale/ical.py @@ -143,7 +143,11 @@ class Timezone(Item): class Calendar(object): - """Internal calendar class.""" + """Internal calendar class. + + This class must be overridden and replaced by a storage backend. + + """ tag = "VCALENDAR" def __init__(self, path, principal=False): @@ -155,7 +159,7 @@ class Calendar(object): """ self.encoding = "utf-8" split_path = path.split("/") - self.path = path + self.path = path if path != '.' else '' if principal and split_path and self.is_collection(self.path): # Already existing principal calendar self.owner = split_path[0] @@ -164,7 +168,6 @@ class Calendar(object): self.owner = split_path[0] else: self.owner = None - self.local_path = path if path != '.' else '' self.is_principal = principal @classmethod @@ -209,8 +212,22 @@ class Calendar(object): result.extend(calendar.components) return result - def open(self, path): - """Return the content of the calendar under ``path``.""" + def save(self, text): + """Save the text into the calendar.""" + raise NotImplemented + + def delete(self): + """Delete the calendar.""" + raise NotImplemented + + @property + def text(self): + """Calendar as plain text.""" + raise NotImplemented + + @classmethod + def children(cls, path): + """Yield the children of the collection at local ``path``.""" raise NotImplemented @classmethod @@ -223,15 +240,24 @@ class Calendar(object): """Return ``True`` if relative ``path`` is a collection item.""" raise NotImplemented + @property + def last_modified(self): + """Get the last time the calendar has been modified. + + The date is formatted according to rfc1123-5.2.14. + + """ + raise NotImplemented + + @property + @contextmanager + def props(self): + """Get the calendar properties.""" + raise NotImplemented + def is_vcalendar(self, path): """Return ``True`` if there is a VCALENDAR under relative ``path``.""" - with self.open(path) as stream: - return 'BEGIN:VCALENDAR' == stream.read(15) - - @classmethod - def children(cls, path): - """Yield the children of the collection at local ``path``.""" - raise NotImplemented + return self.text.startswith('BEGIN:VCALENDAR') @staticmethod def _parse(text, item_types, name=None): @@ -295,10 +321,6 @@ class Calendar(object): self.write(items=items) - def delete(self): - """Delete the calendar.""" - raise NotImplemented - def remove(self, name): """Remove object named ``name`` from calendar.""" components = [ @@ -323,10 +345,6 @@ class Calendar(object): text = serialize(headers, items) self.save(text) - def save(self, text): - """Save the text into the calendar.""" - raise NotImplemented - @property def etag(self): """Etag from calendar.""" @@ -339,11 +357,6 @@ class Calendar(object): return props.get('D:displayname', self.path.split(os.path.sep)[-1]) - @property - def text(self): - """Calendar as plain text.""" - raise NotImplemented - @property def headers(self): """Find headers items in calendar.""" @@ -389,21 +402,6 @@ class Calendar(object): """Get list of ``Timezome`` items in calendar.""" return self._parse(self.text, (Timezone,)) - @property - def last_modified(self): - """Get the last time the calendar has been modified. - - The date is formatted according to rfc1123-5.2.14. - - """ - raise NotImplemented - - @property - @contextmanager - def props(self): - """Get the calendar properties.""" - raise NotImplemented - @property def owner_url(self): """Get the calendar URL according to its owner.""" @@ -415,4 +413,4 @@ class Calendar(object): @property def url(self): """Get the standard calendar URL.""" - return "/%s/" % self.local_path + return "/%s/" % self.path diff --git a/radicale/storage/filesystem.py b/radicale/storage/filesystem.py index 8c1db43..b735d22 100644 --- a/radicale/storage/filesystem.py +++ b/radicale/storage/filesystem.py @@ -33,33 +33,15 @@ from radicale import config, ical FOLDER = os.path.expanduser(config.get("storage", "filesystem_folder")) +# This function overrides the builtin ``open`` function for this module +# pylint: disable=W0622 +def open(path, mode="r"): + abs_path = os.path.join(FOLDER, path.replace("/", os.sep)) + return codecs.open(abs_path, mode, config.get("encoding", "stock")) +# pylint: enable=W0622 + + class Calendar(ical.Calendar): - @staticmethod - def open(path, mode="r"): - abs_path = os.path.join(FOLDER, path.replace("/", os.sep)) - return codecs.open(abs_path, mode, config.get("encoding", "stock")) - - @classmethod - def is_collection(cls, path): - abs_path = os.path.join(FOLDER, path.replace("/", os.sep)) - return os.path.isdir(abs_path) - - @classmethod - def is_item(cls, path): - abs_path = os.path.join(FOLDER, path.replace("/", os.sep)) - return os.path.isfile(abs_path) - - @classmethod - def children(cls, path): - abs_path = os.path.join(FOLDER, path.replace("/", os.sep)) - for filename in next(os.walk(abs_path))[2]: - if cls.is_collection(path): - yield cls(path) - - def delete(self): - os.remove(self._path) - os.remove(self._props_path) - @property def _path(self): """Absolute path of the file at local ``path``.""" @@ -75,6 +57,47 @@ class Calendar(ical.Calendar): if not os.path.exists(os.path.dirname(self._path)): os.makedirs(os.path.dirname(self._path)) + def save(self, text): + self._create_dirs() + open(self._path, "w").write(text) + + def delete(self): + os.remove(self._path) + os.remove(self._props_path) + + @property + def text(self): + try: + return open(self._path).read() + except IOError: + return "" + + @classmethod + def children(cls, path): + abs_path = os.path.join(FOLDER, path.replace("/", os.sep)) + for filename in next(os.walk(abs_path))[2]: + if cls.is_collection(path): + yield cls(path) + + @classmethod + def is_collection(cls, path): + abs_path = os.path.join(FOLDER, path.replace("/", os.sep)) + return os.path.isdir(abs_path) + + @classmethod + def is_item(cls, path): + abs_path = os.path.join(FOLDER, path.replace("/", os.sep)) + return os.path.isfile(abs_path) + + @property + def last_modified(self): + # Create calendar if needed + if not os.path.exists(self._path): + self.write() + + modification_time = time.gmtime(os.path.getmtime(self._path)) + return time.strftime("%a, %d %b %Y %H:%M:%S +0000", modification_time) + @property @contextmanager def props(self): @@ -89,25 +112,5 @@ class Calendar(ical.Calendar): with open(self._props_path, 'w') as prop_file: json.dump(properties, prop_file) - @property - def last_modified(self): - # Create calendar if needed - if not os.path.exists(self._path): - self.write() - - modification_time = time.gmtime(os.path.getmtime(self._path)) - return time.strftime("%a, %d %b %Y %H:%M:%S +0000", modification_time) - - @property - def text(self): - try: - return open(self._path).read() - except IOError: - return "" - - def save(self, text): - self._create_dirs() - self.open(self._path, "w").write(text) - ical.Calendar = Calendar diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index 514e822..9e64bfe 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -120,7 +120,7 @@ def _response(code): def name_from_path(path, calendar): """Return Radicale item name from ``path``.""" - calendar_parts = calendar.local_path.strip("/").split("/") + calendar_parts = calendar.path.split("/") path_parts = path.strip("/").split("/") return path_parts[-1] if (len(path_parts) - len(calendar_parts)) else None @@ -153,7 +153,7 @@ def delete(path, calendar): """ # Reading request - if calendar.local_path == path.strip("/"): + if calendar.path == path.strip("/"): # Delete the whole calendar calendar.delete() else: