[CalendarServer-changes] [9944] CalendarServer/trunk/txdav

source_changes at macosforge.org source_changes at macosforge.org
Tue Oct 16 09:12:09 PDT 2012


Revision: 9944
          http://trac.calendarserver.org//changeset/9944
Author:   cdaboo at apple.com
Date:     2012-10-16 09:12:08 -0700 (Tue, 16 Oct 2012)
Log Message:
-----------
Implement apis to list all homes of a given type.

Modified Paths:
--------------
    CalendarServer/trunk/txdav/caldav/datastore/test/common.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py
    CalendarServer/trunk/txdav/caldav/icalendarstore.py
    CalendarServer/trunk/txdav/carddav/datastore/test/common.py
    CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py
    CalendarServer/trunk/txdav/carddav/iaddressbookstore.py
    CalendarServer/trunk/txdav/common/datastore/file.py
    CalendarServer/trunk/txdav/common/datastore/sql.py

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/common.py	2012-10-16 00:34:32 UTC (rev 9943)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/common.py	2012-10-16 16:12:08 UTC (rev 9944)
@@ -21,7 +21,7 @@
 
 from StringIO import StringIO
 
-from twisted.internet.defer import Deferred, inlineCallbacks, returnValue,\
+from twisted.internet.defer import Deferred, inlineCallbacks, returnValue, \
     maybeDeferred
 from twisted.internet.protocol import Protocol
 from twisted.python import hashlib
@@ -427,7 +427,7 @@
     def test_notificationSyncToken(self):
         """
         L{ICalendar.resourceNamesSinceToken} will return the names of calendar
-        objects changed or deleted since 
+        objects changed or deleted since
         """
         txn = self.transactionUnderTest()
         coll = yield txn.notificationsWithUID("home1")
@@ -593,6 +593,23 @@
 
 
     @inlineCallbacks
+    def test_calendarHomes(self):
+        """
+        Finding all existing calendar homes.
+        """
+        calendarHomes = (yield self.transactionUnderTest().calendarHomes())
+        self.assertEquals(
+            [home.name() for home in calendarHomes],
+            [
+                "home1",
+                "home_no_splits",
+                "home_splits",
+                "home_splits_shared",
+            ]
+        )
+
+
+    @inlineCallbacks
     def test_displayNameNone(self):
         """
         L{ICalendarHome.calendarWithName} returns C{None} for calendars which
@@ -684,6 +701,7 @@
             self.assertProvides(ICalendar, calendar)
             self.assertEquals(calendar.name(), name)
 
+
     @inlineCallbacks
     def test_calendarWithName_exists(self):
         """
@@ -780,7 +798,7 @@
         """
         L{ICalendarHome.createCalendarWithName} raises
         L{CalendarAlreadyExistsError} when the name conflicts with an already-
-        existing 
+        existing
         """
         home = yield self.homeUnderTest()
         for name in home1_calendarNames:
@@ -850,23 +868,25 @@
         result = yield maybeDeferred(calendar.getSupportedComponents)
         self.assertEquals(result, None)
 
+
     @inlineCallbacks
     def test_countComponentTypes(self):
         """
         Test Calendar._countComponentTypes to make sure correct counts are returned.
         """
-        
+
         tests = (
             ("calendar_1", (("VEVENT", 3),)),
             ("calendar_2", (("VEVENT", 3), ("VTODO", 2))),
         )
-        
+
         for calname, results in tests:
             testalendar = yield (yield self.transactionUnderTest().calendarHomeWithUID(
                 "home_splits")).calendarWithName(calname)
             result = yield maybeDeferred(testalendar._countComponentTypes)
             self.assertEquals(result, results)
 
+
     @inlineCallbacks
     def test_calendarObjects(self):
         """
@@ -991,6 +1011,7 @@
             ]
         )
 
+
     @inlineCallbacks
     def test_removeCalendarObjectWithName_exists(self):
         """
@@ -1088,6 +1109,7 @@
         invitedCals = yield cal.asShared()
         self.assertEqual(len(invitedCals), 0)
 
+
     @inlineCallbacks
     def test_unshareSharerSide(self, commit=False):
         """
@@ -1107,6 +1129,7 @@
         invitedCals = yield cal.asShared()
         self.assertEqual(len(invitedCals), 0)
 
+
     @inlineCallbacks
     def test_unshareShareeSide(self, commit=False):
         """
@@ -1126,6 +1149,7 @@
         invitedCals = yield cal.asShared()
         self.assertEqual(len(invitedCals), 0)
 
+
     @inlineCallbacks
     def test_unshareWithInDifferentTransaction(self):
         """
@@ -1174,7 +1198,7 @@
 
         result = (yield home.hasCalendarResourceUIDSomewhereElse("uid2", object, "schedule"))
         self.assertTrue(result)
-        
+
         # FIXME:  do this without legacy calls
         '''
         from twistedcaldav.sharing import SharedCollectionRecord
@@ -1291,7 +1315,6 @@
         self.assertEquals(component.mainType(), "VEVENT")
         self.assertEquals(component.resourceUID(), "uid1")
 
-
     perUserComponent = lambda self: VComponent.fromString("""BEGIN:VCALENDAR
 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
 VERSION:2.0
