[CalendarServer-changes] [11159] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu May 9 12:07:38 PDT 2013


Revision: 11159
          http://trac.calendarserver.org//changeset/11159
Author:   cdaboo at apple.com
Date:     2013-05-09 12:07:38 -0700 (Thu, 09 May 2013)
Log Message:
-----------
Move default alarm properties into DB columns.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/resource.py
    CalendarServer/trunk/twistedcaldav/sharing.py
    CalendarServer/trunk/twistedcaldav/storebridge.py
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
    CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
    CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
    CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql
    CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql
    CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py
    CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/upgrade_from_3_to_4.py

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2013-05-09 19:07:38 UTC (rev 11159)
@@ -530,21 +530,6 @@
         returnValue(res)
 
 
-    def _hasSharedProperty(self, qname, request):
-
-        # Always have default alarms on shared calendars
-        if qname in (
-            caldavxml.DefaultAlarmVEventDateTime.qname(),
-            caldavxml.DefaultAlarmVEventDate.qname(),
-            caldavxml.DefaultAlarmVToDoDateTime.qname(),
-            caldavxml.DefaultAlarmVToDoDate.qname(),
-        ) and self.isCalendarCollection():
-            return True
-
-        p = self.deadProperties().contains(qname)
-        return p
-
-
     def _hasGlobalProperty(self, property, request):
         """
         Need to special case schedule-calendar-transp for backwards compatability.
@@ -715,6 +700,8 @@
             "%r is not a WebDAVElement instance" % (property,)
         )
 
+        self._preProcessWriteProperty(property, request)
+
         res = (yield self._writeGlobalProperty(property, request))
         returnValue(res)
 
@@ -781,8 +768,6 @@
     @inlineCallbacks
     def _writeGlobalProperty(self, property, request):
 
-        self._preProcessWriteProperty(property, request)
-
         if property.qname() == caldavxml.ScheduleCalendarTransp.qname():
             yield self._newStoreObject.setUsedForFreeBusy(property == caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
             returnValue(None)
@@ -1962,35 +1947,70 @@
     the default alarm properties in a more useful way that using readProperty.
     In particular it will handle inheritance of the property from the home if a
     calendar does not explicitly have the property.
+
+    Important: we need to distinguish between the property not being present, or
+    present but empty, however the store by default is unable to distinguish between
+    None and and empty C{str}. So what we do is use the value "empty" to represent
+    a present but empty property.
     """
 
-    def getDefaultAlarm(self, vevent, timed):
+    ALARM_PROPERTIES = {
+        caldavxml.DefaultAlarmVEventDateTime.qname(): (True, True,),
+        caldavxml.DefaultAlarmVEventDate.qname(): (True, False,),
+        caldavxml.DefaultAlarmVToDoDateTime.qname(): (False, True,),
+        caldavxml.DefaultAlarmVToDoDate.qname(): (False, False,),
+    }
 
-        if vevent:
-            propname = caldavxml.DefaultAlarmVEventDateTime if timed else caldavxml.DefaultAlarmVEventDate
-        else:
-            propname = caldavxml.DefaultAlarmVToDoDateTime if timed else caldavxml.DefaultAlarmVToDoDate
+    ALARM_PROPERTY_CLASSES = {
+        caldavxml.DefaultAlarmVEventDateTime.qname(): caldavxml.DefaultAlarmVEventDateTime,
+        caldavxml.DefaultAlarmVEventDate.qname(): caldavxml.DefaultAlarmVEventDate,
+        caldavxml.DefaultAlarmVToDoDateTime.qname(): caldavxml.DefaultAlarmVToDoDateTime,
+        caldavxml.DefaultAlarmVToDoDate.qname(): caldavxml.DefaultAlarmVToDoDate,
+    }
 
+    def getDefaultAlarmProperty(self, propname):
+
+        vevent, timed = DefaultAlarmPropertyMixin.ALARM_PROPERTIES[propname]
+
         if self.isCalendarCollection():
 
             # Get from calendar or inherit from home
-            try:
-                prop = self.deadProperties().get(propname.qname())
-            except HTTPError:
-                prop = None
-            if prop is None:
-                prop = self.parentResource().getDefaultAlarm(vevent, timed)
+            alarm = self._newStoreObject.getDefaultAlarm(vevent, timed)
+            if alarm is None:
+                return self.parentResource().getDefaultAlarmProperty(propname)
+            elif alarm == "empty":
+                return DefaultAlarmPropertyMixin.ALARM_PROPERTY_CLASSES[propname]()
         else:
             # Just return whatever is on the home
-            try:
-                prop = self.deadProperties().get(propname.qname())
-            except HTTPError:
-                prop = None
+            alarm = self._newStoreHome.getDefaultAlarm(vevent, timed)
 
-        return str(prop) if prop is not None else None
+        return DefaultAlarmPropertyMixin.ALARM_PROPERTY_CLASSES[propname](alarm) if alarm else None
 
 
+    @inlineCallbacks
+    def setDefaultAlarmProperty(self, prop):
 
