[CalendarServer-changes] [3654] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue Feb 10 08:56:24 PST 2009


Revision: 3654
          http://trac.macosforge.org/projects/calendarserver/changeset/3654
Author:   cdaboo at apple.com
Date:     2009-02-10 08:56:24 -0800 (Tue, 10 Feb 2009)
Log Message:
-----------
Handle situations where an attendee client forces a change of TZID - we force the TZID back to the original.

Modified Paths:
--------------
    CalendarServer/trunk/run
    CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py

Modified: CalendarServer/trunk/run
===================================================================
--- CalendarServer/trunk/run	2009-02-10 16:55:15 UTC (rev 3653)
+++ CalendarServer/trunk/run	2009-02-10 16:56:24 UTC (rev 3654)
@@ -692,7 +692,7 @@
 
 caldavtester="${top}/CalDAVTester";
 
-svn_get "CalDAVTester" "${caldavtester}" "${svn_uri_base}/CalDAVTester/trunk" 3648;
+svn_get "CalDAVTester" "${caldavtester}" "${svn_uri_base}/CalDAVTester/trunk" 3653;
 
 #
 # Calendar Server

Modified: CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py	2009-02-10 16:55:15 UTC (rev 3653)
+++ CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py	2009-02-10 16:56:24 UTC (rev 3654)
@@ -236,6 +236,8 @@
             return calendar
 
         # Do straight comparison without alarms
+        self.originalCalendar1 = self.calendar1
+        self.originalCalendar2 = self.calendar2
         self.calendar1 = duplicateAndNormalize(self.calendar1)
         self.calendar2 = duplicateAndNormalize(self.calendar2)
 
@@ -250,15 +252,27 @@
             return False, False, ()
         
         # Make sure the same VTIMEZONE components appear
+        tzidRemapping = False
         if not self._compareVTIMEZONEs():
-            self._logDiffError("attendeeDiff: VTIMEZONEs do not match")
-            return False, False, ()
+            # Not an error any more. Instead we need to merge back the original TZIDs
+            # into the event being written.
+            tzidRemapping = True
         
         # Compare each component instance from the new calendar with each derived
         # component instance from the old one
         result = self._compareComponents()
         if not result[0]:
             self._logDiffError("attendeeDiff: Mismatched calendar objects")
+        else:
+            # May need to do some rewriting
+            if tzidRemapping:
+                try:
+                    self._remapTZIDs()
+                    self._logDiffError("attendeeDiff: VTIMEZONEs re-mapped")
+                except ValueError, e:
+                    self._logDiffError("attendeeDiff: VTIMEZONE re-mapping failed: %s" % (str(e),))
+                    return False, False, ()
+
         return result
     
     def _attendeeMerge(self):
@@ -337,6 +351,15 @@
             log.debug("VCALENDAR properties differ: %s" % (propdiff,))
         return result
 
+    @staticmethod
+    def _extractTZIDs(calendar):
+
+        tzids = set()
+        for component in calendar.subcomponents():
+            if component.name() == "VTIMEZONE":
+                tzids.add(component.propertyValue("TZID"))
+        return tzids
+
     def _compareVTIMEZONEs(self):
 
         # FIXME: clients may re-write timezones so the best we can do is
@@ -344,21 +367,117 @@
         # of a VTIMEZONE and thus could show events at different times than the
         # organizer.
         
-        def extractTZIDs(calendar):
-
-            tzids = set()
-            for component in calendar.subcomponents():
-                if component.name() == "VTIMEZONE":
-                    tzids.add(component.propertyValue("TZID"))
-            return tzids
-        
-        tzids1 = extractTZIDs(self.calendar1)
-        tzids2 = extractTZIDs(self.calendar2)
+        tzids1 = self._extractTZIDs(self.calendar1)
+        tzids2 = self._extractTZIDs(self.calendar2)
         result = tzids1 == tzids2
         if not result:
             log.debug("Different VTIMEZONES: %s %s" % (tzids1, tzids2))
         return result
 