@@ -1326,7 +1349,6 @@
 END:VCALENDAR
 """.replace("\n", "\r\n"))
 
-
     asSeenByOwner = lambda self: VComponent.fromString("""BEGIN:VCALENDAR
 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
 VERSION:2.0
@@ -1346,7 +1368,6 @@
 END:VCALENDAR
 """.replace("\n", "\r\n"))
 
-
     asSeenByOther = lambda self: VComponent.fromString("""BEGIN:VCALENDAR
 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
 VERSION:2.0
@@ -1464,6 +1485,7 @@
             set(home1_calendarNames)
         )
 
+
     @inlineCallbacks
     def test_loadAllCalendars(self):
         """
@@ -1485,7 +1507,7 @@
             set(c.name() for c in calendars),
             set(home1_calendarNames)
         )
-        
+
         for c in calendars:
             self.assertTrue(c.properties() is not None)
 
@@ -1570,6 +1592,7 @@
             InvalidObjectResourceError,
         )
 
+
     @inlineCallbacks
     def test_setComponent_invalid(self):
         """
@@ -1739,7 +1762,7 @@
             self.assertNotEquals(event1_text, event1_text_withDifferentSubject)
             newComponent = VComponent.fromString(event1_text_withDifferentSubject)
             yield obj.setComponent(newComponent)
-    
+
             # Putting everything into a separate transaction to account for any
             # caching that may take place.
             yield self.commit()
@@ -1748,7 +1771,6 @@
                 propertyContent
             )
 
