[CalendarServer-changes] [8412] CalendarServer/trunk/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Wed Dec 7 11:14:02 PST 2011
Revision: 8412
http://trac.macosforge.org/projects/calendarserver/changeset/8412
Author: sagen at apple.com
Date: 2011-12-07 11:14:01 -0800 (Wed, 07 Dec 2011)
Log Message:
-----------
Invalid recurrence-ids are made valid by automatically adding an RDATE
Modified Paths:
--------------
CalendarServer/trunk/twistedcaldav/ical.py
CalendarServer/trunk/twistedcaldav/instance.py
CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py
CalendarServer/trunk/twistedcaldav/test/test_icalendar.py
Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py 2011-12-07 19:13:58 UTC (rev 8411)
+++ CalendarServer/trunk/twistedcaldav/ical.py 2011-12-07 19:14:01 UTC (rev 8412)
@@ -979,7 +979,7 @@
return newcomp
- def cacheExpandedTimeRanges(self, limit):
+ def cacheExpandedTimeRanges(self, limit, ignoreInvalidInstances=False):
"""
Expand instances up to the specified limit and cache the results in this object
so we can return cached results in the future.
@@ -996,7 +996,8 @@
# so return cached instances
return self.cachedInstances
- self.cachedInstances = self.expandTimeRanges(limit)
+ self.cachedInstances = self.expandTimeRanges(limit,
+ ignoreInvalidInstances=ignoreInvalidInstances)
return self.cachedInstances
def expandTimeRanges(self, limit, ignoreInvalidInstances=False):
@@ -1037,7 +1038,7 @@
@return: a tuple of recurrence-ids
"""
-
+
# Extract appropriate sub-component if this is a VCALENDAR
if self.name() == "VCALENDAR":
result = ()
@@ -1174,7 +1175,7 @@
return newcomp
- def validInstances(self, rids):
+ def validInstances(self, rids, ignoreInvalidInstances=False):
"""
Test whether the specified recurrence-ids are valid instances in this event.
@@ -1188,34 +1189,36 @@
non_master_rids = [rid for rid in rids if rid is not None]
if non_master_rids:
highest_rid = max(non_master_rids)
- self.cacheExpandedTimeRanges(highest_rid + PyCalendarDuration(days=1))
+ self.cacheExpandedTimeRanges(
+ highest_rid + PyCalendarDuration(days=1),
+ ignoreInvalidInstances=ignoreInvalidInstances
+ )
for rid in rids:
- if self.validInstance(rid, clear_cache=False):
+ if self.validInstance(rid, clear_cache=False, ignoreInvalidInstances=ignoreInvalidInstances):
valid.add(rid)
return valid
- def validInstance(self, rid, clear_cache=True):
+ def validInstance(self, rid, clear_cache=True, ignoreInvalidInstances=False):
"""
Test whether the specified recurrence-id is a valid instance in this event.
@param rid: recurrence-id value
@type rid: L{PyCalendarDateTime}
-
+
@return: C{bool}
"""
-
- # First check overridden instances already in this component
- if not hasattr(self, "cachedComponentInstances") or clear_cache:
- self.cachedComponentInstances = set(self.getComponentInstances())
- if rid in self.cachedComponentInstances:
- return True
-
- # Must have a master component
+
if self.masterComponent() is None:
- return False
+ return rid in set(self.getComponentInstances())
+ if rid is None:
+ return True
+
# Get expansion
- instances = self.cacheExpandedTimeRanges(rid + PyCalendarDuration(days=1))
+ instances = self.cacheExpandedTimeRanges(
+ rid + PyCalendarDuration(days=1),
+ ignoreInvalidInstances=ignoreInvalidInstances
+ )
new_rids = set([instances[key].rid for key in instances])
return rid in new_rids
@@ -1296,8 +1299,38 @@
log.debug("Unknown resource type: %s" % (self,))
raise InvalidICalendarDataError("Unknown resource type")
+ fixed = []
+ unfixed = []
+
+ # Detect invalid occurrences and fix by adding RDATEs for them
+ master = self.masterComponent()
+ if master is not None:
+ # Get the set of all recurrence IDs
+ all_rids = set(self.getComponentInstances())
+ if None in all_rids:
+ all_rids.remove(None)
+ # Get the set of all valid recurrence IDs
+ valid_rids = self.validInstances(all_rids, ignoreInvalidInstances=True)
+ # Get the set of all RDATEs and add those to the valid set
+ rdates = []
+ for property in master.properties("RDATE"):
+ rdates.extend([_rdate.getValue() for _rdate in property.value()])
+ valid_rids.update(set(rdates))
+ # Determine the invalid recurrence IDs by set subtraction
+ invalid_rids = all_rids - valid_rids
+ # Add RDATEs for the invalid ones.
+ for invalid_rid in invalid_rids:
+ if doFix:
+ master.addProperty(Property("RDATE", [invalid_rid,]))
+ fixed.append("Added RDATE for invalid occurrence: %s" %
+ (invalid_rid,))
+ else:
+ unfixed.append("Invalid occurrence: %s" % (invalid_rid,))
+
# Do underlying iCalendar library validation with data fix
- fixed, unfixed = self._pycalendar.validate(doFix=doFix)
+ pyfixed, pyunfixed = self._pycalendar.validate(doFix=doFix)
+ fixed.extend(pyfixed)
+ unfixed.extend(pyunfixed)
if unfixed:
log.debug("Calendar data had unfixable problems:\n %s" % ("\n ".join(unfixed),))
if doRaise:
@@ -1307,6 +1340,8 @@
return fixed, unfixed
+
+
def validCalendarForCalDAV(self, methodAllowed):
"""
@param methodAllowed: True if METHOD property is allowed, False otherwise.
Modified: CalendarServer/trunk/twistedcaldav/instance.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/instance.py 2011-12-07 19:13:58 UTC (rev 8411)
+++ CalendarServer/trunk/twistedcaldav/instance.py 2011-12-07 19:14:01 UTC (rev 8412)
@@ -365,7 +365,7 @@
if oldinstance.overridden:
continue
- # Determine the start/end of the new instance\
+ # Determine the start/end of the new instance
originalStart = oldinstance.rid
start = oldinstance.start
end = oldinstance.end
Modified: CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py 2011-12-07 19:13:58 UTC (rev 8411)
+++ CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py 2011-12-07 19:14:01 UTC (rev 8412)
@@ -596,7 +596,7 @@
rdates = component.properties("RDATE")
for rdate in rdates:
for value in rdate.value():
- if isinstance(PyCalendarDateTime()):
+ if isinstance(value, PyCalendarDateTime):
value = value.duplicate().adjustToUTC()
newrdates.add(value)
Modified: CalendarServer/trunk/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_icalendar.py 2011-12-07 19:13:58 UTC (rev 8411)
+++ CalendarServer/trunk/twistedcaldav/test/test_icalendar.py 2011-12-07 19:14:01 UTC (rev 8412)
@@ -377,7 +377,80 @@
# Now it should pass without fixing
calendar.validCalendarData(doFix=False)
+ # Test invalid occurrences
+ data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 5.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+DTSTART:20070311T020000
+TZNAME:PDT
+TZOFFSETTO:-0700
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+DTSTART:20071104T020000
+TZNAME:PST
+TZOFFSETTO:-0800
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20111206T203543Z
+UID:5F7FF5FB-2253-4895-8BF1-76E8ED868B4C
+DTEND;TZID=America/Los_Angeles:20111207T153000
+RRULE:FREQ=WEEKLY;COUNT=400
+TRANSP:OPAQUE
+SUMMARY:bogus instance
+DTSTART;TZID=America/Los_Angeles:20111207T143000
+DTSTAMP:20111206T203553Z
+SEQUENCE:3
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20111206T203543Z
+UID:5F7FF5FB-2253-4895-8BF1-76E8ED868B4C
+DTEND;TZID=America/Los_Angeles:20111221T124500
+TRANSP:OPAQUE
+SUMMARY:bogus instance
+DTSTART;TZID=America/Los_Angeles:20111221T114500
+DTSTAMP:20111206T203632Z
+SEQUENCE:5
+RECURRENCE-ID;TZID=America/Los_Angeles:20111221T143000
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20111206T203543Z
+UID:5F7FF5FB-2253-4895-8BF1-76E8ED868B4C
+DTEND;TZID=America/Los_Angeles:20111214T163000
+TRANSP:OPAQUE
+SUMMARY:bogus instance
+DTSTART;TZID=America/Los_Angeles:20111214T153000
+DTSTAMP:20111206T203606Z
+SEQUENCE:4
+RECURRENCE-ID;TZID=America/Los_Angeles:20111215T143000
+END:VEVENT
+END:VCALENDAR
+"""
+ # Ensure it starts off invalid
+ calendar = Component.fromString(data)
+ try:
+ calendar.validCalendarData(doFix=False)
+ except InvalidICalendarDataError:
+ pass
+ else:
+ self.fail("Shouldn't validate for CalDAV")
+ # Fix it
+ calendar.validCalendarData(doFix=True)
+ self.assertTrue("RDATE:20111215T223000Z\r\n" in str(calendar))
+
+ # Now it should pass without fixing
+ calendar.validCalendarData(doFix=False)
+
+
def test_component_timeranges(self):
"""
Component time range query.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20111207/02500484/attachment-0001.html>
More information about the calendarserver-changes
mailing list