[CalendarServer-changes] [14669] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu Apr 9 09:57:35 PDT 2015


Revision: 14669
          http://trac.calendarserver.org//changeset/14669
Author:   cdaboo at apple.com
Date:     2015-04-09 09:57:35 -0700 (Thu, 09 Apr 2015)
Log Message:
-----------
Support timezone-id properties for timezones-by-reference.

Modified Paths:
--------------
    CalendarServer/trunk/requirements-dev.txt
    CalendarServer/trunk/twistedcaldav/caldavxml.py
    CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py
    CalendarServer/trunk/twistedcaldav/resource.py
    CalendarServer/trunk/twistedcaldav/storebridge.py
    CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
    CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py

Modified: CalendarServer/trunk/requirements-dev.txt
===================================================================
--- CalendarServer/trunk/requirements-dev.txt	2015-04-09 16:55:50 UTC (rev 14668)
+++ CalendarServer/trunk/requirements-dev.txt	2015-04-09 16:57:35 UTC (rev 14669)
@@ -8,4 +8,4 @@
 q
 tl.eggdeps
 --editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVClientLibrary/trunk@13420#egg=CalDAVClientLibrary
---editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVTester/trunk@14659#egg=CalDAVTester
+--editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVTester/trunk@14668#egg=CalDAVTester

Modified: CalendarServer/trunk/twistedcaldav/caldavxml.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/caldavxml.py	2015-04-09 16:55:50 UTC (rev 14668)
+++ CalendarServer/trunk/twistedcaldav/caldavxml.py	2015-04-09 16:57:35 UTC (rev 14669)
@@ -212,6 +212,7 @@
         """
         Returns the calendar data derived from this element.
         """
+        data = None
         for data in self.children:
             if not isinstance(data, PCDATAElement):
                 return None
@@ -318,6 +319,17 @@
 
 
 @registerElement
+class CalendarTimeZoneID (CalDAVTextElement):
+    """
+    Specifies a time zone id on a calendar collection.
+    (draft-ietf-tzdist-caldav-timezone-ref-01, Section-5.2)
+    """
+    name = "calendar-timezone-id"
+    hidden = True
+
+
+
+ at registerElement
 class SupportedCalendarComponentSets (CalDAVElement):
     """
     Indicates what set of calendar components the server is willing to allow
@@ -469,6 +481,7 @@
         (dav_namespace, "propname"): (0, None),
         (dav_namespace, "prop"): (0, None),
         (caldav_namespace, "timezone"): (0, 1),
+        (caldav_namespace, "timezone-id"): (0, 1),
         (caldav_namespace, "filter"): (0, 1), # Actually (1, 1) unless element is empty
     }
 
@@ -479,6 +492,7 @@
         props = None
         filter = None
         timezone = None
+        timezone_id = None
 
         for child in self.children:
             qname = child.qname()
@@ -496,8 +510,15 @@
                 filter = child
 
             elif qname == (caldav_namespace, "timezone"):
+                if timezone_id is not None:
+                    raise ValueError("Only one of CalDAV:timezone and CalDAV:timezone-id allowed")
                 timezone = child
 
+            elif qname == (caldav_namespace, "timezone-id"):
+                if timezone is not None:
+                    raise ValueError("Only one of CalDAV:timezone and CalDAV:timezone-id allowed")
+                timezone_id = child
+
             else:
                 raise AssertionError("We shouldn't be here")
 
@@ -508,6 +529,7 @@
         self.props = props
         self.filter = filter
         self.timezone = timezone
+        self.timezone_id = timezone_id
 
 
 
@@ -906,6 +928,16 @@
 
 
 @registerElement
+class TimeZoneID (CalDAVTextElement):
+    """
+    Specifies a time zone id.
+    (draft-ietf-tzdist-caldav-timezone-ref-01, Section-5.2)
+    """
+    name = "timezone-id"
+
+
+
+ at registerElement
 class TimeRange (CalDAVTimeRangeElement):
     """
     Specifies a time for testing components against.

