diff --git a/radicale/storage.py b/radicale/storage.py index d14797f..b8cb7bb 100644 --- a/radicale/storage.py +++ b/radicale/storage.py @@ -580,7 +580,17 @@ class Collection(BaseCollection): if href is None: # Delete the collection if os.path.isdir(self._filesystem_path): - shutil.rmtree(self._filesystem_path) + parent_dir = os.path.dirname(self._filesystem_path) + try: + os.rmdir(self._filesystem_path) + except OSError: + with TemporaryDirectory(prefix=".Radicale.tmp-", + dir=parent_dir) as tmp_dir: + os.rename(self._filesystem_path, os.path.join( + tmp_dir, os.path.basename(self._filesystem_path))) + sync_directory(parent_dir) + else: + sync_directory(parent_dir) else: # Delete an item if not is_safe_filesystem_path_component(href): @@ -593,6 +603,7 @@ class Collection(BaseCollection): if etag and etag != get_etag(text): raise EtagMismatchError(etag, get_etag(text)) os.remove(path) + sync_directory(os.path.dirname(path)) def get_meta(self, key): if os.path.exists(self._props_path): diff --git a/radicale/tests/test_base.py b/radicale/tests/test_base.py index c08e864..6f763a0 100644 --- a/radicale/tests/test_base.py +++ b/radicale/tests/test_base.py @@ -163,6 +163,31 @@ class BaseRequests: assert status == 200 assert "DAV" in headers + def test_delete_collection(self): + """Delete a collection.""" + self.request("MKCOL", "/calendar.ics/") + event = get_file_content("event1.ics") + self.request("PUT", "/calendar.ics/event1.ics", event) + status, headers, answer = self.request("DELETE", "/calendar.ics/") + assert status == 200 + assert "href>/calendar.ics//