+        vevent, timed = DefaultAlarmPropertyMixin.ALARM_PROPERTIES[prop.qname()]
+        alarm = str(prop)
+
+        if self.isCalendarCollection():
+            yield self._newStoreObject.setDefaultAlarm(alarm if alarm else "empty", vevent, timed)
+        else:
+            yield self._newStoreHome.setDefaultAlarm(alarm if alarm else "empty", vevent, timed)
+
+
+    @inlineCallbacks
+    def removeDefaultAlarmProperty(self, propname):
+
+        vevent, timed = DefaultAlarmPropertyMixin.ALARM_PROPERTIES[propname]
+
+        if self.isCalendarCollection():
+            yield self._newStoreObject.setDefaultAlarm(None, vevent, timed)
+        else:
+            yield self._newStoreHome.setDefaultAlarm(None, vevent, timed)
+
+
+
 class CommonHomeResource(PropfindCacheMixin, SharedHomeMixin, CalDAVResource):
     """
     Logic common to Calendar and Addressbook home resources.
@@ -2416,6 +2436,24 @@
         return existing
 
 
+    def _hasGlobalProperty(self, property, request):
+        """
+        Need to special case schedule-calendar-transp for backwards compatability.
+        """
+
+        if type(property) is tuple:
+            qname = property
+        else:
+            qname = property.qname()
+
+        # Force calendar collections to always appear to have the property
+        if qname in DefaultAlarmPropertyMixin.ALARM_PROPERTIES:
+            return succeed(self.getDefaultAlarmProperty(qname) is not None)
+
+        else:
+            return super(CalendarHomeResource, self)._hasGlobalProperty(property, request)
+
+
     @inlineCallbacks
     def readProperty(self, property, request):
         if type(property) is tuple:
@@ -2449,10 +2487,39 @@
             else:
                 returnValue(None)
 
+        elif qname in DefaultAlarmPropertyMixin.ALARM_PROPERTIES:
+            returnValue(self.getDefaultAlarmProperty(qname))
+
         result = (yield super(CalendarHomeResource, self).readProperty(property, request))
         returnValue(result)
 
 
+    @inlineCallbacks
+    def _writeGlobalProperty(self, property, request):
+
+        if property.qname() in DefaultAlarmPropertyMixin.ALARM_PROPERTIES:
+            yield self.setDefaultAlarmProperty(property)
+            returnValue(None)
+
+        result = (yield super(CalendarHomeResource, self)._writeGlobalProperty(property, request))
+        returnValue(result)
+
+
+    @inlineCallbacks
+    def removeProperty(self, property, request):
+        if type(property) is tuple:
+            qname = property
+        else:
+            qname = property.qname()
+
+        if qname in DefaultAlarmPropertyMixin.ALARM_PROPERTIES:
+            result = (yield self.removeDefaultAlarmProperty(qname))
+            returnValue(result)
+
+        result = (yield super(CalendarHomeResource, self).removeProperty(property, request))
+        returnValue(result)
+
+
     def _setupProvisions(self):
 
         # Cache children which must be of a specific type

Modified: CalendarServer/trunk/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/sharing.py	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/twistedcaldav/sharing.py	2013-05-09 19:07:38 UTC (rev 11159)
@@ -1202,10 +1202,10 @@
         # Calendars always start out transparent and with empty default alarms
         if isNewShare and shareeCollection.isCalendarCollection():
             yield shareeCollection._newStoreObject.setUsedForFreeBusy(False)
-            yield shareeCollection.writeProperty(caldavxml.DefaultAlarmVEventDateTime.fromString(""), request)
-            yield shareeCollection.writeProperty(caldavxml.DefaultAlarmVEventDate.fromString(""), request)
-            yield shareeCollection.writeProperty(caldavxml.DefaultAlarmVToDoDateTime.fromString(""), request)
-            yield shareeCollection.writeProperty(caldavxml.DefaultAlarmVToDoDate.fromString(""), request)
+            yield shareeCollection._newStoreObject.setDefaultAlarm("empty", True, True)
+            yield shareeCollection._newStoreObject.setDefaultAlarm("empty", True, False)
+            yield shareeCollection._newStoreObject.setDefaultAlarm("empty", False, True)
+            yield shareeCollection._newStoreObject.setDefaultAlarm("empty", False, False)
 
         # Notify client of changes
         yield self.notifyChanged()

Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py	2013-05-09 19:07:38 UTC (rev 11159)
@@ -1214,7 +1214,65 @@
         return caldavxml.CalendarData
 
 
+    def _hasGlobalProperty(self, property, request):
+        """
+        Need to special case schedule-calendar-transp for backwards compatability.
+        """
+
+        if type(property) is tuple:
+            qname = property
+        else:
+            qname = property.qname()
+
+        # Force calendar collections to always appear to have the property
+        if qname in DefaultAlarmPropertyMixin.ALARM_PROPERTIES:
+            return succeed(self.getDefaultAlarmProperty(qname) is not None)
+
+        else:
+            return super(CalendarCollectionResource, self)._hasGlobalProperty(property, request)
+
+
     @inlineCallbacks
+    def readProperty(self, property, request):
+        if type(property) is tuple:
+            qname = property
+        else:
+            qname = property.qname()
+
+        if qname in DefaultAlarmPropertyMixin.ALARM_PROPERTIES:
+            returnValue(self.getDefaultAlarmProperty(qname))
+
+        result = (yield super(CalendarCollectionResource, self).readProperty(property, request))
+        returnValue(result)
+
+
+    @inlineCallbacks
+    def _writeGlobalProperty(self, property, request):
+
+        if property.qname() in DefaultAlarmPropertyMixin.ALARM_PROPERTIES:
+            yield self.setDefaultAlarmProperty(property)
+            returnValue(None)
+
+        result = (yield super(CalendarCollectionResource, self)._writeGlobalProperty(property, request))
+        returnValue(result)
+
+
+    @inlineCallbacks
+    def removeProperty(self, property, request):
+        if type(property) is tuple:
+            qname = property
+        else:
+            qname = property.qname()
+
+        if qname in DefaultAlarmPropertyMixin.ALARM_PROPERTIES:
+            result = (yield self.removeDefaultAlarmProperty(qname))
+            returnValue(result)
+
+        result = (yield super(CalendarCollectionResource, self).removeProperty(property, request))
+        returnValue(result)
+
+
+    @inlineCallbacks
     def storeResourceData(self, newchild, component, returnChangedData=False):
 
         yield newchild.storeComponent(component)

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2013-05-09 19:07:38 UTC (rev 11159)
@@ -358,6 +358,10 @@
         return (
             cls._homeMetaDataSchema.DEFAULT_EVENTS,
             cls._homeMetaDataSchema.DEFAULT_TASKS,
+            cls._homeMetaDataSchema.ALARM_VEVENT_TIMED,
+            cls._homeMetaDataSchema.ALARM_VEVENT_ALLDAY,
+            cls._homeMetaDataSchema.ALARM_VTODO_TIMED,
+            cls._homeMetaDataSchema.ALARM_VTODO_ALLDAY,
             cls._homeMetaDataSchema.CREATED,
             cls._homeMetaDataSchema.MODIFIED,
         )
@@ -376,6 +380,10 @@
         return (
             "_default_events",
             "_default_tasks",
+            "_alarm_vevent_timed",
+            "_alarm_vevent_allday",
+            "_alarm_vtodo_timed",
+            "_alarm_vtodo_allday",
             "_created",
             "_modified",
         )
@@ -784,6 +792,12 @@
         # Not allowed to delete the default calendar
         return calendar._resourceID in (self._default_events, self._default_tasks)
 
+    ALARM_DETAILS = {
+        (True, True): (_homeMetaDataSchema.ALARM_VEVENT_TIMED, "_alarm_vevent_timed"),
+        (True, False): (_homeMetaDataSchema.ALARM_VEVENT_ALLDAY, "_alarm_vevent_allday"),
+        (False, True): (_homeMetaDataSchema.ALARM_VTODO_TIMED, "_alarm_vtodo_timed"),
+        (False, False): (_homeMetaDataSchema.ALARM_VTODO_ALLDAY, "_alarm_vtodo_allday"),
+    }
 
     def getDefaultAlarm(self, vevent, timed):
         """