+    def _remapTZIDs(self):
+        """
+        Re-map TZIDs that changed between the existing calendar data and the new data
+        being written for the attendee.
+        """
+
+        # Do master component re-map first
+        old_master = self.originalCalendar1.masterComponent()
+        new_master = self.originalCalendar2.masterComponent()
+        self._remapTZIDsOnComponent(old_master, new_master)
+        
+        # Now do each corresponding overridden component
+        for newComponent in self.originalCalendar2.subcomponents():
+            
+            # Make sure we have an appropriate component
+            if newComponent.name() == "VTIMEZONE":
+                continue
+            rid = newComponent.getRecurrenceIDUTC()
+            if rid is None:
+                continue
+
+            # Find matching component in new calendar
+            oldComponent = self.originalCalendar1.overriddenComponent(rid)
+            if oldComponent is None:
+                # Derive a new instance from the new calendar and transfer attendee status
+                oldComponent = self.originalCalendar2.deriveInstance(rid)
+
+            if oldComponent:
+                self._remapTZIDsOnComponent(oldComponent, newComponent)
+        
+        
+        # Now manipulate the VTIMEZONE components in the calendar data
+        for newComponent in tuple(self.originalCalendar2.subcomponents()):
+            # Make sure we have an appropriate component
+            if newComponent.name() == "VTIMEZONE":
+                self.originalCalendar2.removeComponent(newComponent)
+                
+        # The following statement is required to force vobject to serialize the
+        # calendar data and in the process add any missing VTIMEZONEs as needed.
+        _ignore = str(self.originalCalendar2)
+        
+    def _remapTZIDsOnComponent(self, oldComponent, newComponent):
+        """
+        Re-map TZIDs that changed between the existing calendar data and the new data
+        being written for the attendee.
+        """
+
+        # Look at each property that culd contain a TZID:
+        # DTSTART, DTEND, RDATE, EXDATE, RECURRENCE-ID, DUE.
+        # NB EXDATE/RDATE can occur multiple times - special case
+        checkPropertiesOneOff = (
+            "DTSTART",
+            "DTEND",
+            "RECURRENCE-ID",
+            "DUE",
+        )
+        checkPropertiesMultiple = (
+            "RDATE",
+            "EXDATE",
+        )
+        
+        for propName in checkPropertiesOneOff:
+            oldProp = oldComponent.getProperty(propName)
+            newProp = newComponent.getProperty(propName)
+            
+            # Special case behavior where DURATIOn is mapped to DTEND
+            if propName == "DTEND" and oldProp is None and newProp is not None:
+                oldProp = oldComponent.getProperty("DTSTART")
+
+            # Transfer tzinfo from old property value to the new one
+            if oldProp is not None and newProp is not None:
+                if "X-VOBJ-ORIGINAL-TZID" in oldProp.params():
+                    oldTZID = oldProp.paramValue("X-VOBJ-ORIGINAL-TZID")
+                    if "X-VOBJ-ORIGINAL-TZID" in newProp.params():
+                        newTZID = newProp.paramValue("X-VOBJ-ORIGINAL-TZID")
+                        
+                        if oldTZID != newTZID:
+                            newProp.params()["X-VOBJ-ORIGINAL-TZID"][0] = oldTZID
+                            newProp.setValue(newProp.value().replace(tzinfo=oldProp.value().tzinfo))
+                    else:
+                        raise ValueError("Cannot handle mismatched TZIDs on %s" % (propName,))
+                        
+        for propName in checkPropertiesMultiple:
+            oldProps = oldComponent.properties(propName)
+            newProps = newComponent.properties(propName)
+            oldTZID = None
+            oldTzinfo = None
+            for prop in oldProps:
+                if "X-VOBJ-ORIGINAL-TZID" in prop.params():
+                    if oldTZID and oldTZID != prop.paramValue("X-VOBJ-ORIGINAL-TZID"):
+                        raise ValueError("Cannot handle different TZIDs on multiple %s" % (propName,))
+                    else:
+                        oldTZID = prop.paramValue("X-VOBJ-ORIGINAL-TZID")
+                        oldTzinfo = prop.value()[0].tzinfo
+            for prop in newProps:
+                if "X-VOBJ-ORIGINAL-TZID" in prop.params():
+                    if oldTZID:
+                        prop.params()["X-VOBJ-ORIGINAL-TZID"][0] = oldTZID
+                        prop.setValue([item.replace(tzinfo=oldTzinfo) for item in prop.value()])
+                    else:
+                        raise ValueError("Cannot handle mismatched TZIDs on %s" % (propName,))
+                elif oldTZID:
+                    raise ValueError("Cannot handle mismatched TZIDs on %s" % (propName,))
+
     def _compareComponents(self):
         
         # First get uid/rid map of components
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090210/34bfe856/attachment.html>


More information about the calendarserver-changes mailing list