Explorar o código

Merge pull request #463 from Unrud/atomicmove

Atomic MOVE
Guillaume Ayoub %!s(int64=9) %!d(string=hai) anos
pai
achega
0cda9f611d
Modificáronse 2 ficheiros con 34 adicións e 5 borrados
  1. 5 5
      radicale/__init__.py
  2. 29 0
      radicale/storage.py

+ 5 - 5
radicale/__init__.py

@@ -473,8 +473,6 @@ class Application:
         to_path = storage.sanitize_path(to_url.path)
         if not self._access(user, to_path, "w"):
             return NOT_ALLOWED
-        if to_path.strip("/").startswith(path.strip("/")):
-            return client.CONFLICT, {}, None
 
         with self._lock_collection("w", user):
             item = next(self.Collection.discover(path), None)
@@ -488,15 +486,17 @@ class Application:
                 return client.CONFLICT, {}, None
 
             to_item = next(self.Collection.discover(to_path), None)
+            if (isinstance(to_item, self.Collection) or
+                    to_item and environ.get("HTTP_OVERWRITE", "F") != "T"):
+                return client.CONFLICT, {}, None
             to_parent_path = storage.sanitize_path(
                 "/%s/" % posixpath.dirname(to_path.strip("/")))
             to_collection = next(
                 self.Collection.discover(to_parent_path), None)
-            if not to_collection or to_item:
+            if not to_collection:
                 return client.CONFLICT, {}, None
             to_href = posixpath.basename(to_path.strip("/"))
-            to_collection.upload(to_href, item.item)
-            item.collection.delete(item.href)
+            self.Collection.move(item, to_collection, to_href)
             return client.CREATED, {}, None
 
     def do_OPTIONS(self, environ, path, content, user):

+ 29 - 0
radicale/storage.py

@@ -265,6 +265,26 @@ class BaseCollection:
         """
         raise NotImplementedError
 
+    @classmethod
+    def move(cls, item, to_collection, to_href):
+        """Move an object.
+
+        ``item`` is the item to move.
+
+        ``to_collection`` is the target collection.
+
+        ``to_href`` is the target name in ``to_collection``. An item with the
+        same name might already exist.
+
+        """
+        if item.collection.path == to_collection.path and item.href == to_href:
+            return
+        if to_collection.has(to_href):
+            to_collection.update(to_href, item.item)
+        else:
+            to_collection.upload(to_href, item.item)
+        item.collection.delete(item.href)
+
     @property
     def etag(self):
         return get_etag(self.serialize())
@@ -514,6 +534,15 @@ class Collection(BaseCollection):
 
         return cls(sane_path, principal=principal)
 
+    @classmethod
+    def move(cls, item, to_collection, to_href):
+        os.rename(
+            path_to_filesystem(item.collection._filesystem_path, item.href),
+            path_to_filesystem(to_collection._filesystem_path, to_href))
+        sync_directory(to_collection._filesystem_path)
+        if item.collection._filesystem_path != to_collection._filesystem_path:
+            sync_directory(item.collection._filesystem_path)
+
     def list(self):
         try:
             hrefs = os.listdir(self._filesystem_path)