@@ -793,20 +807,14 @@
         @type vevent: C{bool}
         @param timed: timed ({C{True}) or all-day ({C{False})
         @type timed: C{bool}
+
         @return: the alarm (text)
         @rtype: C{str}
         """
 
-        if vevent:
-            propname = caldavxml.DefaultAlarmVEventDateTime if timed else caldavxml.DefaultAlarmVEventDate
-        else:
-            propname = caldavxml.DefaultAlarmVToDoDateTime if timed else caldavxml.DefaultAlarmVToDoDate
+        return getattr(self, self.ALARM_DETAILS[(vevent, timed)][1])
 
-        prop = self.properties().get(PropertyName.fromElement(propname))
 
-        return str(prop) if prop is not None else None
-
-
     @inlineCallbacks
     def setDefaultAlarm(self, alarm, vevent, timed):
         """
@@ -820,12 +828,16 @@
         @type timed: C{bool}
         """
 
-        if vevent:
-            prop = caldavxml.DefaultAlarmVEventDateTime if timed else caldavxml.DefaultAlarmVEventDate
-        else:
-            prop = caldavxml.DefaultAlarmVToDoDateTime if timed else caldavxml.DefaultAlarmVToDoDate
+        colname, attr_alarm = self.ALARM_DETAILS[(vevent, timed)]
 
-        self.properties()[PropertyName.fromElement(prop)] = prop.fromString(alarm)
+        setattr(self, attr_alarm, alarm)
+
+        chm = self._homeMetaDataSchema
+        yield Update(
+            {colname: alarm},
+            Where=chm.RESOURCE_ID == self._resourceID,
+        ).on(self._txn)
+        yield self.invalidateQueryCache()
         yield self.notifyChanged()
 
 
@@ -914,6 +926,10 @@
 
         return (
             cls._bindSchema.TRANSP,
+            cls._bindSchema.ALARM_VEVENT_TIMED,
+            cls._bindSchema.ALARM_VEVENT_ALLDAY,
+            cls._bindSchema.ALARM_VTODO_TIMED,
+            cls._bindSchema.ALARM_VTODO_ALLDAY,
         )
 
 
@@ -927,6 +943,10 @@
 
         return (
             "_transp",
+            "_alarm_vevent_timed",
+            "_alarm_vevent_allday",
+            "_alarm_vtodo_timed",
+            "_alarm_vtodo_allday",
         )
 
 
@@ -1009,6 +1029,12 @@
         else:
             return True
 
+    ALARM_DETAILS = {
+        (True, True): (_bindSchema.ALARM_VEVENT_TIMED, "_alarm_vevent_timed"),
+        (True, False): (_bindSchema.ALARM_VEVENT_ALLDAY, "_alarm_vevent_allday"),
+        (False, True): (_bindSchema.ALARM_VTODO_TIMED, "_alarm_vtodo_timed"),
+        (False, False): (_bindSchema.ALARM_VTODO_ALLDAY, "_alarm_vtodo_allday"),
+    }
 
     def getDefaultAlarm(self, vevent, timed):
         """
@@ -1022,19 +1048,9 @@
         @rtype: C{str}
         """
 
-        if vevent:
-            propname = caldavxml.DefaultAlarmVEventDateTime if timed else caldavxml.DefaultAlarmVEventDate
-        else:
-            propname = caldavxml.DefaultAlarmVToDoDateTime if timed else caldavxml.DefaultAlarmVToDoDate
+        return getattr(self, self.ALARM_DETAILS[(vevent, timed)][1])
 
-        prop = self.properties().get(PropertyName.fromElement(propname))
 
-        if prop is None:
-            return self.viewerHome().getDefaultAlarm(vevent, timed)
-        else:
-            return str(prop)
-
-
     @inlineCallbacks
     def setDefaultAlarm(self, alarm, vevent, timed):
         """
@@ -1048,12 +1064,16 @@
         @type timed: C{bool}
         """
 
-        if vevent:
-            prop = caldavxml.DefaultAlarmVEventDateTime if timed else caldavxml.DefaultAlarmVEventDate
-        else:
-            prop = caldavxml.DefaultAlarmVToDoDateTime if timed else caldavxml.DefaultAlarmVToDoDate
+        colname, attr_alarm = self.ALARM_DETAILS[(vevent, timed)]
 
-        self.properties()[PropertyName.fromElement(prop)] = prop.fromString(alarm)
+        setattr(self, attr_alarm, alarm)
+
+        cal = self._bindSchema
+        yield Update(
+            {colname: alarm},
+            Where=(cal.CALENDAR_HOME_RESOURCE_ID == self.viewerHome()._resourceID).And(cal.CALENDAR_RESOURCE_ID == self._resourceID)
+        ).on(self._txn)
+        yield self.invalidateQueryCache()
         yield self.notifyChanged()
 
 
@@ -1080,9 +1100,7 @@
         self._transp = _TRANSP_OPAQUE if use_it else _TRANSP_TRANSPARENT
         cal = self._bindSchema
         yield Update(
-            {
-                cal.TRANSP : self._transp
-            },
+            {cal.TRANSP : self._transp},
             Where=(cal.CALENDAR_HOME_RESOURCE_ID == self.viewerHome()._resourceID).And(cal.CALENDAR_RESOURCE_ID == self._resourceID)
         ).on(self._txn)
         yield self.invalidateQueryCache()
@@ -1806,7 +1824,9 @@
 
         # See if default exists and add using appropriate logic
         alarm = self.calendar().getDefaultAlarm(vevent, timed)
-        if alarm and component.addAlarms(alarm):
+        if alarm is None:
+            alarm = self.calendar().viewerHome().getDefaultAlarm(vevent, timed)
+        if alarm and alarm != "empty" and component.addAlarms(alarm):
             self._componentChanged = True
 
 

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2013-05-09 19:07:38 UTC (rev 11159)
@@ -1566,3 +1566,129 @@
         obj = (yield self.calendarObjectUnderTest())
         calendarObject = (yield home.objectResourceWithID(obj._resourceID))
         self.assertNotEquals(calendarObject, None)
+
+
+    @inlineCallbacks
+    def test_defaultAlarms(self):
+        """
+        L{ICalendarHome.objectResourceWithID} will return the calendar object..
+        """
+
+        alarmhome1 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT1M
+END:VALARM
+"""
+
+        alarmhome2 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT2M
+END:VALARM
+"""
+
+        alarmhome3 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT3M
+END:VALARM
+"""
+
+        alarmhome4 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT4M
+END:VALARM
+"""
+
+        alarmcalendar1 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT1M
+END:VALARM
+"""
+
+        alarmcalendar2 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT2M
+END:VALARM
+"""
+
+        alarmcalendar3 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT3M
+END:VALARM
+"""
+
+        alarmcalendar4 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT4M
+END:VALARM
+"""
+
+        detailshome = (
+            (True, True, alarmhome1,),
+            (True, False, alarmhome2,),
+            (False, True, alarmhome3,),
+            (False, False, alarmhome4,),
+        )
+
+        home = yield self.homeUnderTest()
+        for vevent, timed, _ignore_alarm in detailshome:
+            alarm_result = (yield home.getDefaultAlarm(vevent, timed))
+            self.assertEquals(alarm_result, None)
+
+        for vevent, timed, alarm in detailshome:
+            yield home.setDefaultAlarm(alarm, vevent, timed)
+
+        yield self.commit()
+
+        home = yield self.homeUnderTest()
+        for vevent, timed, alarm in detailshome:
+            alarm_result = (yield home.getDefaultAlarm(vevent, timed))
+            self.assertEquals(alarm_result, alarm)
+
+        for vevent, timed, alarm in detailshome:
+            yield home.setDefaultAlarm(None, vevent, timed)
+
+        yield self.commit()
+
+        home = yield self.homeUnderTest()
+        for vevent, timed, _ignore_alarm in detailshome:
+            alarm_result = (yield home.getDefaultAlarm(vevent, timed))
+            self.assertEquals(alarm_result, None)
+
+        yield self.commit()
+
+        detailscalendar = (
+            (True, True, alarmcalendar1,),
+            (True, False, alarmcalendar2,),
+            (False, True, alarmcalendar3,),
+            (False, False, alarmcalendar4,),
+        )
+
+        calendar = yield self.calendarUnderTest()
+        for vevent, timed, _ignore_alarm in detailscalendar:
+            alarm_result = (yield calendar.getDefaultAlarm(vevent, timed))
+            self.assertEquals(alarm_result, None)
+
+        for vevent, timed, alarm in detailscalendar:
+            yield calendar.setDefaultAlarm(alarm, vevent, timed)
+
+        yield self.commit()
+
+        calendar = yield self.calendarUnderTest()
+        for vevent, timed, alarm in detailscalendar:
+            alarm_result = (yield calendar.getDefaultAlarm(vevent, timed))
+            self.assertEquals(alarm_result, alarm)
+
+        yield self.commit()
+
+        calendar = yield self.calendarUnderTest()
+        for vevent, timed, alarm in detailscalendar:
+            yield calendar.setDefaultAlarm(None, vevent, timed)
+
+        yield self.commit()
+
+        calendar = yield self.calendarUnderTest()
+        for vevent, timed, _ignore_alarm in detailscalendar:
+            alarm_result = (yield calendar.getDefaultAlarm(vevent, timed))
+            self.assertEquals(alarm_result, None)
+
+        yield self.commit()

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql	2013-05-09 19:07:38 UTC (rev 11159)
@@ -30,6 +30,10 @@
     "QUOTA_USED_BYTES" integer default 0 not null,
     "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
     "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+    "ALARM_VEVENT_TIMED" nclob default null,
+    "ALARM_VEVENT_ALLDAY" nclob default null,
+    "ALARM_VTODO_TIMED" nclob default null,
+    "ALARM_VTODO_ALLDAY" nclob default null,
     "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
     "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
 );
@@ -65,7 +69,11 @@
     "BIND_MODE" integer not null,
     "BIND_STATUS" integer not null,
     "MESSAGE" nclob,
-    "TRANSP" integer default 0 not null, 
+    "TRANSP" integer default 0 not null,
+    "ALARM_VEVENT_TIMED" nclob default null,
+    "ALARM_VEVENT_ALLDAY" nclob default null,
+    "ALARM_VTODO_TIMED" nclob default null,
+    "ALARM_VTODO_ALLDAY" nclob default null, 
     primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"), 
     unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
 );

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql	2013-05-09 19:07:38 UTC (rev 11159)
@@ -31,9 +31,9 @@
 -- Note that this must match the node info schema in twext.enterprise.queue.
 create table NODE_INFO (
   HOSTNAME  varchar(255) not null,
-  PID       integer not null,
-  PORT      integer not null,
-  TIME      timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+  PID       integer      not null,
+  PORT      integer      not null,
+  TIME      timestamp    not null default timezone('UTC', CURRENT_TIMESTAMP),
 
   primary key (HOSTNAME, PORT)
 );
@@ -68,12 +68,16 @@
 ----------------------------
 
 create table CALENDAR_HOME_METADATA (
-  RESOURCE_ID      integer      primary key references CALENDAR_HOME on delete cascade, -- implicit index
-  QUOTA_USED_BYTES integer      default 0 not null,
-  DEFAULT_EVENTS   integer      default null references CALENDAR on delete set null,
-  DEFAULT_TASKS    integer      default null references CALENDAR on delete set null,
-  CREATED          timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
-  MODIFIED         timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+  RESOURCE_ID              integer     primary key references CALENDAR_HOME on delete cascade, -- implicit index
+  QUOTA_USED_BYTES         integer     default 0 not null,
+  DEFAULT_EVENTS           integer     default null references CALENDAR on delete set null,
+  DEFAULT_TASKS            integer     default null references CALENDAR on delete set null,
+  ALARM_VEVENT_TIMED       text        default null,
+  ALARM_VEVENT_ALLDAY      text        default null,
+  ALARM_VTODO_TIMED        text        default null,
+  ALARM_VTODO_ALLDAY       text        default null,
+  CREATED                  timestamp   default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                 timestamp   default timezone('UTC', CURRENT_TIMESTAMP)
 );
 
 
@@ -82,10 +86,10 @@
 -----------------------
 
 create table CALENDAR_METADATA (
-  RESOURCE_ID           integer   primary key references CALENDAR on delete cascade, -- implicit index
+  RESOURCE_ID           integer      primary key references CALENDAR on delete cascade, -- implicit index
   SUPPORTED_COMPONENTS  varchar(255) default null,
-  CREATED               timestamp default timezone('UTC', CURRENT_TIMESTAMP),
-  MODIFIED              timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+  CREATED               timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED              timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
 );
 
 
@@ -105,8 +109,8 @@
   XML_TYPE                      varchar(255) not null,
   XML_DATA                      text         not null,
   MD5                           char(32)     not null,
-  CREATED                       timestamp default timezone('UTC', CURRENT_TIMESTAMP),
-  MODIFIED                      timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  CREATED                       timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                      timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
 
   unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
 );
@@ -127,7 +131,11 @@
   BIND_MODE                 integer      not null, -- enum CALENDAR_BIND_MODE
   BIND_STATUS               integer      not null, -- enum CALENDAR_BIND_STATUS
   MESSAGE                   text,
-  TRANSP					integer		 default 0 not null, -- enum CALENDAR_TRANSP
+  TRANSP                    integer      default 0 not null, -- enum CALENDAR_TRANSP
+  ALARM_VEVENT_TIMED        text         default null,
+  ALARM_VEVENT_ALLDAY       text         default null,
+  ALARM_VTODO_TIMED         text         default null,
+  ALARM_VTODO_ALLDAY        text         default null,
 
   primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
   unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME)     -- implicit index
@@ -494,7 +502,7 @@
 create table APN_SUBSCRIPTIONS (
   TOKEN                         varchar(255) not null,
   RESOURCE_KEY                  varchar(255) not null,
-  MODIFIED                      integer not null,
+  MODIFIED                      integer      not null,
   SUBSCRIBER_GUID               varchar(255) not null,
   USER_AGENT                    varchar(255) default null,
   IP_ADDR                       varchar(255) default null,
@@ -514,7 +522,7 @@
   ORGANIZER                     varchar(255) not null,
   ATTENDEE                      varchar(255) not null,
   ICALUID                       varchar(255) not null,
-  ACCESSED                      timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  ACCESSED                      timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
 
   primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
 );
@@ -533,7 +541,7 @@
 ---------------------------
 
 create table IMIP_INVITATION_WORK (
-  WORK_ID                       integer primary key default nextval('WORKITEM_SEQ') not null,
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null,
   NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
   FROM_ADDR                     varchar(255) not null,
   TO_ADDR                       varchar(255) not null,
@@ -545,7 +553,7 @@
 -----------------------
 
 create table IMIP_POLLING_WORK (
-  WORK_ID                       integer primary key default nextval('WORKITEM_SEQ') not null,
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null,
   NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
 );
 
@@ -554,7 +562,7 @@
 ---------------------
 
 create table IMIP_REPLY_WORK (
-  WORK_ID                       integer primary key default nextval('WORKITEM_SEQ') not null,
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null,
   NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
   ORGANIZER                     varchar(255) not null,
   ATTENDEE                      varchar(255) not null,
@@ -566,7 +574,7 @@
 ------------------------
 
 create table PUSH_NOTIFICATION_WORK (
-  WORK_ID                       integer primary key default nextval('WORKITEM_SEQ') not null,
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null,
   NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
   PUSH_ID                       varchar(255) not null
 );
@@ -576,7 +584,7 @@
 -----------------
 
 create table GROUP_CACHER_POLLING_WORK (
-  WORK_ID                       integer primary key default nextval('WORKITEM_SEQ') not null,
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null,
   NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
 );
 

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql	2013-05-09 19:07:38 UTC (rev 11159)
@@ -22,13 +22,21 @@
 
 alter table ATTACHMENT
  add ("DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
- 	  "DEFAULT_TASKS"  integer default null references CALENDAR on delete set null);
+ 	  "DEFAULT_TASKS"  integer default null references CALENDAR on delete set null,
+      "ALARM_VEVENT_TIMED" nclob default null,
+      "ALARM_VEVENT_ALLDAY" nclob default null,
+      "ALARM_VTODO_TIMED" nclob default null,
+      "ALARM_VTODO_ALLDAY" nclob default null);
 
  	  
 -- Calendar bind related updates
 
 alter table CALENDAR_BIND
- add ("TRANSP" integer default 0 not null);
+ add ("TRANSP" integer default 0 not null,
+      "ALARM_VEVENT_TIMED" nclob default null,
+      "ALARM_VEVENT_ALLDAY" nclob default null,
+      "ALARM_VTODO_TIMED" nclob default null,
+      "ALARM_VTODO_ALLDAY" nclob default null);
 
 create table CALENDAR_TRANSP (
     "ID" integer primary key,

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql	2013-05-09 19:07:38 UTC (rev 11159)
@@ -22,14 +22,22 @@
 -- Calendar home related updates
 
 alter table CALENDAR_HOME_METADATA
- add column DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
- add column DEFAULT_TASKS integer default null references CALENDAR on delete set null;
+ add column DEFAULT_EVENTS           integer     default null references CALENDAR on delete set null,
+ add column DEFAULT_TASKS            integer     default null references CALENDAR on delete set null,
+ add column ALARM_VEVENT_TIMED       text        default null,
+ add column ALARM_VEVENT_ALLDAY      text        default null,
+ add column ALARM_VTODO_TIMED        text        default null,
+ add column ALARM_VTODO_ALLDAY       text        default null;
 
 
 -- Calendar bind related updates
 
 alter table CALENDAR_BIND
- add column TRANSP integer default 0 not null;
+ add column TRANSP                   integer     default 0 not null,
+ add column ALARM_VEVENT_TIMED       text        default null,
+ add column ALARM_VEVENT_ALLDAY      text        default null,
+ add column ALARM_VTODO_TIMED        text        default null,
+ add column ALARM_VTODO_ALLDAY       text        default null;
 
 create table CALENDAR_TRANSP (
   ID          integer     primary key,

Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py	2013-05-09 19:07:38 UTC (rev 11159)
@@ -20,8 +20,10 @@
 from txdav.xml.element import HRef
 from twext.enterprise.dal.syntax import Update
 from txdav.common.datastore.upgrade.sql.upgrades.upgrade_from_3_to_4 import moveDefaultCalendarProperties, \
-    moveCalendarTranspProperties, removeResourceType
+    moveCalendarTranspProperties, removeResourceType, moveDefaultAlarmProperties
 from txdav.xml import element
+from twistedcaldav import caldavxml
+from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
 
 """
 Tests for L{txdav.common.datastore.upgrade.sql.upgrade}.
@@ -38,16 +40,17 @@
     def test_defaultCalendarUpgrade(self):
 
         # Set dead property on inbox
-        inbox = (yield self.calendarUnderTest(name="inbox", home="user01"))
-        inbox.properties()[PropertyName.fromElement(ScheduleDefaultCalendarURL)] = ScheduleDefaultCalendarURL(HRef.fromString("/calendars/__uids__/user01/calendar_1"))
+        for user in ("user01", "user02",):
+            inbox = (yield self.calendarUnderTest(name="inbox", home=user))
+            inbox.properties()[PropertyName.fromElement(ScheduleDefaultCalendarURL)] = ScheduleDefaultCalendarURL(HRef.fromString("/calendars/__uids__/%s/calendar_1" % (user,)))
 
-        # Force current default to null
-        home = (yield self.homeUnderTest(name="user01"))
-        chm = home._homeMetaDataSchema
-        yield Update(
-            {chm.DEFAULT_EVENTS: None},
-            Where=chm.RESOURCE_ID == home._resourceID,
-        ).on(self.transactionUnderTest())
+            # Force current default to null
+            home = (yield self.homeUnderTest(name=user))
+            chm = home._homeMetaDataSchema
+            yield Update(
+                {chm.DEFAULT_EVENTS: None},
+                Where=chm.RESOURCE_ID == home._resourceID,
+            ).on(self.transactionUnderTest())
 
         # Force data version to previous
         ch = home._homeSchema
@@ -62,68 +65,206 @@
         yield moveDefaultCalendarProperties(self._sqlCalendarStore)
 
         # Test results
-        home = (yield self.homeUnderTest(name="user01"))
-        calendar = (yield self.calendarUnderTest(name="calendar_1", home="user01"))
-        self.assertTrue(home.isDefaultCalendar(calendar))
-        inbox = (yield self.calendarUnderTest(name="inbox", home="user01"))
-        self.assertTrue(PropertyName.fromElement(ScheduleDefaultCalendarURL) not in inbox.properties())
+        for user in ("user01", "user02",):
+            home = (yield self.homeUnderTest(name=user))
+            calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+            self.assertTrue(home.isDefaultCalendar(calendar))
+            inbox = (yield self.calendarUnderTest(name="inbox", home=user))
+            self.assertTrue(PropertyName.fromElement(ScheduleDefaultCalendarURL) not in inbox.properties())
 
 
     @inlineCallbacks
     def test_calendarTranspUpgrade(self):
 
         # Set dead property on inbox
-        inbox = (yield self.calendarUnderTest(name="inbox", home="user01"))
-        inbox.properties()[PropertyName.fromElement(CalendarFreeBusySet)] = CalendarFreeBusySet(HRef.fromString("/calendars/__uids__/user01/calendar_1"))
+        for user in ("user01", "user02",):
+            inbox = (yield self.calendarUnderTest(name="inbox", home=user))
+            inbox.properties()[PropertyName.fromElement(CalendarFreeBusySet)] = CalendarFreeBusySet(HRef.fromString("/calendars/__uids__/%s/calendar_1" % (user,)))
 
-        # Force current to transparent
-        calendar = (yield self.calendarUnderTest(name="calendar_1", home="user01"))
-        yield calendar.setUsedForFreeBusy(False)
-        calendar.properties()[PropertyName.fromElement(ScheduleCalendarTransp)] = ScheduleCalendarTransp(Opaque())
+            # Force current to transparent
+            calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+            yield calendar.setUsedForFreeBusy(False)
+            calendar.properties()[PropertyName.fromElement(ScheduleCalendarTransp)] = ScheduleCalendarTransp(Opaque())
 
-        # Force data version to previous
-        home = (yield self.homeUnderTest(name="user01"))
-        ch = home._homeSchema
-        yield Update(
-            {ch.DATAVERSION: 3},
-            Where=ch.RESOURCE_ID == home._resourceID,
-        ).on(self.transactionUnderTest())
+            # Force data version to previous
+            home = (yield self.homeUnderTest(name=user))
+            ch = home._homeSchema
+            yield Update(
+                {ch.DATAVERSION: 3},
+                Where=ch.RESOURCE_ID == home._resourceID,
+            ).on(self.transactionUnderTest())
 
         yield self.commit()
 
-        calendar = (yield self.calendarUnderTest(name="calendar_1", home="user01"))
-        self.assertFalse(calendar.isUsedForFreeBusy())
-        self.assertTrue(PropertyName.fromElement(ScheduleCalendarTransp) in calendar.properties())
-        inbox = (yield self.calendarUnderTest(name="inbox", home="user01"))
-        self.assertTrue(PropertyName.fromElement(CalendarFreeBusySet) in inbox.properties())
+        for user in ("user01", "user02",):
+            calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+            self.assertFalse(calendar.isUsedForFreeBusy())
+            self.assertTrue(PropertyName.fromElement(ScheduleCalendarTransp) in calendar.properties())
+            inbox = (yield self.calendarUnderTest(name="inbox", home=user))
+            self.assertTrue(PropertyName.fromElement(CalendarFreeBusySet) in inbox.properties())
         yield self.commit()
 
         # Trigger upgrade
         yield moveCalendarTranspProperties(self._sqlCalendarStore)
 
         # Test results
-        home = (yield self.homeUnderTest(name="user01"))
-        calendar = (yield self.calendarUnderTest(name="calendar_1", home="user01"))
-        self.assertTrue(calendar.isUsedForFreeBusy())
-        inbox = (yield self.calendarUnderTest(name="inbox", home="user01"))
-        self.assertTrue(PropertyName.fromElement(CalendarFreeBusySet) not in inbox.properties())
+        for user in ("user01", "user02",):
+            home = (yield self.homeUnderTest(name=user))
+            calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+            self.assertTrue(calendar.isUsedForFreeBusy())
+            inbox = (yield self.calendarUnderTest(name="inbox", home=user))
+            self.assertTrue(PropertyName.fromElement(CalendarFreeBusySet) not in inbox.properties())
 
 
     @inlineCallbacks
+    def test_defaultAlarmUpgrade(self):
+
+        alarmhome1 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT1M
+END:VALARM
+"""
+
+        alarmhome2 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT2M
+END:VALARM
+"""
+
+        alarmhome3 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT3M
+END:VALARM
+"""
+
+        alarmhome4 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT4M
+END:VALARM
+"""
+
+        alarmcalendar1 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT1M
+END:VALARM
+"""
+
+        alarmcalendar2 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT2M
+END:VALARM
+"""
+
+        alarmcalendar3 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT3M
+END:VALARM
+"""
+
+        alarmcalendar4 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT4M
+END:VALARM
+"""
+
+        alarmshared1 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT1M
+END:VALARM
+"""
+
+        alarmshared2 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT2M
+END:VALARM
+"""
+
+        alarmshared3 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT3M
+END:VALARM
+"""
+
+        alarmshared4 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT4M
+END:VALARM
+"""
+
+        # Setup old properties
+        detailshome = (
+            (True, True, alarmhome1, caldavxml.DefaultAlarmVEventDateTime,),
+            (True, False, alarmhome2, caldavxml.DefaultAlarmVEventDate,),
+            (False, True, alarmhome3, caldavxml.DefaultAlarmVToDoDateTime,),
+            (False, False, alarmhome4, caldavxml.DefaultAlarmVToDoDate,),
+        )
+        detailscalendar = (
+            (True, True, alarmcalendar1, caldavxml.DefaultAlarmVEventDateTime,),
+            (True, False, alarmcalendar2, caldavxml.DefaultAlarmVEventDate,),
+            (False, True, alarmcalendar3, caldavxml.DefaultAlarmVToDoDateTime,),
+            (False, False, alarmcalendar4, caldavxml.DefaultAlarmVToDoDate,),
+        )
+        detailsshared = (
+            (True, True, alarmshared1, caldavxml.DefaultAlarmVEventDateTime,),
+            (True, False, alarmshared2, caldavxml.DefaultAlarmVEventDate,),
+            (False, True, alarmshared3, caldavxml.DefaultAlarmVToDoDateTime,),
+            (False, False, alarmshared4, caldavxml.DefaultAlarmVToDoDate,),
+        )
+
+        home = yield self.homeUnderTest(name="user01")
+        for _ignore_vevent, _ignore_timed, alarm, prop in detailshome:
+            home.properties()[PropertyName.fromElement(prop)] = prop(alarm)
+        calendar = yield self.calendarUnderTest(name="calendar_1", home="user01")
+        for _ignore_vevent, _ignore_timed, alarm, prop in detailscalendar:
+            calendar.properties()[PropertyName.fromElement(prop)] = prop(alarm)
+        home2 = yield self.homeUnderTest(name="user02")
+        shared_name = yield calendar.shareWith(home2, _BIND_MODE_WRITE)
+        shared = yield self.calendarUnderTest(name=shared_name, home="user02")
+        for _ignore_vevent, _ignore_timed, alarm, prop in detailsshared:
+            shared.properties()[PropertyName.fromElement(prop)] = prop(alarm)
+        yield self.commit()
+
+        # Trigger upgrade
+        yield moveDefaultAlarmProperties(self._sqlCalendarStore)
+
+        # Check each type of collection
+        home = yield self.homeUnderTest(name="user01")
+        for vevent, timed, alarm, prop in detailshome:
+            alarm_result = (yield home.getDefaultAlarm(vevent, timed))
+            self.assertEquals(alarm_result, alarm)
+            self.assertTrue(PropertyName.fromElement(prop) not in home.properties())
+
+        calendar = yield self.calendarUnderTest(name="calendar_1", home="user01")
+        for vevent, timed, alarm, prop in detailscalendar:
+            alarm_result = (yield calendar.getDefaultAlarm(vevent, timed))
+            self.assertEquals(alarm_result, alarm)
+            self.assertTrue(PropertyName.fromElement(prop) not in home.properties())
+
+        shared = yield self.calendarUnderTest(name=shared_name, home="user02")
+        for vevent, timed, alarm, prop in detailsshared:
+            alarm_result = (yield shared.getDefaultAlarm(vevent, timed))
+            self.assertEquals(alarm_result, alarm)
+            self.assertTrue(PropertyName.fromElement(prop) not in home.properties())
+
+
+    @inlineCallbacks
     def test_resourceTypeUpgrade(self):
 
         # Set dead property on calendar
-        calendar = (yield self.calendarUnderTest(name="calendar_1", home="user01"))
-        calendar.properties()[PropertyName.fromElement(element.ResourceType)] = element.ResourceType(element.Collection())
+        for user in ("user01", "user02",):
+            calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+            calendar.properties()[PropertyName.fromElement(element.ResourceType)] = element.ResourceType(element.Collection())
         yield self.commit()
 
-        calendar = (yield self.calendarUnderTest(name="calendar_1", home="user01"))
-        self.assertTrue(PropertyName.fromElement(element.ResourceType) in calendar.properties())
+        for user in ("user01", "user02",):
+            calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+            self.assertTrue(PropertyName.fromElement(element.ResourceType) in calendar.properties())
         yield self.commit()
 
         # Trigger upgrade
         yield removeResourceType(self._sqlCalendarStore)
 
         # Test results
-        calendar = (yield self.calendarUnderTest(name="calendar_1", home="user01"))
-        self.assertTrue(PropertyName.fromElement(element.ResourceType) not in calendar.properties())
+        for user in ("user01", "user02",):
+            calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+            self.assertTrue(PropertyName.fromElement(element.ResourceType) not in calendar.properties())

Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/upgrade_from_3_to_4.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/upgrade_from_3_to_4.py	2013-05-09 19:03:32 UTC (rev 11158)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/upgrade_from_3_to_4.py	2013-05-09 19:07:38 UTC (rev 11159)
@@ -27,6 +27,7 @@
     updateAllCalendarHomeDataVersions, removeProperty, cleanPropertyStore
 from txdav.xml.parser import WebDAVDocument
 from txdav.xml import element
+from twisted.python.failure import Failure
 
 """
 Data upgrade from database version 3 to 4
@@ -42,6 +43,7 @@
     """
     yield moveDefaultCalendarProperties(sqlStore)
     yield moveCalendarTranspProperties(sqlStore)
+    yield moveDefaultAlarmProperties(sqlStore)
     yield removeResourceType(sqlStore)
 
     # Always bump the DB value
@@ -59,13 +61,13 @@
     """
 
     meta = schema.CALENDAR_HOME_METADATA
-    yield _processProperty(sqlStore, caldavxml.ScheduleDefaultCalendarURL, meta.DEFAULT_EVENTS)
-    yield _processProperty(sqlStore, customxml.ScheduleDefaultTasksURL, meta.DEFAULT_TASKS)
+    yield _processDefaultCalendarProperty(sqlStore, caldavxml.ScheduleDefaultCalendarURL, meta.DEFAULT_EVENTS)
+    yield _processDefaultCalendarProperty(sqlStore, customxml.ScheduleDefaultTasksURL, meta.DEFAULT_TASKS)
 
 
 
 @inlineCallbacks
-def _processProperty(sqlStore, propname, colname):
+def _processDefaultCalendarProperty(sqlStore, propname, colname):
     """
     Move the specified property value to the matching CALENDAR_HOME_METADATA table column.
 
@@ -121,8 +123,9 @@
         yield cleanPropertyStore()
 
     except RuntimeError:
+        f = Failure()
         yield sqlTxn.abort()
-        raise
+        f.raiseException()
 
 
 
@@ -186,12 +189,130 @@
         yield cleanPropertyStore()
 
     except RuntimeError:
+        f = Failure()
         yield sqlTxn.abort()
-        raise
+        f.raiseException()
 
 
 
 @inlineCallbacks
+def moveDefaultAlarmProperties(sqlStore):
+    """
+    Need to move all the CalDAV:default-calendar and CS:default-tasks properties in the
+    RESOURCE_PROPERTY table to the new CALENDAR_HOME_METADATA table columns, extracting
+    the new value from the XML property.
+    """
+
+    yield _processDefaultAlarmProperty(
+        sqlStore,
+        caldavxml.DefaultAlarmVEventDateTime,
+        True,
+        True,
+    )
+    yield _processDefaultAlarmProperty(
+        sqlStore,
+        caldavxml.DefaultAlarmVEventDate,
+        True,
+        False,
+    )
+    yield _processDefaultAlarmProperty(
+        sqlStore,
+        caldavxml.DefaultAlarmVToDoDateTime,
+        False,
+        True,
+    )
+    yield _processDefaultAlarmProperty(
+        sqlStore,
+        caldavxml.DefaultAlarmVToDoDate,
+        False,
+        False,
+    )
+
+
+
+ at inlineCallbacks
+def _processDefaultAlarmProperty(sqlStore, propname, vevent, timed):
+    """
+    Move the specified property value to the matching CALENDAR_HOME_METADATA or CALENDAR_BIND table column.
+
+    Since the number of properties may well be large, we need to do this in batches.
+    """
+
+    hm = schema.CALENDAR_HOME_METADATA
+    cb = schema.CALENDAR_BIND
+    rp = schema.RESOURCE_PROPERTY
+
+    try:
+        calendars_for_id = {}
+        while True:
+            sqlTxn = sqlStore.newTransaction()
+            rows = (yield rowsForProperty(sqlTxn, propname, with_uid=True, batch=BATCH_SIZE))
+            if len(rows) == 0:
+                yield sqlTxn.commit()
+                break
+            delete_ids = []
+            for rid, value, viewer in rows:
+                delete_ids.append(rid)
+
+                prop = WebDAVDocument.fromString(value).root_element
+                alarm = str(prop.children[0]) if prop.children else None
+
+                # First check if the rid is a home - this is the most common case
+                ids = yield Select(
+                    [hm.RESOURCE_ID, ],
+                    From=hm,
+                    Where=hm.RESOURCE_ID == rid,
+                ).on(sqlTxn)
+
+                if len(ids) > 0:
+                    # Home object
+                    calendarHome = (yield sqlTxn.calendarHomeWithResourceID(ids[0][0]))
+                    if calendarHome is not None:
+                        yield calendarHome.setDefaultAlarm(alarm, vevent, timed)
+                else:
+                    # rid is a calendar - we need to find the per-user calendar for the resource viewer
+                    if rid not in calendars_for_id:
+                        ids = yield Select(
+                            [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
+                            From=cb,
+                            Where=cb.CALENDAR_RESOURCE_ID == rid,
+                        ).on(sqlTxn)
+                        calendars_for_id[rid] = ids
+
+                    if viewer:
+                        calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
+                    else:
+                        calendarHome = None
+                        for row in calendars_for_id[rid]:
+                            home_id, bind_mode = row
+                            if bind_mode == _BIND_MODE_OWN:
+                                calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
+                                break
+
+                    if calendarHome is not None:
+                        calendar = yield calendarHome.childWithID(rid)
+                        if calendar is not None:
+                            yield calendar.setDefaultAlarm(alarm, vevent, timed)
+
+                # Always delete the row so that batch processing works correctly
+                yield Delete(
+                    From=rp,
+                    Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
+                          (rp.NAME == PropertyName.fromElement(propname).toString()),
+                ).on(sqlTxn, ids=delete_ids)
+
+            yield sqlTxn.commit()
+
+        yield cleanPropertyStore()
+
+    except RuntimeError:
+        f = Failure()
+        yield sqlTxn.abort()
+        f.raiseException()
+
+
+
+ at inlineCallbacks
 def removeResourceType(sqlStore):
     sqlTxn = sqlStore.newTransaction()
     yield removeProperty(sqlTxn, PropertyName.fromElement(element.ResourceType))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130509/85e88214/attachment-0001.html>


More information about the calendarserver-changes mailing list