Better collection discovery

This commit is contained in:
Guillaume Ayoub 2016-04-09 15:11:47 +02:00
parent 058bd454c0
commit 7a0e267f19
4 changed files with 50 additions and 42 deletions

View File

@ -355,7 +355,7 @@ class Application(object):
def do_DELETE(self, environ, read_collections, write_collections, content, def do_DELETE(self, environ, read_collections, write_collections, content,
user): user):
"""Manage DELETE request.""" """Manage DELETE request."""
if not len(write_collections): if not write_collections:
return NOT_ALLOWED return NOT_ALLOWED
collection = write_collections[0] collection = write_collections[0]
@ -387,7 +387,7 @@ class Application(object):
answer = b"<!DOCTYPE html>\n<title>Radicale</title>Radicale works!" answer = b"<!DOCTYPE html>\n<title>Radicale</title>Radicale works!"
return client.OK, headers, answer return client.OK, headers, answer
if not len(read_collections): if not read_collections:
return NOT_ALLOWED return NOT_ALLOWED
collection = read_collections[0] collection = read_collections[0]
@ -407,8 +407,8 @@ class Application(object):
else: else:
return client.NOT_FOUND, {}, None return client.NOT_FOUND, {}, None
elif not collection.exists: elif not collection.exists:
log.LOGGER.debug("Collection %s unknown" % collection.name) log.LOGGER.debug("Collection at %s unknown" % environ["PATH_INFO"])
return client.NOT_FOUND return client.NOT_FOUND, {}, None
else: else:
# Get whole collection # Get whole collection
answer_text = collection.text answer_text = collection.text
@ -431,7 +431,7 @@ class Application(object):
def do_MKCALENDAR(self, environ, read_collections, write_collections, def do_MKCALENDAR(self, environ, read_collections, write_collections,
content, user): content, user):
"""Manage MKCALENDAR request.""" """Manage MKCALENDAR request."""
if not len(write_collections): if not write_collections:
return NOT_ALLOWED return NOT_ALLOWED
collection = write_collections[0] collection = write_collections[0]
@ -450,7 +450,7 @@ class Application(object):
def do_MKCOL(self, environ, read_collections, write_collections, content, def do_MKCOL(self, environ, read_collections, write_collections, content,
user): user):
"""Manage MKCOL request.""" """Manage MKCOL request."""
if not len(write_collections): if not write_collections:
return NOT_ALLOWED return NOT_ALLOWED
collection = write_collections[0] collection = write_collections[0]
@ -465,7 +465,7 @@ class Application(object):
def do_MOVE(self, environ, read_collections, write_collections, content, def do_MOVE(self, environ, read_collections, write_collections, content,
user): user):
"""Manage MOVE request.""" """Manage MOVE request."""
if not len(write_collections): if not write_collections:
return NOT_ALLOWED return NOT_ALLOWED
from_collection = write_collections[0] from_collection = write_collections[0]
@ -510,7 +510,8 @@ class Application(object):
def do_PROPFIND(self, environ, read_collections, write_collections, def do_PROPFIND(self, environ, read_collections, write_collections,
content, user): content, user):
"""Manage PROPFIND request.""" """Manage PROPFIND request."""
# Rights is handled by collection in xmlutils.propfind if not any(collection.exists for collection in read_collections):
return client.NOT_FOUND, {}, None
headers = { headers = {
"DAV": "1, 2, 3, calendar-access, addressbook, extended-mkcol", "DAV": "1, 2, 3, calendar-access, addressbook, extended-mkcol",
"Content-Type": "text/xml"} "Content-Type": "text/xml"}
@ -521,7 +522,7 @@ class Application(object):
def do_PROPPATCH(self, environ, read_collections, write_collections, def do_PROPPATCH(self, environ, read_collections, write_collections,
content, user): content, user):
"""Manage PROPPATCH request.""" """Manage PROPPATCH request."""
if not len(write_collections): if not write_collections:
return NOT_ALLOWED return NOT_ALLOWED
collection = write_collections[0] collection = write_collections[0]
@ -536,7 +537,7 @@ class Application(object):
def do_PUT(self, environ, read_collections, write_collections, content, def do_PUT(self, environ, read_collections, write_collections, content,
user): user):
"""Manage PUT request.""" """Manage PUT request."""
if not len(write_collections): if not write_collections:
return NOT_ALLOWED return NOT_ALLOWED
collection = write_collections[0] collection = write_collections[0]
@ -571,7 +572,7 @@ class Application(object):
def do_REPORT(self, environ, read_collections, write_collections, content, def do_REPORT(self, environ, read_collections, write_collections, content,
user): user):
"""Manage REPORT request.""" """Manage REPORT request."""
if not len(read_collections): if not read_collections:
return NOT_ALLOWED return NOT_ALLOWED
collection = read_collections[0] collection = read_collections[0]