Modified: CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py	2015-04-09 16:55:50 UTC (rev 14668)
+++ CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py	2015-04-09 16:57:35 UTC (rev 14669)
@@ -13,6 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
+from twistedcaldav.timezones import TimezoneException, readVTZ
+from twistedcaldav.ical import Component
 
 """
 CalDAV calendar-query report
@@ -72,18 +74,30 @@
 
     # Get the original timezone provided in the query, if any, and validate it now
     query_timezone = None
-    query_tz = calendar_query.timezone
-    if query_tz is not None and not query_tz.valid():
-        msg = "CalDAV:timezone must contain one VTIMEZONE component only: %s" % (query_tz,)
-        log.error(msg)
-        raise HTTPError(ErrorResponse(
-            responsecode.FORBIDDEN,
-            (caldav_namespace, "valid-calendar-data"),
-            "Invalid calendar-data",
-        ))
-    if query_tz:
+    if calendar_query.timezone:
+        query_tz = calendar_query.timezone
+        if not query_tz.valid():
+            msg = "CalDAV:timezone must contain one VTIMEZONE component only: %s" % (query_tz,)
+            log.error(msg)
+            raise HTTPError(ErrorResponse(
+                responsecode.FORBIDDEN,
+                (caldav_namespace, "valid-calendar-data"),
+                "Invalid calendar-data",
+            ))
         filter.settimezone(query_tz)
-        query_timezone = tuple(calendar_query.timezone.calendar().subcomponents())[0]
+        query_timezone = tuple(query_tz.calendar().subcomponents())[0]
+    elif calendar_query.timezone_id:
+        query_tzid = calendar_query.timezone_id.toString()
+        try:
+            query_tz = Component(None, pycalendar=readVTZ(query_tzid))
+        except TimezoneException:
+            raise HTTPError(ErrorResponse(
+                responsecode.FORBIDDEN,
+                (caldav_namespace, "valid-timezone"),
+                "Invalid timezone-id",
+            ))
+        filter.settimezone(query_tz)
+        query_timezone = tuple(query_tz.subcomponents())[0]
 
     if props.qname() == ("DAV:", "allprop"):
         propertiesForResource = report_common.allPropertiesForResource
@@ -171,12 +185,13 @@
         if calresource.isPseudoCalendarCollection():
             # Get the timezone property from the collection if one was not set in the query,
             # and store in the query filter for later use
-            has_prop = (yield calresource.hasProperty(CalendarTimeZone(), request))
             timezone = query_timezone
-            if query_tz is None and has_prop:
-                tz = (yield calresource.readProperty(CalendarTimeZone(), request))
-                filter.settimezone(tz)
-                timezone = tuple(tz.calendar().subcomponents())[0]
+            if timezone is None:
+                has_prop = (yield calresource.hasProperty(CalendarTimeZone(), request))
+                if has_prop:
+                    tz = (yield calresource.readProperty(CalendarTimeZone(), request))
+                    filter.settimezone(tz)
+                    timezone = tuple(tz.calendar().subcomponents())[0]
 
             # Do some optimization of access control calculation by determining any inherited ACLs outside of
             # the child resource loop and supply those to the checkPrivileges on each child.
@@ -226,7 +241,7 @@
             # Get the timezone property from the collection if one was not set in the query,
             # and store in the query object for later use
             timezone = query_timezone
-            if query_tz is None:
+            if timezone is None:
 
                 parent = (yield calresource.locateParent(request, uri))
                 assert parent is not None and parent.isPseudoCalendarCollection()

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2015-04-09 16:55:50 UTC (rev 14668)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2015-04-09 16:57:35 UTC (rev 14669)
@@ -690,11 +690,11 @@
                         "Component %s is not supported by this server" % (component.toxml(),)
                     ))
 
-        # Strictly speaking CalDAV:timezone is a live property in the sense that the
-        # server enforces what can be stored, however it need not actually
-        # exist so we cannot list it in liveProperties on this resource, since its
-        # its presence there means that hasProperty will always return True for it.
-        elif property.qname() == caldavxml.CalendarTimeZone.qname():
+        # Strictly speaking CalDAV:calendar-timezone and CalDAV:calendar-timezone-id are live properties
+        # in the sense that the server enforces what can be stored, however they need not actually
+        # exist so we cannot list them in liveProperties on this resource, since their presence there
+        # means that hasProperty will always return True for it.
+        elif property.qname() in (caldavxml.CalendarTimeZone.qname(), caldavxml.CalendarTimeZoneID.qname(),):
             if not self.isCalendarCollection():
                 raise HTTPError(StatusResponse(
                     responsecode.FORBIDDEN,

Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py	2015-04-09 16:55:50 UTC (rev 14668)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py	2015-04-09 16:57:35 UTC (rev 14669)
@@ -96,6 +96,7 @@
     BAD_REQUEST, OK, INSUFFICIENT_STORAGE_SPACE, SERVICE_UNAVAILABLE
 )
 from txweb2.stream import ProducerStream, readStream, MemoryStream
+from twistedcaldav.timezones import TimezoneException
 
 
 """