-
     eventWithDropbox = "\r\n".join("""
 BEGIN:VCALENDAR
 CALSCALE:GREGORIAN
@@ -1857,7 +1879,7 @@
     def test_collectionSyncToken(self):
         """
         L{ICalendar.resourceNamesSinceToken} will return the names of calendar
-        objects changed or deleted since 
+        objects changed or deleted since
         """
         cal = yield self.calendarUnderTest()
         st = yield cal.syncToken()
@@ -2081,7 +2103,7 @@
         that fails with L{QuotaExceeded}.
         """
         home = yield self.homeUnderTest()
-        attachment = yield getit() 
+        attachment = yield getit()
         t = attachment.store(MimeType("text", "x-fixture"))
         sample = "all work and no play makes jack a dull boy"
         chunk = (sample * (home.quotaAllowedBytes() / len(sample)))
@@ -2141,7 +2163,7 @@
         yield checkOriginal()
 
 
-    def test_removeAttachmentWithName(self, refresh=lambda x:x):
+    def test_removeAttachmentWithName(self, refresh=lambda x: x):
         """
         L{ICalendarObject.removeAttachmentWithName} will remove the calendar
         object with the given name.
@@ -2196,7 +2218,7 @@
 
     @inlineCallbacks
     def test_finishedOnCommit(self):
-        """ 
+        """
         Calling L{ITransaction.abort} or L{ITransaction.commit} after
         L{ITransaction.commit} has already been called raises an
         L{AlreadyFinishedError}.
@@ -2278,4 +2300,3 @@
         additionalUIDs.add("home_attachments")
         expectedUIDs = additionalUIDs.union(requiredUIDs)
         self.assertEquals(foundUIDs, expectedUIDs)
-

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py	2012-10-16 00:34:32 UTC (rev 9943)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py	2012-10-16 16:12:08 UTC (rev 9944)
@@ -67,7 +67,7 @@
     calendarPath.parent().makedirs()
     storePath.copyTo(calendarPath)
 
-    # Set year values to current year    
+    # Set year values to current year
     nowYear = PyCalendarDateTime.getToday().getYear()
     for home in calendarPath.child("ho").child("me").children():
         if not home.basename().startswith("."):
@@ -75,7 +75,7 @@
                 if not calendar.basename().startswith("."):
                     for resource in calendar.children():
                         if resource.basename().endswith(".ics"):
-                            resource.setContent(resource.getContent() % {"now":nowYear})
+                            resource.setContent(resource.getContent() % {"now": nowYear})
 
     testID = test.id()
     test.calendarStore = CalendarStore(storeRootPath, test.notifierFactory,
@@ -314,8 +314,8 @@
             self.calendar1.removeCalendarObjectWithName, name
         )
 
+    counter = 0
 
-    counter = 0
     @inlineCallbacks
     def _refresh(self):
         """
@@ -442,7 +442,7 @@
         L{CalendarObject} has instance attributes, C{_path} and C{_calendar},
         which refer to its position in the filesystem and the calendar in which
         it is contained, respectively.
-        """ 
+        """
         self.failUnless(
             isinstance(self.object1._path, FilePath),
             self.object1._path
@@ -482,7 +482,6 @@
         Overridden to be skipped.
         """
 
-
     # TODO: ideally the file store would support all of this sharing stuff.
     test_shareWith.skip = "Not implemented for file store yet."
     test_shareAgainChangesMode = test_shareWith
@@ -503,6 +502,25 @@
 
 
     @inlineCallbacks
+    def test_calendarHomes(self):
+        """
+        Finding all existing calendar homes.
+        """
+        calendarHomes = (yield self.transactionUnderTest().calendarHomes())
+        self.assertEquals(
+            [home.name() for home in calendarHomes],
+            [
+                "home1",
+                "home_attachments",
+                "home_bad",
+                "home_no_splits",
+                "home_splits",
+                "home_splits_shared",
+            ]
+        )
+
+
+    @inlineCallbacks
     def test_calendarObjectsWithDotFile(self):
         """
         Adding a dotfile to the calendar home should not increase the number of

Modified: CalendarServer/trunk/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/icalendarstore.py	2012-10-16 00:34:32 UTC (rev 9943)
+++ CalendarServer/trunk/txdav/caldav/icalendarstore.py	2012-10-16 16:12:08 UTC (rev 9944)
@@ -72,6 +72,7 @@
         self.limit = lowerLimit
 
 
+
 class TimeRangeUpperLimit(Exception):
     """
     A request for time-range information too far in the future cannot be satisfied.
@@ -81,11 +82,20 @@
         self.limit = upperLimit
 
 
+
 class ICalendarTransaction(ICommonTransaction):
     """
     Transaction functionality required to be implemented by calendar stores.
     """
 
+    def calendarHomes():
+        """
+        Retrieve each calendar home in the store.
+
+        @return: a L{Deferred} which fires with a list of L{ICalendarHome}.
+        """
+
+
     def calendarHomeWithUID(uid, create=False):
         """
         Retrieve the calendar home for the principal with the given C{uid}.
@@ -115,6 +125,7 @@
         """
 
 
+
 #
 # Interfaces
 #
@@ -134,6 +145,7 @@
         @return: a string.
         """
 
+
     def calendars():
         """
         Retrieve calendars contained in this calendar home.
@@ -141,6 +153,7 @@
         @return: an iterable of L{ICalendar}s.
         """
 
+
     def loadCalendars():
         """
         Pre-load all calendars Depth:1.
@@ -148,6 +161,7 @@
         @return: an iterable of L{ICalendar}s.
         """
 
+
     def calendarWithName(name):
         """
         Retrieve the calendar with the given C{name} contained in this
@@ -185,6 +199,7 @@
             given C{name} already exists.
         """
 
+
     def removeCalendarWithName(name):
         """
         Remove the calendar with the given C{name} from this calendar
@@ -288,6 +303,7 @@
         @return: an L{ICalendarHome}.
         """
 
+
     def calendarObjects():
         """
         Retrieve the calendar objects contained in this calendar.
@@ -295,6 +311,7 @@
         @return: an iterable of L{ICalendarObject}s.
         """
 
+
     def calendarObjectWithName(name):
         """
         Retrieve the calendar object with the given C{name} contained
@@ -305,6 +322,7 @@
             object exists.
         """
 
+
     def calendarObjectWithUID(uid):
         """
         Retrieve the calendar object with the given C{uid} contained
@@ -316,6 +334,7 @@
             such calendar object exists.
         """
 
+
     def createCalendarObjectWithName(name, component):
         """
         Create a calendar component with the given C{name} in this
@@ -333,6 +352,7 @@
             a calendar object.
         """
 
+
     def removeCalendarObjectWithName(name):
         """
         Remove the calendar object with the given C{name} from this
@@ -343,6 +363,7 @@
             exists.
         """
 
+
     def removeCalendarObjectWithUID(uid):
         """
         Remove the calendar object with the given C{uid} from this
@@ -353,6 +374,7 @@
             not exist.
         """
 
+
     def syncToken():
         """
         Retrieve the current sync token for this calendar.
@@ -360,6 +382,7 @@
         @return: a string containing a sync token.
         """
 
+
     def calendarObjectsInTimeRange(start, end, timeZone):
         """
         Retrieve all calendar objects in this calendar which have
@@ -372,6 +395,7 @@
         @return: an iterable of L{ICalendarObject}s.
         """
 
+
     def calendarObjectsSinceToken(token):
         """
         Retrieve all calendar objects in this calendar that have
@@ -430,6 +454,8 @@
         """
         # TODO: implement this for the file store.
 
+
+
 class ICalendarObject(IDataStoreObject):
     """
     Calendar object
@@ -444,6 +470,7 @@
         @rtype: L{ICalendar}
         """
 
+
     def setComponent(component):
         """
         Rewrite this calendar object to match the given C{component}.
@@ -456,6 +483,7 @@
             a calendar object.
         """
 
+
     def component():
         """
         Retrieve the calendar component for this calendar object.
@@ -467,6 +495,7 @@
         @return: a C{VCALENDAR} L{VComponent}.
         """
 
+
     def uid():
         """
         Retrieve the UID for this calendar object.
@@ -474,6 +503,7 @@
         @return: a string containing a UID.
         """
 
+
     def componentType():
         """
         Retrieve the iCalendar component type for the main component
@@ -497,6 +527,7 @@
         @return: a URI string.
         """
 
+
     def dropboxID():
         """
         An identifier, unique to the calendar home, that specifies a location
@@ -629,5 +660,3 @@
             that the stream is complete to its C{connectionLost} method.
         @type protocol: L{IProtocol}
         """
-
-

Modified: CalendarServer/trunk/txdav/carddav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/common.py	2012-10-16 00:34:32 UTC (rev 9943)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/common.py	2012-10-16 16:12:08 UTC (rev 9944)
@@ -234,6 +234,20 @@
 
 
     @inlineCallbacks
+    def test_addressbookHomes(self):
+        """
+        Finding all existing addressbook homes.
+        """
+        addressbookHomes = (yield self.transactionUnderTest().addressbookHomes())
+        self.assertEquals(
+            [home.name() for home in addressbookHomes],
+            [
+                "home1",
+            ]
+        )
+
+
+    @inlineCallbacks
     def test_addressbookHomeWithUID_exists(self):
         """
         Finding an existing addressbook home by UID results in an object that
@@ -664,7 +678,7 @@
             set(c.name() for c in addressbooks),
             set(home1_addressbookNames)
         )
-        
+
         for c in addressbooks:
             self.assertTrue(c.properties() is not None)
 
@@ -910,7 +924,7 @@
             self.assertNotEquals(vcard1_text, vcard1_text_withDifferentNote)
             newComponent = VComponent.fromString(vcard1_text_withDifferentNote)
             yield obj.setComponent(newComponent)
-    
+
             # Putting everything into a separate transaction to account for any
             # caching that may take place.
             yield self.commit()
@@ -979,6 +993,3 @@
         additionalUIDs.add("home_bad")
         expectedUIDs = additionalUIDs.union(requiredUIDs)
         self.assertEquals(foundUIDs, expectedUIDs)
-
-
-

Modified: CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py	2012-10-16 00:34:32 UTC (rev 9943)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py	2012-10-16 16:12:08 UTC (rev 9944)
@@ -434,7 +434,7 @@
         L{AddressBookObject} has instance attributes, C{_path} and C{_addressbook},
         which refer to its position in the filesystem and the addressbook in which
         it is contained, respectively.
-        """ 
+        """
         self.failUnless(
             isinstance(self.object1._path, FilePath),
             self.object1._path
@@ -445,6 +445,7 @@
         )
 
 
+
 class FileStorageTests(CommonTests, unittest.TestCase):
     """
     File storage tests.
@@ -471,6 +472,21 @@
 
 
     @inlineCallbacks
+    def test_addressbookHomes(self):
+        """
+        Finding all existing addressbook homes.
+        """
+        addressbookHomes = (yield self.transactionUnderTest().addressbookHomes())
+        self.assertEquals(
+            [home.name() for home in addressbookHomes],
+            [
+                "home1",
+                "home_bad",
+            ]
+        )
+
+
+    @inlineCallbacks
     def test_addressbookObjectsWithDotFile(self):
         """
         Adding a dotfile to the addressbook home should not create a new
@@ -489,5 +505,3 @@
         ((yield self.addressbookUnderTest())._path.child("not-a-vcard")
          .createDirectory())
         yield self.test_addressbookObjects()
-
-

Modified: CalendarServer/trunk/txdav/carddav/iaddressbookstore.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/iaddressbookstore.py	2012-10-16 00:34:32 UTC (rev 9943)
+++ CalendarServer/trunk/txdav/carddav/iaddressbookstore.py	2012-10-16 16:12:08 UTC (rev 9944)
@@ -19,7 +19,7 @@
 Address book store interfaces
 """
 
-from txdav.common.icommondatastore import ICommonTransaction,\
+from txdav.common.icommondatastore import ICommonTransaction, \
     IShareableCollection
 from txdav.idav import INotifier
 from txdav.idav import IDataStoreObject
@@ -37,6 +37,14 @@
     Transaction interface that addressbook stores must provide.
     """
 
+    def addressbookHomes():
+        """
+        Retrieve each addressbook home in the store.
+
+        @return: a L{Deferred} which fires with a list of L{ICalendarHome}.
+        """
+
+
     def addressbookHomeWithUID(uid, create=False):
         """
         Retrieve the addressbook home for the principal with the given C{uid}.
@@ -79,6 +87,7 @@
         @return: an iterable of L{IAddressBook}s.
         """
 
+
     def loadAddressbooks():
         """
         Pre-load all addressbooks Depth:1.
@@ -86,6 +95,7 @@
         @return: an iterable of L{IAddressBook}s.
         """
 
+
     def addressbookWithName(name):
         """
         Retrieve the addressbook with the given C{name} contained in this
@@ -96,6 +106,7 @@
             exists.
         """
 
+
     def createAddressBookWithName(name):
         """
         Create an addressbook with the given C{name} in this addressbook
@@ -106,6 +117,7 @@
             given C{name} already exists.
         """
 
+
     def removeAddressBookWithName(name):
         """
         Remove the addressbook with the given C{name} from this addressbook
@@ -117,6 +129,7 @@
         """
 
 
+
 class IAddressBook(INotifier, IShareableCollection, IDataStoreObject):
     """
     AddressBook
@@ -132,6 +145,7 @@
         Change the name of this addressbook.
         """
 
+
     def ownerAddressBookHome():
         """
         Retrieve the addressbook home for the owner of this addressbook.
@@ -141,6 +155,7 @@
         @return: an L{IAddressBookHome}.
         """
 
+
     def addressbookObjects():
         """
         Retrieve the addressbook objects contained in this addressbook.
@@ -148,13 +163,14 @@
         @return: an iterable of L{IAddressBookObject}s.
         """
 
+
     def addressbookObjectWithName(name):
         """
         Retrieve the addressbook object with the given C{name} contained
         in this addressbook.
 
         @param name: a string.
-        
+
         @return: a L{Deferred} that fires with an L{IAddressBookObject} or
             C{None} if no such addressbook object exists.
         """
@@ -169,6 +185,7 @@
             object exists.
         """
 
+
     def createAddressBookObjectWithName(name, component):
         """
         Create an addressbook component with the given C{name} in this
@@ -186,6 +203,7 @@
             an addressbook object.
         """
 
+
     def removeAddressBookObjectWithName(name):
         """
         Remove the addressbook object with the given C{name} from this
@@ -196,6 +214,7 @@
             exists.
         """
 
+
     def removeAddressBookObjectWithUID(uid):
         """
         Remove the addressbook object with the given C{uid} from this
@@ -206,6 +225,7 @@
             not exist.
         """
 
+
     def syncToken():
         """
         Retrieve the current sync token for this addressbook.
@@ -213,6 +233,7 @@
         @return: a string containing a sync token.
         """
 
+
     def addressbookObjectsSinceToken(token):
         """
         Retrieve all addressbook objects in this addressbook that have
@@ -225,6 +246,7 @@
         """
 
 
+
 class IAddressBookObject(IDataStoreObject):
     """
     AddressBook object
@@ -250,6 +272,7 @@
             an addressbook object.
         """
 
+
     def component():
         """
         Retrieve the addressbook component for this addressbook object.
@@ -261,6 +284,7 @@
         @return: a C{VCARD} L{VComponent}.
         """
 
+
     def uid():
         """
         Retrieve the UID for this addressbook object.

Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py	2012-10-16 00:34:32 UTC (rev 9943)
+++ CalendarServer/trunk/txdav/common/datastore/file.py	2012-10-16 16:12:08 UTC (rev 9944)
@@ -24,7 +24,7 @@
 from txdav.xml.rfc2518 import ResourceType, GETContentType, HRef
 from txdav.xml.rfc5842 import ResourceID
 from twext.web2.http_headers import generateContentType, MimeType
-from twext.web2.dav.resource import TwistedGETContentMD5,\
+from twext.web2.dav.resource import TwistedGETContentMD5, \
     TwistedQuotaUsedProperty
 
 from twisted.internet.defer import succeed, inlineCallbacks, returnValue
@@ -139,6 +139,7 @@
         self._migrating = state
         self._enableNotifications = not state
 
+
     def setUpgrading(self, state):
         """
         Set the "upgrading" state
@@ -179,6 +180,7 @@
         return self._homesOfType(EADDRESSBOOKTYPE)
 
 
+
 class CommonStoreTransaction(DataStoreTransaction):
     """
     In-memory implementation of
@@ -226,14 +228,45 @@
         CommonStoreTransaction._homeClass[EADDRESSBOOKTYPE] = AddressBookHome
 
 
-    @memoizedKey('uid', '_calendarHomes')
+    def calendarHomes(self):
+        return self.homes(ECALENDARTYPE)
+
+
     def calendarHomeWithUID(self, uid, create=False):
         return self.homeWithUID(ECALENDARTYPE, uid, create=create)
 
-    @memoizedKey("uid", "_addressbookHomes")
+
+    def addressbookHomes(self):
+        return self.homes(EADDRESSBOOKTYPE)
+
+
     def addressbookHomeWithUID(self, uid, create=False):
         return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
 
+
+    def _determineMemo(self, storeType, uid, create=False):
+        """
+        Determine the memo dictionary to use for homeWithUID.
+        """
+        if storeType == ECALENDARTYPE:
+            return self._calendarHomes
+        else:
+            return self._addressbookHomes
+
+
+    def homes(self, storeType):
+        """
+        Load all calendar or addressbook homes.
+        """
+        uids = self._homeClass[storeType].listHomes(self)
+        for uid in uids:
+            self.homeWithUID(storeType, uid, create=False)
+
+        # Return the memoized list directly
+        returnValue([kv[1] for kv in sorted(self._determineMemo(storeType, None).items(), key=lambda x: x[0])])
+
+
+    @memoizedKey("uid", _determineMemo)
     def homeWithUID(self, storeType, uid, create=False):
         if uid.startswith("."):
             return None
@@ -243,6 +276,7 @@
 
         return self._homeClass[storeType].homeWithUID(self, uid, create, storeType == ECALENDARTYPE)
 
+
     @memoizedKey("uid", "_notificationHomes")
     def notificationsWithUID(self, uid, home=None):
 
@@ -255,27 +289,35 @@
     def addAPNSubscription(self, token, key, timestamp, subscriber, userAgent, ipAddr):
         return NotImplementedError
 
+
     def removeAPNSubscription(self, token, key):
         return NotImplementedError
 
+
     def purgeOldAPNSubscriptions(self, purgeSeconds):
         return NotImplementedError
 
+
     def apnSubscriptionsByToken(self, token):
         return NotImplementedError
 
+
     def apnSubscriptionsByKey(self, key):
         return NotImplementedError
 
+
     def apnSubscriptionsBySubscriber(self, guid):
         return NotImplementedError
 
+
     def isNotifiedAlready(self, obj):
         return obj in self._notifiedAlready
 
+
     def notificationAddedForObject(self, obj):
         self._notifiedAlready.add(obj)
 
+
     def isBumpedAlready(self, obj):
         """
         Indicates whether or not bumpAddedForObject has already been
@@ -284,6 +326,7 @@
         """
         return obj in self._bumpedAlready
 
+
     def bumpAddedForObject(self, obj):
         """
         Records the fact that a bumpModified( ) call has already been
@@ -332,6 +375,31 @@
 
 
     @classmethod
+    def listHomes(cls, txn):
+        """
+        Retrieve the owner UIDs of all existing homes.
+
+        @return: an iterable of C{str}s.
+        """
+        results = []
+        top = txn._dataStore._path.child(cls._topPath)
+        if top.exists() and top.isdir() and top.child(UIDPATH).exists():
+            for firstPrefix in top.child(UIDPATH).children():
+                if not isValidName(firstPrefix.basename()):
+                    continue
+                for secondPrefix in firstPrefix.children():
+                    if not isValidName(secondPrefix.basename()):
+                        continue
+                    for actualHome in secondPrefix.children():
+                        uid = actualHome.basename()
+                        if not isValidName(uid):
+                            continue
+                        results.append(uid)
+
+        return results
+
+
+    @classmethod
     def homeWithUID(cls, txn, uid, create=False, withNotifications=False):
 
         assert len(uid) >= 4
@@ -391,6 +459,7 @@
 
         return home
 
+
     def __repr__(self):
         return "<%s: %s>" % (self.__class__.__name__, self._path)
 
@@ -402,6 +471,7 @@
     def transaction(self):
         return self._transaction
 
+
     def retrieveOldShares(self):
         """
         Retrieve the old Index object.
@@ -434,7 +504,7 @@
         ) | set(
             name
             for name in self._path.listdir()
-            if not name.startswith(".") and 
+            if not name.startswith(".") and
                 self._path.child(name).isdir() and
                 name not in self._removedChildren
         ))
@@ -516,6 +586,7 @@
         self.notifyChanged()
         return c
 
+
     @writeOperation
     def removeChildWithName(self, name):
         if name.startswith(".") or name in self._removedChildren:
@@ -533,13 +604,14 @@
             else:
                 self._removedChildren.add(name)
 
+
     @inlineCallbacks
     def syncToken(self):
-        
+
         maxrev = 0
         for child in self.children():
             maxrev = max(int((yield child.syncToken()).split("_")[1]), maxrev)
-            
+
         try:
             urnuuid = str(self.properties()[PropertyName.fromElement(ResourceID)].children[0])
         except KeyError:
@@ -565,6 +637,7 @@
         self._transaction.addOperation(props.flush, "flush home properties")
         return props
 
+
     def objectResourcesWithUID(self, uid, ignore_children=()):
         """
         Return all child object resources with the specified UID, ignoring any in the
@@ -579,6 +652,7 @@
                 results.append(object)
         return results
 
+
     def quotaUsedBytes(self):
 
         try:
@@ -586,31 +660,34 @@
         except KeyError:
             return 0
 
+
     def adjustQuotaUsedBytes(self, delta):
         """
         Adjust quota used. We need to get a lock on the row first so that the adjustment
         is done atomically.
         """
-        
+
         old_used = self.quotaUsedBytes()
         new_used = old_used + delta
         if new_used < 0:
             self.log_error("Fixing quota adjusted below zero to %s by change amount %s" % (new_used, delta,))
             new_used = 0
         self.properties()[PropertyName.fromElement(TwistedQuotaUsedProperty)] = TwistedQuotaUsedProperty(str(new_used))
-            
 
+
     def addNotifier(self, notifier):
         if self._notifiers is None:
             self._notifiers = ()
         self._notifiers += (notifier,)
- 
+
+
     def notifierID(self, label="default"):
         if self._notifiers:
             return self._notifiers[0].getID(label)
         else:
             return None
 
+
     @inlineCallbacks
     def nodeName(self, label="default"):
         if self._notifiers:
@@ -621,6 +698,7 @@
         else:
             returnValue(None)
 
+
     def notifyChanged(self):
         """
         Trigger a notification of a change
@@ -633,6 +711,7 @@
             self._transaction.notificationAddedForObject(self)
 
 
+
 class CommonHomeChild(FileMetaDataMixin, LoggingMixIn, FancyEqMixin, HomeChildBase):
     """
     Common ancestor class of AddressBooks and Calendars.
@@ -684,6 +763,7 @@
     def objectWithName(cls, home, name, owned):
         return cls(name, home, owned) if home._path.child(name).isdir() else None
 
+
     @property
     def _path(self):
         return self._home._path.child(self._name)
@@ -692,12 +772,14 @@
     def resourceType(self):
         return NotImplementedError
 
+
     def retrieveOldIndex(self):
         """
         Retrieve the old Index object.
         """
         return self._index._oldIndex
 
+
     def retrieveOldInvites(self):
         """
         Retrieve the old Invites DB object.
@@ -722,6 +804,7 @@
         """
         return BIND_OWN
 
+
     def owned(self):
         return self._owned
 
@@ -743,6 +826,7 @@
 
         self.notifyChanged()
 
+
     @writeOperation
     def remove(self):
 
@@ -856,7 +940,7 @@
         rname = self.retrieveOldIndex().resourceNameForUID(uid)
         if rname and rname not in self._removedObjectResources:
             return self.objectResourceWithName(rname)
-        
+
         return None
 
 
@@ -864,7 +948,7 @@
     def createObjectResourceWithName(self, name, component, metadata=None):
         """
         Create a new resource with component data and optional metadata. We create the
-        python object using the metadata then create the actual store object with setComponent. 
+        python object using the metadata then create the actual store object with setComponent.
         """
         if name.startswith("."):
             raise ObjectResourceNameNotAllowedError(name)
@@ -935,6 +1019,7 @@
         """
         return True
 
+
     # FIXME: property writes should be a write operation
     @cached
     def properties(self):
@@ -958,17 +1043,20 @@
         """
         pass
 
+
     def addNotifier(self, notifier):
         if self._notifiers is None:
             self._notifiers = ()
         self._notifiers += (notifier,)
- 
+
+
     def notifierID(self, label="default"):
         if self._notifiers:
             return self._notifiers[0].getID(label)
         else:
             return None
 
+
     @inlineCallbacks
     def nodeName(self, label="default"):
         if self._notifiers:
@@ -979,6 +1067,7 @@
         else:
             returnValue(None)
 
+
     def notifyChanged(self):
         """
         Trigger a notification of a change
@@ -991,6 +1080,7 @@
             self._transaction.notificationAddedForObject(self)
 
 
+
 class CommonObjectResource(FileMetaDataMixin, LoggingMixIn, FancyEqMixin):
     """
     @ivar _path: The path of the file on disk
@@ -1018,9 +1108,11 @@
     def __repr__(self):
         return "<%s: %s>" % (self.__class__.__name__, self._path.path)
 
+
     def transaction(self):
         return self._transaction
 
+
     @writeOperation
     def setComponent(self, component, inserting=False):
         raise NotImplementedError
@@ -1037,6 +1129,7 @@
     def uid(self):
         raise NotImplementedError
 
+
     @cached
     def properties(self):
         home = self._parentCollection._home
@@ -1050,6 +1143,7 @@
         self._transaction.addOperation(props.flush, "object properties flush")
         return props
 
+
     def initPropertyStore(self, props):
         """
         A hook for subclasses to override in order to set up their property
@@ -1059,6 +1153,8 @@
         """
         pass
 
+
+
 class CommonStubResource(object):
     """
     Just enough resource to keep the collection sql DB classes going.
@@ -1067,6 +1163,7 @@
         self.resource = resource
         self.fp = self.resource._path
 
+
     def bumpSyncToken(self, reset=False):
         # FIXME: needs direct tests
         return self.resource._updateSyncToken(reset)
@@ -1077,6 +1174,7 @@
         self.bumpSyncToken(True)
 
 
+
 class NotificationCollection(CommonHomeChild):
     """
     File-based implementation of L{INotificationCollection}.
@@ -1101,6 +1199,7 @@
         self._invites = None
         self._objectResourceClass = NotificationObject
 
+
     @classmethod
     def notificationsFromHome(cls, txn, home):
 
@@ -1144,6 +1243,7 @@
         props[PropertyName(*ResourceType.qname())] = c.resourceType()
         return c
 
+
     def resourceType(self):
         return ResourceType.notification #@UndefinedVariable
 
@@ -1156,6 +1256,7 @@
         name = uid + ".xml"
         return self.notificationObjectWithName(name)
 
+
     def writeNotificationObject(self, uid, xmltype, xmldata):
         name = uid + ".xml"
         if name.startswith("."):
@@ -1167,9 +1268,10 @@
 
         # Update database
         self.retrieveOldIndex().addOrUpdateRecord(NotificationRecord(uid, name, xmltype.name))
-        
+
         self.notifyChanged()
 
+
     @writeOperation
     def removeNotificationObjectWithName(self, name):
         if name.startswith("."):
@@ -1186,17 +1288,19 @@
                 return lambda: None
             self._transaction.addOperation(do, "remove object resource object %r" %
                                            (name,))
-        
+
             self.notifyChanged()
         else:
             raise NoSuchObjectResourceError(name)
 
+
     @writeOperation
     def removeNotificationObjectWithUID(self, uid):
         name = uid + ".xml"
         self.removeNotificationObjectWithName(name)
 
 
+
 class NotificationObject(CommonObjectResource):
     """
     """
@@ -1206,6 +1310,7 @@
         super(NotificationObject, self).__init__(name, notifications)
         self._uid = name[:-4]
 
+
     def notificationCollection(self):
         return self._parentCollection
 
@@ -1216,6 +1321,7 @@
             return int(reactor.seconds())
         return super(NotificationObject, self).created()
 
+
     def modified(self):
         if not self._path.exists():
             from twisted.internet import reactor
@@ -1261,11 +1367,10 @@
         self.properties().update(self.properties())
 
         props = self.properties()
-        props[PropertyName(*GETContentType.qname())] = GETContentType.fromString(generateContentType(MimeType("text", "xml", params={"charset":"utf-8"})))
+        props[PropertyName(*GETContentType.qname())] = GETContentType.fromString(generateContentType(MimeType("text", "xml", params={"charset": "utf-8"})))
         props[PropertyName.fromElement(NotificationType)] = NotificationType(xmltype)
         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
@@ -1273,7 +1378,6 @@
         # manipulation methods won't work.
         self._transaction.addOperation(self.properties().flush, "post-update property flush")
 
-
     _xmldata = None
 
     def xmldata(self):
@@ -1298,10 +1402,12 @@
     def uid(self):
         return self._uid
 
+
     def xmlType(self):
         # NB This is the NotificationType property element
         return self.properties()[PropertyName.fromElement(NotificationType)]
 
+
     def initPropertyStore(self, props):
         # Setup peruser special properties
         props.setSpecialProperties(
@@ -1313,6 +1419,7 @@
         )
 
 
+
 class NotificationIndex(object):
     #
     # OK, here's where we get ugly.
@@ -1322,4 +1429,3 @@
         self.notificationCollection = notificationCollection
         stubResource = CommonStubResource(notificationCollection)
         self._oldIndex = OldNotificationIndex(stubResource)
-

Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py	2012-10-16 00:34:32 UTC (rev 9943)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py	2012-10-16 16:12:08 UTC (rev 9944)
@@ -465,10 +465,18 @@
         raise RuntimeError("Database key %s cannot be determined." % (key,))
 
 
+    def calendarHomes(self):
+        return self.homes(ECALENDARTYPE)
+
+
     def calendarHomeWithUID(self, uid, create=False):
         return self.homeWithUID(ECALENDARTYPE, uid, create=create)
 
 
+    def addressbookHomes(self):
+        return self.homes(EADDRESSBOOKTYPE)
+
+
     def addressbookHomeWithUID(self, uid, create=False):
         return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
 
@@ -483,6 +491,21 @@
             return self._addressbookHomes
 
 
+    @inlineCallbacks
+    def homes(self, storeType):
+        """
+        Load all calendar or addressbook homes.
+        """
+
+        # Get all UIDs and load them - this will memoize all existing ones
+        uids = (yield self._homeClass[storeType].listHomes(self))
+        for uid in uids:
+            yield self.homeWithUID(storeType, uid, create=False)
+
+        # Return the memoized list directly
+        returnValue([kv[1] for kv in sorted(self._determineMemo(storeType, None).items(), key=lambda x: x[0])])
+
+
     @memoizedKey("uid", _determineMemo)
     def homeWithUID(self, storeType, uid, create=False):
         if storeType not in (ECALENDARTYPE, EADDRESSBOOKTYPE):
@@ -1103,6 +1126,22 @@
 
     @classmethod
     @inlineCallbacks
+    def listHomes(cls, txn):
+        """
+        Retrieve the owner UIDs of all existing homes.
+
+        @return: an iterable of C{str}s.
+        """
+        rows = yield Select(
+            [cls._homeSchema.OWNER_UID],
+            From=cls._homeSchema,
+        ).on(txn)
+        rids = [row[0] for row in rows]
+        returnValue(rids)
+
+
+    @classmethod
+    @inlineCallbacks
     def homeWithUID(cls, txn, uid, create=False):
         if txn._notifierFactory:
             notifiers = (txn._notifierFactory.newNotifier(
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20121016/60c687b5/attachment-0001.html>


More information about the calendarserver-changes mailing list