proppatch actually writes properties.
This commit is contained in:
parent
85e283830a
commit
911cd48efe
@ -260,6 +260,10 @@ class Application(object):
|
||||
tz = props.get('C:calendar-timezone')
|
||||
if tz:
|
||||
calendar.replace('', tz)
|
||||
del props['C:calendar-timezone']
|
||||
with calendar.props as calendar_props:
|
||||
for key, value in props.items():
|
||||
calendar_props[key] = value
|
||||
calendar.write()
|
||||
return client.CREATED, {}, None
|
||||
|
||||
@ -283,11 +287,11 @@ class Application(object):
|
||||
|
||||
def proppatch(self, environ, calendar, content):
|
||||
"""Manage PROPPATCH request."""
|
||||
xmlutils.proppatch(environ["PATH_INFO"], content, calendar)
|
||||
answer = xmlutils.proppatch(environ["PATH_INFO"], content, calendar)
|
||||
headers = {
|
||||
"DAV": "1, calendar-access",
|
||||
"Content-Type": "text/xml"}
|
||||
return client.MULTI_STATUS, headers, None
|
||||
return client.MULTI_STATUS, headers, answer
|
||||
|
||||
def put(self, environ, calendar, content):
|
||||
"""Manage PUT request."""
|
||||
|
@ -25,8 +25,10 @@ Define the main classes of a calendar as seen from the server.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import codecs
|
||||
from contextlib import contextmanager
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
|
||||
from radicale import config
|
||||
@ -254,7 +256,9 @@ class Calendar(object):
|
||||
@property
|
||||
def name(self):
|
||||
"""Calendar name."""
|
||||
return self.path.split(os.path.sep)[-1]
|
||||
with self.props as props:
|
||||
return props.get('D:displayname',
|
||||
self.path.split(os.path.sep)[-1])
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
@ -322,3 +326,17 @@ class Calendar(object):
|
||||
|
||||
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):
|
||||
props_path = self.path + '.props'
|
||||
# on enter
|
||||
properties = {}
|
||||
if os.path.exists(props_path):
|
||||
with open(props_path) as prop_file:
|
||||
properties.update(json.load(prop_file))
|
||||
yield properties
|
||||
# on exit
|
||||
with open(props_path, 'w') as prop_file:
|
||||
json.dump(properties, prop_file)
|
||||
|
@ -93,6 +93,23 @@ def _tag(short_name, local):
|
||||
return "{%s}%s" % (NAMESPACES[short_name], local)
|
||||
|
||||
|
||||
def _tag_from_clark(name):
|
||||
"""For a given name using the XML Clark notation returns a human-readable
|
||||
variant of the tag name for known namespaces. Otherwise returns the name
|
||||
as is.
|
||||
|
||||
"""
|
||||
match = CLARK_TAG_REGEX.match(name)
|
||||
if match and match.group('namespace') in NAMESPACES_REV:
|
||||
args = {
|
||||
'ns': NAMESPACES_REV[match.group('namespace')],
|
||||
'tag': match.group('tag')}
|
||||
tag_name = '%(ns)s:%(tag)s' % args
|
||||
else:
|
||||
tag_name = prop.tag
|
||||
return tag_name
|
||||
|
||||
|
||||
def _response(code):
|
||||
"""Return full W3C names from HTTP status codes."""
|
||||
return "HTTP/1.1 %i %s" % (code, client.responses[code])
|
||||
@ -105,28 +122,24 @@ def name_from_path(path, calendar):
|
||||
return path_parts[-1] if (len(path_parts) - len(calendar_parts)) else None
|
||||
|
||||
|
||||
def props_from_request(xml_request):
|
||||
def props_from_request(root, actions=("set", "remove")):
|
||||
"""Returns a list of properties as a dictionary."""
|
||||
|
||||
result = OrderedDict()
|
||||
root = ET.fromstring(xml_request.encode("utf8"))
|
||||
if not isinstance(root, ET.Element):
|
||||
root = ET.fromstring(root.encode("utf8"))
|
||||
|
||||
set_element = root.find(_tag("D", "set"))
|
||||
if not set_element:
|
||||
set_element = root
|
||||
for action in actions:
|
||||
action_element = root.find(_tag("D", action))
|
||||
if action_element is not None:
|
||||
break
|
||||
else:
|
||||
action_element = root
|
||||
|
||||
prop_element = set_element.find(_tag("D", "prop"))
|
||||
if prop_element:
|
||||
prop_element = action_element.find(_tag("D", "prop"))
|
||||
if prop_element is not None:
|
||||
for prop in prop_element:
|
||||
match = CLARK_TAG_REGEX.match(prop.tag)
|
||||
if match and match.group('namespace') in NAMESPACES_REV:
|
||||
args = {
|
||||
'ns': NAMESPACES_REV[match.group('namespace')],
|
||||
'tag': match.group('tag')}
|
||||
tag_name = '%(ns)s:%(tag)s' % args
|
||||
else:
|
||||
tag_name = prop.tag
|
||||
result[tag_name] = prop.text
|
||||
result[_tag_from_clark(prop.tag)] = prop.text
|
||||
return result
|
||||
|
||||
|
||||
@ -256,6 +269,27 @@ def propfind(path, xml_request, calendar, depth):
|
||||
return _pretty_xml(multistatus)
|
||||
|
||||
|
||||
def _add_propstat_to(element, tag, status_number):
|
||||
"""Adds a propstat structure to the given element for the
|
||||
following `tag` with the given `status_number`."""
|
||||
propstat = ET.Element(_tag("D", "propstat"))
|
||||
element.append(propstat)
|
||||
|
||||
prop = ET.Element(_tag("D", "prop"))
|
||||
propstat.append(prop)
|
||||
|
||||
if '{' in tag:
|
||||
clark_tag = tag
|
||||
else:
|
||||
clark_tag = _tag(*tag.split(':', 1))
|
||||
prop_tag = ET.Element(clark_tag)
|
||||
prop.append(prop_tag)
|
||||
|
||||
status = ET.Element(_tag("D", "status"))
|
||||
status.text = _response(status_number)
|
||||
propstat.append(status)
|
||||
|
||||
|
||||
def proppatch(path, xml_request, calendar):
|
||||
"""Read and answer PROPPATCH requests.
|
||||
|
||||
@ -264,13 +298,8 @@ def proppatch(path, xml_request, calendar):
|
||||
"""
|
||||
# Reading request
|
||||
root = ET.fromstring(xml_request.encode("utf8"))
|
||||
props = []
|
||||
|
||||
for action in ("set", "remove"):
|
||||
action_element = root.find(_tag("D", action))
|
||||
if action_element is not None:
|
||||
prop_element = action_element.find(_tag("D", "prop"))
|
||||
props.extend(prop.tag for prop in prop_element)
|
||||
props_to_set = props_from_request(root, actions=('set',))
|
||||
props_to_remove = props_from_request(root, actions=('remove',))
|
||||
|
||||
# Writing answer
|
||||
multistatus = ET.Element(_tag("D", "multistatus"))
|
||||
@ -282,19 +311,17 @@ def proppatch(path, xml_request, calendar):
|
||||
href.text = path
|
||||
response.append(href)
|
||||
|
||||
propstat = ET.Element(_tag("D", "propstat"))
|
||||
response.append(propstat)
|
||||
|
||||
prop = ET.Element(_tag("D", "prop"))
|
||||
propstat.append(prop)
|
||||
|
||||
for tag in props:
|
||||
element = ET.Element(tag)
|
||||
prop.append(element)
|
||||
|
||||
status = ET.Element(_tag("D", "status"))
|
||||
status.text = _response(200)
|
||||
propstat.append(status)
|
||||
with calendar.props as calendar_props:
|
||||
for short_name, value in props_to_set.items():
|
||||
calendar_props[short_name] = value
|
||||
_add_propstat_to(response, short_name, 200)
|
||||
for short_name in props_to_remove:
|
||||
try:
|
||||
del calendar_props[short_name]
|
||||
except KeyError:
|
||||
_add_propstat_to(response, short_name, 412)
|
||||
else:
|
||||
_add_propstat_to(response, short_name, 200)
|
||||
|
||||
return _pretty_xml(multistatus)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user