@@ -1199,6 +1200,7 @@
             DefaultAlarmPropertyMixin.ALARM_PROPERTIES.keys()
         ) + (
             caldavxml.CalendarTimeZone.qname(),
+            caldavxml.CalendarTimeZoneID.qname(),
         )
 
 
@@ -1212,7 +1214,7 @@
         if qname in DefaultAlarmPropertyMixin.ALARM_PROPERTIES:
             return succeed(self.getDefaultAlarmProperty(qname) is not None)
 
-        elif qname == caldavxml.CalendarTimeZone.qname():
+        elif qname in (caldavxml.CalendarTimeZone.qname(), caldavxml.CalendarTimeZoneID.qname(),):
             return succeed(self._newStoreObject.getTimezone() is not None)
 
         else:
@@ -1234,6 +1236,10 @@
             format = property.content_type if isinstance(property, caldavxml.CalendarTimeZone) else None
             returnValue(caldavxml.CalendarTimeZone.fromCalendar(timezone, format=format) if timezone else None)
 
+        elif qname == caldavxml.CalendarTimeZoneID.qname():
+            tzid = self._newStoreObject.getTimezoneID()
+            returnValue(caldavxml.CalendarTimeZoneID.fromString(tzid) if tzid else None)
+
         result = (yield super(CalendarCollectionResource, self).readProperty(property, request))
         returnValue(result)
 
@@ -1261,6 +1267,18 @@
             yield self._newStoreObject.setTimezone(property.calendar())
             returnValue(None)
 
