[CalendarServer-changes] [11207] CalendarServer/trunk/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Thu May 16 19:11:38 PDT 2013
Revision: 11207
http://trac.calendarserver.org//changeset/11207
Author: cdaboo at apple.com
Date: 2013-05-16 19:11:38 -0700 (Thu, 16 May 2013)
Log Message:
-----------
Fix for case where R-ID property is malformed with T000000. Try using the master's time value and see if
R-ID is then valid.
Modified Paths:
--------------
CalendarServer/trunk/twistedcaldav/instance.py
CalendarServer/trunk/twistedcaldav/test/test_icalendar.py
Modified: CalendarServer/trunk/twistedcaldav/instance.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/instance.py 2013-05-17 02:07:21 UTC (rev 11206)
+++ CalendarServer/trunk/twistedcaldav/instance.py 2013-05-17 02:11:38 UTC (rev 11207)
@@ -139,7 +139,7 @@
"""
# Look at each component type
- got_master = False
+ master = None
overrides = []
for component in componentSet:
if component.name() == "VEVENT":
@@ -147,13 +147,13 @@
overrides.append(component)
else:
self._addMasterEventComponent(component, lowerLimit, limit)
- got_master = True
+ master = component
elif component.name() == "VTODO":
if component.hasProperty("RECURRENCE-ID"):
overrides.append(component)
else:
self._addMasterToDoComponent(component, lowerLimit, limit)
- got_master = True
+ master = component
elif component.name() == "VJOURNAL":
#TODO: VJOURNAL
raise NotImplementedError("VJOURNAL recurrence expansion not supported yet")
@@ -167,19 +167,19 @@
else:
# AVAILABLE components are just like VEVENT components
self._addMasterEventComponent(component, lowerLimit, limit)
- got_master = True
+ master = component
for component in overrides:
if component.name() == "VEVENT":
- self._addOverrideEventComponent(component, lowerLimit, limit, got_master)
+ self._addOverrideEventComponent(component, lowerLimit, limit, master)
elif component.name() == "VTODO":
- self._addOverrideToDoComponent(component, lowerLimit, limit, got_master)
+ self._addOverrideToDoComponent(component, lowerLimit, limit, master)
elif component.name() == "VJOURNAL":
#TODO: VJOURNAL
raise NotImplementedError("VJOURNAL recurrence expansion not supported yet")
elif component.name() == "AVAILABLE":
# AVAILABLE components are just like VEVENT components
- self._addOverrideEventComponent(component, lowerLimit, limit, got_master)
+ self._addOverrideEventComponent(component, lowerLimit, limit, master)
def addInstance(self, instance):
@@ -260,12 +260,12 @@
self._addMasterComponent(component, lowerLimit, upperLimit, rulestart, start, end, duration)
- def _addOverrideEventComponent(self, component, lowerLimit, upperLimit, got_master):
+ def _addOverrideEventComponent(self, component, lowerLimit, upperLimit, master):
"""
Add the specified overridden VEVENT Component to the instance list, replacing
the one generated by the master component.
@param component: the overridden Component.
- @param got_master: whether a master component has already been expanded.
+ @param master: the master component which has already been expanded, or C{None}.
"""
#TODO: This does not take into account THISANDPRIOR - only THISANDFUTURE
@@ -276,7 +276,7 @@
_ignore_rulestart, start, end, _ignore_duration = details
lowerLimit, upperLimit = self._setupLimits(start, lowerLimit, upperLimit)
- self._addOverrideComponent(component, lowerLimit, upperLimit, start, end, got_master)
+ self._addOverrideComponent(component, lowerLimit, upperLimit, start, end, master)
def _getMasterToDoDetails(self, component):
@@ -341,12 +341,12 @@
self._addMasterComponent(component, lowerLimit, upperLimit, rulestart, start, end, duration)
- def _addOverrideToDoComponent(self, component, lowerLimit, upperLimit, got_master):
+ def _addOverrideToDoComponent(self, component, lowerLimit, upperLimit, master):
"""
Add the specified overridden VTODO Component to the instance list, replacing
the one generated by the master component.
@param component: the overridden Component.
- @param got_master: whether a master component has already been expanded.
+ @param master: the master component which has already been expanded, or C{None}.
"""
#TODO: This does not take into account THISANDPRIOR - only THISANDFUTURE
@@ -357,7 +357,7 @@
_ignore_rulestart, start, end, _ignore_duration = details
lowerLimit, upperLimit = self._setupLimits(start, lowerLimit, upperLimit)
- self._addOverrideComponent(component, lowerLimit, upperLimit, start, end, got_master)
+ self._addOverrideComponent(component, lowerLimit, upperLimit, start, end, master)
def _addMasterComponent(self, component, lowerLimit, upperlimit, rulestart, start, end, duration):
@@ -395,7 +395,7 @@
self.master_cancelled = component.propertyValue("STATUS") == "CANCELLED"
- def _addOverrideComponent(self, component, lowerLimit, upperlimit, start, end, got_master):
+ def _addOverrideComponent(self, component, lowerLimit, upperlimit, start, end, master):
# Get the recurrence override info
rid = component.getRecurrenceIDUTC()
@@ -414,13 +414,27 @@
# Make sure override RECURRENCE-ID is a valid instance of the master
cancelled = component.propertyValue("STATUS") == "CANCELLED"
- if got_master:
+ if master is not None:
if str(rid) not in self.instances and rid < upperlimit and (lowerLimit is None or rid >= lowerLimit):
if self.master_cancelled or cancelled:
# Ignore invalid overrides when either the master or override is cancelled
pass
elif self.ignoreInvalidInstances:
return
+ elif component.name() == "VEVENT":
+ # Try to fix the R-ID in the case where the hour/minute/second components are all zero
+ original_rid = component.propertyValue("RECURRENCE-ID").duplicate()
+ if not original_rid.isDateOnly() and original_rid.mHours == 0 and original_rid.mMinutes == 0 and original_rid.mSeconds == 0:
+ master_start = master.propertyValue("DTSTART")
+ original_rid.setHHMMSS(master_start.mHours, master_start.mMinutes, master_start.mSeconds)
+ rid = original_rid.duplicateAsUTC()
+ rid = self.normalizeFunction(rid)
+ if str(rid) not in self.instances:
+ raise InvalidOverriddenInstanceError(str(rid))
+ else:
+ component.getProperty("RECURRENCE-ID").setValue(original_rid)
+ else:
+ raise InvalidOverriddenInstanceError(str(rid))
else:
raise InvalidOverriddenInstanceError(str(rid))
Modified: CalendarServer/trunk/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_icalendar.py 2013-05-17 02:07:21 UTC (rev 11206)
+++ CalendarServer/trunk/twistedcaldav/test/test_icalendar.py 2013-05-17 02:11:38 UTC (rev 11207)
@@ -5317,6 +5317,108 @@
self.assertEqual(len(unfixed), result_unfixed, "Failed unfixed: %s %s" % (title, unfixed,))
+ def test_fix_invalid_recurrence_id(self):
+
+ data = (
+ (
+ "Recurring with override",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=US/Pacific:20071114T120000
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+RRULE:FREQ=DAILY;COUNT=2
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID;TZID=US/Pacific:20071115T000000
+DTSTART;TZID=US/Pacific:20071115T130000
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
+TZNAME:PST
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=US/Pacific:20071114T120000
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY;COUNT=2
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID;TZID=US/Pacific:20071115T120000
+DTSTART;TZID=US/Pacific:20071115T130000
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ (
+ (
+ PyCalendarDateTime(2007, 11, 14, 20, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 11, 14, 21, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ ),
+ (
+ PyCalendarDateTime(2007, 11, 15, 21, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 11, 15, 22, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ ),
+ )
+ ),
+ )
+
+ for description, original, fixed, results in data:
+ component = Component.fromString(original)
+ instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=False)
+ self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
+ periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
+ self.assertEqual(periods, results)
+ for start, end in periods:
+ self.assertEqual(start.isDateOnly(), results[0][0].isDateOnly(), "%s: %s wrong date/time start state" % (description, start,))
+ self.assertEqual(end.isDateOnly(), results[0][1].isDateOnly(), "%s: %s wrong date/time end state" % (description, end,))
+ self.assertEqual(str(component), fixed.replace("\n", "\r\n"))
+
+
def test_mismatched_until(self):
invalid = (
"""BEGIN:VCALENDAR
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130516/e3ab000b/attachment-0001.html>
More information about the calendarserver-changes
mailing list