parent
2f97d7d1e1
commit
a28df4dd4b
@ -117,6 +117,108 @@ def _href(collection, href):
|
|||||||
href.lstrip("/"))
|
href.lstrip("/"))
|
||||||
|
|
||||||
|
|
||||||
|
def _comp_match(item, filter_, scope="collection"):
|
||||||
|
"""Check whether the ``item`` matches the comp ``filter_``.
|
||||||
|
|
||||||
|
If ``scope`` is ``"collection"``, the filter is applied on the
|
||||||
|
item's collection. Otherwise, it's applied on the item.
|
||||||
|
|
||||||
|
See rfc4791-9.7.1.
|
||||||
|
|
||||||
|
"""
|
||||||
|
filter_length = len(filter_)
|
||||||
|
if scope == "collection":
|
||||||
|
tag = item.collection.get_meta("tag")
|
||||||
|
else:
|
||||||
|
for component in item.components():
|
||||||
|
if component.name in ("VTODO", "VEVENT", "VJOURNAL"):
|
||||||
|
tag = component.name
|
||||||
|
if filter_length == 0:
|
||||||
|
# Point #1 of rfc4791-9.7.1
|
||||||
|
return filter_.get("name") == tag
|
||||||
|
else:
|
||||||
|
if filter_length == 1:
|
||||||
|
if filter_[0].tag == _tag("C", "is-not-defined"):
|
||||||
|
# Point #2 of rfc4791-9.7.1
|
||||||
|
return filter_.get("name") != tag
|
||||||
|
if filter_[0].tag == _tag("C", "time-range"):
|
||||||
|
# Point #3 of rfc4791-9.7.1
|
||||||
|
if not _time_range_match(item, filter_[0]):
|
||||||
|
return False
|
||||||
|
filter_.remove(filter_[0])
|
||||||
|
# Point #4 of rfc4791-9.7.1
|
||||||
|
return all(
|
||||||
|
_prop_match(item, child) if child.tag == _tag("C", "prop-filter")
|
||||||
|
else _comp_match(item, child, scope="component")
|
||||||
|
for child in filter_)
|
||||||
|
|
||||||
|
|
||||||
|
def _prop_match(item, filter_):
|
||||||
|
"""Check whether the ``item`` matches the prop ``filter_``.
|
||||||
|
|
||||||
|
See rfc4791-9.7.2 and rfc6352-10.5.1.
|
||||||
|
|
||||||
|
"""
|
||||||
|
filter_length = len(filter_)
|
||||||
|
if item.collection.get_meta("tag") == "VCALENDAR":
|
||||||
|
for component in item.components():
|
||||||
|
if component.name in ("VTODO", "VEVENT", "VJOURNAL"):
|
||||||
|
vobject_item = component
|
||||||
|
else:
|
||||||
|
vobject_item = item.item
|
||||||
|
if filter_length == 0:
|
||||||
|
# Point #1 of rfc4791-9.7.2
|
||||||
|
return filter_.get("name").lower() in vobject_item.contents
|
||||||
|
else:
|
||||||
|
if filter_length == 1:
|
||||||
|
if filter_[0].tag == _tag("C", "is-not-defined"):
|
||||||
|
# Point #2 of rfc4791-9.7.2
|
||||||
|
return filter_.get("name").lower() not in vobject_item.contents
|
||||||
|
if filter_[0].tag == _tag("C", "time-range"):
|
||||||
|
# Point #3 of rfc4791-9.7.2
|
||||||
|
if not _time_range_match(item, filter_[0]):
|
||||||
|
return False
|
||||||
|
filter_.remove(filter_[0])
|
||||||
|
elif filter_[0].tag == _tag("C", "text-match"):
|
||||||
|
# Point #4 of rfc4791-9.7.2
|
||||||
|
if not _text_match(item, filter_[0]):
|
||||||
|
return False
|
||||||
|
filter_.remove(filter_[0])
|
||||||
|
return all(
|
||||||
|
_param_filter_match(item, param_filter)
|
||||||
|
for param_filter in filter_)
|
||||||
|
|
||||||
|
|
||||||
|
def _time_range_match(item, _filter):
|
||||||
|
"""Check whether the ``item`` matches the time-range ``filter_``.
|
||||||
|
|
||||||
|
See rfc4791-9.9.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO: implement this
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _text_match(item, _filter):
|
||||||
|
"""Check whether the ``item`` matches the text-match ``filter_``.
|
||||||
|
|
||||||
|
See rfc4791-9.7.3.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO: implement this
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _param_filter_match(item, _filter):
|
||||||
|
"""Check whether the ``item`` matches the param-filter ``filter_``.
|
||||||
|
|
||||||
|
See rfc4791-9.7.3.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TODO: implement this
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def name_from_path(path, collection):
|
def name_from_path(path, collection):
|
||||||
"""Return Radicale item name from ``path``."""
|
"""Return Radicale item name from ``path``."""
|
||||||
collection_parts = collection.path.strip("/").split("/")
|
collection_parts = collection.path.strip("/").split("/")
|
||||||
@ -491,16 +593,11 @@ def report(path, xml_request, collection):
|
|||||||
hreferences.add(href_path[len(base_prefix) - 1:])
|
hreferences.add(href_path[len(base_prefix) - 1:])
|
||||||
else:
|
else:
|
||||||
hreferences = (path,)
|
hreferences = (path,)
|
||||||
# TODO: handle other filters
|
filters = (
|
||||||
# TODO: handle the nested comp-filters correctly
|
root.findall(".//%s" % _tag("C", "filter")) +
|
||||||
# Read rfc4791-9.7.1 for info
|
root.findall(".//%s" % _tag("CR", "filter")))
|
||||||
tag_filters = set(
|
|
||||||
element.get("name").upper() for element
|
|
||||||
in root.findall(".//%s" % _tag("C", "comp-filter")))
|
|
||||||
tag_filters.discard("VCALENDAR")
|
|
||||||
else:
|
else:
|
||||||
hreferences = ()
|
hreferences = filters = ()
|
||||||
tag_filters = set()
|
|
||||||
|
|
||||||
# Writing answer
|
# Writing answer
|
||||||
multistatus = ET.Element(_tag("D", "multistatus"))
|
multistatus = ET.Element(_tag("D", "multistatus"))
|
||||||
@ -526,10 +623,12 @@ def report(path, xml_request, collection):
|
|||||||
items = [collection.get(href) for href, etag in collection.list()]
|
items = [collection.get(href) for href, etag in collection.list()]
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
if (tag_filters and
|
if filters:
|
||||||
item.name not in tag_filters and
|
match = (
|
||||||
not {tag.upper() for tag in item.contents} & tag_filters):
|
_comp_match if collection.get_meta("tag") == "VCALENDAR"
|
||||||
continue
|
else _prop_match)
|
||||||
|
if not all(match(item, filter_[0]) for filter_ in filters):
|
||||||
|
continue
|
||||||
|
|
||||||
found_props = []
|
found_props = []
|
||||||
not_found_props = []
|
not_found_props = []
|
||||||
|
@ -93,6 +93,129 @@ class BaseRequests:
|
|||||||
status, headers, answer = self.request("GET", "/calendar.ics/")
|
status, headers, answer = self.request("GET", "/calendar.ics/")
|
||||||
assert "VEVENT" not in answer
|
assert "VEVENT" not in answer
|
||||||
|
|
||||||
|
def _test_filter(self, filters):
|
||||||
|
filters_text = "".join(
|
||||||
|
"<C:filter>%s</C:filter>" % filter_ for filter_ in filters)
|
||||||
|
self.request(
|
||||||
|
"PUT", "/calendar.ics/", "BEGIN:VCALENDAR\r\nEND:VCALENDAR")
|
||||||
|
event = get_file_content("event.ics")
|
||||||
|
self.request("PUT", "/calendar.ics/event.ics", event)
|
||||||
|
status, headers, answer = self.request(
|
||||||
|
"REPORT", "/calendar.ics",
|
||||||
|
"""<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||||
|
<D:prop xmlns:D="DAV:">
|
||||||
|
<D:getetag/>
|
||||||
|
</D:prop>
|
||||||
|
%s
|
||||||
|
</C:calendar-query>""" % filters_text)
|
||||||
|
return answer
|
||||||
|
|
||||||
|
def test_calendar_tag_filter(self):
|
||||||
|
"""Report request with tag-based filter on calendar."""
|
||||||
|
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR"></C:comp-filter>"""])
|
||||||
|
|
||||||
|
def test_item_tag_filter(self):
|
||||||
|
"""Report request with tag-based filter on an item."""
|
||||||
|
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT"></C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VTODO"></C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
|
||||||
|
def test_item_not_tag_filter(self):
|
||||||
|
"""Report request with tag-based is-not filter on an item."""
|
||||||
|
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:is-not-defined />
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VTODO">
|
||||||
|
<C:is-not-defined />
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
|
||||||
|
def test_item_prop_filter(self):
|
||||||
|
"""Report request with prop-based filter on an item."""
|
||||||
|
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="SUMMARY"></C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="UNKNOWN"></C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
|
||||||
|
def test_item_not_prop_filter(self):
|
||||||
|
"""Report request with prop-based is-not filter on an item."""
|
||||||
|
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="SUMMARY">
|
||||||
|
<C:is-not-defined />
|
||||||
|
</C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="UNKNOWN">
|
||||||
|
<C:is-not-defined />
|
||||||
|
</C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
|
||||||
|
def test_mutiple_filters(self):
|
||||||
|
"""Report request with multiple filters on an item."""
|
||||||
|
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="SUMMARY">
|
||||||
|
<C:is-not-defined />
|
||||||
|
</C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>""", """
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="UNKNOWN">
|
||||||
|
<C:is-not-defined />
|
||||||
|
</C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="SUMMARY"></C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>""", """
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="UNKNOWN">
|
||||||
|
<C:is-not-defined />
|
||||||
|
</C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="SUMMARY"></C:prop-filter>
|
||||||
|
<C:prop-filter name="UNKNOWN">
|
||||||
|
<C:is-not-defined />
|
||||||
|
</C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>"""])
|
||||||
|
|
||||||
|
|
||||||
class TestMultiFileSystem(BaseRequests, BaseTest):
|
class TestMultiFileSystem(BaseRequests, BaseTest):
|
||||||
"""Base class for filesystem tests."""
|
"""Base class for filesystem tests."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user