+        elif property.qname() == caldavxml.CalendarTimeZoneID.qname():
+            tzid = property.toString()
+            try:
+                yield self._newStoreObject.setTimezoneID(tzid)
+            except TimezoneException:
+                raise HTTPError(ErrorResponse(
+                    responsecode.FORBIDDEN,
+                    (caldav_namespace, "valid-timezone"),
+                    description="Invalid property"
+                ))
+            returnValue(None)
+
         elif property.qname() == caldavxml.ScheduleCalendarTransp.qname():
             yield self._newStoreObject.setUsedForFreeBusy(property == caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
             returnValue(None)
@@ -1280,7 +1298,7 @@
             result = (yield self.removeDefaultAlarmProperty(qname))
             returnValue(result)
 
-        elif qname == caldavxml.CalendarTimeZone.qname():
+        elif qname in (caldavxml.CalendarTimeZone.qname(), caldavxml.CalendarTimeZoneID.qname(),):
             yield self._newStoreObject.setTimezone(None)
             returnValue(None)
 

Modified: CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py	2015-04-09 16:55:50 UTC (rev 14668)
+++ CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py	2015-04-09 16:57:35 UTC (rev 14669)
@@ -22,7 +22,7 @@
 from txweb2.iweb import IResponse
 from txweb2.stream import MemoryStream
 from txdav.xml import element as davxml
-from txweb2.dav.util import davXMLFromStream
+from txweb2.dav.util import davXMLFromStream, allDataFromStream
 
 from twistedcaldav import caldavxml
 from twistedcaldav import ical
@@ -36,6 +36,7 @@
 from txdav.caldav.icalendarstore import ComponentUpdateState
 from txdav.caldav.datastore.query.filter import TimeRange
 from twext.who.idirectory import RecordType
+from twistedcaldav.timezones import readVTZ, TimezoneCache
 
 
 @inlineCallbacks
@@ -174,6 +175,167 @@
         return self.calendar_query(query, got_xml)
 
 
+    def test_calendar_query_timezone(self):
+        """
+        Partial retrieval of events by time range.
+        (CalDAV-access-09, section 7.6.1)
+        """
+        TimezoneCache.create()
+        self.addCleanup(TimezoneCache.clear)
+
+        tzid1 = "Etc/GMT+1"
+        tz1 = Component(None, pycalendar=readVTZ(tzid1))
+
+        calendar_properties = (
+            davxml.GETETag(),
+            caldavxml.CalendarData(),
+        )
+
+        query_timerange = caldavxml.TimeRange(
+            start="%04d1001T000000Z" % (DateTime.getToday().getYear(),),
+            end="%04d1101T000000Z" % (DateTime.getToday().getYear(),),
+        )
+
+        query = caldavxml.CalendarQuery(
+            davxml.PropertyContainer(*calendar_properties),
+            caldavxml.Filter(
+                caldavxml.ComponentFilter(
+                    caldavxml.ComponentFilter(
+                        query_timerange,
+                        name="VEVENT",
+                    ),
+                    name="VCALENDAR",
+                ),
+            ),
+            caldavxml.TimeZone.fromCalendar(tz1),
+        )
+
+        def got_xml(doc):
+            if not isinstance(doc.root_element, davxml.MultiStatus):
+                self.fail("REPORT response XML root element is not multistatus: %r" % (doc.root_element,))
+
+        return self.calendar_query(query, got_xml)
+
+
+    def test_calendar_query_timezone_id(self):
+        """
+        Partial retrieval of events by time range.
+        (CalDAV-access-09, section 7.6.1)
+        """
+        TimezoneCache.create()
+        self.addCleanup(TimezoneCache.clear)
+
+        tzid1 = "Etc/GMT+1"
+
+        calendar_properties = (
+            davxml.GETETag(),
+            caldavxml.CalendarData(),
+        )
+
+        query_timerange = caldavxml.TimeRange(
+            start="%04d1001T000000Z" % (DateTime.getToday().getYear(),),
+            end="%04d1101T000000Z" % (DateTime.getToday().getYear(),),
+        )
+
+        query = caldavxml.CalendarQuery(
+            davxml.PropertyContainer(*calendar_properties),
+            caldavxml.Filter(
+                caldavxml.ComponentFilter(
+                    caldavxml.ComponentFilter(
+                        query_timerange,
+                        name="VEVENT",
+                    ),
+                    name="VCALENDAR",
+                ),
+            ),
+            caldavxml.TimeZoneID.fromString(tzid1),
+        )
+
+        def got_xml(doc):
+            if not isinstance(doc.root_element, davxml.MultiStatus):
+                self.fail("REPORT response XML root element is not multistatus: %r" % (doc.root_element,))
+
+        return self.calendar_query(query, got_xml)
+
+
+    @inlineCallbacks
+    def test_calendar_query_bogus_timezone_id(self):
+        """
+        Partial retrieval of events by time range.
+        (CalDAV-access-09, section 7.6.1)
+        """
+        TimezoneCache.create()
+        self.addCleanup(TimezoneCache.clear)
+
+        calendar_properties = (
+            davxml.GETETag(),
+            caldavxml.CalendarData(),
+        )
+
+        query_timerange = caldavxml.TimeRange(
+            start="%04d1001T000000Z" % (DateTime.getToday().getYear(),),
+            end="%04d1101T000000Z" % (DateTime.getToday().getYear(),),
+        )
+
+        query = caldavxml.CalendarQuery(
+            davxml.PropertyContainer(*calendar_properties),
+            caldavxml.Filter(
+                caldavxml.ComponentFilter(
+                    caldavxml.ComponentFilter(
+                        query_timerange,
+                        name="VEVENT",
+                    ),
+                    name="VCALENDAR",
+                ),
+            ),
+            caldavxml.TimeZoneID.fromString("bogus"),
+        )
+
+        result = yield self.calendar_query(query, got_xml=None, expected_code=responsecode.FORBIDDEN)
+        self.assertTrue("valid-timezone" in result)
+
+
+    @inlineCallbacks
+    def test_calendar_query_wrong_timezone_elements(self):
+        """
+        Partial retrieval of events by time range.
+        (CalDAV-access-09, section 7.6.1)
+        """
+        TimezoneCache.create()
+        self.addCleanup(TimezoneCache.clear)
+
+        tzid1 = "Etc/GMT+1"
+        tz1 = Component(None, pycalendar=readVTZ(tzid1))
+
+        calendar_properties = (
+            davxml.GETETag(),
+            caldavxml.CalendarData(),
+        )
+
+        query_timerange = caldavxml.TimeRange(
+            start="%04d1001T000000Z" % (DateTime.getToday().getYear(),),
+            end="%04d1101T000000Z" % (DateTime.getToday().getYear(),),
+        )
+
+        query = caldavxml.CalendarQuery(
+            davxml.PropertyContainer(*calendar_properties),
+            caldavxml.Filter(
+                caldavxml.ComponentFilter(
+                    caldavxml.ComponentFilter(
+                        query_timerange,
+                        name="VEVENT",
+                    ),
+                    name="VCALENDAR",
+                ),
+            ),
+            caldavxml.TimeZone.fromCalendar(tz1),
+        )
+        query.children += (caldavxml.TimeZoneID.fromString(tzid1),)
+
+        result = yield self.calendar_query(query, got_xml=None, expected_code=responsecode.BAD_REQUEST)
+        self.assertTrue("Only one of" in result)
+
+
     def test_calendar_query_partial_recurring(self):
         """
         Partial retrieval of recurring events.
@@ -343,7 +505,7 @@
 
 
     @inlineCallbacks
-    def calendar_query(self, query, got_xml):
+    def calendar_query(self, query, got_xml, expected_code=responsecode.MULTI_STATUS):
 
         principal = yield self.actualRoot.findPrincipalForAuthID("wsanchez")
         request = SimpleStoreRequest(self, "REPORT", "/calendars/users/wsanchez/calendar/", authPrincipal=principal)
@@ -352,9 +514,14 @@
 
         response = IResponse(response)
 
-        if response.code != responsecode.MULTI_STATUS:
+        if response.code != expected_code:
             self.fail("REPORT failed: %s" % (response.code,))
 
-        returnValue(
-            (yield davXMLFromStream(response.stream).addCallback(got_xml))
-        )
+        if got_xml is not None:
+            returnValue(
+                (yield davXMLFromStream(response.stream).addCallback(got_xml))
+            )
+        else:
+            returnValue(
+                (yield allDataFromStream(response.stream))
+            )

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2015-04-09 16:55:50 UTC (rev 14668)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2015-04-09 16:57:35 UTC (rev 14669)
@@ -50,7 +50,7 @@
     pyCalendarToSQLTimestamp, parseSQLDateToPyCalendar
 from twistedcaldav.ical import Component, InvalidICalendarDataError, Property
 from twistedcaldav.instance import InvalidOverriddenInstanceError
-from twistedcaldav.timezones import TimezoneException
+from twistedcaldav.timezones import TimezoneException, readVTZ, hasTZ
 
 from txdav.base.propertystore.base import PropertyName
 from txdav.caldav.datastore.query.builder import buildExpression
@@ -1368,26 +1368,88 @@
 
     def getTimezone(self):
         """
-        Return the VTIMEZONE data.
+        Return the VTIMEZONE data. L{self._timezone} will either be a full iCalendar component
+        (BEGIN:VCALENDAR ... END:VCALENDAR) or just the timezone id.
 
-        @return: the component (text)
+        @return: the component
         @rtype: L{Component}
         """
 
-        return Component.fromString(self._timezone) if self._timezone else None
+        if self._timezone:
+            if self._timezone.startswith("BEGIN:VCALENDAR"):
+                return Component.fromString(self._timezone)
+            else:
+                try:
+                    return Component(None, pycalendar=readVTZ(self._timezone))
+                except TimezoneException:
+                    return None
+        else:
+            return None
 
 
-    @inlineCallbacks
+    def getTimezoneID(self):
+        """
+        Return the VTIMEZONE TZID value. L{self._timezone} will either be a full iCalendar component
+        (BEGIN:VCALENDAR ... END:VCALENDAR) or just the timezone id.
+
+        @return: the timezone id
+        @rtype: L{str}
+        """
+
+        if self._timezone:
+            if self._timezone.startswith("BEGIN:VCALENDAR"):
+                tz = Component.fromString(self._timezone)
+                return list(tz.timezones())[0]
+            else:
+                return self._timezone
+        else:
+            return None
+
+
     def setTimezone(self, timezone):
         """
-        Set VTIMEZONE data.
+        Set VTIMEZONE data. If the TZID is in our database, then just store the TZID value, otherwise store the
+        entire iCalendar object as text.
 
         @param timezone: the component
         @type timezone: L{Component}
         """
 
-        self._timezone = str(timezone) if timezone else None
+        if timezone is not None:
+            try:
+                tzid = list(timezone.timezones())[0]
+                if hasTZ(tzid):
+                    self._timezone = tzid
+            except TimezoneException:
+                self._timezone = str(timezone)
+        else:
+            self._timezone = None
+        return self._setTimezoneValue()
 
+
+    def setTimezoneID(self, tzid):
+        """
+        Set VTIMEZONE via a TZID value. Make sure the TZID is in our TZ database.
+
+        @param tzid: the component
+        @type tzid: L{Component}
+
+        @raise L{TimezoneException} if tzid is not in our database
+        """
+
+        if tzid is not None:
+            hasTZ(tzid)
+            self._timezone = tzid
+        else:
+            self._timezone = None
+        return self._setTimezoneValue()
+
+
+    @inlineCallbacks
+    def _setTimezoneValue(self):
+        """
+        Store the current L{self._timezone} value in the database.
+        """
         cal = self._bindSchema
         yield Update(
             {

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2015-04-09 16:55:50 UTC (rev 14668)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2015-04-09 16:57:35 UTC (rev 14669)
@@ -30,7 +30,7 @@
 from twisted.python.filepath import FilePath
 from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks, returnValue, DeferredList, \
-    succeed
+    succeed, maybeDeferred
 from twisted.internet.task import deferLater, Clock
 from twisted.trial import unittest
 
@@ -40,7 +40,7 @@
 from twistedcaldav.dateops import datetimeMktime
 from twistedcaldav.ical import Component, normalize_iCalStr, diff_iCalStrs
 from twistedcaldav.instance import InvalidOverriddenInstanceError
-from twistedcaldav.timezones import TimezoneCache
+from twistedcaldav.timezones import TimezoneCache, readVTZ, TimezoneException
 
 from txdav.base.propertystore.base import PropertyName
 from txdav.caldav.datastore.query.filter import Filter
@@ -1844,41 +1844,74 @@
         Make sure a L{CalendarHomeChild}.setTimezone() works.
         """
 
-        tz1 = Component.fromString("""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//calendarserver.org//Zonal//EN
-BEGIN:VTIMEZONE
-TZID:Etc/GMT+1
-X-LIC-LOCATION:Etc/GMT+1
-BEGIN:STANDARD
-DTSTART:18000101T000000
-RDATE:18000101T000000
-TZNAME:GMT+1
-TZOFFSETFROM:-0100
-TZOFFSETTO:-0100
-END:STANDARD
-END:VTIMEZONE
-END:VCALENDAR
-""")
+        TimezoneCache.create()
+        self.addCleanup(TimezoneCache.clear)
 
+        tzid1 = "Etc/GMT+1"
+        tz1 = Component(None, pycalendar=readVTZ(tzid1))
+
         cal = yield self.calendarUnderTest()
         self.assertEqual(cal.getTimezone(), None)
+        self.assertEqual(cal.getTimezoneID(), None)
         yield cal.setTimezone(tz1)
         self.assertEqual(cal.getTimezone(), tz1)
+        self.assertEqual(cal.getTimezoneID(), tzid1)
         yield self.commit()
 
         cal = yield self.calendarUnderTest()
         self.assertEqual(cal.getTimezone(), tz1)
+        self.assertEqual(cal.getTimezoneID(), tzid1)
         yield cal.setTimezone(None)
         yield self.commit()
 
         cal = yield self.calendarUnderTest()
         self.assertEqual(cal.getTimezone(), None)
+        self.assertEqual(cal.getTimezoneID(), None)
         yield self.commit()
 
 
     @inlineCallbacks
+    def test_setTimezoneID(self):
+        """
+        Make sure a L{CalendarHomeChild}.setTimezoneID() works.
+        """
+
+        TimezoneCache.create()
+        self.addCleanup(TimezoneCache.clear)
+
+        tzid1 = "Etc/GMT+1"
+        tz1 = Component(None, pycalendar=readVTZ(tzid1))
+
+        cal = yield self.calendarUnderTest()
+        self.assertEqual(cal.getTimezone(), None)
+        self.assertEqual(cal.getTimezoneID(), None)
+        yield cal.setTimezoneID(tzid1)
+        self.assertEqual(cal.getTimezone(), tz1)
+        self.assertEqual(cal.getTimezoneID(), tzid1)
+        yield self.commit()
+
+        cal = yield self.calendarUnderTest()
+        self.assertEqual(cal.getTimezone(), tz1)
+        self.assertEqual(cal.getTimezoneID(), tzid1)
+        yield cal.setTimezoneID(None)
+        yield self.commit()
+
+        cal = yield self.calendarUnderTest()
+        self.assertEqual(cal.getTimezone(), None)
+        self.assertEqual(cal.getTimezoneID(), None)
+        yield self.commit()
+
+        # Invalid TZID
+        cal = yield self.calendarUnderTest()
+        self.assertEqual(cal.getTimezone(), None)
+        self.assertEqual(cal.getTimezoneID(), None)
+        yield self.failUnlessFailure(maybeDeferred(cal.setTimezoneID, "bogus"), TimezoneException)
+        self.assertEqual(cal.getTimezone(), None)
+        self.assertEqual(cal.getTimezoneID(), None)
+        yield self.commit()
+
+
+    @inlineCallbacks
     def test_calendarRevisionChangeConcurrency(self):
         """
         Test that two concurrent attempts to add resources in two separate

Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py	2015-04-09 16:55:50 UTC (rev 14668)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py	2015-04-09 16:57:35 UTC (rev 14669)
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
+from twistedcaldav.timezones import readVTZ, TimezoneCache
 
 """
 Tests for L{txdav.common.datastore.upgrade.sql.upgrade}.
@@ -40,58 +41,13 @@
     @inlineCallbacks
     def _calendarTimezoneUpgrade_setup(self):
 
-        tz1 = Component.fromString("""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//calendarserver.org//Zonal//EN
-BEGIN:VTIMEZONE
-TZID:Etc/GMT+1
-X-LIC-LOCATION:Etc/GMT+1
-BEGIN:STANDARD
-DTSTART:18000101T000000
-RDATE:18000101T000000
-TZNAME:GMT+1
-TZOFFSETFROM:-0100
-TZOFFSETTO:-0100
-END:STANDARD
-END:VTIMEZONE
-END:VCALENDAR
-""")
-        tz2 = Component.fromString("""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//calendarserver.org//Zonal//EN
-BEGIN:VTIMEZONE
-TZID:Etc/GMT+2
-X-LIC-LOCATION:Etc/GMT+2
-BEGIN:STANDARD
-DTSTART:18000101T000000
-RDATE:18000101T000000
-TZNAME:GMT+2
-TZOFFSETFROM:-0200
-TZOFFSETTO:-0200
-END:STANDARD
-END:VTIMEZONE
-END:VCALENDAR
-""")
-        tz3 = Component.fromString("""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//calendarserver.org//Zonal//EN
-BEGIN:VTIMEZONE
-TZID:Etc/GMT+3
-X-LIC-LOCATION:Etc/GMT+3
-BEGIN:STANDARD
-DTSTART:18000101T000000
-RDATE:18000101T000000
-TZNAME:GMT+3
-TZOFFSETFROM:-0300
-TZOFFSETTO:-0300
-END:STANDARD
-END:VTIMEZONE
-END:VCALENDAR
-""")
+        TimezoneCache.create()
+        self.addCleanup(TimezoneCache.clear)
 
+        tz1 = Component(None, pycalendar=readVTZ("Etc/GMT+1"))
+        tz2 = Component(None, pycalendar=readVTZ("Etc/GMT+2"))
+        tz3 = Component(None, pycalendar=readVTZ("Etc/GMT+3"))
+
         # Share user01 calendar with user03
         calendar = (yield self.calendarUnderTest(name="calendar_1", home="user01"))
         home3 = yield self.homeUnderTest(name="user03")
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150409/2d343889/attachment-0001.html>


More information about the calendarserver-changes mailing list