View File

@ -222,8 +222,7 @@ class Collection(object):
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 (cls.is_leaf("/".join(attributes[:-1])) or not if cls.is_leaf("/".join(attributes[:-1])):
path.endswith(("/", "/caldav", "/carddav"))):
attributes.pop() attributes.pop()
result = [] result = []

View File

@ -77,6 +77,10 @@ class Collection(ical.Collection):
if not os.path.exists(self._filesystem_path): if not os.path.exists(self._filesystem_path):
os.makedirs(self._filesystem_path) os.makedirs(self._filesystem_path)
def set_mimetype(self, mimetype):
self._create_dirs()
return super().set_mimetype(mimetype)
def save(self, text): def save(self, text):
self._create_dirs() self._create_dirs()
item_types = ( item_types = (
@ -185,7 +189,6 @@ class Collection(ical.Collection):
old_properties = properties.copy() old_properties = properties.copy()
yield properties yield properties
# On exit # On exit
self._create_dirs()
if old_properties != properties: if old_properties != properties:
with open(self._props_path, "w") as prop_file: with open(self._props_path, "w") as prop_file:
json.dump(properties, prop_file) json.dump(properties, prop_file)

View File

@ -192,9 +192,8 @@ def propfind(path, xml_request, read_collections, write_collections, user=None):
Read rfc4918-9.1 for info. Read rfc4918-9.1 for info.
The collections parameter is a list of collections that are The collections parameter is a list of collections that are to be included
to be included in the output. Rights checking has to be done in the output.
by the caller.
""" """
# Reading request # Reading request
@ -231,6 +230,7 @@ def _propfind_response(path, item, props, user, write=False):
"""Build and return a PROPFIND response.""" """Build and return a PROPFIND response."""
is_collection = isinstance(item, ical.Collection) is_collection = isinstance(item, ical.Collection)
if is_collection: if is_collection:
is_leaf = item.is_leaf(item.path)
with item.props as properties: with item.props as properties:
collection_props = properties collection_props = properties
@ -271,8 +271,8 @@ def _propfind_response(path, item, props, user, write=False):
# This is not a Todo # This is not a Todo
# pylint: disable=W0511 # pylint: disable=W0511
human_tag = _tag_from_clark(tag) human_tag = _tag_from_clark(tag)
if is_collection and human_tag in collection_props: if is_collection and is_leaf:
# TODO: what do we have to do if it's not a collection? if human_tag in collection_props:
components = collection_props[human_tag].split(",") components = collection_props[human_tag].split(",")
else: else:
components = ("VTODO", "VEVENT", "VJOURNAL") components = ("VTODO", "VEVENT", "VJOURNAL")
@ -280,6 +280,8 @@ def _propfind_response(path, item, props, user, write=False):
comp = ET.Element(_tag("C", "comp")) comp = ET.Element(_tag("C", "comp"))
comp.set("name", component) comp.set("name", component)
element.append(comp) element.append(comp)
else:
is404 = True
# pylint: enable=W0511 # pylint: enable=W0511
elif tag == _tag("D", "current-user-principal") and user: elif tag == _tag("D", "current-user-principal") and user:
tag = ET.Element(_tag("D", "href")) tag = ET.Element(_tag("D", "href"))
@ -310,7 +312,7 @@ def _propfind_response(path, item, props, user, write=False):
if item.is_principal: if item.is_principal:
tag = ET.Element(_tag("D", "principal")) tag = ET.Element(_tag("D", "principal"))
element.append(tag) element.append(tag)
if item.is_leaf(item.path) or ( if is_leaf or (
not item.exists and item.resource_type): not item.exists and item.resource_type):
# 2nd case happens when the collection is not stored yet, # 2nd case happens when the collection is not stored yet,
# but the resource type is guessed # but the resource type is guessed
@ -321,7 +323,8 @@ def _propfind_response(path, item, props, user, write=False):
element.append(tag) element.append(tag)
tag = ET.Element(_tag("D", "collection")) tag = ET.Element(_tag("D", "collection"))
element.append(tag) element.append(tag)
elif tag == _tag("D", "owner") and item.owner_url: elif is_leaf:
if tag == _tag("D", "owner") and item.owner_url:
element.text = item.owner_url element.text = item.owner_url
elif tag == _tag("CS", "getctag"): elif tag == _tag("CS", "getctag"):
element.text = item.etag element.text = item.etag
@ -338,6 +341,8 @@ def _propfind_response(path, item, props, user, write=False):
element.text = collection_props[human_tag] element.text = collection_props[human_tag]
else: else:
is404 = True is404 = True
else:
is404 = True
# Not for collections # Not for collections
elif tag == _tag("D", "getcontenttype"): elif tag == _tag("D", "getcontenttype"):
element.text = "%s; component=%s" % ( element.text = "%s; component=%s" % (