def removeOtherProperties(sqlStore): """ Remove the following properties: DAV:acl DAV:getcontenttype DAV:resource-id {urn:ietf:params:xml:ns:caldav}originator {urn:ietf:params:xml:ns:caldav}recipient {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set {http://calendarserver.org/ns/}getctag {http://twistedmatrix.com/xml_namespace/dav/private/}quota-used {http://twistedmatrix.com/xml_namespace/dav/}getcontentmd5 {http://twistedmatrix.com/xml_namespace/dav/}schedule-auto-respond """ logUpgradeStatus("Starting Calendar Remove Other Properties") sqlTxn = sqlStore.newTransaction(label="calendar_upgrade_from_4_to_5.removeOtherProperties") yield removeProperty(sqlTxn, PropertyName.fromElement(element.ACL)) yield removeProperty(sqlTxn, PropertyName.fromElement(element.GETContentType)) yield removeProperty(sqlTxn, PropertyName.fromElement(element.ResourceID)) yield removeProperty(sqlTxn, PropertyName(caldavxml.caldav_namespace, "originator")) yield removeProperty(sqlTxn, PropertyName(caldavxml.caldav_namespace, "recipient")) yield removeProperty(sqlTxn, PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet)) yield removeProperty(sqlTxn, PropertyName.fromElement(customxml.GETCTag)) yield removeProperty(sqlTxn, PropertyName.fromElement(TwistedQuotaUsedProperty)) yield removeProperty(sqlTxn, PropertyName.fromElement(TwistedGETContentMD5)) yield removeProperty(sqlTxn, PropertyName(element.twisted_dav_namespace, "schedule-auto-respond")) yield sqlTxn.commit() yield cleanPropertyStore() logUpgradeStatus("End Calendar Remove Other Properties")
def test_upgradeProperties(self): """ L{UpgradeToDatabaseService.startService} will do the upgrade, then start its dependent service by adding it to its service hierarchy. """ yield self.upgrader.stepWithResult(None) txn = self.sqlStore.newTransaction() self.addCleanup(txn.commit) # Want metadata preserved home = (yield txn.calendarHomeWithUID("home_defaults")) cal = (yield home.calendarWithName("calendar_1")) inbox = (yield home.calendarWithName("inbox")) # Supported components self.assertEqual(cal.getSupportedComponents(), "VEVENT") self.assertTrue(cal.properties().get(PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet)) is None) # Resource type removed self.assertTrue(cal.properties().get(PropertyName.fromElement(element.ResourceType)) is None) # Ctag removed self.assertTrue(cal.properties().get(PropertyName.fromElement(customxml.GETCTag)) is None) # Availability self.assertEquals(str(home.getAvailability()), str(self.av1)) self.assertTrue(inbox.properties().get(PropertyName.fromElement(customxml.CalendarAvailability)) is None) # Default calendar self.assertTrue(home.isDefaultCalendar(cal)) self.assertTrue(inbox.properties().get(PropertyName.fromElement(caldavxml.ScheduleDefaultCalendarURL)) is None)
def _removeOtherPropertiesUpgrade_check(self, full=True): # Test results for user in ( "user01", "user02", ): if full: calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) self.assertTrue( PropertyName.fromElement(element.ResourceID) not in calendar.properties()) version = yield self.transactionUnderTest( ).calendarserverValue("CALENDAR-DATAVERSION") self.assertEqual(int(version), 5) else: calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) self.assertTrue( PropertyName.fromElement(element.ResourceID) in calendar.properties()) version = yield self.transactionUnderTest( ).calendarserverValue("CALENDAR-DATAVERSION") self.assertEqual(int(version), 4)
def _removeOtherPropertiesUpgrade_setup(self): # Set dead property on calendar for user in ( "user01", "user02", ): calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) calendar.properties()[PropertyName.fromElement( element.ResourceID)] = element.ResourceID( element.HRef("urn:uuid:%s" % (user, ))) yield self.commit() for user in ( "user01", "user02", ): calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) self.assertTrue( PropertyName.fromElement(element.ResourceID) in calendar.properties()) yield self.transactionUnderTest().updateCalendarserverValue( "CALENDAR-DATAVERSION", "4") yield self.commit()
def test_toString(self): name = PropertyName("http://calendarserver.org/", "bleargh") self.assertEquals( name.toString(), "{http://calendarserver.org/}bleargh" )
def test_defaultCalendarUpgrade(self): # Set dead property on inbox 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=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 yield Update( {ch.DATAVERSION: 3}, Where=ch.RESOURCE_ID == home._resourceID, ).on(self.transactionUnderTest()) yield self.commit() # Trigger upgrade yield moveDefaultCalendarProperties(self._sqlCalendarStore) # Test results 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())
def _calendarTranspUpgrade_check(self, changed_users, unchanged_users): # Test results for user in changed_users: home = (yield self.homeUnderTest(name=user)) version = (yield home.dataVersion()) self.assertEqual(version, 4) calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) if user == "user01": self.assertTrue(calendar.isUsedForFreeBusy()) else: self.assertFalse(calendar.isUsedForFreeBusy()) self.assertTrue(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp) not in calendar.properties()) inbox = (yield self.calendarUnderTest(name="inbox", home=user)) self.assertTrue(PropertyName.fromElement(CalendarFreeBusySet) not in inbox.properties()) for user in unchanged_users: home = (yield self.homeUnderTest(name=user)) version = (yield home.dataVersion()) self.assertEqual(version, 3) calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) if user == "user01": self.assertFalse(calendar.isUsedForFreeBusy()) else: self.assertFalse(calendar.isUsedForFreeBusy()) self.assertTrue(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp) in calendar.properties()) inbox = (yield self.calendarUnderTest(name="inbox", home=user)) self.assertTrue(PropertyName.fromElement(CalendarFreeBusySet) in inbox.properties())
def _calendarTimezoneUpgrade_check(self, changed_users, unchanged_users, user_details): # Test results for user, calname, tz in user_details: if user in changed_users: home = (yield self.homeUnderTest(name=user)) version = (yield home.dataVersion()) self.assertEqual(version, 5) calendar = (yield self.calendarUnderTest(name=calname, home=user)) self.assertEqual(calendar.getTimezone(), tz) self.assertTrue( PropertyName.fromElement(caldavxml.CalendarTimeZone) not in calendar.properties()) else: home = (yield self.homeUnderTest(name=user)) version = (yield home.dataVersion()) self.assertEqual(version, 4) calendar = (yield self.calendarUnderTest(name=calname, home=user)) self.assertEqual(calendar.getTimezone(), None) if tz: self.assertTrue( PropertyName.fromElement(caldavxml.CalendarTimeZone) in calendar.properties()) else: self.assertTrue( PropertyName.fromElement(caldavxml.CalendarTimeZone) not in calendar.properties())
def _processDefaultCalendarProperty(home, propname): """ Move the specified property value to the matching CALENDAR_HOME_METADATA table column. """ inbox = (yield home.calendarWithName("inbox")) if inbox is not None: prop = inbox.properties().get(PropertyName.fromElement(propname)) if prop is not None: defaultCalendar = str(prop.children[0]) parts = defaultCalendar.split("/") if len(parts) == 5: calendarName = parts[-1] calendarHomeUID = parts[-2] if calendarHomeUID == home.uid(): calendar = (yield home.calendarWithName(calendarName)) if calendar is not None: try: yield home.setDefaultCalendar( calendar, tasks=(propname == customxml.ScheduleDefaultTasksURL) ) except InvalidDefaultCalendar: # Ignore these - the server will recover pass del inbox.properties()[PropertyName.fromElement(propname)]
def _processDefaultAlarmProperty(home, 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. """ # Check the home first prop = home.properties().get(PropertyName.fromElement(propname)) if prop is not None: alarm = str(prop.children[0]) if prop.children else None yield home.setDefaultAlarm(alarm, vevent, timed) del home.properties()[PropertyName.fromElement(propname)] # Now each child calendars = (yield home.loadChildren()) for calendar in calendars: if calendar.isInbox(): continue prop = calendar.properties().get(PropertyName.fromElement(propname)) if prop is not None: alarm = str(prop.children[0] ) if prop.children and prop.children[0] else "empty" yield calendar.setDefaultAlarm(alarm, vevent, timed) del calendar.properties()[PropertyName.fromElement(propname)]
def _defaultAlarmUpgrade_check(self, changed_users, unchanged_users, detailshome, detailscalendar, detailsshared, shared_name): # Check each type of collection home = yield self.homeUnderTest(name="user01") version = (yield home.dataVersion()) self.assertEqual(version, 4) 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 calendar.properties()) if "user02" in changed_users: home = (yield self.homeUnderTest(name="user02")) version = (yield home.dataVersion()) self.assertEqual(version, 4) 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 shared.properties()) else: home = (yield self.homeUnderTest(name="user02")) version = (yield home.dataVersion()) self.assertEqual(version, 3) 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, None) self.assertTrue(PropertyName.fromElement(prop) in shared.properties())
def _processDefaultCalendarProperty(home, propname): """ Move the specified property value to the matching CALENDAR_HOME_METADATA table column. """ inbox = (yield home.calendarWithName("inbox")) if inbox is not None: prop = inbox.properties().get(PropertyName.fromElement(propname)) if prop is not None: defaultCalendar = str(prop.children[0]) parts = defaultCalendar.split("/") if len(parts) == 5: calendarName = parts[-1] calendarHomeUID = parts[-2] if calendarHomeUID == home.uid(): calendar = (yield home.calendarWithName(calendarName)) if calendar is not None: try: if propname == caldavxml.ScheduleDefaultCalendarURL: ctype = "VEVENT" elif propname == customxml.ScheduleDefaultTasksURL: ctype = "VTODO" yield home.setDefaultCalendar(calendar, ctype) except InvalidDefaultCalendar: # Ignore these - the server will recover pass del inbox.properties()[PropertyName.fromElement(propname)]
def initPropertyStore(self, props): # Setup peruser special properties props.setSpecialProperties( (PropertyName.fromElement(carddavxml.AddressBookDescription), ), (PropertyName.fromElement(customxml.GETCTag), ), (), )
def _defaultCalendarUpgrade_check(self, changed_users, unchanged_users): # Test results for user in changed_users: home = (yield self.homeUnderTest(name=user)) version = (yield home.dataVersion()) self.assertEqual(version, 4) 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()) for user in unchanged_users: home = (yield self.homeUnderTest(name=user)) version = (yield home.dataVersion()) self.assertEqual(version, 3) calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) self.assertFalse(home.isDefaultCalendar(calendar)) inbox = (yield self.calendarUnderTest(name="inbox", home=user)) self.assertTrue( PropertyName.fromElement(ScheduleDefaultCalendarURL) in inbox.properties())
def _resourceTypeUpgrade_setup(self): # Set dead property on calendar 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() 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.transactionUnderTest().updateCalendarserverValue( "CALENDAR-DATAVERSION", "3") yield self.commit()
def initPropertyStore(self, props): # Setup peruser special properties props.setSpecialProperties( (PropertyName.fromElement(carddavxml.AddressBookDescription),), (PropertyName.fromElement(customxml.GETCTag),), (), )
def test_migrateHomeFromFile(self): """ L{migrateHome} will migrate an L{IAddressbookHome} provider from one backend to another; in this specific case, from the file-based backend to the SQL-based backend. """ fromHome = yield self.fileTransaction().addressbookHomeWithUID("home1") # Populate an arbitrary / unused dead properties so there's something # to verify against. key = PropertyName.fromElement(GETContentLanguage) fromHome.properties()[key] = GETContentLanguage("C") (yield fromHome.addressbookWithName("addressbook")).properties()[ key] = ( GETContentLanguage("pig-latin") ) (yield fromHome.addressbookWithName("addressbook")).properties()[ PropertyName.fromElement(ResourceType)] = ( carddavxml.ResourceType.addressbook ) toHome = yield self.transactionUnderTest().addressbookHomeWithUID( "new-home", create=True ) yield migrateHome(fromHome, toHome, lambda x: x.component()) toAddressbooks = yield toHome.addressbooks() self.assertEquals(set([c.name() for c in toAddressbooks]), set([k for k in self.requirements['home1'].keys() if self.requirements['home1'][k] is not None])) fromAddressbooks = yield fromHome.addressbooks() for c in fromAddressbooks: self.assertPropertiesSimilar( c, (yield toHome.addressbookWithName(c.name())), ) self.assertPropertiesSimilar(fromHome, toHome,)
def _calendarAvailabilityUpgrade_check(self, changed_users, unchanged_users, user_details): # Test results for user, av in user_details: if user in changed_users: home = (yield self.homeUnderTest(name=user)) version = (yield home.dataVersion()) self.assertEqual(version, 5) calendar = (yield self.calendarUnderTest(name="inbox", home=user)) self.assertEqual(home.getAvailability(), av) self.assertTrue( PropertyName.fromElement(customxml.CalendarAvailability) not in calendar.properties()) else: home = (yield self.homeUnderTest(name=user)) version = (yield home.dataVersion()) self.assertEqual(version, 4) calendar = (yield self.calendarUnderTest(name="inbox", home=user)) self.assertEqual(home.getAvailability(), None) if av: self.assertTrue( PropertyName.fromElement(customxml.CalendarAvailability ) in calendar.properties()) else: self.assertTrue( PropertyName.fromElement( customxml.CalendarAvailability) not in calendar.properties())
def setUp(self): """ Set up two stores to migrate between. """ # Add some files to the file store. self.filesPath = CachingFilePath(self.mktemp()) self.filesPath.createDirectory() fileStore = self.fileStore = CommonDataStore( self.filesPath, {"push": StubNotifierFactory()}, TestStoreDirectoryService(), True, True ) self.sqlStore = yield theStoreBuilder.buildStore( self, StubNotifierFactory() ) self.upgrader = UpgradeToDatabaseStep(self.fileStore, self.sqlStore) requirements = CommonTests.requirements extras = deriveValue(self, "extraRequirements", lambda t: {}) requirements = self.mergeRequirements(requirements, extras) yield populateCalendarsFrom(requirements, fileStore) md5s = CommonTests.md5s yield resetCalendarMD5s(md5s, fileStore) self.filesPath.child("calendars").child( "__uids__").child("ho").child("me").child("home1").child( ".some-extra-data").setContent("some extra data") requirements = ABCommonTests.requirements yield populateAddressBooksFrom(requirements, fileStore) md5s = ABCommonTests.md5s yield resetAddressBookMD5s(md5s, fileStore) self.filesPath.child("addressbooks").child( "__uids__").child("ho").child("me").child("home1").child( ".some-extra-data").setContent("some extra data") # Add some properties we want to check get migrated over txn = self.fileStore.newTransaction() home = yield txn.calendarHomeWithUID("home_defaults") cal = yield home.calendarWithName("calendar_1") props = cal.properties() props[PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet)] = caldavxml.SupportedCalendarComponentSet( caldavxml.CalendarComponent(name="VEVENT"), caldavxml.CalendarComponent(name="VTODO"), ) props[PropertyName.fromElement(element.ResourceType)] = element.ResourceType( element.Collection(), caldavxml.Calendar(), ) props[PropertyName.fromElement(customxml.GETCTag)] = customxml.GETCTag.fromString("foobar") inbox = yield home.calendarWithName("inbox") props = inbox.properties() props[PropertyName.fromElement(customxml.CalendarAvailability)] = customxml.CalendarAvailability.fromString(str(self.av1)) props[PropertyName.fromElement(caldavxml.ScheduleDefaultCalendarURL)] = caldavxml.ScheduleDefaultCalendarURL( element.HRef.fromString("/calendars/__uids__/home_defaults/calendar_1"), ) yield txn.commit()
def _calendarTimezoneUpgrade_setup(self): 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") shared_name = yield calendar.shareWith(home3, _BIND_MODE_WRITE) user_details = ( ("user01", "calendar_1", tz1), ("user02", "calendar_1", tz2), ("user03", "calendar_1", None), ("user03", shared_name, tz3), ) # Set dead properties on calendars for user, calname, tz in user_details: calendar = (yield self.calendarUnderTest(name=calname, home=user)) if tz: calendar.properties()[PropertyName.fromElement(caldavxml.CalendarTimeZone)] = caldavxml.CalendarTimeZone.fromString(str(tz)) # Force data version to previous home = (yield self.homeUnderTest(name=user)) ch = home._homeSchema yield Update( {ch.DATAVERSION: 4}, Where=ch.RESOURCE_ID == home._resourceID, ).on(self.transactionUnderTest()) yield self.commit() for user, calname, tz in user_details: calendar = (yield self.calendarUnderTest(name=calname, home=user)) self.assertEqual(calendar.getTimezone(), None) self.assertEqual(PropertyName.fromElement(caldavxml.CalendarTimeZone) in calendar.properties(), tz is not None) yield self.commit() # Create "fake" entry for non-existent share txn = self.transactionUnderTest() calendar = (yield self.calendarUnderTest(name="calendar_1", home="user01")) rp = schema.RESOURCE_PROPERTY yield Insert( { rp.RESOURCE_ID: calendar._resourceID, rp.NAME: PropertyName.fromElement(caldavxml.CalendarTimeZone).toString(), rp.VALUE: caldavxml.CalendarTimeZone.fromString(str(tz3)).toxml(), rp.VIEWER_UID: "user04", } ).on(txn) yield self.commit() returnValue(user_details)
def _set_scheduleEtags(self, value): if value: etags = [davxml.GETETag.fromString(etag) for etag in value] self.properties()[PropertyName.fromElement(customxml.TwistedScheduleMatchETags)] = customxml.TwistedScheduleMatchETags(*etags) else: try: del self.properties()[PropertyName.fromElement(customxml.TwistedScheduleMatchETags)] except KeyError: pass
def test_copy(self): tempDir = FilePath(self.mktemp()) tempDir.makedirs() tempFile1 = tempDir.child("test1") tempFile1.touch() tempFile2 = tempDir.child("test2") tempFile2.touch() # Existing store store1_user1 = PropertyStore("user01", lambda: tempFile1) store1_user2 = PropertyStore("user01", lambda: tempFile1) store1_user2._setPerUserUID("user02") # New store store2_user1 = PropertyStore("user01", lambda: tempFile2) store2_user2 = PropertyStore("user01", lambda: tempFile2) store2_user2._setPerUserUID("user02") # Populate current store with data class DummyProperty1(WebDAVTextElement): namespace = "http://calendarserver.org/ns/" name = "dummy1" class DummyProperty2(WebDAVTextElement): namespace = "http://calendarserver.org/ns/" name = "dummy2" class DummyProperty3(WebDAVTextElement): namespace = "http://calendarserver.org/ns/" name = "dummy3" props_user1 = ( DummyProperty1.fromString("value1-user1"), DummyProperty2.fromString("value2-user1"), ) props_user2 = ( DummyProperty1.fromString("value1-user2"), DummyProperty3.fromString("value3-user2"), ) for prop in props_user1: store1_user1[PropertyName.fromElement(prop)] = prop for prop in props_user2: store1_user2[PropertyName.fromElement(prop)] = prop store1_user1.flush() store1_user2.flush() # Do copy and check results store2_user1.copyAllProperties(store1_user1) store2_user1.flush() self.assertEqual(store1_user1.attrs.items(), store2_user1.attrs.items()) self.assertEqual(store1_user2.attrs.items(), store2_user2.attrs.items())
def setDisplayName(self, name): if name is None: del self.properties()[PropertyName.fromElement(DisplayName)] else: if not isinstance(name, unicode): raise ValueError("Display name must be unicode: %r" % (name, )) self.properties()[PropertyName.fromElement( DisplayName)] = DisplayName.fromString(name) return None
def setDisplayName(self, name): if name is None: del self.properties()[PropertyName.fromElement(DisplayName)] else: if not isinstance(name, unicode): raise ValueError("Display name must be unicode: %r" % (name,)) self.properties()[ PropertyName.fromElement(DisplayName) ] = DisplayName.fromString(name) return None
def moveCalendarAvailabilityProperties(home): """ Need to move all the CS:calendar-availability properties in the RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting the new value from the XML property. """ inbox = (yield home.calendarWithName("inbox")) if inbox is not None: prop = inbox.properties().get(PropertyName.fromElement(customxml.CalendarAvailability)) if prop is not None: yield home.setAvailability(prop.calendar()) del inbox.properties()[PropertyName.fromElement(customxml.CalendarAvailability)]
def initPropertyStore(self, props): # Setup peruser special properties props.setSpecialProperties( ( PropertyName.fromElement(caldavxml.CalendarDescription), PropertyName.fromElement(caldavxml.CalendarTimeZone), ), ( PropertyName.fromElement(customxml.GETCTag), PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet), ), )
def test_ImportComponentNoScheduling(self): component = Component.allFromString(DATA_NO_SCHEDULING) yield importCollectionComponent(self.store, component) txn = self.store.newTransaction() home = yield txn.calendarHomeWithUID("user01") collection = yield home.childWithName("calendar") # Verify properties have been set collectionProperties = collection.properties() for element, value in ( (davxml.DisplayName, "Sample Import Calendar"), (customxml.CalendarColor, "#0E61B9FF"), ): self.assertEquals( value, collectionProperties[PropertyName.fromElement(element)] ) # Verify child objects objects = yield collection.listObjectResources() self.assertEquals(len(objects), 2) yield txn.commit() # Reimport different component into same collection component = Component.allFromString(DATA_NO_SCHEDULING_REIMPORT) yield importCollectionComponent(self.store, component) txn = self.store.newTransaction() home = yield txn.calendarHomeWithUID("user01") collection = yield home.childWithName("calendar") # Verify properties have been changed collectionProperties = collection.properties() for element, value in ( (davxml.DisplayName, "Sample Import Calendar Reimported"), (customxml.CalendarColor, "#FFFFFFFF"), ): self.assertEquals( value, collectionProperties[PropertyName.fromElement(element)] ) # Verify child objects (should be 3 now) objects = yield collection.listObjectResources() self.assertEquals(len(objects), 3) yield txn.commit()
def test_copy(self): tempDir = FilePath(self.mktemp()) tempDir.makedirs() tempFile1 = tempDir.child("test1") tempFile1.touch() tempFile2 = tempDir.child("test2") tempFile2.touch() # Existing store store1_user1 = PropertyStore("user01", lambda : tempFile1) store1_user2 = PropertyStore("user01", lambda : tempFile1) store1_user2._setPerUserUID("user02") # New store store2_user1 = PropertyStore("user01", lambda : tempFile2) store2_user2 = PropertyStore("user01", lambda : tempFile2) store2_user2._setPerUserUID("user02") # Populate current store with data class DummyProperty1(WebDAVTextElement): namespace = "http://calendarserver.org/ns/" name = "dummy1" class DummyProperty2(WebDAVTextElement): namespace = "http://calendarserver.org/ns/" name = "dummy2" class DummyProperty3(WebDAVTextElement): namespace = "http://calendarserver.org/ns/" name = "dummy3" props_user1 = ( DummyProperty1.fromString("value1-user1"), DummyProperty2.fromString("value2-user1"), ) props_user2 = ( DummyProperty1.fromString("value1-user2"), DummyProperty3.fromString("value3-user2"), ) for prop in props_user1: store1_user1[PropertyName.fromElement(prop)] = prop for prop in props_user2: store1_user2[PropertyName.fromElement(prop)] = prop store1_user1.flush() store1_user2.flush() # Do copy and check results store2_user1.copyAllProperties(store1_user1) store2_user1.flush() self.assertEqual(store1_user1.attrs.items(), store2_user1.attrs.items()) self.assertEqual(store1_user2.attrs.items(), store2_user2.attrs.items())
def initPropertyStore(self, props): # Setup peruser special properties props.setSpecialProperties( ( PropertyName.fromElement(caldavxml.CalendarDescription), PropertyName.fromElement(caldavxml.CalendarTimeZone), ), ( PropertyName.fromElement(customxml.GETCTag), PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet), ), (), )
def _removeOtherPropertiesUpgrade_check(self, full=True): # Test results for user in ("user01", "user02",): if full: calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) self.assertTrue(PropertyName.fromElement(element.ResourceID) not in calendar.properties()) version = yield self.transactionUnderTest().calendarserverValue("CALENDAR-DATAVERSION") self.assertEqual(int(version), 5) else: calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) self.assertTrue(PropertyName.fromElement(element.ResourceID) in calendar.properties()) version = yield self.transactionUnderTest().calendarserverValue("CALENDAR-DATAVERSION") self.assertEqual(int(version), 4)
def _resourceTypeUpgrade_setup(self): # Set dead property on calendar 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() 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.transactionUnderTest().updateCalendarserverValue("CALENDAR-DATAVERSION", "3") yield self.commit()
def _removeOtherPropertiesUpgrade_setup(self): # Set dead property on calendar for user in ("user01", "user02",): calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) calendar.properties()[PropertyName.fromElement(element.ResourceID)] = element.ResourceID(element.HRef("urn:uuid:%s" % (user,))) yield self.commit() for user in ("user01", "user02",): calendar = (yield self.calendarUnderTest(name="calendar_1", home=user)) self.assertTrue(PropertyName.fromElement(element.ResourceID) in calendar.properties()) yield self.transactionUnderTest().updateCalendarserverValue("CALENDAR-DATAVERSION", "4") yield self.commit()
def setData(self, uid, notificationtype, notificationdata, inserting=False): rname = uid + ".xml" self._parentCollection.retrieveOldIndex().addOrUpdateRecord( NotificationRecord(uid, rname, notificationtype) ) self._notificationdata = notificationdata notificationtext = json.dumps(self._notificationdata) md5 = hashlib.md5(notificationtext).hexdigest() def do(): backup = None if self._path.exists(): backup = hidden(self._path.temporarySibling()) self._path.moveTo(backup) fh = self._path.open("w") try: # FIXME: concurrency problem; if this write is interrupted # halfway through, the underlying file will be corrupt. fh.write(notificationtext) finally: fh.close() def undo(): if backup: backup.moveTo(self._path) else: self._path.remove() return undo self._transaction.addOperation(do, "set notification data %r" % (self.name(),)) # Mark all properties as dirty, so they will be re-added to the # temporary file when the main file is deleted. NOTE: if there were a # temporary file and a rename() as there should be, this should really # happen after the write but before the rename. self.properties().update(self.properties()) props = self.properties() props[PropertyName(*GETContentType.qname())] = GETContentType.fromString(generateContentType(MimeType("text", "xml", params={"charset": "utf-8"}))) props[PropertyName.fromElement(NotificationType)] = NotificationType(json.dumps(notificationtype)) props[PropertyName.fromElement(TwistedGETContentMD5)] = TwistedGETContentMD5.fromString(md5) # FIXME: the property store's flush() method may already have been # added to the transaction, but we need to add it again to make sure it # happens _after_ the new file has been written. we may end up doing # the work multiple times, and external callers to property- # manipulation methods won't work. self._transaction.addOperation(self.properties().flush, "post-update property flush")
def test_ImportComponentNoScheduling(self): component = Component.allFromString(DATA_NO_SCHEDULING) yield importCollectionComponent(self.store, component) txn = self.store.newTransaction() home = yield txn.calendarHomeWithUID("user01") collection = yield home.childWithName("calendar") # Verify properties have been set collectionProperties = collection.properties() for element, value in ( (davxml.DisplayName, "Sample Import Calendar"), (customxml.CalendarColor, "#0E61B9FF"), ): self.assertEquals( value, collectionProperties[PropertyName.fromElement(element)]) # Verify child objects objects = yield collection.listObjectResources() self.assertEquals(len(objects), 2) yield txn.commit() # Reimport different component into same collection component = Component.allFromString(DATA_NO_SCHEDULING_REIMPORT) yield importCollectionComponent(self.store, component) txn = self.store.newTransaction() home = yield txn.calendarHomeWithUID("user01") collection = yield home.childWithName("calendar") # Verify properties have been changed collectionProperties = collection.properties() for element, value in ( (davxml.DisplayName, "Sample Import Calendar Reimported"), (customxml.CalendarColor, "#FFFFFFFF"), ): self.assertEquals( value, collectionProperties[PropertyName.fromElement(element)]) # Verify child objects (should be 3 now) objects = yield collection.listObjectResources() self.assertEquals(len(objects), 3) yield txn.commit()
def test_migrateMergeCalendars(self): """ Migrating a home with a conflicting (non-default) calendar in merge mode will cause the properties on the conflicting calendar to be overridden by the new calendar of the same name, and calendar objects to be copied over. """ yield self.createConflicted() from txdav.base.propertystore.base import PropertyName from txdav.xml import element as davxml class StubConflictingElement(davxml.WebDAVTextElement): namespace = "http://example.com/ns/stub-conflict" name = "conflict" beforeProp = StubConflictingElement.fromString("before") afterProp = StubConflictingElement.fromString("after") conflictPropName = PropertyName.fromElement(beforeProp) txn = self.transactionUnderTest() conflict1 = yield txn.calendarHomeWithUID("conflict1") conflict2 = yield txn.calendarHomeWithUID("conflict2") cal1 = yield conflict1.calendarWithName("conflicted") cal2 = yield conflict2.calendarWithName("conflicted") p1 = cal1.properties() p2 = cal2.properties() p1[conflictPropName] = afterProp p2[conflictPropName] = beforeProp yield migrateHome(conflict1, conflict2, merge=True) self.assertEquals(p2[conflictPropName].children[0].data, "after") obj1 = yield cal2.calendarObjectWithName("1.ics") obj2 = yield cal2.calendarObjectWithName("2.ics") # just a really cursory check to make sure they're really there. self.assertEquals(obj1.uid(), "uid1") self.assertEquals(obj2.uid(), "uid2")
def moveCalendarTimezoneProperties(sqlStore): """ Need to move all the CalDAV:calendar-timezone properties in the RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting the new value from the XML property. """ cb = schema.CALENDAR_BIND rp = schema.RESOURCE_PROPERTY try: calendars_for_id = {} while True: sqlTxn = sqlStore.newTransaction() rows = (yield rowsForProperty(sqlTxn, caldavxml.CalendarTimeZone, with_uid=True, batch=BATCH_SIZE)) if len(rows) == 0: yield sqlTxn.commit() break delete_ids = [] for calendar_rid, value, viewer in rows: delete_ids.append(calendar_rid) if calendar_rid not in calendars_for_id: ids = yield Select( [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ], From=cb, Where=cb.CALENDAR_RESOURCE_ID == calendar_rid, ).on(sqlTxn) calendars_for_id[calendar_rid] = ids if viewer: calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer)) else: calendarHome = None for row in calendars_for_id[calendar_rid]: home_id, bind_mode = row if bind_mode == _BIND_MODE_OWN: calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id)) break if calendarHome is not None: prop = WebDAVDocument.fromString(value).root_element calendar = (yield calendarHome.childWithID(calendar_rid)) if calendar is not None: yield calendar.setTimezone(prop.calendar()) # Always delete the rows 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(caldavxml.CalendarTimeZone).toString()), ).on(sqlTxn, ids=delete_ids) yield sqlTxn.commit() yield cleanPropertyStore() except RuntimeError: f = Failure() yield sqlTxn.abort() f.raiseException()
def getCollectionPropertyValue(collection, element): collectionProperties = collection.properties() name = PropertyName.fromElement(element) if name in collectionProperties: return str(collectionProperties[name]) else: return None
def _defaultCalendarUpgrade_setup(self): # Set dead property on inbox 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=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 yield Update( { ch.DATAVERSION: 3 }, Where=ch.RESOURCE_ID == home._resourceID, ).on(self.transactionUnderTest()) yield self.commit()
def _invalidDefaultCalendarUpgrade_setup(self): # Set dead property on inbox for user in ("user01", "user02"): inbox = (yield self.calendarUnderTest(name="inbox", home=user)) inbox.properties()[PropertyName.fromElement(ScheduleDefaultCalendarURL)] = ScheduleDefaultCalendarURL( HRef.fromString("/calendars/__uids__/%s/tasks_1" % (user,)) ) # 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() ) # Create tasks only calendar tasks = (yield home.createCalendarWithName("tasks_1")) yield tasks.setSupportedComponents("VTODO") # Force data version to previous ch = home._homeSchema yield Update({ch.DATAVERSION: 3}, Where=ch.RESOURCE_ID == home._resourceID).on(self.transactionUnderTest()) yield self.commit()
def moveCalendarTimezoneProperties(home): """ Need to move all the CalDAV:calendar-timezone properties in the RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting the new value from the XML property. """ # Iterate over each calendar (both owned and shared) calendars = (yield home.loadChildren()) for calendar in calendars: if calendar.isInbox(): continue prop = calendar.properties().get(PropertyName.fromElement(caldavxml.CalendarTimeZone)) if prop is not None: yield calendar.setTimezone(prop.calendar()) del calendar.properties()[PropertyName.fromElement(caldavxml.CalendarTimeZone)]
def displayName(self): name = self.properties().get(PropertyName.fromElement(DisplayName), None) if name is None: return None else: return name.toString()
def readProperty(self, property, request): if type(property) is tuple: qname = property else: qname = property.qname() if qname == (caldav_namespace, "calendar-free-busy-set"): # Always return at least an empty list if not self.hasDeadProperty(property): top = self.parent.url() values = [] for cal in self.parent._newStoreHome.calendars(): prop = cal.properties().get(PropertyName.fromString(ScheduleCalendarTransp.sname())) if prop == ScheduleCalendarTransp(Opaque()): values.append(HRef(joinURL(top, cal.name()))) returnValue(CalendarFreeBusySet(*values)) elif qname == (caldav_namespace, "schedule-default-calendar-URL"): # Must have a valid default try: defaultCalendarProperty = self.readDeadProperty(property) except HTTPError: defaultCalendarProperty = None if defaultCalendarProperty and len(defaultCalendarProperty.children) == 1: defaultCalendar = str(defaultCalendarProperty.children[0]) cal = (yield request.locateResource(str(defaultCalendar))) if cal is not None and cal.exists() and isCalendarCollectionResource(cal): returnValue(defaultCalendarProperty) # Default is not valid - we have to try to pick one defaultCalendarProperty = (yield self.pickNewDefaultCalendar(request)) returnValue(defaultCalendarProperty) result = (yield super(ScheduleInboxResource, self).readProperty(property, request)) returnValue(result)
def createdHome(self): # Check whether components type must be separate if config.RestrictCalendarsToOneComponentType: for name in ical.allowedStoreComponents: cal = self.createCalendarWithName(self._componentCalendarName[name]) cal.setSupportedComponents(name) props = cal.properties() if name not in ("VEVENT", "VAVAILABILITY",): props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(Transparent()) else: props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(Opaque()) else: cal = self.createCalendarWithName("calendar") self.createCalendarWithName("inbox")
def countProperty(txn, propelement): pname = PropertyName.fromElement(propelement) rp = schema.RESOURCE_PROPERTY count = (yield Select([Count(rp.RESOURCE_ID)], From=rp, Where=rp.NAME == pname.toString()).on(txn))[0][0] returnValue(count)
def _set_accessMode(self, value): pname = PropertyName.fromElement( customxml.TwistedCalendarAccessProperty) if value: self.properties()[pname] = customxml.TwistedCalendarAccessProperty( value) elif pname in self.properties(): del self.properties()[pname]
def removeProperty(txn, propelement): pname = PropertyName.fromElement(propelement) rp = schema.RESOURCE_PROPERTY yield Delete( From=rp, Where=rp.NAME == pname.toString(), ).on(txn)
def syncToken(self): try: urnuuid = str(self.properties()[PropertyName.fromElement(ResourceID)].children[0]) except KeyError: urnuuid = uuid.uuid4().urn self.properties()[PropertyName(*ResourceID.qname())] = ResourceID(HRef.fromString(urnuuid)) return succeed("%s_%s" % (urnuuid[9:], self.retrieveOldIndex().lastRevision()))
def _set_hasPrivateComment(self, value): pname = PropertyName.fromElement( customxml.TwistedCalendarHasPrivateCommentsProperty) if value: self.properties( )[pname] = customxml.TwistedCalendarHasPrivateCommentsProperty() elif pname in self.properties(): del self.properties()[pname]
def _keys_uid(self, uid): rows = self._txn.execSQL( "select NAME from RESOURCE_PROPERTY where " "VIEWER_UID = %s and RESOURCE_ID = %s", [uid, self._resourceID] ) for row in rows: yield PropertyName.fromString(row[0])
def displayNameForCollection(collection): try: displayName = collection.properties()[PropertyName.fromElement( element.DisplayName)] displayName = displayName.toString() except: displayName = collection.name() return displayName