Improve WebDAV compatibility
Add getlastmodified and getcontentlength properties, and fix nested collections support. Fix #321.
This commit is contained in:
parent
99544fcf80
commit
65659fc909
@ -114,14 +114,19 @@ def path_to_filesystem(root, *paths):
|
|||||||
|
|
||||||
|
|
||||||
class Item:
|
class Item:
|
||||||
def __init__(self, item, href, etag):
|
def __init__(self, item, href, etag, last_modified=None):
|
||||||
self.item = item
|
self.item = item
|
||||||
self.href = href
|
self.href = href
|
||||||
self.etag = etag
|
self.etag = etag
|
||||||
|
self.last_modified = last_modified
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
return getattr(self.item, attr)
|
return getattr(self.item, attr)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def content_length(self):
|
||||||
|
return len(self.serialize().encode(config.get("encoding", "request")))
|
||||||
|
|
||||||
|
|
||||||
class Collection:
|
class Collection:
|
||||||
"""Collection stored in several files per calendar."""
|
"""Collection stored in several files per calendar."""
|
||||||
@ -170,8 +175,7 @@ class Collection:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Try to guess if the path leads to a collection or an item
|
# Try to guess if the path leads to a collection or an item
|
||||||
if os.path.exists(path_to_filesystem(
|
if os.path.isfile(path_to_filesystem(FOLDER, sane_path)):
|
||||||
FOLDER, *attributes[:-1]) + ".props"):
|
|
||||||
attributes.pop()
|
attributes.pop()
|
||||||
|
|
||||||
path = "/".join(attributes)
|
path = "/".join(attributes)
|
||||||
@ -185,12 +189,11 @@ class Collection:
|
|||||||
if items:
|
if items:
|
||||||
for item in items:
|
for item in items:
|
||||||
yield collection.get(item[0])
|
yield collection.get(item[0])
|
||||||
else:
|
_, directories, _ = next(os.walk(collection._filesystem_path))
|
||||||
_, directories, _ = next(os.walk(collection._filesystem_path))
|
for sub_path in directories:
|
||||||
for sub_path in directories:
|
full_path = os.path.join(collection._filesystem_path, sub_path)
|
||||||
full_path = os.path.join(collection._filesystem_path, sub_path)
|
if os.path.exists(path_to_filesystem(full_path)):
|
||||||
if os.path.exists(path_to_filesystem(full_path)):
|
yield cls(posixpath.join(path, sub_path))
|
||||||
yield cls(posixpath.join(path, sub_path))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_collection(cls, href, collection=None, tag=None):
|
def create_collection(cls, href, collection=None, tag=None):
|
||||||
@ -248,7 +251,11 @@ class Collection:
|
|||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
with open(path, encoding=STORAGE_ENCODING) as fd:
|
with open(path, encoding=STORAGE_ENCODING) as fd:
|
||||||
text = fd.read()
|
text = fd.read()
|
||||||
return Item(vobject.readOne(text), href, get_etag(text))
|
last_modified = time.strftime(
|
||||||
|
"%a, %d %b %Y %H:%M:%S GMT",
|
||||||
|
time.gmtime(os.path.getmtime(path)))
|
||||||
|
return Item(
|
||||||
|
vobject.readOne(text), href, get_etag(text), last_modified)
|
||||||
else:
|
else:
|
||||||
log.LOGGER.debug(
|
log.LOGGER.debug(
|
||||||
"Can't tranlate name safely to filesystem, "
|
"Can't tranlate name safely to filesystem, "
|
||||||
@ -365,10 +372,10 @@ class Collection:
|
|||||||
@property
|
@property
|
||||||
def last_modified(self):
|
def last_modified(self):
|
||||||
"""Get the HTTP-datetime of when the collection was modified."""
|
"""Get the HTTP-datetime of when the collection was modified."""
|
||||||
last = max([
|
last = max([os.path.getmtime(self._filesystem_path)] + [
|
||||||
os.path.getmtime(os.path.join(self._filesystem_path, filename))
|
os.path.getmtime(os.path.join(self._filesystem_path, filename))
|
||||||
for filename in os.listdir(self._filesystem_path)] or [0])
|
for filename in os.listdir(self._filesystem_path)] or [0])
|
||||||
return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(last))
|
return time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(last))
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
items = []
|
items = []
|
||||||
|
@ -276,6 +276,8 @@ def _propfind_response(path, item, props, user, write=False):
|
|||||||
tag = ET.Element(_tag("D", "href"))
|
tag = ET.Element(_tag("D", "href"))
|
||||||
tag.text = _href(path)
|
tag.text = _href(path)
|
||||||
element.append(tag)
|
element.append(tag)
|
||||||
|
elif tag == _tag("D", "getlastmodified"):
|
||||||
|
element.text = item.last_modified
|
||||||
elif tag in (_tag("D", "principal-collection-set"),
|
elif tag in (_tag("D", "principal-collection-set"),
|
||||||
_tag("C", "calendar-user-address-set"),
|
_tag("C", "calendar-user-address-set"),
|
||||||
_tag("CR", "addressbook-home-set"),
|
_tag("CR", "addressbook-home-set"),
|
||||||
@ -377,6 +379,8 @@ def _propfind_response(path, item, props, user, write=False):
|
|||||||
elif tag == _tag("D", "resourcetype"):
|
elif tag == _tag("D", "resourcetype"):
|
||||||
# resourcetype must be returned empty for non-collection elements
|
# resourcetype must be returned empty for non-collection elements
|
||||||
pass
|
pass
|
||||||
|
elif tag == _tag("D", "getcontentlength"):
|
||||||
|
element.text = str(item.content_length)
|
||||||
else:
|
else:
|
||||||
is404 = True
|
is404 = True
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user