[CalendarServer-changes] [11042] CalendarServer/branches/users/cdaboo/store-scheduling

source_changes at macosforge.org source_changes at macosforge.org
Tue Apr 16 08:40:04 PDT 2013


Revision: 11042
          http://trac.calendarserver.org//changeset/11042
Author:   cdaboo at apple.com
Date:     2013-04-16 08:40:04 -0700 (Tue, 16 Apr 2013)
Log Message:
-----------
Checkpoint: hook up a directory service interface for the store. Working on getting store unit tests to pass. Next step
is to complete work on the scheduling apis and refactoring of scheduling resources between app layer and store.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/caldav.py
    CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/util.py
    CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/shell/vfs.py
    CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/util.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/directory.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/principal.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/file.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/index_file.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/delivery.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/resource.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/scheduler.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/implicit.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/processing.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/scheduler.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_implicit.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_utils.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/utils.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/attachments/accounts.xml
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/common.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_attachments.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_file.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_implicit.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_sql.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_util.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/util.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/common.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/test_file.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/test_sql.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/iaddressbookstore.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/file.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/sql.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/test/util.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/__init__.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/migrate.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/test/__init__.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/test/test_migrate.py

Added Paths:
-----------
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/schedule.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_schedule.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/util.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendardirectoryservice.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/idirectoryservice.py

Removed Paths:
-------------
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_scheduling.py

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/caldav.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/caldav.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -78,7 +78,7 @@
 from twistedcaldav.upgrade import UpgradeFileSystemFormatService, PostDBImportService
 
 from calendarserver.tap.util import pgServiceFromConfig, getDBPool, MemoryLimitService
-from calendarserver.tap.util import directoryFromConfig, checkDirectories
+from calendarserver.tap.util import checkDirectories
 
 from twext.enterprise.ienterprise import POSTGRES_DIALECT
 from twext.enterprise.ienterprise import ORACLE_DIALECT
@@ -642,7 +642,7 @@
         store = storeFromConfig(config, txnFactory)
         logObserver = AMPCommonAccessLoggingObserver()
         result = self.requestProcessingService(options, store, logObserver)
-        directory = result.rootResource.getDirectory()
+        directory = store.directoryService()
         if pool is not None:
             pool.setServiceParent(result)
 
@@ -1003,7 +1003,7 @@
                 if observers:
                     pushDistributor = PushDistributor(observers)
 
-            directory = result.rootResource.getDirectory()
+            directory = store.directoryService()
 
             # Optionally set up mail retrieval
             if config.Scheduling.iMIP.Enabled:
@@ -1397,7 +1397,7 @@
             if config.UseMetaFD:
                 cl.setServiceParent(multi)
 
-            directory = directoryFromConfig(config)
+            directory = store.directoryService()
             rootResource = getRootResource(config, store, [])
 
             # Optionally set up mail retrieval

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/util.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/util.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -214,7 +214,7 @@
 
 
 
-def storeFromConfig(config, txnFactory):
+def storeFromConfig(config, txnFactory, directoryService=None):
     """
     Produce an L{IDataStore} from the given configuration, transaction factory,
     and notifier factory.
@@ -232,6 +232,10 @@
             config.Notifications.CoalesceSeconds)
     else:
         notifierFactory = None
+
+    if directoryService is None:
+        directoryService = directoryFromConfig(config)
+
     quota = config.UserQuota
     if quota == 0:
         quota = None
@@ -243,6 +247,7 @@
         attachments_uri = uri + "/calendars/__uids__/%(home)s/dropbox/%(dropbox_id)s/%(name)s"
         store = CommonSQLDataStore(
             txnFactory, notifierFactory,
+            directoryService,
             FilePath(config.AttachmentsRoot), attachments_uri,
             config.EnableCalDAV, config.EnableCardDAV,
             config.EnableManagedAttachments,
@@ -260,7 +265,8 @@
     else:
         store = CommonFileDataStore(
             FilePath(config.DocumentRoot),
-            notifierFactory, config.EnableCalDAV, config.EnableCardDAV,
+            notifierFactory, directoryService,
+            config.EnableCalDAV, config.EnableCardDAV,
             quota=quota
         )
     if notifierFactory is not None:
@@ -366,7 +372,7 @@
 
 
 
-def getRootResource(config, newStore, resources=None, directory=None):
+def getRootResource(config, newStore, resources=None):
     """
     Set up directory service and resource hierarchy based on config.
     Return root resource.
@@ -402,8 +408,7 @@
     directoryBackedAddressBookResourceClass = DirectoryBackedAddressBookResource
     apnSubscriptionResourceClass = APNSubscriptionResource
 
-    if directory is None:
-        directory = directoryFromConfig(config)
+    directory = newStore.directoryService()
 
     #
     # Setup the ProxyDB Service

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/shell/vfs.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/shell/vfs.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/shell/vfs.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -59,10 +59,10 @@
     """
 
     def __init__(self, parent, Class, Name, **fields):
-        self.parent    = parent # The class implementing list()
+        self.parent = parent # The class implementing list()
         self.fileClass = Class
-        self.fileName  = Name
-        self.fields    = fields
+        self.fileName = Name
+        self.fields = fields
 
         fields["Name"] = Name
 
@@ -133,7 +133,7 @@
         assert type(path) is tuple
 
         self.service = service
-        self.path    = path
+        self.path = path
 
     def __str__(self):
         return "/" + "/".join(self.path)
@@ -385,7 +385,7 @@
             try:
                 if (
                     self.record is not None and
-                    self.service.config.EnableCalDAV and 
+                    self.service.config.EnableCalDAV and
                     self.record.enabledForCalendaring
                 ):
                     create = True
@@ -420,7 +420,7 @@
 
                 if (
                     self.record is not None and
-                    self.service.config.EnableCardDAV and 
+                    self.service.config.EnableCardDAV and
                     self.record.enabledForAddressBooks
                 ):
                     create = True
@@ -481,7 +481,7 @@
     def __init__(self, service, path, home, record):
         Folder.__init__(self, service, path)
 
-        self.home   = home
+        self.home = home
         self.record = record
 
     @inlineCallbacks
@@ -515,13 +515,13 @@
         #
         # Attributes
         #
-        uid          = (yield self.home.uid())
-        created      = (yield self.home.created())
-        modified     = (yield self.home.modified())
-        quotaUsed    = (yield self.home.quotaUsedBytes())
+        uid = (yield self.home.uid())
+        created = (yield self.home.created())
+        modified = (yield self.home.modified())
+        quotaUsed = (yield self.home.quotaUsedBytes())
         quotaAllowed = (yield self.home.quotaAllowedBytes())
 
-        recordType      = (yield self.record.recordType)
+        recordType = (yield self.record.recordType)
         recordShortName = (yield self.record.shortNames[0])
 
         rows = []
@@ -611,14 +611,13 @@
         returnValue("\n".join(description))
 
     def delete(self, implicit=True):
-        calendar = self.calendarObject.calendar()
 
         if implicit:
             # We need data store-level scheduling support to implement
             # this.
             raise NotImplementedError("Delete not implemented.")
         else:
-            calendar.removeCalendarObjectWithUID(self.uid)
+            self.calendarObject.remove()
 
 
 class CalendarObject(File):
@@ -629,7 +628,7 @@
         File.__init__(self, service, path)
 
         self.object = calendarObject
-        self.uid    = uid
+        self.uid = uid
 
     @inlineCallbacks
     def lookup(self):
@@ -642,14 +641,14 @@
                 assert self.uid == mainComponent.propertyValue("UID")
 
                 self.componentType = mainComponent.name()
-                self.summary       = mainComponent.propertyValue("SUMMARY")
+                self.summary = mainComponent.propertyValue("SUMMARY")
                 self.mainComponent = mainComponent
 
             except InvalidICalendarDataError, e:
                 log.err("%s: %s" % (self.path, e))
 
                 self.componentType = "?"
-                self.summary       = "** Invalid data **"
+                self.summary = "** Invalid data **"
                 self.mainComponent = None
 
             self.component = component
@@ -683,13 +682,13 @@
         rows.append(("UID", self.uid))
         rows.append(("Component Type", self.componentType))
         rows.append(("Summary", self.summary))
-        
+
         organizer = self.mainComponent.getProperty("ORGANIZER")
         if organizer:
             organizerName = organizer.parameterValue("CN")
             organizerEmail = organizer.parameterValue("EMAIL")
 
-            name  = " (%s)" % (organizerName ,) if organizerName  else ""
+            name = " (%s)" % (organizerName ,) if organizerName  else ""
             email = " <%s>" % (organizerEmail,) if organizerEmail else ""
 
             rows.append(("Organizer", "%s%s%s" % (organizer.value(), name, email)))
@@ -736,7 +735,7 @@
     def __init__(self, service, path, home, record):
         Folder.__init__(self, service, path)
 
-        self.home   = home
+        self.home = home
         self.record = record
 
     # FIXME

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/util.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/util.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -96,7 +96,7 @@
 
                 # Need a data store
                 _newStore = CommonDataStore(FilePath(config.DocumentRoot),
-                    notifierFactory, True, False)
+                    notifierFactory, self, True, False)
                 if notifierFactory is not None:
                     notifierFactory.store = _newStore
 
@@ -181,7 +181,7 @@
     root.putChild("principals", principalCollection)
 
     # Need a data store
-    _newStore = CommonDataStore(FilePath(config.DocumentRoot), None, True, False)
+    _newStore = CommonDataStore(FilePath(config.DocumentRoot), None, aggregate, True, False)
 
     from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource
     calendarCollection = DirectoryCalendarHomeProvisioningResource(

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/directory.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/directory.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -14,6 +14,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
+from txdav.caldav.icalendardirectoryservice import ICalendarStoreDirectoryService, \
+    ICalendarStoreDirectoryRecord
 
 
 """
@@ -67,7 +69,7 @@
 
 
 class DirectoryService(LoggingMixIn):
-    implements(IDirectoryService, ICredentialsChecker)
+    implements(IDirectoryService, ICalendarStoreDirectoryService, ICredentialsChecker)
 
     ##
     # IDirectoryService
@@ -924,6 +926,7 @@
         returnValue((fast, len(members), len(changedMembers)))
 
 
+
 class GroupCacherPollingWork(WorkItem, fromTable(schema.GROUP_CACHER_POLLING_WORK)):
 
     group = "group_cacher_polling"
@@ -954,6 +957,7 @@
                 notBefore=notBefore)
 
 
+
 @inlineCallbacks
 def scheduleNextGroupCachingUpdate(store, seconds):
     txn = store.newTransaction()
@@ -963,6 +967,7 @@
     yield txn.commit()
 
 
+
 def diffAssignments(old, new):
     """
     Compare two proxy assignment lists and return their differences in the form of
@@ -994,7 +999,7 @@
 
 
 class DirectoryRecord(LoggingMixIn):
-    implements(IDirectoryRecord)
+    implements(IDirectoryRecord, ICalendarStoreDirectoryRecord)
 
     def __repr__(self):
         return "<%s[%s@%s(%s)] %s(%s) %r @ %s/#%s>" % (
@@ -1165,6 +1170,10 @@
                 self.enabledForAddressBooks = False
 
 
+    def displayName(self):
+        return self.record.fullName if self.record.fullName else self.record.shortNames[0]
+
+
     def isLoginEnabled(self):
         """
         Returns True if the user should be allowed to log in, based on the
@@ -1248,6 +1257,46 @@
     def verifyCredentials(self, credentials):
         return False
 
+
+    def calendarsEnabled(self):
+        return config.EnableCalDAV and self.enabledForCalendaring
+
+
+    def canonicalCalendarUserAddress(self):
+        """
+            Return a CUA for this principal, preferring in this order:
+            urn:uuid: form
+            mailto: form
+            first in calendarUserAddresses list
+        """
+
+        cua = ""
+        for candidate in self.calendarUserAddresses:
+            # Pick the first one, but urn:uuid: and mailto: can override
+            if not cua:
+                cua = candidate
+            # But always immediately choose the urn:uuid: form
+            if candidate.startswith("urn:uuid:"):
+                cua = candidate
+                break
+            # Prefer mailto: if no urn:uuid:
+            elif candidate.startswith("mailto:"):
+                cua = candidate
+        return cua
+
+
+    def enabledAsOrganizer(self):
+        if self.recordType == DirectoryService.recordType_users:
+            return True
+        elif self.recordType == DirectoryService.recordType_groups:
+            return config.Scheduling.Options.AllowGroupAsOrganizer
+        elif self.recordType == DirectoryService.recordType_locations:
+            return config.Scheduling.Options.AllowLocationAsOrganizer
+        elif self.recordType == DirectoryService.recordType_resources:
+            return config.Scheduling.Options.AllowResourceAsOrganizer
+        else:
+            return False
+
     # Mapping from directory record.recordType to RFC2445 CUTYPE values
     _cuTypes = {
         'users' : 'INDIVIDUAL',
@@ -1268,6 +1317,33 @@
         return None
 
 
+    def canAutoSchedule(self, organizer):
+        if config.Scheduling.Options.AutoSchedule.Enabled:
+            if (config.Scheduling.Options.AutoSchedule.Always or
+                self.getAutoSchedule() or
+                self.autoAcceptFromOrganizer(organizer)):
+                if (self.getCUType() != "INDIVIDUAL" or
+                    config.Scheduling.Options.AutoSchedule.AllowUsers):
+                    return True
+        return False
+
+
+    def getAutoScheduleMode(self, organizer):
+        autoScheduleMode = self.autoScheduleMode
+        if self.autoAcceptFromOrganizer(organizer):
+            autoScheduleMode = "automatic"
+        return autoScheduleMode
+
+
+    def autoAcceptFromOrganizer(self, organizer):
+        if organizer is not None and self.autoAcceptGroup is not None:
+            organizerRecord = self.service.recordWithCalendarUserAddress(organizer)
+            if organizerRecord is not None:
+                if organizerRecord.guid in self.autoAcceptMembers():
+                    return True
+        return False
+
+
     def serverURI(self):
         """
         URL of the server hosting this record. Return None if hosted on this server.
@@ -1352,6 +1428,7 @@
         return self._cachedAutoAcceptMembers
 
 
+
 class DirectoryError(RuntimeError):
     """
     Generic directory error.

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/principal.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/principal.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -844,10 +844,7 @@
 
 
     def displayName(self):
-        if self.record.fullName:
-            return self.record.fullName
-        else:
-            return self.record.shortNames[0]
+        return self.record.displayName()
 
     ##
     # ACL
@@ -1076,17 +1073,9 @@
         @param organizer: the CUA of the organizer trying to schedule this principal
         @type organizer: C{str}
         """
+        return self.record.canAutoSchedule(organizer)
 
-        if config.Scheduling.Options.AutoSchedule.Enabled:
-            if (config.Scheduling.Options.AutoSchedule.Always or
-                self.getAutoSchedule() or
-                self.autoAcceptFromOrganizer(organizer)):
-                if (self.getCUType() != "INDIVIDUAL" or
-                    config.Scheduling.Options.AutoSchedule.AllowUsers):
-                    return True
-        return False
 
-
     @inlineCallbacks
     def setAutoScheduleMode(self, autoScheduleMode):
         self.record.autoScheduleMode = autoScheduleMode if autoScheduleMode in allowedAutoScheduleModes else "default"
@@ -1110,10 +1099,7 @@
             accept-if-free, decline-if-busy, automatic (see stdconfig.py)
         @rtype: C{str}
         """
-        autoScheduleMode = self.record.autoScheduleMode
-        if self.autoAcceptFromOrganizer(organizer):
-            autoScheduleMode = "automatic"
-        return autoScheduleMode
+        return self.record.getAutoScheduleMode(organizer)
 
 
     @inlineCallbacks
@@ -1149,12 +1135,7 @@
             of that group.  False otherwise.
         @rtype: C{bool}
         """
-        if organizer is not None and self.record.autoAcceptGroup is not None:
-            organizerPrincipal = self.parent.principalForCalendarUserAddress(organizer)
-            if organizerPrincipal is not None:
-                if organizerPrincipal.record.guid in self.record.autoAcceptMembers():
-                    return True
-        return False
+        return self.record.autoAcceptFromOrganizer()
 
 
     def getCUType(self):
@@ -1200,7 +1181,7 @@
 
 
     def calendarsEnabled(self):
-        return config.EnableCalDAV and self.record.enabledForCalendaring
+        return self.record.calendarsEnabled()
 
 
     def addressBooksEnabled(self):
@@ -1222,21 +1203,9 @@
 
 
     def calendarUserAddresses(self):
+        return self.record.calendarUserAddresses()
 
-        # No CUAs if not enabledForCalendaring.
-        if not self.record.enabledForCalendaring:
-            return set()
 
-        # Get any CUAs defined by the directory implementation.
-        addresses = set(self.record.calendarUserAddresses)
-
-        # Add the principal URL and alternate URIs to the list.
-        for uri in ((self.principalURL(),) + tuple(self.alternateURIs())):
-            addresses.add(uri)
-
-        return addresses
-
-
     def htmlElement(self):
         """
         Customize HTML generation for calendar principals.
@@ -1251,33 +1220,11 @@
             mailto: form
             first in calendarUserAddresses( ) list
         """
+        return self.record.canonicalCalendarUserAddress()
 
-        cua = ""
-        for candidate in self.calendarUserAddresses():
-            # Pick the first one, but urn:uuid: and mailto: can override
-            if not cua:
-                cua = candidate
-            # But always immediately choose the urn:uuid: form
-            if candidate.startswith("urn:uuid:"):
-                cua = candidate
-                break
-            # Prefer mailto: if no urn:uuid:
-            elif candidate.startswith("mailto:"):
-                cua = candidate
-        return cua
 
-
     def enabledAsOrganizer(self):
-        if self.record.recordType == DirectoryService.recordType_users:
-            return True
-        elif self.record.recordType == DirectoryService.recordType_groups:
-            return config.Scheduling.Options.AllowGroupAsOrganizer
-        elif self.record.recordType == DirectoryService.recordType_locations:
-            return config.Scheduling.Options.AllowLocationAsOrganizer
-        elif self.record.recordType == DirectoryService.recordType_resources:
-            return config.Scheduling.Options.AllowResourceAsOrganizer
-        else:
-            return False
+        return self.record.enabledAsOrganizer()
 
 
     @inlineCallbacks

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/file.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/file.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
+from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
 
 """
 File calendar store.
@@ -274,6 +275,10 @@
     calendarObjectsSinceToken = CommonHomeChild.objectResourcesSinceToken
 
 
+    def _createCalendarObjectWithNameInternal(self, name, component, internal_state, options=None):
+        return self.createCalendarObjectWithName(name, component, options)
+
+
     def calendarObjectsInTimeRange(self, start, end, timeZone):
         raise NotImplementedError()
 
@@ -495,10 +500,22 @@
         return component
 
 
-    def remove(self):
-        pass
+    def componentForUser(self, user_uuid=None):
+        """
+        Return the iCalendar component filtered for the specified user's per-user data.
 
+        @param user_uuid: the user UUID to filter on
+        @type user_uuid: C{str}
 
+        @return: the filtered calendar component
+        @rtype: L{twistedcaldav.ical.Component}
+        """
+
+        if user_uuid is None:
+            user_uuid = self._parentCollection.viewerHome().uid()
+        return PerUserDataFilter(user_uuid).filter(self.component().duplicate())
+
+
     def _text(self):
         if self._objectText is not None:
             return self._objectText

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/index_file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/index_file.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/index_file.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -43,7 +43,7 @@
 
 from twext.python.log import Logger, LoggingMixIn
 
-from txdav.common.icommondatastore import SyncTokenValidException,\
+from txdav.common.icommondatastore import SyncTokenValidException, \
     ReservationError, IndexedSearchException
 
 from twistedcaldav.dateops import pyCalendarTodatetime
@@ -71,7 +71,7 @@
     "BUSY-UNAVAILABLE": 'U',
     "BUSY-TENTATIVE"  : 'T',
 }
-indexfbtype_to_icalfbtype = dict([(v, k) for k,v in icalfbtype_to_indexfbtype.iteritems()])
+indexfbtype_to_icalfbtype = dict([(v, k) for k, v in icalfbtype_to_indexfbtype.iteritems()])
 
 
 class AbstractCalendarIndex(AbstractSQLDatabase, LoggingMixIn):
@@ -91,12 +91,14 @@
         db_filename = self.resource.fp.child(db_basename).path
         super(AbstractCalendarIndex, self).__init__(db_filename, False)
 
+
     def create(self):
         """
         Create the index and initialize it.
         """
         self._db()
 
+
     def reserveUID(self, uid):
         """
         Reserve a UID for this index's resource.
@@ -105,6 +107,7 @@
         """
         raise NotImplementedError
 
+
     def unreserveUID(self, uid):
         """
         Unreserve a UID for this index's resource.
@@ -113,6 +116,7 @@
         """
         raise NotImplementedError
 
+
     def isReservedUID(self, uid):
         """
         Check to see whether a UID is reserved.
@@ -121,6 +125,7 @@
         """
         raise NotImplementedError
 
+
     def isAllowedUID(self, uid, *names):
         """
         Checks to see whether to allow an operation with adds the the specified
@@ -135,6 +140,7 @@
         """
         raise NotImplementedError
 
+
     def resourceNamesForUID(self, uid):
         """
         Looks up the names of the resources with the given UID.
@@ -160,6 +166,7 @@
 
         return resources
 
+
     def resourceNameForUID(self, uid):
         """
         Looks up the name of the resource with the given UID.
@@ -174,6 +181,7 @@
 
         return result
 
+
     def resourceUIDForName(self, name):
         """
         Looks up the UID of the resource with the given name.
@@ -185,12 +193,14 @@
 
         return uid
 
+
     def componentTypeCounts(self):
         """
         Count each type of component.
         """
         return self._db_execute("select TYPE, COUNT(TYPE) from RESOURCE group by TYPE")
 
+
     def addResource(self, name, calendar, fast=False, reCreate=False):
         """
         Adding or updating an existing resource.
@@ -209,6 +219,7 @@
         if not fast:
             self._db_commit()
 
+
     def deleteResource(self, name):
         """
         Remove this resource from the index.
@@ -220,6 +231,7 @@
             self._delete_from_db(name, uid)
             self._db_commit()
 
+
     def resourceExists(self, name):
         """
         Determines whether the specified resource name exists in the index.
@@ -229,6 +241,7 @@
         uid = self._db_value_for_sql("select UID from RESOURCE where NAME = :1", name)
         return uid is not None
 
+
     def resourcesExist(self, names):
         """
         Determines whether the specified resource name exists in the index.
@@ -253,11 +266,12 @@
             self.log_info("Search falls outside range of index for %s %s" % (name, minDate))
             self.reExpandResource(name, minDate)
 
+
     def whatchanged(self, revision):
 
         results = [(name.encode("utf-8"), deleted) for name, deleted in self._db_execute("select NAME, DELETED from REVISIONS where REVISION > :1", revision)]
-        results.sort(key=lambda x:x[1])
-        
+        results.sort(key=lambda x: x[1])
+
         changed = []
         deleted = []
         for name, wasdeleted in results:
@@ -269,14 +283,16 @@
                     changed.append(name)
             else:
                 raise SyncTokenValidException
-        
+
         return changed, deleted,
 
+
     def lastRevision(self):
         return self._db_value_for_sql(
             "select REVISION from REVISION_SEQUENCE"
         )
 
+
     def bumpRevision(self, fast=False):
         self._db_execute(
             """
@@ -290,6 +306,7 @@
             """,
         )
 
+
     def indexedSearch(self, filter, useruid="", fbtype=False):
         """
         Finds resources matching the given qualifiers.
@@ -339,7 +356,7 @@
             if fbtype:
                 # For a free-busy time-range query we return all instances
                 rowiter = self._db_execute(
-                    "select DISTINCT RESOURCE.NAME, RESOURCE.UID, RESOURCE.TYPE, RESOURCE.ORGANIZER, TIMESPAN.FLOAT, TIMESPAN.START, TIMESPAN.END, TIMESPAN.FBTYPE, TIMESPAN.TRANSPARENT, TRANSPARENCY.TRANSPARENT" + 
+                    "select DISTINCT RESOURCE.NAME, RESOURCE.UID, RESOURCE.TYPE, RESOURCE.ORGANIZER, TIMESPAN.FLOAT, TIMESPAN.START, TIMESPAN.END, TIMESPAN.FBTYPE, TIMESPAN.TRANSPARENT, TRANSPARENCY.TRANSPARENT" +
                     qualifiers[0],
                     *qualifiers[1]
                 )
@@ -364,6 +381,7 @@
 
         return results
 
+
     def bruteForceSearch(self):
         """
         List the whole index and tests for existence, updating the index
@@ -393,6 +411,7 @@
         """
         return schema_version
 
+
     def _add_to_db(self, name, calendar, cursor=None, expand_until=None, reCreate=False):
         """
         Records the given calendar resource in the index with the given name.
@@ -406,6 +425,7 @@
         """
         raise NotImplementedError
 
+
     def _delete_from_db(self, name, uid, dorevision=True):
         """
         Deletes the specified entry from all dbs.
@@ -414,6 +434,8 @@
         """
         raise NotImplementedError
 
+
+
 class CalendarIndex (AbstractCalendarIndex):
     """
     Calendar index - abstract class for indexer that indexes calendar objects in a collection.
@@ -426,6 +448,7 @@
         """
         super(CalendarIndex, self).__init__(resource)
 
+
     def _db_init_data_tables_base(self, q, uidunique):
         """
         Initialise the underlying database tables.
@@ -587,15 +610,17 @@
             end
             """
         )
-        
+
+
     def _db_can_upgrade(self, old_version):
         """
         Can we do an in-place upgrade
         """
-        
+
         # v10 is a big change - no upgrade possible
         return False
 
+
     def _db_upgrade_data_tables(self, q, old_version):
         """
         Upgrade the data from an older version of the DB.
@@ -604,6 +629,7 @@
         # v10 is a big change - no upgrade possible
         pass
 
+
     def notExpandedBeyond(self, minDate):
         """
         Gives all resources which have not been expanded beyond a given date
@@ -611,6 +637,7 @@
         """
         return self._db_values_for_sql("select NAME from RESOURCE where RECURRANCE_MAX < :1", pyCalendarTodatetime(minDate))
 
+
     def reExpandResource(self, name, expand_until):
         """
         Given a resource name, remove it from the database and re-add it
@@ -620,7 +647,8 @@
         self._add_to_db(name, calendar, expand_until=expand_until, reCreate=True)
         self._db_commit()
 
-    def _add_to_db(self, name, calendar, cursor = None, expand_until=None, reCreate=False):
+
+    def _add_to_db(self, name, calendar, cursor=None, expand_until=None, reCreate=False):
         """
         Records the given calendar resource in the index with the given name.
         Resource names and UIDs must both be unique; only one resource name may
@@ -684,11 +712,11 @@
             log.err("Invalid instance %s when indexing %s in %s" % (e.rid, name, self.resource,))
             raise
 
-        # Now coerce indexing to off if needed 
+        # Now coerce indexing to off if needed
         if not doInstanceIndexing:
             instances = None
             recurrenceLimit = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
-            
+
         self._delete_from_db(name, uid, False)
 
         # Add RESOURCE item
@@ -719,7 +747,7 @@
                 )
                 peruserid = self.lastrowid
             useruidmap[useruid] = peruserid
-            
+
         if doInstanceIndexing:
             for key in instances:
                 instance = instances[key]
@@ -749,8 +777,7 @@
                         values (:1, :2, :3)
                         """, peruserid, instanceid, 'T' if transp else 'F'
                     )
-                        
-    
+
             # Special - for unbounded recurrence we insert a value for "infinity"
             # that will allow an open-ended time-range to always match it.
             if calendar.isRecurringUnbounded():
@@ -773,7 +800,7 @@
                         values (:1, :2, :3)
                         """, peruserid, instanceid, 'T' if transp else 'F'
                     )
-            
+
         self._db_execute(
             """
             insert or replace into REVISIONS (NAME, REVISION, DELETED)
@@ -781,6 +808,7 @@
             """, name, self.bumpRevision(fast=True), 'N',
         )
 
+
     def _delete_from_db(self, name, uid, dorevision=True):
         """
         Deletes the specified entry from all dbs.
@@ -797,6 +825,7 @@
             )
 
 
+
 def wrapInDeferred(f):
     def _(*args, **kwargs):
         return maybeDeferred(f, *args, **kwargs)
@@ -804,16 +833,19 @@
     return _
 
 
+
 class MemcachedUIDReserver(CachePoolUserMixIn, LoggingMixIn):
     def __init__(self, index, cachePool=None):
         self.index = index
         self._cachePool = cachePool
 
+
     def _key(self, uid):
         return 'reservation:%s' % (
             hashlib.md5('%s:%s' % (uid,
                                    self.index.resource.fp.path)).hexdigest())
 
+
     def reserveUID(self, uid):
         uid = uid.encode('utf-8')
         self.log_debug("Reserving UID %r @ %r" % (
@@ -847,7 +879,7 @@
                     % (uid, self.index.resource)
                     )
 
-        d =self.getCachePool().delete(self._key(uid))
+        d = self.getCachePool().delete(self._key(uid))
         d.addCallback(_handleFalse)
         return d
 
@@ -874,6 +906,7 @@
     def __init__(self, index):
         self.index = index
 
+
     @wrapInDeferred
     def reserveUID(self, uid):
         """
@@ -896,6 +929,7 @@
             self.index._db_rollback()
             raise
 
+
     def unreserveUID(self, uid):
         """
         Unreserve a UID for this index's resource.
@@ -937,7 +971,7 @@
             # Double check that the time is within a reasonable period of now
             # otherwise we probably have a stale reservation
             tm = time.strptime(attime[:19], "%Y-%m-%d %H:%M:%S")
-            dt = datetime.datetime(year=tm.tm_year, month=tm.tm_mon, day=tm.tm_mday, hour=tm.tm_hour, minute=tm.tm_min, second = tm.tm_sec)
+            dt = datetime.datetime(year=tm.tm_year, month=tm.tm_mon, day=tm.tm_mday, hour=tm.tm_hour, minute=tm.tm_min, second=tm.tm_sec)
             if datetime.datetime.now() - dt > datetime.timedelta(seconds=config.UIDReservationTimeOut):
                 try:
                     self.index._db_execute("delete from RESERVED where UID = :1", uid)
@@ -976,6 +1010,7 @@
         else:
             self.reserver = SQLUIDReserver(self)
 
+
     #
     # A dict of sets. The dict keys are calendar collection paths,
     # and the sets contains reserved UIDs for each path.
@@ -1007,12 +1042,14 @@
         rname = self.resourceNameForUID(uid)
         return (rname is None or rname in names)
 
+
     def _db_type(self):
         """
         @return: the collection type assigned to this index.
         """
         return collection_types["Calendar"]
 
+
     def _db_init_data_tables(self, q):
         """
         Initialise the underlying database tables.
@@ -1022,6 +1059,7 @@
         # Create database where the RESOURCE table has unique UID column.
         self._db_init_data_tables_base(q, True)
 
+
     def _db_recreate(self, do_commit=True):
         """
         Re-create the database tables from existing calendar data.
@@ -1060,6 +1098,8 @@
         if do_commit:
             self._db_commit()
 
+
+
 class IndexSchedule (CalendarIndex):
     """
     Schedule collection index - does not require UID uniqueness.
@@ -1075,6 +1115,7 @@
         # iTIP does not require unique UIDs
         return succeed(None)
 
+
     def unreserveUID(self, uid): #@UnusedVariable
         """
         Unreserve a UID for this index's resource.
@@ -1085,6 +1126,7 @@
         # iTIP does not require unique UIDs
         return succeed(None)
 
+
     def isReservedUID(self, uid): #@UnusedVariable
         """
         Check to see whether a UID is reserved.
@@ -1095,6 +1137,7 @@
         # iTIP does not require unique UIDs
         return succeed(False)
 
+
     def isAllowedUID(self, uid, *names): #@UnusedVariable
         """
         Checks to see whether to allow an operation with adds the the specified
@@ -1111,12 +1154,14 @@
         # iTIP does not require unique UIDs
         return True
 
+
     def _db_type(self):
         """
         @return: the collection type assigned to this index.
         """
         return collection_types["iTIP"]
 
+
     def _db_init_data_tables(self, q):
         """
         Initialise the underlying database tables.
@@ -1126,6 +1171,7 @@
         # Create database where the RESOURCE table has a UID column that is not unique.
         self._db_init_data_tables_base(q, False)
 
+
     def _db_recreate(self, do_commit=True):
         """
         Re-create the database tables from existing calendar data.

Copied: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/schedule.py (from rev 11028, CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling.py)
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/schedule.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/schedule.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -0,0 +1,217 @@
+# -*- test-case-name: txdav.caldav.datastore.test.test_scheduling -*-
+##
+# Copyright (c) 2010-2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+from zope.interface.declarations import implements
+from txdav.caldav.icalendarstore import ICalendarHome, ICalendar, ICalendarObject, \
+    ICalendarTransaction, ICalendarStore
+
+from twisted.python.util import FancyEqMixin
+from twisted.python.components import proxyForInterface
+from twisted.internet.defer import inlineCallbacks, returnValue
+
+
+
+class ImplicitTransaction(
+        proxyForInterface(ICalendarTransaction,
+                          originalAttribute="_transaction")):
+    """
+    Wrapper around an L{ICalendarStoreTransaction}.
+    """
+
+    def __init__(self, transaction):
+        """
+        Initialize an L{ImplicitTransaction}.
+
+        @type transaction: L{ICalendarStoreTransaction}
+        """
+        self._transaction = transaction
+
+
+    @inlineCallbacks
+    def calendarHomeWithUID(self, uid, create=False):
+        # FIXME: 'create' flag
+        newHome = yield super(ImplicitTransaction, self
+            ).calendarHomeWithUID(uid, create)
+#        return ImplicitCalendarHome(newHome, self)
+        if newHome is None:
+            returnValue(None)
+        else:
+            # FIXME: relay transaction
+            returnValue(ImplicitCalendarHome(newHome, None))
+
+
+
+class ImplicitCalendarHome(
+        proxyForInterface(ICalendarHome, "_calendarHome")
+    ):
+
+    implements(ICalendarHome)
+
+    def __init__(self, calendarHome, transaction):
+        """
+        Initialize L{ImplicitCalendarHome} with an underlying
+        calendar home and L{ImplicitTransaction}.
+        """
+        self._calendarHome = calendarHome
+        self._transaction = transaction
+
+
+#    def properties(self):
+#        # FIXME: wrap?
+#        return self._calendarHome.properties()
+
+    @inlineCallbacks
+    def calendars(self):
+        superCalendars = (yield super(ImplicitCalendarHome, self).calendars())
+        wrapped = []
+        for calendar in superCalendars:
+            wrapped.append(ImplicitCalendar(self, calendar))
+        returnValue(wrapped)
+
+
+    @inlineCallbacks
+    def loadCalendars(self):
+        superCalendars = (yield super(ImplicitCalendarHome, self).loadCalendars())
+        wrapped = []
+        for calendar in superCalendars:
+            wrapped.append(ImplicitCalendar(self, calendar))
+        returnValue(wrapped)
+
+
+    def createCalendarWithName(self, name):
+        self._calendarHome.createCalendarWithName(name)
+
+
+    def removeCalendarWithName(self, name):
+        self._calendarHome.removeCalendarWithName(name)
+
+
+    @inlineCallbacks
+    def calendarWithName(self, name):
+        calendar = yield self._calendarHome.calendarWithName(name)
+        if calendar is not None:
+            returnValue(ImplicitCalendar(self, calendar))
+        else:
+            returnValue(None)
+
+
+    def hasCalendarResourceUIDSomewhereElse(self, uid, ok_object, type):
+        return self._calendarHome.hasCalendarResourceUIDSomewhereElse(uid, ok_object, type)
+
+
+    def getCalendarResourcesForUID(self, uid, allow_shared=False):
+        return self._calendarHome.getCalendarResourcesForUID(uid, allow_shared)
+
+
+
+class ImplicitCalendarObject(object):
+    implements(ICalendarObject)
+
+    def setComponent(self, component):
+        pass
+
+
+    def component(self):
+        pass
+
+
+    def uid(self):
+        pass
+
+
+    def componentType(self):
+        pass
+
+
+    def organizer(self):
+        pass
+
+
+    def properties(self):
+        pass
+
+
+
+class ImplicitCalendar(FancyEqMixin,
+                       proxyForInterface(ICalendar, "_subCalendar")):
+
+    compareAttributes = (
+        "_subCalendar",
+        "_parentHome",
+    )
+
+    def __init__(self, parentHome, subCalendar):
+        self._parentHome = parentHome
+        self._subCalendar = subCalendar
+        self._supportedComponents = None
+
+#    def ownerCalendarHome(self):
+#        return self._parentHome
+#    def calendarObjects(self):
+#        # FIXME: wrap
+#        return self._subCalendar.calendarObjects()
+#    def calendarObjectWithUID(self, uid): ""
+#    def createCalendarObjectWithName(self, name, component):
+#        # FIXME: implement most of StoreCalendarObjectResource here!
+#        self._subCalendar.createCalendarObjectWithName(name, component)
+#    def syncToken(self): ""
+#    def calendarObjectsInTimeRange(self, start, end, timeZone): ""
+#    def calendarObjectsSinceToken(self, token): ""
+#    def properties(self):
+#        # FIXME: probably need to wrap this as well
+#        return self._subCalendar.properties()
+#
+#    def calendarObjectWithName(self, name):
+#        #FIXME: wrap
+#        return self._subCalendar.calendarObjectWithName(name)
+
+
+    def _createCalendarObjectWithNameInternal(self, name, component, internal_state, options=None):
+        return self.createCalendarObjectWithName(name, component, options)
+
+    def setSupportedComponents(self, supported_components):
+        """
+        Update the database column with the supported components. Technically this should only happen once
+        on collection creation, but for migration we may need to change after the fact - hence a separate api.
+        """
+        self._supportedComponents = supported_components
+
+
+    def getSupportedComponents(self):
+        return self._supportedComponents
+
+
+
+class ImplicitStore(proxyForInterface(ICalendarStore, "_calendarStore")):
+    """
+    This is a wrapper around an L{ICalendarStore} that implements implicit
+    scheduling.
+    """
+
+    def __init__(self, calendarStore):
+        """
+        Create an L{ImplicitStore} wrapped around another
+        L{ICalendarStore} provider.
+        """
+        self._calendarStore = calendarStore
+
+
+    def newTransaction(self, label="unlabeled"):
+        """
+        Wrap an underlying L{ITransaction}.
+        """
+        return ImplicitTransaction(
+                    self._calendarStore.newTransaction(label))

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/delivery.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/delivery.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/delivery.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -101,7 +101,7 @@
 
         organizerPrincipal = None
         if type(self.scheduler.organizer) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,):
-            organizerPrincipal = self.scheduler.organizer.principal.uid()
+            organizerPrincipal = self.scheduler.organizer.principal.uid
 
         for recipient in self.recipients:
 
@@ -180,7 +180,7 @@
     def generateFreeBusyResponse(self, recipient, responses, organizerProp, organizerPrincipal, uid, event_details):
 
         # Extract the ATTENDEE property matching current recipient from the calendar data
-        cuas = recipient.principal.calendarUserAddresses()
+        cuas = recipient.principal.calendarUserAddresses
         attendeeProp = self.scheduler.calendar.getAttendeeProperty(cuas)
 
         remote = isinstance(self.scheduler.organizer, RemoteCalendarUser)
@@ -239,7 +239,7 @@
         # Check to see if the recipient is the same calendar user as the organizer.
         # Needed for masked UID stuff.
         if isinstance(self.scheduler.organizer, LocalCalendarUser):
-            same_calendar_user = self.scheduler.organizer.principal.principalURL() == recipient.principal.principalURL()
+            same_calendar_user = self.scheduler.organizer.principal.uid == recipient.principal.uid
         else:
             same_calendar_user = False
 

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/resource.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/resource.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -473,7 +473,7 @@
         scheduler = CalDAVScheduler(request, self)
 
         # Do the POST processing treating
-        result = (yield scheduler.doSchedulingViaPOST(self._associatedTransaction))
+        result = (yield scheduler.doSchedulingViaPOST())
         returnValue(result.response())
 
 

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/scheduler.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/scheduler.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/scheduler.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -29,9 +29,7 @@
     InvalidCalendarUser, calendarUserFromPrincipal, RemoteCalendarUser
 from txdav.caldav.datastore.scheduling.scheduler import Scheduler, ScheduleResponseQueue
 
-from txdav.xml import element as davxml
 
-
 """
 L{CalDAVScheduler} - handles deliveries for scheduling messages within the CalDAV server.
 """
@@ -63,8 +61,8 @@
         "max-recipients": (caldav_namespace, "recipient-limit"),
     }
 
-    def __init__(self, calendar_home, calendar_collection, resource, **kwargs):
-        super(CalDAVScheduler, self).__init__(calendar_home, calendar_collection, resource, **kwargs)
+    def __init__(self, txn, originator_uid, **kwargs):
+        super(CalDAVScheduler, self).__init__(txn, originator_uid, **kwargs)
         self.doingPOST = False
 
 
@@ -78,7 +76,7 @@
 
     def checkAuthorization(self):
         # Must have an authenticated user
-        if not self.internal_request and self.resource.currentPrincipal(self.request) == davxml.Principal(davxml.Unauthenticated()):
+        if not self.internal_request and self.originator_uid == None:
             log.err("Unauthenticated originators not allowed: %s" % (self.originator,))
             raise HTTPError(self.errorResponse(
                 responsecode.FORBIDDEN,
@@ -93,7 +91,7 @@
         """
 
         # Verify that Originator is a valid calendar user
-        originatorPrincipal = self.calendar_home.principalForCalendarUserAddress(self.originator)
+        originatorPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.originator)
         if originatorPrincipal is None:
             # Local requests MUST have a principal.
             log.err("Could not find principal for originator: %s" % (self.originator,))
@@ -116,7 +114,7 @@
         results = []
         for recipient in self.recipients:
             # Get the principal resource for this recipient
-            principal = self.calendar_home.principalForCalendarUserAddress(recipient)
+            principal = self.txn.directoryService().recordWithCalendarUserAddress(recipient)
 
             # If no principal we may have a remote recipient but we should check whether
             # the address is one that ought to be on our server and treat that as a missing
@@ -131,7 +129,7 @@
                 inbox = None
                 if principal.thisServer():
                     if principal.locallyHosted():
-                        recipient_home = yield self.calendar_home.transaction().calendarHomeWithUID(principal.uid())
+                        recipient_home = yield self.txn.calendarHomeWithUID(principal.uid)
                         if recipient_home:
                             inbox = (yield recipient_home.calendarWithName("inbox"))
                     else:
@@ -155,7 +153,7 @@
         # Verify that the ORGANIZER's cu address maps to a valid user
         organizer = self.calendar.getOrganizer()
         if organizer:
-            organizerPrincipal = self.calendar_home.principalForCalendarUserAddress(organizer)
+            organizerPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(organizer)
             if organizerPrincipal:
                 if organizerPrincipal.calendarsEnabled():
 
@@ -210,7 +208,7 @@
             ))
 
         # Make sure that the ORGANIZER's Outbox is the request URI
-        if self.doingPOST and self.organizer.principal.uid() != self.calendar_home.uid():
+        if self.doingPOST is not None and self.organizer.principal.uid != self.originator_uid:
             log.err("Wrong outbox for ORGANIZER in calendar data: %s" % (self.calendar,))
             raise HTTPError(self.errorResponse(
                 responsecode.FORBIDDEN,
@@ -226,9 +224,9 @@
         """
 
         # Attendee's Outbox MUST be the request URI
-        attendeePrincipal = self.calendar_home.principalForCalendarUserAddress(self.attendee)
+        attendeePrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.attendee)
         if attendeePrincipal:
-            if self.doingPOST and attendeePrincipal.uid() != self.calendar_home.uid():
+            if self.doingPOST is not None and attendeePrincipal.uid != self.originator_uid:
                 log.err("ATTENDEE in calendar data does not match owner of Outbox: %s" % (self.calendar,))
                 raise HTTPError(self.errorResponse(
                     responsecode.FORBIDDEN,

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -15,32 +15,36 @@
 ##
 
 
-from twistedcaldav.test.util import TestCase
-import email
+from calendarserver.tap.util import getRootResource, directoryFromConfig
+
 from twisted.internet.defer import inlineCallbacks
 from twisted.python.modules import getModule
+
+from twistedcaldav.config import config, ConfigDict
 from twistedcaldav.ical import Component
-from twistedcaldav.scheduling.imip.inbound import MailReceiver
-from twistedcaldav.scheduling.imip.inbound import MailRetriever
-from twistedcaldav.scheduling.imip.inbound import injectMessage
-from twistedcaldav.scheduling.imip.inbound import IMIPReplyWork
-from twistedcaldav.scheduling.itip import iTIPRequestStatus
+from twistedcaldav.test.util import TestCase
 from twistedcaldav.test.util import xmlFile
-from txdav.common.datastore.test.util import buildStore
-from calendarserver.tap.util import getRootResource
-from twistedcaldav.config import config, ConfigDict
 
+from txdav.caldav.datastore.scheduling.imip.inbound import IMIPReplyWork
+from txdav.caldav.datastore.scheduling.imip.inbound import MailReceiver
+from txdav.caldav.datastore.scheduling.imip.inbound import MailRetriever
+from txdav.caldav.datastore.scheduling.imip.inbound import injectMessage
+from txdav.caldav.datastore.scheduling.itip import iTIPRequestStatus
+from txdav.caldav.datastore.test.util import buildCalendarStore
 
+import email
+
+
 class InboundTests(TestCase):
 
     @inlineCallbacks
     def setUp(self):
         super(InboundTests, self).setUp()
 
-        self.store = yield buildStore(self, None)
         self.patch(config.DirectoryService.params, "xmlFile", xmlFile)
+        self.store = yield buildCalendarStore(self, None, directoryFromConfig(config))
         self.root = getRootResource(config, self.store)
-        self.directory = self.root.getDirectory()
+        self.directory = self.store.directoryService()
         self.receiver = MailReceiver(self.store, self.directory)
         self.retriever = MailRetriever(self.store, self.directory,
             ConfigDict({

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/implicit.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/implicit.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -33,7 +33,7 @@
     normalizeCUAddr
 from txdav.caldav.datastore.scheduling.icaldiff import iCalDiff
 from txdav.caldav.datastore.scheduling.itip import iTipGenerator, iTIPRequestStatus
-from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForPrincipals
+from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForRecord
 
 import collections
 
@@ -103,6 +103,7 @@
         @type internal_request: C{bool}
         """
 
+        self.txn = parent._txn
         self.parent = parent
         self.resource = resource
         self.calendar = calendar
@@ -183,6 +184,7 @@
         @type internal_request: C{bool}
         """
 
+        self.txn = parent._txn
         self.parent = parent
         self.resource = resource
         self.calendar = calendar
@@ -329,6 +331,7 @@
         Refresh the iCalendar data for all attendees except the one specified in attendees.
         """
 
+        self.txn = resource._txn
         self.request = request
         self.resource = resource
         self.calendar = (yield self.resource.iCalendarForUser(self.request))
@@ -344,7 +347,7 @@
 
         # Get some useful information from the calendar
         yield self.extractCalendarData()
-        self.organizerPrincipal = self.home.principalForCalendarUserAddress(self.organizer)
+        self.organizerPrincipal = self.home.directoryService().recordWithCalendarUserAddress(self.organizer)
         self.organizerAddress = (yield addressmapping.mapper.getCalendarUser(self.organizer, self.organizerPrincipal))
 
         # Originator is the organizer in this case
@@ -376,6 +379,7 @@
     @inlineCallbacks
     def sendAttendeeReply(self, request, resource, calendar, attendee):
 
+        self.txn = resource._txn
         self.request = request
         self.resource = resource
         self.calendar = calendar
@@ -401,7 +405,7 @@
     def extractCalendarData(self):
 
         # Get the originator who is the owner of the calendar resource being modified
-        self.originatorPrincipal = self.calendar_home.principalForUID(self.calendar_owner)
+        self.originatorPrincipal = self.calendar_home.directoryService().recordWithUID(self.calendar_owner)
 
         # Pick the canonical CUA:
         self.originator = self.originatorPrincipal.canonicalCalendarUserAddress()
@@ -465,13 +469,13 @@
             returnValue(False)
 
         # Organizer must map to a valid principal
-        self.organizerPrincipal = self.calendar_home.principalForCalendarUserAddress(self.organizer)
+        self.organizerPrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(self.organizer)
         self.organizerAddress = (yield addressmapping.mapper.getCalendarUser(self.organizer, self.organizerPrincipal))
         if not self.organizerPrincipal:
             returnValue(False)
 
         # Organizer must be the owner of the calendar resource
-        if self.calendar_owner != self.organizerPrincipal.uid():
+        if self.calendar_owner != self.organizerPrincipal.uid:
             returnValue(False)
 
         returnValue(True)
@@ -485,8 +489,8 @@
 
         # Check to see whether any attendee is the owner
         for attendee in self.attendees:
-            attendeePrincipal = self.calendar_home.principalForCalendarUserAddress(attendee)
-            if attendeePrincipal and attendeePrincipal.uid() == self.calendar_owner:
+            attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(attendee)
+            if attendeePrincipal and attendeePrincipal.uid == self.calendar_owner:
                 self.attendee = attendee
                 self.attendeePrincipal = attendeePrincipal
                 return True
@@ -498,7 +502,7 @@
         """
         Convenience method which we can override in unit tests to make testing easier.
         """
-        return CalDAVScheduler(self.calendar_home, self.parent, self.resource, logItems=self.logItems)
+        return CalDAVScheduler(self.txn, self.calendar_owner, logItems=self.logItems)
 
 
     @inlineCallbacks
@@ -560,7 +564,7 @@
                         if attendee.hasParameter("PARTSTAT"):
                             cuaddr = attendee.value()
 
-                            if cuaddr in self.organizerPrincipal.calendarUserAddresses():
+                            if cuaddr in self.organizerPrincipal.calendarUserAddresses:
                                 # If the attendee is the organizer then do not update
                                 # the PARTSTAT to NEEDS-ACTION.
                                 # The organizer is automatically ACCEPTED to the event.
@@ -799,7 +803,7 @@
         """
         for attendee in self.calendar.getAllAttendeeProperties():
             # Don't adjust ORGANIZER's ATTENDEE
-            if attendee.value() in self.organizerPrincipal.calendarUserAddresses():
+            if attendee.value() in self.organizerPrincipal.calendarUserAddresses:
                 continue
             if attendee.parameterValue("SCHEDULE-AGENT", "SERVER").upper() == "SERVER" and attendee.hasParameter("PARTSTAT"):
                 attendee.setParameter("PARTSTAT", "NEEDS-ACTION")
@@ -852,7 +856,7 @@
         changed = False
         for cuaddr, newattendee in new_attendees.items():
             # Don't adjust ORGANIZER's ATTENDEE
-            if newattendee.value() in self.organizerPrincipal.calendarUserAddresses():
+            if newattendee.value() in self.organizerPrincipal.calendarUserAddresses:
                 continue
             new_partstat = newattendee.parameterValue("PARTSTAT", "NEEDS-ACTION").upper()
             if newattendee.parameterValue("SCHEDULE-AGENT", "SERVER").upper() == "SERVER" and new_partstat != "NEEDS-ACTION":
@@ -877,7 +881,7 @@
             if attendee.parameterValue("SCHEDULE-AGENT", "SERVER").upper() == "CLIENT":
                 cuaddr = attendee.value()
                 if cuaddr not in coerced:
-                    attendeePrincipal = self.resource.principalForCalendarUserAddress(cuaddr)
+                    attendeePrincipal = self.resource.directoryService().recordWithCalendarUserAddress(cuaddr)
                     attendeeAddress = (yield addressmapping.mapper.getCalendarUser(cuaddr, attendeePrincipal))
                     local_attendee = type(attendeeAddress) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,)
                     coerced[cuaddr] = local_attendee
@@ -916,7 +920,7 @@
         for attendee, rids in aggregated.iteritems():
 
             # Don't send message back to the ORGANIZER
-            if attendee in self.organizerPrincipal.calendarUserAddresses():
+            if attendee in self.organizerPrincipal.calendarUserAddresses:
                 continue
 
             # Generate an iTIP CANCEL message for this attendee, cancelling
@@ -955,7 +959,7 @@
         for attendee in self.attendees:
 
             # Don't send message back to the ORGANIZER
-            if attendee in self.organizerPrincipal.calendarUserAddresses():
+            if attendee in self.organizerPrincipal.calendarUserAddresses:
                 continue
 
             # Don't send message to specified attendees
@@ -1157,8 +1161,8 @@
             log.debug("Missing attendee is allowed to update UID: '%s' with invalid organizer '%s'" % (self.uid, self.organizer))
 
             # Make sure ORGANIZER is not changed if originally SCHEDULE-AGENT=SERVER
-            if self.resource.exists():
-                self.oldcalendar = (yield self.resource.iCalendarForUser(self.request))
+            if self.resource is not None:
+                self.oldcalendar = (yield self.resource.componentForUser())
                 oldOrganizer = self.oldcalendar.getOrganizer()
                 newOrganizer = self.calendar.getOrganizer()
                 if oldOrganizer != newOrganizer and self.oldcalendar.getOrganizerScheduleAgent():
@@ -1204,9 +1208,9 @@
         """
 
         self.organizer_calendar = None
-        calendar_resource = (yield getCalendarObjectForPrincipals(self.calendar_home.transaction(), self.organizerPrincipal, self.uid))
+        calendar_resource = (yield getCalendarObjectForRecord(self.calendar_home.transaction(), self.organizerPrincipal, self.uid))
         if calendar_resource is not None:
-            self.organizer_calendar = (yield calendar_resource.componentForUser(self.organizerPrincipal.uid()))
+            self.organizer_calendar = (yield calendar_resource.componentForUser(self.organizerPrincipal.uid))
         elif type(self.organizerAddress) in (PartitionedCalendarUser, OtherServerCalendarUser,):
             # For partitioning where the organizer is on a different node, we will assume that the attendee's copy
             # of the event is up to date and "authoritative". So we pretend that is the organizer copy

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/processing.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/processing.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -37,7 +37,7 @@
 
 from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
 from txdav.caldav.datastore.scheduling.itip import iTipProcessing, iTIPRequestStatus
-from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForPrincipals
+from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForRecord
 
 import collections
 import hashlib
@@ -160,9 +160,9 @@
 
         self.recipient_calendar = None
         self.recipient_calendar_resource = None
-        calendar_resource = (yield getCalendarObjectForPrincipals(self.txn, self.recipient.principal, self.uid))
+        calendar_resource = (yield getCalendarObjectForRecord(self.txn, self.recipient.principal, self.uid))
         if calendar_resource:
-            self.recipient_calendar = (yield calendar_resource.componentForUser(self.recipient.principal.uid()))
+            self.recipient_calendar = (yield calendar_resource.componentForUser(self.recipient.principal.uid))
             self.recipient_calendar_resource = calendar_resource
 
 
@@ -1036,7 +1036,7 @@
             raise ImplicitProcessorException("5.1;Service unavailable")
 
         # Locate the originator's copy of the event
-        calendar_resource, _ignore_name, _ignore_collection, _ignore_uri = (yield getCalendarObjectForPrincipals(self.request, self.originator.principal, self.uid))
+        calendar_resource, _ignore_name, _ignore_collection, _ignore_uri = (yield getCalendarObjectForRecord(self.request, self.originator.principal, self.uid))
         if not calendar_resource:
             raise ImplicitProcessorException("5.1;Service unavailable")
         originator_calendar = (yield calendar_resource.iCalendarForUser(self.request))
@@ -1045,7 +1045,7 @@
         originator_calendar.attendeesView((self.recipient.cuaddr,))
 
         # Locate the attendee's copy of the event if it exists.
-        recipient_resource, recipient_resource_name, recipient_collection, recipient_collection_uri = (yield getCalendarObjectForPrincipals(self.request, self.recipient.principal, self.uid))
+        recipient_resource, recipient_resource_name, recipient_collection, recipient_collection_uri = (yield getCalendarObjectForRecord(self.request, self.recipient.principal, self.uid))
 
         # We only need to fix data that already exists
         if recipient_resource:

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/scheduler.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/scheduler.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/scheduler.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -132,12 +132,10 @@
         "max-recipients": (),
     }
 
-    def __init__(self, calendar_home, calendar_collection, resource, logItems=None, noAttendeeRefresh=False):
+    def __init__(self, txn, originator_uid, logItems=None, noAttendeeRefresh=False):
 
-        self.txn = calendar_home.transaction()
-        self.calendar_home = calendar_home
-        self.calendar_collection = calendar_collection
-        self.resource = resource
+        self.txn = txn
+        self.originator_uid = originator_uid
         self.logItems = logItems
         self.noAttendeeRefresh = noAttendeeRefresh
 
@@ -155,7 +153,7 @@
 
 
     @inlineCallbacks
-    def doSchedulingViaPOST(self, transaction, use_request_headers=False):
+    def doSchedulingViaPOST(self, use_request_headers=False):
         """
         The Scheduling POST operation on an Outbox.
         """
@@ -194,8 +192,8 @@
                 raise HTTPError(StatusResponse(responsecode.CONFLICT, "UID: %s currently in use on the server." % (uid,)))
             else:
                 # Release lock after commit or abort
-                transaction.postCommit(lock.clean)
-                transaction.postAbort(lock.clean)
+                self.txn.postCommit(lock.clean)
+                self.txn.postAbort(lock.clean)
 
         result = (yield self.doScheduling())
         returnValue(result)
@@ -205,9 +203,16 @@
         """
         The implicit scheduling PUT operation.
         """
+        return self.doSchedulingDirectly("PUT", originator, recipients, calendar, internal_request, suppress_refresh)
 
-        self.method = "PUT"
 
+    def doSchedulingDirectly(self, descriptor, originator, recipients, calendar, internal_request=False, suppress_refresh=False):
+        """
+        The implicit scheduling operation.
+        """
+
+        self.method = descriptor
+
         # Load various useful bits doing some basic checks on those
         self.originator = originator
         self.recipients = recipients
@@ -259,17 +264,11 @@
     @inlineCallbacks
     def loadOriginatorFromRequestDetails(self):
         # Get the originator who is the authenticated user
-        originatorPrincipal = None
-        originator = ""
-        authz_principal = self.resource.currentPrincipal(self.request).children[0]
-        if isinstance(authz_principal, davxml.HRef):
-            originatorPrincipalURL = str(authz_principal)
-            if originatorPrincipalURL:
-                originatorPrincipal = (yield self.request.locateResource(originatorPrincipalURL))
-                if originatorPrincipal:
-                    # Pick the canonical CUA:
-                    originator = originatorPrincipal.canonicalCalendarUserAddress()
+        originatorPrincipal = self.txn.directoryService().recordWithUID(self.originator_uid)
 
+        # Pick the canonical CUA:
+        originator = originatorPrincipal.canonicalCalendarUserAddress() if originatorPrincipal else ""
+
         if not originator:
             log.err("%s request must have Originator" % (self.method,))
             raise HTTPError(self.errorResponse(
@@ -691,7 +690,7 @@
         results = []
         for recipient in self.recipients:
             # Get the principal resource for this recipient
-            principal = self.resource.principalForCalendarUserAddress(recipient)
+            principal = self.txn.directoryService.recordWithCalendarUserAddress(recipient)
 
             # If no principal we may have a remote recipient but we should check whether
             # the address is one that ought to be on our server and treat that as a missing
@@ -705,11 +704,17 @@
                 results.append(InvalidCalendarUser(recipient))
             else:
                 # Map recipient to their inbox
-                inboxURL = principal.scheduleInboxURL()
-                inbox = (yield self.request.locateResource(inboxURL)) if principal.locallyHosted() else "dummy"
+                inbox = None
+                if principal.thisServer():
+                    if principal.locallyHosted():
+                        recipient_home = yield self.txn.calendarHomeWithUID(principal.uid)
+                        if recipient_home:
+                            inbox = (yield recipient_home.calendarWithName("inbox"))
+                    else:
+                        inbox = "dummy"
 
                 if inbox:
-                    results.append(calendarUserFromPrincipal(recipient, principal, inbox, inboxURL))
+                    results.append(calendarUserFromPrincipal(recipient, principal, inbox))
                 else:
                     log.err("No schedule inbox for principal: %s" % (principal,))
                     results.append(InvalidCalendarUser(recipient))

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_implicit.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_implicit.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -17,24 +17,24 @@
 from pycalendar.datetime import PyCalendarDateTime
 from pycalendar.timezone import PyCalendarTimezone
 
+from twext.python.clsprop import classproperty
 from twext.web2 import responsecode
 from twext.web2.http import HTTPError
 
 from twisted.internet.defer import succeed, inlineCallbacks
+from twisted.trial.unittest import TestCase
 
 from twistedcaldav.ical import Component
+import twistedcaldav.test.util
 
 from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler
 from txdav.caldav.datastore.scheduling.scheduler import ScheduleResponseQueue
+from txdav.caldav.datastore.test.util import buildCalendarStore, \
+    buildDirectoryRecord
+from txdav.caldav.icalendarstore import AttendeeAllowedError
+from txdav.common.datastore.test.util import CommonCommonTests, populateCalendarsFrom
 
-import twistedcaldav.test.util
-from txdav.common.datastore.test.util import CommonCommonTests, buildStore, \
-    populateCalendarsFrom
-from twisted.trial.unittest import TestCase
-from twext.python.clsprop import classproperty
-from txdav.caldav.datastore.sql import CalendarPrincipal
 import hashlib
-from txdav.caldav.icalendarstore import AttendeeAllowedError
 import sys
 
 class FakeScheduler(object):
@@ -52,10 +52,19 @@
 
 
 
+class FakeDirectoryService(object):
+
+    def recordWithUID(self, uid):
+        return buildDirectoryRecord(uid)
+
+
+
 class FakeCalendarHome(object):
 
-    def principalForUID(self, uid):
-        return CalendarPrincipal(uid, ("urn:uuid:%s" % (uid,), "mailto:%s at example.com" % (uid,),))
+    def directoryService(self):
+        if not hasattr(self, "_directoryService"):
+            self._directoryService = FakeDirectoryService()
+        return self._directoryService
 
 
 
@@ -843,11 +852,11 @@
             scheduler.reinvites = None
 
             scheduler.calendar_home = FakeCalendarHome()
-            scheduler.calendar_owner = "user01"
+            scheduler.calendar_owner = "user1"
 
             # Get some useful information from the calendar
             yield scheduler.extractCalendarData()
-            scheduler.organizerPrincipal = CalendarPrincipal(scheduler.organizer, scheduler.organizer)
+            scheduler.organizerPrincipal = buildDirectoryRecord(scheduler.calendar_owner)
 
             recipients = []
 
@@ -870,7 +879,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(ImplicitRequests, self).setUp()
-        self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
+        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
         yield self.populate()
 
 

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_utils.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_utils.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_utils.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -18,7 +18,7 @@
 Tests for calendarserver.tools.purge
 """
 
-from calendarserver.tap.util import getRootResource
+from calendarserver.tap.util import getRootResource, directoryFromConfig
 
 from pycalendar.datetime import PyCalendarDateTime
 
@@ -27,9 +27,10 @@
 
 from twistedcaldav.config import config
 
-from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForPrincipals
-from txdav.caldav.datastore.sql import CalendarPrincipal
-from txdav.common.datastore.test.util import buildStore, populateCalendarsFrom, CommonCommonTests
+from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForRecord
+from txdav.caldav.datastore.test.util import buildCalendarStore, \
+    buildDirectoryRecord
+from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
 
 import os
 
@@ -110,10 +111,6 @@
     @inlineCallbacks
     def setUp(self):
 
-        yield super(RecipientCopy, self).setUp()
-        self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
-        yield self.populate()
-
         self.patch(config.DirectoryService.params, "xmlFile",
             os.path.join(
                 os.path.dirname(__file__), "accounts.xml"
@@ -124,8 +121,13 @@
                 os.path.dirname(__file__), "resources.xml"
             )
         )
+
+        yield super(RecipientCopy, self).setUp()
+        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory, directoryFromConfig(config))
+        yield self.populate()
+
         self.rootResource = getRootResource(config, self._sqlCalendarStore)
-        self.directory = self.rootResource.getDirectory()
+        self.directory = self._sqlCalendarStore.directoryService()
 
 
     @inlineCallbacks
@@ -142,9 +144,9 @@
 
 
     @inlineCallbacks
-    def test_getCalendarObjectForPrincipals(self):
+    def test_getCalendarObjectForRecord(self):
         """
-        Test that L{twistedcaldav.scheduling.utils.getCalendarObjectForPrincipals} detects and removes
+        Test that L{twistedcaldav.scheduling.utils.getCalendarObjectForRecord} detects and removes
         resources with duplicate UIDs in the same calendar home.
         """
 
@@ -160,9 +162,9 @@
         yield self.commit()
 
         # Look up resource by UID in home where only one exists
-        principal = CalendarPrincipal("user01", ("urn:uuid:user01",))
+        principal = buildDirectoryRecord("user01")
         txn = self.transactionUnderTest()
-        resource = (yield getCalendarObjectForPrincipals(txn, principal, "685BC3A1-195A-49B3-926D-388DDACA78A6"))
+        resource = (yield getCalendarObjectForRecord(txn, principal, "685BC3A1-195A-49B3-926D-388DDACA78A6"))
         self.assertEqual(resource.name(), "1.ics")
         self.assertEqual(resource._parentCollection.name(), "calendar1")
         self.assertEqual(resource._parentCollection.viewerHome().uid(), "user01")
@@ -180,9 +182,9 @@
         yield self.commit()
 
         # Look up resource by UID in home where two exists
-        principal = CalendarPrincipal("user02", ("urn:uuid:user02",))
+        principal = buildDirectoryRecord("user02")
         txn = self.transactionUnderTest()
-        resource = (yield getCalendarObjectForPrincipals(txn, principal, "685BC3A1-195A-49B3-926D-388DDACA78A6"))
+        resource = (yield getCalendarObjectForRecord(txn, principal, "685BC3A1-195A-49B3-926D-388DDACA78A6"))
         self.assertTrue(resource.name() in ("2.ics", "3.ics",))
         self.assertTrue(resource._parentCollection.name() in ("calendar2", "calendar3",))
         self.assertEqual(resource._parentCollection.viewerHome().uid(), "user02")

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/utils.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/utils.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/utils.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -21,17 +21,17 @@
 log = Logger()
 
 @inlineCallbacks
-def getCalendarObjectForPrincipals(txn, principal, uid, allow_shared=False):
+def getCalendarObjectForRecord(txn, record, uid, allow_shared=False):
     """
-    Get a copy of the event for a principal.
+    Get a copy of the event for a calendar user identified by a directory record.
 
     NOTE: if more than one resource with the same UID is found, we will delete all but
     one of them to avoid scheduling problems.
     """
 
-    if principal and principal.locallyHosted():
-        # Get principal's calendar-home
-        calendar_home = yield txn.calendarHomeWithUID(principal.uid())
+    if record and record.locallyHosted():
+        # Get record's calendar-home
+        calendar_home = yield txn.calendarHomeWithUID(record.uid)
 
         # Get matching newstore objects
         objectResources = (yield calendar_home.getCalendarResourcesForUID(uid, allow_shared))

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -1,195 +0,0 @@
-# -*- test-case-name: txdav.caldav.datastore.test.test_scheduling -*-
-##
-# Copyright (c) 2010-2013 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-from zope.interface.declarations import implements
-from txdav.caldav.icalendarstore import ICalendarHome, ICalendar, ICalendarObject,\
-    ICalendarTransaction, ICalendarStore
-
-from twisted.python.util import FancyEqMixin
-from twisted.python.components import proxyForInterface
-from twisted.internet.defer import inlineCallbacks, returnValue
-
-
-
-class ImplicitTransaction(
-        proxyForInterface(ICalendarTransaction,
-                          originalAttribute="_transaction")):
-    """
-    Wrapper around an L{ICalendarStoreTransaction}.
-    """
-
-    def __init__(self, transaction):
-        """
-        Initialize an L{ImplicitTransaction}.
-
-        @type transaction: L{ICalendarStoreTransaction}
-        """
-        self._transaction = transaction
-
-
-    @inlineCallbacks
-    def calendarHomeWithUID(self, uid, create=False):
-        # FIXME: 'create' flag
-        newHome = yield super(ImplicitTransaction, self
-            ).calendarHomeWithUID(uid, create)
-#        return ImplicitCalendarHome(newHome, self)
-        if newHome is None:
-            returnValue(None)
-        else:
-            # FIXME: relay transaction
-            returnValue(ImplicitCalendarHome(newHome, None))
-
-
-
-class ImplicitCalendarHome(
-        proxyForInterface(ICalendarHome, "_calendarHome")
-    ):
-
-    implements(ICalendarHome)
-
-    def __init__(self, calendarHome, transaction):
-        """
-        Initialize L{ImplicitCalendarHome} with an underlying
-        calendar home and L{ImplicitTransaction}.
-        """
-        self._calendarHome = calendarHome
-        self._transaction = transaction
-
-
-#    def properties(self):
-#        # FIXME: wrap?
-#        return self._calendarHome.properties()
-
-    @inlineCallbacks
-    def calendars(self):
-        superCalendars = (yield super(ImplicitCalendarHome, self).calendars())
-        wrapped = []
-        for calendar in superCalendars:
-            wrapped.append(ImplicitCalendar(self, calendar))
-        returnValue(wrapped)
-
-
-    @inlineCallbacks
-    def loadCalendars(self):
-        superCalendars = (yield super(ImplicitCalendarHome, self).loadCalendars())
-        wrapped = []
-        for calendar in superCalendars:
-            wrapped.append(ImplicitCalendar(self, calendar))
-        returnValue(wrapped)
-
-
-    def createCalendarWithName(self, name):
-        self._calendarHome.createCalendarWithName(name)
-
-
-    def removeCalendarWithName(self, name):
-        self._calendarHome.removeCalendarWithName(name)
-
-
-    @inlineCallbacks
-    def calendarWithName(self, name):
-        calendar = yield self._calendarHome.calendarWithName(name)
-        if calendar is not None:
-            returnValue(ImplicitCalendar(self, calendar))
-        else:
-            returnValue(None)
-
-    def hasCalendarResourceUIDSomewhereElse(self, uid, ok_object, type):
-        return self._calendarHome.hasCalendarResourceUIDSomewhereElse(uid, ok_object, type)
-
-    def getCalendarResourcesForUID(self, uid, allow_shared=False):
-        return self._calendarHome.getCalendarResourcesForUID(uid, allow_shared)
-
-
-
-class ImplicitCalendarObject(object):
-    implements(ICalendarObject)
-    def setComponent(self, component): ""
-    def component(self): ""
-    def uid(self): ""
-    def componentType(self): ""
-    def organizer(self): ""
-    def properties(self):""
-
-
-
-class ImplicitCalendar(FancyEqMixin,
-                       proxyForInterface(ICalendar, "_subCalendar")):
-
-    compareAttributes = (
-        "_subCalendar",
-        "_parentHome",
-    )
-
-    def __init__(self, parentHome, subCalendar):
-        self._parentHome = parentHome
-        self._subCalendar = subCalendar
-        self._supportedComponents = None
-
-#    def ownerCalendarHome(self):
-#        return self._parentHome
-#    def calendarObjects(self):
-#        # FIXME: wrap
-#        return self._subCalendar.calendarObjects()
-#    def calendarObjectWithUID(self, uid): ""
-#    def createCalendarObjectWithName(self, name, component):
-#        # FIXME: implement most of StoreCalendarObjectResource here!
-#        self._subCalendar.createCalendarObjectWithName(name, component)
-#    def removeCalendarObjectWithName(self, name):
-#        # FIXME: implement deletion logic here!
-#        return self._subCalendar.removeCalendarObjectWithName(name)
-#    def removeCalendarObjectWithUID(self, uid): ""
-#    def syncToken(self): ""
-#    def calendarObjectsInTimeRange(self, start, end, timeZone): ""
-#    def calendarObjectsSinceToken(self, token): ""
-#    def properties(self):
-#        # FIXME: probably need to wrap this as well
-#        return self._subCalendar.properties()
-#
-#    def calendarObjectWithName(self, name):
-#        #FIXME: wrap
-#        return self._subCalendar.calendarObjectWithName(name)
-
-    def setSupportedComponents(self, supported_components):
-        """
-        Update the database column with the supported components. Technically this should only happen once
-        on collection creation, but for migration we may need to change after the fact - hence a separate api.
-        """
-        self._supportedComponents = supported_components
-
-    def getSupportedComponents(self):
-        return self._supportedComponents
-
-class ImplicitStore(proxyForInterface(ICalendarStore, "_calendarStore")):
-    """
-    This is a wrapper around an L{ICalendarStore} that implements implicit
-    scheduling.
-    """
-
-    def __init__(self, calendarStore):
-        """
-        Create an L{ImplicitStore} wrapped around another
-        L{ICalendarStore} provider.
-        """
-        self._calendarStore = calendarStore
-
-
-    def newTransaction(self, label="unlabeled"):
-        """
-        Wrap an underlying L{ITransaction}.
-        """
-        return ImplicitTransaction(
-                    self._calendarStore.newTransaction(label))

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -314,67 +314,67 @@
 
 
 
-class CalendarPrincipal(object):
+#class CalendarPrincipal(object):
+#
+#    def __init__(self, uid, cuaddrs):
+#        self.principal_uid = uid
+#        self.cuaddrs = cuaddrs
+#
+#
+#    def uid(self):
+#        return self.principal_uid
+#
+#
+#    def shortNames(self):
+#        return [self.principal_uid, ]
+#
+#
+#    def fullName(self):
+#        return "%s %s" % (self.principal_uid[:4].capitalize(), self.principal_uid[4:])
+#
+#
+#    def displayName(self):
+#        fullName = self.fullName()
+#        return fullName if fullName else self.shortNames()[0]
+#
+#
+#    def calendarUserAddresses(self):
+#        return self.cuaddrs
+#
+#
+#    def canonicalCalendarUserAddress(self):
+#        return [cuaddr for cuaddr in self.cuaddrs if cuaddr.startswith("urn:uuid")][0]
+#
+#
+#    def locallyHosted(self):
+#        return True
+#
+#
+#    def thisServer(self):
+#        return True
+#
+#
+#    def calendarsEnabled(self):
+#        return True
+#
+#
+#    def getCUType(self):
+#        return "INDIVIDUAL"
+#
+#
+#    def enabledAsOrganizer(self):
+#        return True
+#
+#
+#    def canAutoSchedule(self, organizer):
+#        return False
+#
+#
+#    def getAutoScheduleMode(self, organizer):
+#        return "auto"
 
-    def __init__(self, uid, cuaddrs):
-        self.principal_uid = uid
-        self.cuaddrs = cuaddrs
 
 
-    def uid(self):
-        return self.principal_uid
-
-
-    def shortNames(self):
-        return [self.principal_uid, ]
-
-
-    def fullName(self):
-        return "%s %s" % (self.principal_uid[:4].capitalize(), self.principal_uid[4:])
-
-
-    def displayName(self):
-        fullName = self.fullName()
-        return fullName if fullName else self.shortNames()[0]
-
-
-    def calendarUserAddresses(self):
-        return self.cuaddrs
-
-
-    def canonicalCalendarUserAddress(self):
-        return [cuaddr for cuaddr in self.cuaddrs if cuaddr.startswith("urn:uuid")][0]
-
-
-    def locallyHosted(self):
-        return True
-
-
-    def thisServer(self):
-        return True
-
-
-    def calendarsEnabled(self):
-        return True
-
-
-    def getCUType(self):
-        return "INDIVIDUAL"
-
-
-    def enabledAsOrganizer(self):
-        return True
-
-
-    def canAutoSchedule(self, organizer):
-        return False
-
-
-    def getAutoScheduleMode(self, organizer):
-        return "auto"
-
-
-
 class CalendarHome(CommonHome):
 
     implements(ICalendarHome)
@@ -831,27 +831,27 @@
         self.properties()[PropertyName.fromElement(prop)] = prop.fromString(alarm)
 
 
-    def principal(self):
-        return self.principalForUID(self.uid())
+#    def principal(self):
+#        return self.principalForUID(self.uid())
+#
+#
+#    def principalForUID(self, uid):
+#        return CalendarPrincipal(uid, ("urn:uuid:%s" % (uid,), "mailto:%s at example.com" % (uid,),))
+#
+#
+#    def principalForCalendarUserAddress(self, cuaddr):
+#        if cuaddr.startswith("mailto:"):
+#            uid, domain = cuaddr[7:].split('@')
+#            if domain != "example.com":
+#                return None
+#            return CalendarPrincipal(uid, (cuaddr, "urn:uuid:%s" % (uid,)))
+#        elif cuaddr.startswith("urn:uuid:"):
+#            uid = cuaddr[9:]
+#            return CalendarPrincipal(uid, (cuaddr, "mailto:%s at example.com" % (uid,)))
+#        else:
+#            return None
 
 
-    def principalForUID(self, uid):
-        return CalendarPrincipal(uid, ("urn:uuid:%s" % (uid,), "mailto:%s at example.com" % (uid,),))
-
-
-    def principalForCalendarUserAddress(self, cuaddr):
-        if cuaddr.startswith("mailto:"):
-            uid, domain = cuaddr[7:].split('@')
-            if domain != "example.com":
-                return None
-            return CalendarPrincipal(uid, (cuaddr, "urn:uuid:%s" % (uid,)))
-        elif cuaddr.startswith("urn:uuid:"):
-            uid = cuaddr[9:]
-            return CalendarPrincipal(uid, (cuaddr, "mailto:%s at example.com" % (uid,)))
-        else:
-            return None
-
-
 CalendarHome._register(ECALENDARTYPE)
 
 
@@ -978,7 +978,7 @@
         inbox resources need to store Originator, Recipient etc properties.
         Other calendars do not have object resources with properties.
         """
-        return not self.isInbox()
+        return self.isInbox()
 
 
     @inlineCallbacks
@@ -1445,10 +1445,10 @@
 
             # Normalize the calendar user addresses once we know we have valid
             # calendar data
-            component.normalizeCalendarUserAddresses(normalizationLookup, self.calendar().viewerHome().principalForCalendarUserAddress)
+            component.normalizeCalendarUserAddresses(normalizationLookup, self.directoryService().recordWithCalendarUserAddress)
 
         # Check location/resource organizer requirement
-        yield self.validLocationResourceOrganizer(component, inserting, internal_state)
+        self.validLocationResourceOrganizer(component, inserting, internal_state)
 
         # Check access
         if config.EnablePrivateEvents:
@@ -1536,14 +1536,13 @@
                     raise TooManyAttendeesError("Attendee list size %d is larger than allowed limit %d" % (attendeeListLength, config.MaxAttendeesPerInstance))
 
 
-    @inlineCallbacks
     def validLocationResourceOrganizer(self, component, inserting, internal_state):
         """
         If the calendar owner is a location or resource, check whether an ORGANIZER property is required.
         """
 
         if internal_state == ComponentUpdateState.NORMAL:
-            originatorPrincipal = (yield self.calendar().ownerHome().principal())
+            originatorPrincipal = self.calendar().ownerHome().directoryRecord()
             cutype = originatorPrincipal.getCUType() if originatorPrincipal is not None else "INDIVIDUAL"
             organizer = component.getOrganizer()
 
@@ -1560,7 +1559,7 @@
 
                 # Find current principal and update modified by details
                 if hasattr(self._txn, "_authz_uid"):
-                    authz = (yield self.calendar().ownerHome().principalForUID(self._txn._authz_uid))
+                    authz = self.directoryService().recordWithUID(self._txn._authz_uid)
                     prop = Property("X-CALENDARSERVER-MODIFIED-BY", authz.canonicalCalendarUserAddress())
                     prop.setParameter("CN", authz.displayName())
                     for candidate in authz.calendarUserAddresses():
@@ -1649,8 +1648,8 @@
                 log.debug("Organizer and attendee properties were entirely removed by the client. Restoring existing properties.")
 
                 # Get the originator who is the owner of the calendar resource being modified
-                originatorPrincipal = (yield self.calendar().ownerHome().principal())
-                originatorAddresses = originatorPrincipal.calendarUserAddresses()
+                originatorPrincipal = self.calendar().ownerHome().directoryRecord()
+                originatorAddresses = originatorPrincipal.calendarUserAddresses
 
                 for component in calendar.subcomponents():
                     if component.name() != "VTODO":
@@ -1686,8 +1685,8 @@
                 log.debug("Sync COMPLETED property change.")
 
                 # Get the originator who is the owner of the calendar resource being modified
-                originatorPrincipal = (yield self.calendar().ownerHome().principal())
-                originatorAddresses = originatorPrincipal.calendarUserAddresses()
+                originatorPrincipal = self.calendar().ownerHome().directoryRecord()
+                originatorAddresses = originatorPrincipal.calendarUserAddresses
 
                 for component in calendar.subcomponents():
                     if component.name() != "VTODO":
@@ -2006,6 +2005,10 @@
 
             self.processScheduleTags(component, inserting, internal_state)
 
+        # When migrating we always do validity check to fix issues
+        elif self._txn._migrating:
+            self.validCalendarDataCheck(component, inserting)
+
         yield self.updateDatabase(component, inserting=inserting)
 
         # Post process managed attachments
@@ -2933,7 +2936,7 @@
         # Write the component back (and no need to re-index as we have not
         # changed any timing properties in the calendar data).
         cal.noInstanceIndexing = True
-        yield self.setComponent(cal)
+        yield self._setComponentInternal(cal, internal_state=ComponentUpdateState.RAW)
 
 
     @inlineCallbacks

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/attachments/accounts.xml
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/attachments/accounts.xml	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/attachments/accounts.xml	2013-04-16 15:40:04 UTC (rev 11042)
@@ -20,10 +20,17 @@
 
 <accounts realm="/Search">
   <user>
-    <uid>example</uid>
-    <guid>6423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
-    <password>example</password>
-    <name>Example User</name>
-    <email-address>example at example.com</email-address>
+    <uid>home1</uid>
+    <guid>home1</guid>
+    <password>home1</password>
+    <name>Example User 1</name>
+    <email-address>home1 at example.com</email-address>
   </user>
+  <user>
+    <uid>home2</uid>
+    <guid>home2</guid>
+    <password>home2</password>
+    <name>Example User 2</name>
+    <email-address>home2 at example.com</email-address>
+  </user>
 </accounts>

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/common.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/common.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -38,7 +38,6 @@
     ICommonTransaction
 from txdav.common.icommondatastore import InvalidObjectResourceError
 from txdav.common.icommondatastore import NoSuchHomeChildError
-from txdav.common.icommondatastore import NoSuchObjectResourceError
 from txdav.common.icommondatastore import ObjectResourceNameAlreadyExistsError
 from txdav.common.inotifications import INotificationObject
 from txdav.common.datastore.test.util import CommonCommonTests
@@ -46,7 +45,8 @@
 
 from txdav.caldav.icalendarstore import (
     ICalendarObject, ICalendarHome,
-    ICalendar, ICalendarTransaction)
+    ICalendar, ICalendarTransaction, InvalidUIDError,
+    InvalidComponentForStoreError, ComponentUpdateState)
 
 from twistedcaldav.customxml import InviteNotification, InviteSummary
 from txdav.common.datastore.test.util import transactionClean
@@ -849,11 +849,12 @@
     def test_calendarObjectsWithRemovedObject(self):
         """
         L{ICalendar.calendarObjects} skips those objects which have been
-        removed by L{Calendar.removeCalendarObjectWithName} in the same
+        removed by L{CalendarObject.remove} in the same
         transaction, even if it has not yet been committed.
         """
         calendar1 = yield self.calendarUnderTest()
-        yield calendar1.removeCalendarObjectWithName("2.ics")
+        obj1 = yield calendar1.calendarObjectWithName("2.ics")
+        yield obj1.remove()
         calendarObjects = list((yield calendar1.calendarObjects()))
         self.assertEquals(set(o.name() for o in calendarObjects),
                           set(calendar1_objectNames) - set(["2.ics"]))
@@ -871,7 +872,8 @@
         calendarObject = yield self.calendarObjectUnderTest()
         ctxn = self.concurrentTransaction()
         calendar1prime = yield self.calendarUnderTest(ctxn)
-        yield calendar1prime.removeCalendarObjectWithName("1.ics")
+        obj1 = yield calendar1prime.calendarObjectWithName("1.ics")
+        yield obj1.remove()
         yield ctxn.commit()
         try:
             retrieval = yield calendarObject.component()
@@ -918,16 +920,16 @@
 
 
     @inlineCallbacks
-    def test_removeCalendarObjectWithUID_exists(self):
+    def test_CalendarObjectWithUID_remove_exists(self):
         """
         Remove an existing calendar object.
         """
         calendar = yield self.calendarUnderTest()
         for name in calendar1_objectNames:
             uid = (u'uid' + name.rstrip(".ics"))
-            self.assertNotIdentical((yield calendar.calendarObjectWithUID(uid)),
-                                    None)
-            yield calendar.removeCalendarObjectWithUID(uid)
+            obj1 = (yield calendar.calendarObjectWithUID(uid))
+            self.assertNotIdentical(obj1, None)
+            yield obj1.remove()
             self.assertEquals(
                 (yield calendar.calendarObjectWithUID(uid)),
                 None
@@ -949,34 +951,21 @@
 
 
     @inlineCallbacks
-    def test_removeCalendarObjectWithName_exists(self):
+    def test_CalendarObject_remove(self):
         """
         Remove an existing calendar object.
         """
         calendar = yield self.calendarUnderTest()
         for name in calendar1_objectNames:
-            self.assertNotIdentical(
-                (yield calendar.calendarObjectWithName(name)), None
-            )
-            yield calendar.removeCalendarObjectWithName(name)
+            obj1 = (yield calendar.calendarObjectWithName(name))
+            self.assertNotIdentical(obj1, None)
+            yield obj1.remove()
             self.assertIdentical(
                 (yield calendar.calendarObjectWithName(name)), None
             )
 
 
     @inlineCallbacks
-    def test_removeCalendarObjectWithName_absent(self):
-        """
-        Attempt to remove an non-existing calendar object should raise.
-        """
-        calendar = yield self.calendarUnderTest()
-        yield self.failUnlessFailure(
-            maybeDeferred(calendar.removeCalendarObjectWithName, "xyzzy"),
-            NoSuchObjectResourceError
-        )
-
-
-    @inlineCallbacks
     def test_calendarName(self):
         """
         L{Calendar.name} reflects the name of the calendar.
@@ -1350,9 +1339,10 @@
         Set up state for testing of per-user components.
         """
         cal = yield self.calendarUnderTest()
-        yield cal.createCalendarObjectWithName(
+        yield cal._createCalendarObjectWithNameInternal(
             "per-user-stuff.ics",
-            self.perUserComponent())
+            self.perUserComponent(),
+            internal_state=ComponentUpdateState.RAW)
         returnValue((yield cal.calendarObjectWithName("per-user-stuff.ics")))
 
 
@@ -1502,10 +1492,10 @@
             "scheduleEtags": (),
             "hasPrivateComment": False,
         }
-        yield calendar1.createCalendarObjectWithName(name, component, metadata=metadata)
+        yield calendar1._createCalendarObjectWithNameInternal(name, component, internal_state=ComponentUpdateState.RAW, options=metadata)
 
         calendarObject = yield calendar1.calendarObjectWithName(name)
-        self.assertEquals((yield calendarObject.component()), component)
+        self.assertEquals((yield calendarObject.componentForUser()), component)
         self.assertEquals((yield calendarObject.getMetadata()), metadata)
 
         yield self.commit()
@@ -1546,6 +1536,7 @@
             maybeDeferred((yield self.calendarUnderTest()).createCalendarObjectWithName,
             "new", VComponent.fromString(test_event_notCalDAV_text)),
             InvalidObjectResourceError,
+            InvalidComponentForStoreError,
         )
 
 
@@ -1560,6 +1551,7 @@
             maybeDeferred(calendarObject.setComponent,
                           VComponent.fromString(test_event_notCalDAV_text)),
             InvalidObjectResourceError,
+            InvalidComponentForStoreError,
         )
 
 
@@ -1575,6 +1567,7 @@
         yield self.failUnlessFailure(
             maybeDeferred(calendarObject.setComponent, component),
             InvalidObjectResourceError,
+            InvalidUIDError,
         )
 
 
@@ -1616,14 +1609,14 @@
 
         calendar1 = yield self.calendarUnderTest()
         calendarObject = yield calendar1.calendarObjectWithName("1.ics")
-        oldComponent = yield calendarObject.component()
+        oldComponent = yield calendarObject.componentForUser()
         self.assertNotEqual(component, oldComponent)
         yield calendarObject.setComponent(component)
-        self.assertEquals((yield calendarObject.component()), component)
+        self.assertEquals((yield calendarObject.componentForUser()), component)
 
         # Also check a new instance
         calendarObject = yield calendar1.calendarObjectWithName("1.ics")
-        self.assertEquals((yield calendarObject.component()), component)
+        self.assertEquals((yield calendarObject.componentForUser()), component)
 
         yield self.commit()
 
@@ -1757,7 +1750,8 @@
             )
         )
 
-        yield cal.removeCalendarObjectWithName("2.ics")
+        obj1 = yield cal.calendarObjectWithName("2.ics")
+        yield obj1.remove()
         yield home.createCalendarWithName("other-calendar")
         st2 = yield home.syncToken()
         self.failIfEquals(st, st2)
@@ -1791,7 +1785,8 @@
                 test_event_text
             )
         )
-        yield cal.removeCalendarObjectWithName("2.ics")
+        obj1 = yield cal.calendarObjectWithName("2.ics")
+        yield obj1.remove()
         st2 = yield cal.syncToken()
         rev2 = self.token2revision(st2)
         changed, deleted = yield cal.resourceNamesSinceToken(rev)

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_attachments.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_attachments.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_attachments.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -14,7 +14,7 @@
 # limitations under the License.
 ##
 
-from calendarserver.tap.util import getRootResource
+from calendarserver.tap.util import getRootResource, directoryFromConfig
 
 from pycalendar.datetime import PyCalendarDateTime
 from pycalendar.value import PyCalendarValue
@@ -34,15 +34,15 @@
 from txdav.caldav.datastore.sql import CalendarStoreFeatures, DropBoxAttachment, \
     ManagedAttachment
 from txdav.caldav.datastore.test.common import CaptureProtocol
+from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.caldav.icalendarstore import IAttachmentStorageTransport, IAttachment, \
     QuotaExceeded
 from txdav.common.datastore.sql_tables import schema
-from txdav.common.datastore.test.util import CommonCommonTests, buildStore, \
+from txdav.common.datastore.test.util import CommonCommonTests, \
     populateCalendarsFrom, deriveQuota, withSpecialQuota
 
 import hashlib
 import os
-import uuid
 
 """
 Tests for txdav.caldav.datastore.sql attachment handling.
@@ -99,7 +99,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(AttachmentTests, self).setUp()
-        self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
+        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
         yield self.populate()
 
 
@@ -346,7 +346,7 @@
         two attachments with the same name won't overwrite each other.
         """
         obj = yield self.calendarObjectUnderTest()
-        obj2 = yield self.calendarObjectUnderTest("2.ics")
+        obj2 = yield self.calendarObjectUnderTest(name="2.ics")
         att1 = yield self.stringToAttachment(obj, "sample.attachment",
                                              "test data 1")
         att2 = yield self.stringToAttachment(obj2, "sample.attachment",
@@ -761,7 +761,7 @@
         self.assertNotEqual(quota, 0)
 
         # Remove resource
-        obj = yield self.calendarObjectUnderTest("test.ics")
+        obj = yield self.calendarObjectUnderTest(name="test.ics")
         yield obj.remove()
         yield self.commit()
 
@@ -888,7 +888,7 @@
         two attachments with the same name won't overwrite each other.
         """
         obj = yield self.calendarObjectUnderTest()
-        obj2 = yield self.calendarObjectUnderTest("2.ics")
+        obj2 = yield self.calendarObjectUnderTest(name="2.ics")
         att1 = yield self.stringToAttachment(obj, "sample.attachment",
                                              "test data 1")
         att2 = yield self.stringToAttachment(obj2, "sample.attachment",
@@ -1206,18 +1206,14 @@
 
         # Create attachment
         obj = yield self.calendarObjectUnderTest()
-        cdata = yield obj.component()
+        cdata = yield obj.componentForUser()
 
         attachment, _ignore_location = yield obj.addAttachment(None, MimeType("text", "x-fixture"), "new.attachment", MemoryStream("new attachment text"), cdata)
-        mid = attachment.managedID()
         apath = attachment._path.path
 
         newcdata = Component.fromString(str(cdata).replace("uid1", "uid1-attached"))
         calendar = yield self.calendarUnderTest()
-        cobj = yield calendar.createCalendarObjectWithName(
-            "test.ics", newcdata
-        )
-        yield cobj.copyResourceAttachments(((mid, str(uuid.uuid4()),),))
+        yield calendar.createCalendarObjectWithName("test.ics", newcdata)
         yield self.commit()
 
         self.assertTrue(os.path.exists(apath))
@@ -1240,7 +1236,7 @@
         self.assertNotEqual(quota, 0)
 
         # Remove resource
-        obj = yield self.calendarObjectUnderTest("test.ics")
+        obj = yield self.calendarObjectUnderTest(name="test.ics")
         yield obj.remove()
         yield self.commit()
 
@@ -1393,8 +1389,6 @@
     @inlineCallbacks
     def setUp(self):
         yield super(AttachmentMigrationTests, self).setUp()
-        self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
-        yield self.populate()
 
         self.patch(config.DirectoryService.params, "xmlFile",
             os.path.join(
@@ -1406,8 +1400,12 @@
                 os.path.dirname(__file__), "attachments", "resources.xml"
             )
         )
+
+        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory, directoryFromConfig(config))
+        yield self.populate()
+
         self.rootResource = getRootResource(config, self._sqlCalendarStore)
-        self.directory = self.rootResource.getDirectory()
+        self.directory = self._sqlCalendarStore.directoryService()
 
 
     @inlineCallbacks
@@ -1451,7 +1449,7 @@
         t.write(" attachment")
         yield t.loseConnection()
 
-        cal = (yield event.component())
+        cal = (yield event.componentForUser())
         cal.mainComponent().addProperty(Property(
             "ATTACH",
             "http://localhost/calendars/users/%s/dropbox/%s.dropbox/%s" % (home.name(), dropboxid, name,),
@@ -1474,7 +1472,7 @@
         calendar = (yield home.calendarWithName(calendar))
         event = (yield calendar.calendarObjectWithName(event))
 
-        cal = (yield event.component())
+        cal = (yield event.componentForUser())
         cal.mainComponent().addProperty(Property(
             "ATTACH",
             "http://localhost/calendars/users/%s/dropbox/%s.dropbox/%s" % (owner_home, dropboxid, name,),
@@ -1510,13 +1508,13 @@
         home = (yield txn.calendarHomeWithUID(home))
         calendar = (yield home.calendarWithName(calendar))
         event = (yield calendar.calendarObjectWithName(event))
-        component = (yield event.component()).mainComponent()
+        component = (yield event.componentForUser()).mainComponent()
 
         # No more X-APPLE-DROPBOX
         self.assertFalse(component.hasProperty("X-APPLE-DROPBOX"))
 
         # Check only managed attachments exist
-        attachments = (yield event.component()).mainComponent().properties("ATTACH")
+        attachments = (yield event.componentForUser()).mainComponent().properties("ATTACH")
         dropbox_count = 0
         managed_count = 0
         for attach in attachments:
@@ -1540,13 +1538,13 @@
         home = (yield txn.calendarHomeWithUID(home))
         calendar = (yield home.calendarWithName(calendar))
         event = (yield calendar.calendarObjectWithName(event))
-        component = (yield event.component()).mainComponent()
+        component = (yield event.componentForUser()).mainComponent()
 
         # X-APPLE-DROPBOX present
         self.assertTrue(component.hasProperty("X-APPLE-DROPBOX"))
 
         # Check only managed attachments exist
-        attachments = (yield event.component()).mainComponent().properties("ATTACH")
+        attachments = (yield event.componentForUser()).mainComponent().properties("ATTACH")
         dropbox_count = 0
         managed_count = 0
         for attach in attachments:
@@ -1675,7 +1673,7 @@
         event = (yield calendar.calendarObjectWithName("1.2.ics"))
 
         # Check that dropbox ATTACH exists
-        attachments = (yield event.component()).mainComponent().properties("ATTACH")
+        attachments = (yield event.componentForUser()).mainComponent().properties("ATTACH")
         for attach in attachments:
             self.assertTrue(attach.value().find("1.2.dropbox") != -1)
             self.assertTrue(attach.value().endswith("attach_1_2_1.txt") or attach.value().endswith("attach_1_2_2.txt"))
@@ -1694,7 +1692,7 @@
         event = (yield calendar.calendarObjectWithName("1.2.ics"))
 
         # Check that one managed-id and one dropbox ATTACH exist
-        attachments = (yield event.component()).mainComponent().properties("ATTACH")
+        attachments = (yield event.componentForUser()).mainComponent().properties("ATTACH")
         dropbox_count = 0
         managed_count = 0
         for attach in attachments:
@@ -1726,13 +1724,13 @@
         home = (yield txn.calendarHomeWithUID("home1"))
         calendar = (yield home.calendarWithName("calendar1"))
         event = (yield calendar.calendarObjectWithName("1.2.ics"))
-        component = (yield event.component()).mainComponent()
+        component = (yield event.componentForUser()).mainComponent()
 
         # No more X-APPLE-DROPBOX
         self.assertFalse(component.hasProperty("X-APPLE-DROPBOX"))
 
         # Check that one managed-id and one dropbox ATTACH exist
-        attachments = (yield event.component()).mainComponent().properties("ATTACH")
+        attachments = (yield event.componentForUser()).mainComponent().properties("ATTACH")
         dropbox_count = 0
         managed_count = 0
         for attach in attachments:

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_file.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_file.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
+from txdav.caldav.datastore.test.util import buildDirectory
 
 """
 File calendar store tests.
@@ -33,7 +34,6 @@
 from txdav.common.icommondatastore import ObjectResourceNameNotAllowedError
 from txdav.common.icommondatastore import ObjectResourceUIDAlreadyExistsError
 from txdav.common.icommondatastore import NoSuchHomeChildError
-from txdav.common.icommondatastore import NoSuchObjectResourceError
 
 from txdav.caldav.datastore.file import CalendarStore, CalendarHome
 from txdav.caldav.datastore.file import Calendar, CalendarObject
@@ -79,6 +79,7 @@
 
     testID = test.id()
     test.calendarStore = CalendarStore(storeRootPath, test.notifierFactory,
+                                       buildDirectory(),
                                        quota=deriveQuota(test))
     test.txn = test.calendarStore.newTransaction(testID + "(old)")
     assert test.calendarStore is not None, "No calendar store?"
@@ -290,30 +291,17 @@
 
 
     @inlineCallbacks
-    def test_removeCalendarObject_delayedEffect(self):
+    def test_CalendarObject_remove_delayedEffect(self):
         """
         Removing a calendar object should not immediately remove the underlying
         file; it should only be removed upon commit() of the transaction.
         """
-        self.calendar1.removeCalendarObjectWithName("2.ics")
+        obj1 = self.calendar1.calendarObjectWithName("2.ics")
+        obj1.remove()
         self.failUnless(self.calendar1._path.child("2.ics").exists())
         yield self.txn.commit()
         self.failIf(self.calendar1._path.child("2.ics").exists())
 
-
-    def test_removeCalendarObjectWithName_dot(self):
-        """
-        Filenames starting with "." are reserved by this
-        implementation, so no calendar object names may start with
-        ".".
-        """
-        name = ".foo"
-        self.calendar1._path.child(name).touch()
-        self.assertRaises(
-            NoSuchObjectResourceError,
-            self.calendar1.removeCalendarObjectWithName, name
-        )
-
     counter = 0
 
     @inlineCallbacks
@@ -391,17 +379,6 @@
         )
 
 
-    @featureUnimplemented
-    def test_removeCalendarObjectWithUID_absent(self):
-        """
-        Attempt to remove an non-existing calendar object should raise.
-        """
-        self.assertRaises(
-            NoSuchObjectResourceError,
-            self.calendar1.removeCalendarObjectWithUID, "xyzzy"
-        )
-
-
     @testUnimplemented
     def test_syncToken(self):
         """

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_implicit.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_implicit.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -19,8 +19,7 @@
 
 from twistedcaldav.ical import Component
 
-from txdav.common.datastore.test.util import CommonCommonTests, buildStore, \
-    populateCalendarsFrom
+from txdav.common.datastore.test.util import CommonCommonTests, populateCalendarsFrom
 from twisted.trial.unittest import TestCase
 from twext.python.clsprop import classproperty
 from twistedcaldav.config import config
@@ -29,8 +28,8 @@
 from txdav.caldav.icalendarstore import InvalidComponentTypeError, \
     TooManyAttendeesError, InvalidCalendarAccessError, InvalidUIDError, \
     UIDExistsError, ComponentUpdateState, InvalidComponentForStoreError
-import sys
 from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
+from txdav.caldav.datastore.test.util import buildCalendarStore
 
 class ImplicitRequests (CommonCommonTests, TestCase):
     """
@@ -40,7 +39,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(ImplicitRequests, self).setUp()
-        self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
+        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
         yield self.populate()
 
 
@@ -144,14 +143,7 @@
         self.patch(config, "MaxResourceSize", 100)
         calendar_collection = (yield self.calendarUnderTest(home="user01"))
         calendar1 = Component.fromString(data1)
-        try:
-            yield calendar_collection.createCalendarObjectWithName("test.ics", calendar1)
-        except ObjectResourceTooBigError:
-            pass
-        except:
-            self.fail("Wrong exception raised: %s" % (sys.exc_info()[0].__name__,))
-        else:
-            self.fail("Exception not raised")
+        yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar1), ObjectResourceTooBigError)
         yield self.commit()
 
         self.patch(config, "MaxResourceSize", 10000)
@@ -163,14 +155,7 @@
         self.patch(config, "MaxResourceSize", 100)
         calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
         calendar2 = Component.fromString(data2)
-        try:
-            yield calendar_resource.setComponent(calendar2)
-        except ObjectResourceTooBigError:
-            pass
-        except:
-            self.fail("Wrong exception raised: %s" % (sys.exc_info()[0].__name__,))
-        else:
-            self.fail("Exception not raised")
+        yield self.failUnlessFailure(calendar_resource.setComponent(calendar2), ObjectResourceTooBigError)
         yield self.commit()
 
 
@@ -213,14 +198,7 @@
         for item in data:
             calendar_collection = (yield self.calendarUnderTest(home="user01"))
             calendar = item
-            try:
-                yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
-            except (InvalidObjectResourceError, InvalidComponentForStoreError):
-                pass
-            except:
-                self.fail("Wrong exception raised: %s" % (sys.exc_info()[0].__name__,))
-            else:
-                self.fail("Exception not raised")
+            yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), InvalidObjectResourceError, InvalidComponentForStoreError)
             yield self.commit()
 
 
@@ -245,14 +223,7 @@
         calendar_collection = (yield self.calendarUnderTest(home="user01"))
         calendar_collection.setSupportedComponents("VTODO")
         calendar = Component.fromString(data1)
-        try:
-            yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
-        except InvalidComponentTypeError:
-            pass
-        except:
-            self.fail("Wrong exception raised: %s" % (sys.exc_info()[0].__name__,))
-        else:
-            self.fail("Exception not raised")
+        yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), InvalidComponentTypeError)
         yield self.commit()
 
 
@@ -283,14 +254,7 @@
         self.patch(config, "MaxAttendeesPerInstance", 2)
         calendar_collection = (yield self.calendarUnderTest(home="user01"))
         calendar = Component.fromString(data1)
-        try:
-            yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
-        except TooManyAttendeesError:
-            pass
-        except:
-            self.fail("Wrong exception raised: %s" % (sys.exc_info()[0].__name__,))
-        else:
-            self.fail("Exception not raised")
+        yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), TooManyAttendeesError)
         yield self.commit()
 
 
@@ -316,14 +280,7 @@
         self.patch(config, "EnablePrivateEvents", True)
         calendar_collection = (yield self.calendarUnderTest(home="user01"))
         calendar = Component.fromString(data1)
-        try:
-            yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
-        except InvalidCalendarAccessError:
-            pass
-        except:
-            self.fail("Wrong exception raised: %s" % (sys.exc_info()[0].__name__,))
-        else:
-            self.fail("Exception not raised")
+        yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), InvalidCalendarAccessError)
         yield self.commit()
 
 
@@ -351,14 +308,7 @@
         calendar = Component.fromString(data1)
         txn = self.transactionUnderTest()
         txn._authz_uid = "user02"
-        try:
-            yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
-        except InvalidCalendarAccessError:
-            pass
-        except:
-            self.fail("Wrong exception raised: %s" % (sys.exc_info()[0].__name__,))
-        else:
-            self.fail("Exception not raised")
+        yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), InvalidCalendarAccessError)
         yield self.commit()
 
         # This one should be OK
@@ -436,14 +386,7 @@
 
         calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
         calendar = Component.fromString(data2)
-        try:
-            yield calendar_resource.setComponent(calendar)
-        except InvalidUIDError:
-            pass
-        except:
-            self.fail("Wrong exception raised: %s" % (sys.exc_info()[0].__name__,))
-        else:
-            self.fail("Exception not raised")
+        yield self.failUnlessFailure(calendar_resource.setComponent(calendar), InvalidUIDError)
         yield self.commit()
 
 
@@ -486,14 +429,7 @@
 
         calendar_collection = (yield self.calendarUnderTest(home="user01"))
         calendar = Component.fromString(data2)
-        try:
-            yield calendar_collection.createCalendarObjectWithName("test2.ics", calendar)
-        except UIDExistsError:
-            pass
-        except:
-            self.fail("Wrong exception raised: %s" % (sys.exc_info()[0].__name__,))
-        else:
-            self.fail("Exception not raised")
+        yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test2.ics", calendar), UIDExistsError)
         yield self.commit()
 
 
@@ -537,14 +473,7 @@
         home_collection = (yield self.homeUnderTest(name="user01"))
         calendar_collection_2 = (yield home_collection.createCalendarWithName("calendar_2"))
         calendar = Component.fromString(data2)
-        try:
-            yield calendar_collection_2.createCalendarObjectWithName("test2.ics", calendar)
-        except UIDExistsError:
-            pass
-        except:
-            self.fail("Wrong exception raised: %s" % (sys.exc_info()[0].__name__,))
-        else:
-            self.fail("Exception not raised")
+        yield self.failUnlessFailure(calendar_collection_2.createCalendarObjectWithName("test2.ics", calendar), UIDExistsError)
         yield self.commit()
 
 

Copied: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_schedule.py (from rev 11028, CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_scheduling.py)
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_schedule.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_schedule.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -0,0 +1,70 @@
+##
+# Copyright (c) 2010-2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+Tests for L{txdav.caldav.datastore.scheduling}.
+
+The aforementioned module is intended to eventually support implicit
+scheduling; however, it does not currently.  The interim purpose of this module
+and accompanying tests is to effectively test the interface specifications to
+make sure that the common tests don't require anything I{not} specified in the
+interface, so that dynamic proxies specified with a tool like
+C{proxyForInterface} can be used to implement features such as implicit
+scheduling or data caching as middleware in the data-store layer.
+"""
+
+from twisted.trial.unittest import TestCase, SkipTest
+from txdav.caldav.datastore.test.test_file import FileStorageTests
+from txdav.caldav.datastore.schedule import ImplicitStore
+
+simpleEvent = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+class ImplicitStoreTests(FileStorageTests, TestCase):
+    """
+    Tests for L{ImplicitSchedulingStore}.
+    """
+
+    implicitStore = None
+
+    def storeUnderTest(self):
+        if self.implicitStore is None:
+            sut = super(ImplicitStoreTests, self).storeUnderTest()
+            self.implicitStore = ImplicitStore(sut)
+        return self.implicitStore
+
+
+    def skipit(self):
+        raise SkipTest("No private attribute tests.")
+
+    test_calendarObjectsWithDotFile = skipit
+    test_countComponentTypes = skipit
+    test_init = skipit
+    test_calendarObjectsWithDirectory = skipit
+    test_hasCalendarResourceUIDSomewhereElse = skipit
+
+del FileStorageTests

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_scheduling.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_scheduling.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_scheduling.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -1,69 +0,0 @@
-##
-# Copyright (c) 2010-2013 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-Tests for L{txdav.caldav.datastore.scheduling}.
-
-The aforementioned module is intended to eventually support implicit
-scheduling; however, it does not currently.  The interim purpose of this module
-and accompanying tests is to effectively test the interface specifications to
-make sure that the common tests don't require anything I{not} specified in the
-interface, so that dynamic proxies specified with a tool like
-C{proxyForInterface} can be used to implement features such as implicit
-scheduling or data caching as middleware in the data-store layer.
-"""
-
-from twisted.trial.unittest import TestCase, SkipTest
-from txdav.caldav.datastore.test.test_file import FileStorageTests
-from txdav.caldav.datastore.scheduling import ImplicitStore
-
-simpleEvent = """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER:mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-END:VEVENT
-END:VCALENDAR
-"""
-
-class ImplicitStoreTests(FileStorageTests, TestCase):
-    """
-    Tests for L{ImplicitSchedulingStore}.
-    """
-
-    implicitStore = None
-
-    def storeUnderTest(self):
-        if self.implicitStore is None:
-            sut = super(ImplicitStoreTests, self).storeUnderTest()
-            self.implicitStore = ImplicitStore(sut)
-        return self.implicitStore
-
-    def skipit(self):
-        raise SkipTest("No private attribute tests.")
-
-    test_calendarObjectsWithDotFile = skipit
-    test_countComponentTypes = skipit
-    test_init = skipit
-    test_calendarObjectsWithDirectory = skipit
-    test_hasCalendarResourceUIDSomewhereElse = skipit
-
-del FileStorageTests

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_sql.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_sql.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -13,6 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
+from txdav.caldav.datastore.test.util import buildCalendarStore
+from txdav.caldav.icalendarstore import ComponentUpdateState
 
 """
 Tests for txdav.caldav.datastore.postgres, mostly based on
@@ -46,7 +48,7 @@
 from txdav.common.datastore.sql_legacy import PostgresLegacyIndexEmulator
 from txdav.common.datastore.sql_tables import schema, _BIND_MODE_DIRECT, \
     _BIND_STATUS_ACCEPTED
-from txdav.common.datastore.test.util import buildStore, populateCalendarsFrom
+from txdav.common.datastore.test.util import populateCalendarsFrom
 from txdav.common.icommondatastore import NoSuchObjectResourceError
 from txdav.xml.rfc2518 import GETContentLanguage, ResourceType
 
@@ -60,7 +62,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(CalendarSQLStorageTests, self).setUp()
-        self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
+        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
         yield self.populate()
 
         self.nowYear = {"now": PyCalendarDateTime.getToday().getYear()}
@@ -186,7 +188,7 @@
             "new-home", create=True)
         toCalendar = yield toHome.calendarWithName("calendar")
         toResource = yield toCalendar.calendarObjectWithName("1.ics")
-        caldata = yield toResource.component()
+        caldata = yield toResource.componentForUser()
         self.assertEqual(str(caldata), """BEGIN:VCALENDAR
 VERSION:2.0
 CALSCALE:GREGORIAN
@@ -237,7 +239,7 @@
 """.replace("\n", "\r\n") % self.nowYear)
 
         toResource = yield toCalendar.calendarObjectWithName("2.ics")
-        caldata = yield toResource.component()
+        caldata = yield toResource.componentForUser()
         self.assertEqual(str(caldata), """BEGIN:VCALENDAR
 VERSION:2.0
 CALSCALE:GREGORIAN
@@ -290,7 +292,7 @@
 """.replace("\n", "\r\n") % self.nowYear)
 
         toResource = yield toCalendar.calendarObjectWithName("3.ics")
-        caldata = yield toResource.component()
+        caldata = yield toResource.componentForUser()
         self.assertEqual(str(caldata), """BEGIN:VCALENDAR
 VERSION:2.0
 CALSCALE:GREGORIAN
@@ -585,7 +587,7 @@
 
         # Provision the home and calendar now
         txn = calendarStore.newTransaction()
-        home = yield txn.homeWithUID(ECALENDARTYPE, "uid1", create=True)
+        home = yield txn.homeWithUID(ECALENDARTYPE, "user01", create=True)
         self.assertNotEqual(home, None)
         cal = yield home.calendarWithName("calendar")
         self.assertNotEqual(cal, None)
@@ -594,8 +596,8 @@
         txn1 = calendarStore.newTransaction()
         txn2 = calendarStore.newTransaction()
 
-        home1 = yield txn1.homeWithUID(ECALENDARTYPE, "uid1", create=True)
-        home2 = yield txn2.homeWithUID(ECALENDARTYPE, "uid1", create=True)
+        home1 = yield txn1.homeWithUID(ECALENDARTYPE, "user01", create=True)
+        home2 = yield txn2.homeWithUID(ECALENDARTYPE, "user01", create=True)
 
         cal1 = yield home1.calendarWithName("calendar")
         cal2 = yield home2.calendarWithName("calendar")
@@ -817,7 +819,7 @@
             "scheduleEtags": (),
             "hasPrivateComment": False,
         }
-        calobject = yield calendar1.createCalendarObjectWithName(name, component, metadata=metadata)
+        calobject = yield calendar1.createCalendarObjectWithName(name, component, options=metadata)
         resourceID = calobject._resourceID
 
         prop = schema.RESOURCE_PROPERTY
@@ -833,7 +835,8 @@
 
         # Remove calendar and check for no properties
         calendar1 = yield self.calendarUnderTest()
-        yield calendar1.removeCalendarObjectWithName(name)
+        obj1 = yield calendar1.calendarObjectWithName(name)
+        yield obj1.remove()
         rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
         self.assertEqual(len(tuple(rows)), 0)
         yield self.commit()
@@ -864,7 +867,7 @@
             "scheduleEtags": (),
             "hasPrivateComment": False,
         }
-        calobject = yield inbox.createCalendarObjectWithName(name, component, metadata=metadata)
+        calobject = yield inbox.createCalendarObjectWithName(name, component, options=metadata)
         resourceID = calobject._resourceID
         calobjectProperties = calobject.properties()
 
@@ -886,7 +889,8 @@
         # Remove calendar object and check for no properties
         home = yield self.homeUnderTest()
         inbox = yield home.calendarWithName("inbox")
-        yield inbox.removeCalendarObjectWithName(name)
+        obj1 = yield inbox.calendarObjectWithName(name)
+        yield obj1.remove()
         rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
         self.assertEqual(len(tuple(rows)), 0)
         yield self.commit()
@@ -1003,7 +1007,7 @@
 
         child = yield calendar2.calendarObjectWithName("5.ics")
 
-        yield calendar2.moveObjectResource(child, calendar1)
+        yield child.moveTo(calendar1, child.name())
 
         child = yield calendar2.calendarObjectWithName("5.ics")
         self.assertTrue(child is None)
@@ -1368,7 +1372,8 @@
             sorted(instances3, key=lambda x: x[0])[0],
         )
 
-        yield calendar.removeCalendarObjectWithName("indexing.ics")
+        obj1 = yield calendar.calendarObjectWithName("indexing.ics")
+        yield obj1.remove()
         yield self.commit()
 
 
@@ -1433,7 +1438,7 @@
 
         @inlineCallbacks
         def _createInboxItem(rname, pvalue):
-            obj = yield inbox.createCalendarObjectWithName(rname, component)
+            obj = yield inbox._createCalendarObjectWithNameInternal(rname, component, internal_state=ComponentUpdateState.ATTENDEE_ITIP_UPDATE)
             prop = caldavxml.CalendarDescription.fromString(pvalue)
             obj.properties()[PropertyName.fromElement(prop)] = prop
 

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_util.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_util.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
+from txdav.caldav.datastore.test.util import buildCalendarStore
 
 """
 Tests for txdav.caldav.datastore.util.
@@ -28,9 +29,9 @@
 from twistedcaldav.ical import Component
 from twistedcaldav.test.util import TestCase
 
-from txdav.common.datastore.test.util import buildStore, populateCalendarsFrom, CommonCommonTests
+from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
 
-from txdav.caldav.datastore.util import dropboxIDFromCalendarObject,\
+from txdav.caldav.datastore.util import dropboxIDFromCalendarObject, \
     StorageTransportBase, migrateHome
 
 from txdav.common.icommondatastore import HomeChildNameAlreadyExistsError
@@ -322,7 +323,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(HomeMigrationTests, self).setUp()
-        self.theStore = yield buildStore(self, self.notifierFactory)
+        self.theStore = yield buildCalendarStore(self, self.notifierFactory, homes=("conflict1", "conflict2",))
 
 
     def storeUnderTest(self):

Added: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/util.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/util.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -0,0 +1,179 @@
+# -*- test-case-name: txdav.carddav.datastore.test -*-
+##
+# Copyright (c) 2010-2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+Store test utility functions
+"""
+
+from twistedcaldav.config import config
+from txdav.caldav.icalendardirectoryservice import ICalendarStoreDirectoryService, \
+    ICalendarStoreDirectoryRecord
+from txdav.common.datastore.test.util import TestStoreDirectoryService, \
+    TestStoreDirectoryRecord, theStoreBuilder
+from zope.interface.declarations import implements
+
+class TestCalendarStoreDirectoryService(TestStoreDirectoryService):
+
+    implements(ICalendarStoreDirectoryService)
+
+    def __init__(self):
+        super(TestCalendarStoreDirectoryService, self).__init__()
+        self.recordsByCUA = {}
+
+
+    def recordWithCalendarUserAddress(self, cuaddr):
+        return self.recordsByCUA.get(cuaddr)
+
+
+    def addRecord(self, record):
+        super(TestCalendarStoreDirectoryService, self).addRecord(record)
+        for cuaddr in record.calendarUserAddresses:
+            self.recordsByCUA[cuaddr] = record
+
+
+
+class TestCalendarStoreDirectoryRecord(TestStoreDirectoryRecord):
+
+    implements(ICalendarStoreDirectoryRecord)
+
+    def __init__(
+        self,
+        uid,
+        shortNames,
+        fullName,
+        calendarUserAddresses,
+        cutype="INDIVIDUAL",
+        locallyHosted=True,
+        thisServer=True,
+    ):
+
+        super(TestCalendarStoreDirectoryRecord, self).__init__(uid, shortNames, fullName)
+        self.uid = uid
+        self.shortNames = shortNames
+        self.fullName = fullName
+        self.displayName = self.fullName if self.fullName else self.shortNames[0]
+        self.calendarUserAddresses = calendarUserAddresses
+        self.cutype = cutype
+        self._locallyHosted = locallyHosted
+        self._thisServer = thisServer
+
+
+    def canonicalCalendarUserAddress(self):
+        cua = ""
+        for candidate in self.calendarUserAddresses:
+            # Pick the first one, but urn:uuid: and mailto: can override
+            if not cua:
+                cua = candidate
+            # But always immediately choose the urn:uuid: form
+            if candidate.startswith("urn:uuid:"):
+                cua = candidate
+                break
+            # Prefer mailto: if no urn:uuid:
+            elif candidate.startswith("mailto:"):
+                cua = candidate
+        return cua
+
+
+    def locallyHosted(self):
+        return self._locallyHosted
+
+
+    def thisServer(self):
+        return self._thisServer
+
+
+    def calendarsEnabled(self):
+        return True
+
+
+    def enabledAsOrganizer(self):
+        if self.cutype == "INDIVIDUAL":
+            return True
+        elif self.recordType == "GROUP":
+            return config.Scheduling.Options.AllowGroupAsOrganizer
+        elif self.recordType == "ROOM":
+            return config.Scheduling.Options.AllowLocationAsOrganizer
+        elif self.recordType == "RESOURCE":
+            return config.Scheduling.Options.AllowResourceAsOrganizer
+        else:
+            return False
+
+
+    def getCUType(self):
+        return self.cutype
+
+
+    def canAutoSchedule(self, organizer):
+        return False
+
+
+    def getAutoScheduleMode(self, organizer):
+        return "automatic"
+
+
+
+def buildDirectory(homes=None):
+
+    directory = TestCalendarStoreDirectoryService()
+
+    # User accounts
+    for ctr in range(1, 100):
+        directory.addRecord(TestCalendarStoreDirectoryRecord(
+            "user%02d" % (ctr,),
+            ("user%02d" % (ctr,),),
+            "User %02d" % (ctr,),
+            frozenset((
+                "urn:uuid:user%02d" % (ctr,),
+                "mailto:user%02d at example.com" % (ctr,),
+            )),
+        ))
+
+    homes = set(homes) if homes is not None else set()
+    homes.update((
+        "home1",
+        "home2",
+        "Home_attachments",
+        "home_bad",
+        "home_defaults",
+        "home_no_splits",
+        "home_splits",
+        "home_splits_shared",
+    ))
+    for uid in homes:
+        directory.addRecord(buildDirectoryRecord(uid))
+
+    return directory
+
+
+
+def buildDirectoryRecord(uid):
+    return TestCalendarStoreDirectoryRecord(
+        uid,
+        (uid,),
+        uid.capitalize(),
+        frozenset((
+            "urn:uuid:%s" % (uid,),
+            "mailto:%s at example.com" % (uid,),
+        )),
+    )
+
+
+
+def buildCalendarStore(testCase, notifierFactory, directoryService=None, homes=None):
+    if directoryService is None:
+        directoryService = buildDirectory(homes=homes)
+    return theStoreBuilder.buildStore(testCase, notifierFactory, directoryService)

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/util.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/util.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -23,7 +23,8 @@
 
 from zope.interface.declarations import implements
 
-from txdav.caldav.icalendarstore import IAttachmentStorageTransport
+from txdav.caldav.icalendarstore import IAttachmentStorageTransport, \
+    ComponentUpdateState
 
 from twisted.python.failure import Failure
 from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
@@ -98,20 +99,20 @@
 
 
 
-def normalizationLookup(cuaddr, principalFunction, config):
+def normalizationLookup(cuaddr, recordFunction, config):
     """
     Lookup function to be passed to ical.normalizeCalendarUserAddresses.
     Returns a tuple of (Full name, guid, and calendar user address list)
-    for the given cuaddr.  The principalFunction is called to retrieve the
-    principal for the cuaddr.
+    for the given cuaddr.  The recordFunction is called to retrieve the
+    record for the cuaddr.
     """
     try:
-        principal = principalFunction(cuaddr)
+        record = recordFunction(cuaddr)
     except Exception, e:
         log.debug("Lookup of %s failed: %s" % (cuaddr, e))
-        principal = None
+        record = None
 
-    if principal is None:
+    if record is None:
         return (None, None, None)
     else:
         # RFC5545 syntax does not allow backslash escaping in
@@ -120,9 +121,9 @@
         # Single quotes are allowed, so we convert any double-quotes
         # to single-quotes.
         return (
-            principal.fullName().replace('"', "'"),
-            principal.uid(),
-            principal.calendarUserAddresses(),
+            record.fullName.replace('"', "'"),
+            record.uid,
+            record.calendarUserAddresses,
         )
 
 
@@ -228,10 +229,11 @@
             # Must account for metadata
             component = yield getComponent(calendarObject)
             component.md5 = calendarObject.md5()
-            yield outCalendar.createCalendarObjectWithName(
+            yield outCalendar._createCalendarObjectWithNameInternal(
                 calendarObject.name(),
                 component,
-                metadata=calendarObject.getMetadata(),
+                internal_state=ComponentUpdateState.RAW,
+                options=calendarObject.getMetadata(),
             )
 
             # Only the owner's properties are migrated, since previous releases of
@@ -443,12 +445,11 @@
         @return: a L{Deferred} which fires with a
             L{twistedcaldav.ical.Component}.
         """
-        component = yield self.component()
+        component = yield self.componentForUser(accessUID)
         calendar = self.calendar()
         isOwner = asAdmin or (calendar.owned() and
                               calendar.ownerCalendarHome().uid() == accessUID)
         for data_filter in [
-            PerUserDataFilter(accessUID),
             HiddenInstanceFilter(),
             PrivateEventFilter(self.accessMode, isOwner),
         ]:

Added: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendardirectoryservice.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendardirectoryservice.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendardirectoryservice.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -0,0 +1,124 @@
+##
+# Copyright (c) 2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+Common directory service interfaces for calendaring
+"""
+
+from txdav.common.idirectoryservice import IStoreDirectoryService, \
+    IStoreDirectoryRecord
+from zope.interface.interface import Attribute
+
+
+__all__ = [
+    "ICalendarStoreDirectoryService",
+    "ICalendarStoreDirectoryRecord",
+]
+
+class ICalendarStoreDirectoryService(IStoreDirectoryService):
+    """
+    Directory Service for looking up users.
+    """
+
+    def recordWithCalendarUserAddress(cuaddr): #@NoSelf
+        """
+        Return the record for the specified calendar user address.
+
+        @return: the record.
+        @rtype: L{ICalendarStoreDirectoryRecord}
+        """
+
+
+
+class ICalendarStoreDirectoryRecord(IStoreDirectoryRecord):
+    """
+    Record object for calendar users.
+
+    A record identifies a "user" in the system.
+    """
+
+    calendarUserAddresses = Attribute("Calendar users address for entity associated with the record: C{frozenset}")
+
+    def canonicalCalendarUserAddress(): #@NoSelf
+        """
+        The canonical calendar user address to use for this record.
+
+        @return: the canonical calendar user address.
+        @rtype: C{str}
+        """
+
+    def locallyHosted(): #@NoSelf
+        """
+        Indicates whether the record is host on this specific server "pod".
+
+        @return: C{True} if locally hosted.
+        @rtype: C{bool}
+        """
+
+    def thisServer(): #@NoSelf
+        """
+        Indicates whether the record is hosted on this server or another "pod"
+        that hosts the same directory service.
+
+        @return: C{True} if hosted by this service.
+        @rtype: C{bool}
+        """
+
+    def calendarsEnabled(): #@NoSelf
+        """
+        Indicates whether the record enabled for using the calendar service.
+
+        @return: C{True} if enabled for this service.
+        @rtype: C{bool}
+        """
+
+    def enabledAsOrganizer(): #@NoSelf
+        """
+        Indicates that the record is allowed to be the Organizer in calendar data.
+
+        @return: C{True} if allowed to be the Organizer.
+        @rtype: C{bool}
+        """
+
+    def getCUType(): #@NoSelf
+        """
+        Indicates the calendar user type for this record. It is the RFC 5545 CUTYPE value.
+
+        @return: the calendar user type.
+        @rtype: C{str}
+        """
+
+    def canAutoSchedule(organizer): #@NoSelf
+        """
+        Indicates that calendar data for this record can be automatically scheduled.
+
+        @param organizer: the organizer of the scheduling message being processed
+        @type organizer: C{str}
+
+        @return: C{True} if automatically scheduled.
+        @rtype: C{bool}
+        """
+
+    def getAutoScheduleMode(organizer): #@NoSelf
+        """
+        Indicates the mode of automatic scheduling used for this record.
+
+        @param organizer: the organizer of the scheduling message being processed
+        @type organizer: C{str}
+
+        @return: C{True} if automatically scheduled.
+        @rtype: C{bool}
+        """

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -571,28 +571,6 @@
         """
 
 
-    def removeCalendarObjectWithName(name):
-        """
-        Remove the calendar object with the given C{name} from this
-        calendar.
-
-        @param name: a string.
-        @raise NoSuchCalendarObjectError: if no such calendar object
-            exists.
-        """
-
-
-    def removeCalendarObjectWithUID(uid):
-        """
-        Remove the calendar object with the given C{uid} from this
-        calendar.
-
-        @param uid: a string.
-        @raise NoSuchCalendarObjectError: if the calendar object does
-            not exist.
-        """
-
-
     def syncToken():
         """
         Retrieve the current sync token for this calendar.

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/common.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/common.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -28,7 +28,6 @@
 )
 from txdav.common.icommondatastore import InvalidObjectResourceError
 from txdav.common.icommondatastore import NoSuchHomeChildError
-from txdav.common.icommondatastore import NoSuchObjectResourceError
 from txdav.common.icommondatastore import ObjectResourceNameAlreadyExistsError
 
 from txdav.carddav.iaddressbookstore import (
@@ -427,11 +426,12 @@
     def test_addressbookObjectsWithRemovedObject(self):
         """
         L{IAddressBook.addressbookObjects} skips those objects which have been
-        removed by L{AddressBook.removeAddressBookObjectWithName} in the same
+        removed by L{AddressBookObject.remove} in the same
         transaction, even if it has not yet been committed.
         """
         addressbook1 = yield self.addressbookUnderTest()
-        yield addressbook1.removeAddressBookObjectWithName("2.vcf")
+        obj1 = yield addressbook1.addressbookObjectWithName("2.vcf")
+        yield obj1.remove()
         addressbookObjects = list((yield addressbook1.addressbookObjects()))
         self.assertEquals(set(o.name() for o in addressbookObjects),
                           set(addressbook1_objectNames) - set(["2.vcf"]))
@@ -473,18 +473,19 @@
 
 
     @inlineCallbacks
-    def test_removeAddressBookObjectWithUID_exists(self):
+    def test_AddressBookObject_remove_exists(self):
         """
         Remove an existing addressbook object.
         """
         addressbook = yield self.addressbookUnderTest()
         for name in addressbook1_objectNames:
             uid = (u'uid' + name.rstrip(".vcf"))
+            obj1 = (yield addressbook.addressbookObjectWithUID(uid))
             self.assertNotIdentical(
-                (yield addressbook.addressbookObjectWithUID(uid)),
+                obj1,
                 None
             )
-            yield addressbook.removeAddressBookObjectWithUID(uid)
+            yield obj1.remove()
             self.assertEquals(
                 (yield addressbook.addressbookObjectWithUID(uid)),
                 None
@@ -496,16 +497,15 @@
 
 
     @inlineCallbacks
-    def test_removeAddressBookObjectWithName_exists(self):
+    def test_AddressBookObject_remove(self):
         """
         Remove an existing addressbook object.
         """
         addressbook = yield self.addressbookUnderTest()
         for name in addressbook1_objectNames:
-            self.assertNotIdentical(
-                (yield addressbook.addressbookObjectWithName(name)), None
-            )
-            yield addressbook.removeAddressBookObjectWithName(name)
+            obj1 = (yield addressbook.addressbookObjectWithName(name))
+            self.assertNotIdentical(obj1, None)
+            yield obj1.remove()
             self.assertIdentical(
                 (yield addressbook.addressbookObjectWithName(name)), None
             )
@@ -522,18 +522,6 @@
 
 
     @inlineCallbacks
-    def test_removeAddressBookObjectWithName_absent(self):
-        """
-        Attempt to remove an non-existing addressbook object should raise.
-        """
-        addressbook = yield self.addressbookUnderTest()
-        yield self.failUnlessFailure(
-            maybeDeferred(addressbook.removeAddressBookObjectWithName, "xyzzy"),
-            NoSuchObjectResourceError
-        )
-
-
-    @inlineCallbacks
     def test_addressbookName(self):
         """
         L{AddressBook.name} reflects the name of the addressbook.
@@ -951,5 +939,3 @@
                 (yield addressbook2.addressbookObjectWithName(resourceName)), None)
             self.assertIdentical(
                 (yield addressbook2.addressbookObjectWithUID(obj.uid())), None)
-
-

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/test_file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/test_file.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/test_file.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -29,7 +29,6 @@
 from txdav.common.icommondatastore import ObjectResourceNameNotAllowedError
 from txdav.common.icommondatastore import ObjectResourceUIDAlreadyExistsError
 from txdav.common.icommondatastore import NoSuchHomeChildError
-from txdav.common.icommondatastore import NoSuchObjectResourceError
 
 from txdav.carddav.datastore.file import AddressBookStore, AddressBookHome
 from txdav.carddav.datastore.file import AddressBook, AddressBookObject
@@ -62,7 +61,7 @@
     storePath.copyTo(addressbookPath)
 
     test.notifierFactory = StubNotifierFactory()
-    test.addressbookStore = AddressBookStore(storeRootPath, test.notifierFactory)
+    test.addressbookStore = AddressBookStore(storeRootPath, test.notifierFactory, None)
     test.txn = test.addressbookStore.newTransaction(test.id() + " (old)")
     assert test.addressbookStore is not None, "No addressbook store?"
 
@@ -277,26 +276,13 @@
         Removing a addressbook object should not immediately remove the underlying
         file; it should only be removed upon commit() of the transaction.
         """
-        self.addressbook1.removeAddressBookObjectWithName("2.vcf")
+        obj1 = self.addressbook1.addressbookObjectWithName("2.vcf")
+        obj1.remove()
         self.failUnless(self.addressbook1._path.child("2.vcf").exists())
         self.txn.commit()
         self.failIf(self.addressbook1._path.child("2.vcf").exists())
 
 
-    def test_removeAddressBookObjectWithName_dot(self):
-        """
-        Filenames starting with "." are reserved by this
-        implementation, so no addressbook object names may start with
-        ".".
-        """
-        name = ".foo"
-        self.addressbook1._path.child(name).touch()
-        self.assertRaises(
-            NoSuchObjectResourceError,
-            self.addressbook1.removeAddressBookObjectWithName, name
-        )
-
-
     @inlineCallbacks
     def _refresh(self):
         """
@@ -384,17 +370,6 @@
         self.txn.commit()
 
 
-    @featureUnimplemented
-    def test_removeAddressBookObjectWithUID_absent(self):
-        """
-        Attempt to remove an non-existing addressbook object should raise.
-        """
-        self.assertRaises(
-            NoSuchObjectResourceError,
-            self.addressbook1.removeAddressBookObjectWithUID, "xyzzy"
-        )
-
-
     @testUnimplemented
     def test_syncToken(self):
         """

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/test_sql.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/datastore/test/test_sql.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -51,6 +51,7 @@
         self._sqlStore = yield buildStore(self, self.notifierFactory)
         yield self.populate()
 
+
     @inlineCallbacks
     def populate(self):
         populateTxn = self.storeUnderTest().newTransaction()
@@ -76,7 +77,6 @@
         self.notifierFactory.reset()
 
 
-
     def storeUnderTest(self):
         """
         Create and return a L{AddressBookStore} for testing.
@@ -91,7 +91,7 @@
         events).
         """
         @inlineCallbacks
-        def namesAndComponents(x, filter=lambda x:x.component()):
+        def namesAndComponents(x, filter=lambda x: x.component()):
             fromObjs = yield x.addressbookObjects()
             returnValue(dict([(fromObj.name(), (yield filter(fromObj)))
                               for fromObj in fromObjs]))
@@ -107,7 +107,7 @@
         """
         Assert that two objects with C{properties} methods have similar
         properties.
-        
+
         @param disregard: a list of L{PropertyName} keys to discard from both
             input and output.
         """
@@ -215,7 +215,7 @@
         version = yield txn.calendarserverValue("ADDRESSBOOK-DATAVERSION")[0][0]
         ch = schema.ADDRESSBOOK_HOME
         homeVersion = yield Select(
-            [ch.DATAVERSION,],
+            [ch.DATAVERSION, ],
             From=ch,
             Where=ch.OWNER_UID == "home_version",
         ).on(txn)[0][0]
@@ -288,6 +288,7 @@
         yield d1
         yield d2
 
+
     @inlineCallbacks
     def test_removeAddressBookPropertiesOnDelete(self):
         """
@@ -301,7 +302,7 @@
         addressbook = yield home.createAddressBookWithName(name)
         resourceID = addressbook._resourceID
         addressbookProperties = addressbook.properties()
-        
+
         prop = carddavxml.AddressBookDescription.fromString("Address Book to be removed")
         addressbookProperties[PropertyName.fromElement(prop)] = prop
         yield self.commit()
@@ -329,6 +330,7 @@
         self.assertEqual(len(tuple(rows)), 0)
         yield self.commit()
 
+
     @inlineCallbacks
     def test_removeAddressBookObjectPropertiesOnDelete(self):
         """
@@ -341,7 +343,7 @@
         adbk1 = yield self.addressbookUnderTest()
         name = "4.vcf"
         component = VComponent.fromString(vcard4_text)
-        addressobject = yield adbk1.createAddressBookObjectWithName(name, component, metadata={})
+        addressobject = yield adbk1.createAddressBookObjectWithName(name, component, options={})
         resourceID = addressobject._resourceID
 
         prop = schema.RESOURCE_PROPERTY
@@ -357,7 +359,8 @@
 
         # Remove address book object and check for no properties
         adbk1 = yield self.addressbookUnderTest()
-        yield adbk1.removeAddressBookObjectWithName(name)
+        obj1 = yield adbk1.addressbookObjectWithName(name)
+        yield obj1.remove()
         rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
         self.assertEqual(len(tuple(rows)), 0)
         yield self.commit()
@@ -366,4 +369,3 @@
         rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
         self.assertEqual(len(tuple(rows)), 0)
         yield self.commit()
-

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/iaddressbookstore.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/iaddressbookstore.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/carddav/iaddressbookstore.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -196,28 +196,6 @@
         """
 
 
-    def removeAddressBookObjectWithName(name):
-        """
-        Remove the addressbook object with the given C{name} from this
-        addressbook.
-
-        @param name: a string.
-        @raise NoSuchAddressBookObjectError: if no such addressbook object
-            exists.
-        """
-
-
-    def removeAddressBookObjectWithUID(uid):
-        """
-        Remove the addressbook object with the given C{uid} from this
-        addressbook.
-
-        @param uid: a string.
-        @raise NoSuchAddressBookObjectError: if the addressbook object does
-            not exist.
-        """
-
-
     def syncToken():
         """
         Retrieve the current sync token for this addressbook.

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/file.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/file.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -43,6 +43,7 @@
     HomeChildNameAlreadyExistsError, NoSuchHomeChildError, \
     InternalDataStoreError, ObjectResourceNameNotAllowedError, \
     ObjectResourceNameAlreadyExistsError, NoSuchObjectResourceError
+from txdav.common.idirectoryservice import IStoreDirectoryService
 from txdav.common.inotifications import INotificationCollection, \
     INotificationObject
 from txdav.base.datastore.file import DataStoreTransaction, DataStore, writeOperation, \
@@ -109,8 +110,11 @@
     """
     implements(ICalendarStore)
 
-    def __init__(self, path, notifierFactory, enableCalendars=True,
-                 enableAddressBooks=True, quota=(2 ** 20),
+    def __init__(self, path, notifierFactory,
+                 directoryService,
+                 enableCalendars=True,
+                 enableAddressBooks=True,
+                 quota=(2 ** 20),
                  propertyStoreClass=XattrPropertyStore):
         """
         Create a store.
@@ -120,6 +124,7 @@
         assert enableCalendars or enableAddressBooks
 
         super(CommonDataStore, self).__init__(path)
+        self._directoryService = IStoreDirectoryService(directoryService) if directoryService is not None else None
         self.enableCalendars = enableCalendars
         self.enableAddressBooks = enableAddressBooks
         self._notifierFactory = notifierFactory
@@ -133,6 +138,10 @@
         self.queuer = _StubQueuer()
 
 
+    def directoryService(self):
+        return self._directoryService
+
+
     def callWithNewTransactions(self, callback):
         """
         Registers a method to be called whenever a new transaction is
@@ -701,6 +710,14 @@
         return self._transaction
 
 
+    def directoryService(self):
+        return self._transaction.store().directoryService()
+
+
+    def directoryRecord(self):
+        return self.directoryService().recordWithUID(self.uid())
+
+
     def retrieveOldShares(self):
         """
         Retrieve the old Index object.
@@ -1007,6 +1024,15 @@
         return self._home._path.child(self._name)
 
 
+    @property
+    def _txn(self):
+        return self._transaction
+
+
+    def directoryService(self):
+        return self._transaction.store().directoryService()
+
+
     def resourceType(self):
         return NotImplementedError
 
@@ -1205,28 +1231,14 @@
         return objectResource
 
 
-    @writeOperation
-    def removeObjectResourceWithName(self, name):
-        if name.startswith("."):
-            raise NoSuchObjectResourceError(name)
+    def removedObjectResource(self, child):
 
-        self.retrieveOldIndex().deleteResource(name)
+        self.retrieveOldIndex().deleteResource(child.name())
 
-        objectResourcePath = self._path.child(name)
-        if objectResourcePath.isfile():
-            self._removedObjectResources.add(name)
-            # FIXME: test for undo
-            def do():
-                objectResourcePath.remove()
-                return lambda: None
-            self._transaction.addOperation(do, "remove object resource object %r" %
-                                           (name,))
+        self._removedObjectResources.add(child.name())
+        self.notifyChanged()
 
-            self.notifyChanged()
-        else:
-            raise NoSuchObjectResourceError(name)
 
-
     def syncToken(self):
 
         try:
@@ -1368,6 +1380,10 @@
         return self._transaction
 
 
+    def directoryService(self):
+        return self._transaction.store().directoryService()
+
+
     @writeOperation
     def setComponent(self, component, inserting=False):
         raise NotImplementedError
@@ -1378,9 +1394,17 @@
 
 
     def remove(self):
-        self._parentCollection.removeObjectResourceWithName(self._name)
 
+        # FIXME: test for undo
+        objectResourcePath = self._path
+        def do():
+            objectResourcePath.remove()
+            return lambda: None
+        self._transaction.addOperation(do, "remove object resource object %r" % (self._name,))
 
+        self._parentCollection.removedObjectResource(self)
+
+
     def _text(self):
         raise NotImplementedError
 

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/sql.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/sql.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -66,6 +66,7 @@
     ObjectResourceNameNotAllowedError, ObjectResourceNameAlreadyExistsError, \
     NoSuchObjectResourceError, AllRetriesFailed, InvalidSubscriptionValues, \
     InvalidIMIPTokenValues, TooManyObjectResourcesError
+from txdav.common.idirectoryservice import IStoreDirectoryService
 from txdav.common.inotifications import INotificationCollection, \
     INotificationObject
 
@@ -149,6 +150,7 @@
     implements(ICalendarStore)
 
     def __init__(self, sqlTxnFactory, notifierFactory,
+                 directoryService,
                  attachmentsPath, attachmentsURIPattern,
                  enableCalendars=True, enableAddressBooks=True,
                  enableManagedAttachments=True,
@@ -161,6 +163,7 @@
 
         self.sqlTxnFactory = sqlTxnFactory
         self.notifierFactory = notifierFactory
+        self._directoryService = IStoreDirectoryService(directoryService) if directoryService is not None else None
         self.attachmentsPath = attachmentsPath
         self.attachmentsURIPattern = attachmentsURIPattern
         self.enableCalendars = enableCalendars
@@ -191,6 +194,10 @@
         __import__("txdav.carddav.datastore.sql")
 
 
+    def directoryService(self):
+        return self._directoryService
+
+
     def callWithNewTransactions(self, callback):
         """
         Registers a method to be called whenever a new transaction is
@@ -493,6 +500,10 @@
         return self._store
 
 
+    def directoryService(self):
+        return self._store.directoryService()
+
+
     def __repr__(self):
         return 'PG-TXN<%s>' % (self._label,)
 
@@ -1632,6 +1643,14 @@
         return self._txn
 
 
+    def directoryService(self):
+        return self._txn.store().directoryService()
+
+
+    def directoryRecord(self):
+        return self.directoryService().recordWithUID(self.uid())
+
+
     @inlineCallbacks
     def invalidateQueryCache(self):
         queryCacher = self._txn._queryCacher
@@ -3366,6 +3385,10 @@
         return self._home._txn
 
 
+    def directoryService(self):
+        return self._txn.store().directoryService()
+
+
     def resourceType(self):
         return NotImplementedError
 
@@ -3822,6 +3845,9 @@
         child._parentCollection = newparent
 
         # Signal sync change on new collection
+        newparent._objects.pop(name, None)
+        newparent._objects.pop(uid, None)
+        newparent._objects.pop(child._resourceID, None)
         yield newparent._insertRevision(newname)
         yield newparent.notifyChanged()
 
@@ -4284,6 +4310,10 @@
         return self._parentCollection._txn
 
 
+    def directoryService(self):
+        return self._txn.store().directoryService()
+
+
     @classmethod
     def _selectForUpdateQuery(cls, nowait): #@NoSelf
         """
@@ -4337,7 +4367,7 @@
 
 
     @inlineCallbacks
-    def moveTo(self, destination, name):
+    def moveTo(self, destination, name=None):
         """
         Move object to another collection.
 

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/test/util.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/test/util.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -21,6 +21,10 @@
 
 from __future__ import print_function
 
+from zope.interface.declarations import implements
+from txdav.common.idirectoryservice import IStoreDirectoryService, \
+    IStoreDirectoryRecord
+
 from calendarserver.push.notifier import Notifier
 
 from hashlib import md5
@@ -87,6 +91,35 @@
 
 
 
+class TestStoreDirectoryService(object):
+
+    implements(IStoreDirectoryService)
+
+    def __init__(self):
+        self.records = {}
+
+
+    def recordWithUID(self, uid):
+        return self.records.get(uid)
+
+
+    def addRecord(self, record):
+        self.records[record.uid] = record
+
+
+
+class TestStoreDirectoryRecord(object):
+
+    implements(IStoreDirectoryRecord)
+
+    def __init__(self, uid, shortNames, fullName):
+        self.uid = uid
+        self.shortNames = shortNames
+        self.fullName = fullName
+        self.displayName = self.fullName if self.fullName else self.shortNames[0]
+
+
+
 class SQLStoreBuilder(object):
     """
     Test-fixture-builder which can construct a PostgresStore.
@@ -136,13 +169,14 @@
         reactor.addSystemEventTrigger("before", "shutdown", cp.stopService)
         cds = CommonDataStore(
             cp.connection, StubNotifierFactory(),
+            TestStoreDirectoryService(),
             attachmentRoot, "",
             quota=staticQuota
         )
         return cds
 
 
-    def buildStore(self, testCase, notifierFactory):
+    def buildStore(self, testCase, notifierFactory, directoryService=None):
         """
         Do the necessary work to build a store for a particular test case.
 
@@ -151,11 +185,13 @@
         disableMemcacheForTest(testCase)
         dbRoot = CachingFilePath(self.SHARED_DB_PATH)
         attachmentRoot = dbRoot.child("attachments")
+        if directoryService is None:
+            directoryService = TestStoreDirectoryService()
         if self.sharedService is None:
             ready = Deferred()
             def getReady(connectionFactory):
                 self.makeAndCleanStore(
-                    testCase, notifierFactory, attachmentRoot
+                    testCase, notifierFactory, directoryService, attachmentRoot
                 ).chainDeferred(ready)
                 return Service()
             self.sharedService = self.createService(getReady)
@@ -169,7 +205,7 @@
             result = ready
         else:
             result = self.makeAndCleanStore(
-                testCase, notifierFactory, attachmentRoot
+                testCase, notifierFactory, directoryService, attachmentRoot
             )
         def cleanUp():
             def stopit():
@@ -180,7 +216,7 @@
 
 
     @inlineCallbacks
-    def makeAndCleanStore(self, testCase, notifierFactory, attachmentRoot):
+    def makeAndCleanStore(self, testCase, notifierFactory, directoryService, attachmentRoot):
         """
         Create a L{CommonDataStore} specific to the given L{TestCase}.
 
@@ -201,6 +237,7 @@
         store = CommonDataStore(
             cp.connection,
             notifierFactory,
+            directoryService,
             attachmentRoot,
             "https://example.com/calendars/__uids__/%(home)s/attachments/%(name)s",
             quota=quota

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/__init__.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/__init__.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -13,4 +13,3 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
-

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/migrate.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/migrate.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/migrate.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -63,7 +63,7 @@
     @return: a L{Deferred} which fires with the appropriate L{Component}.
     """
     comp = yield cobj.component()
-    fixes, fixed = fixOneCalendarObject(comp)
+    _ignore_fixes, fixed = fixOneCalendarObject(comp)
     returnValue(fixed)
 
 
@@ -203,7 +203,7 @@
         subsvc = None
         self.upgrader = UpgradeToDatabaseService(
             FileStore(
-                CachingFilePath(filename), None, True, True,
+                CachingFilePath(filename), None, None, True, True,
                 propertyStoreClass=namedAny(appropriateStoreClass)
             ), self.store, subsvc, merge=merge
         )
@@ -215,7 +215,7 @@
         """
         Upgrade one calendar home.
         """
-        migrateFunc, destFunc = homeTypeLookup[homeType]
+        _ignore_migrateFunc, destFunc = homeTypeLookup[homeType]
         fileTxn = self.upgrader.fileStore.newTransaction()
         return (
             maybeDeferred(destFunc(fileTxn), uid)
@@ -305,7 +305,7 @@
                     appropriateStoreClass = AppleDoubleStore
 
                 self = cls(
-                    FileStore(path, None, True, True,
+                    FileStore(path, None, None, True, True,
                               propertyStoreClass=appropriateStoreClass),
                     store, service, uid=uid, gid=gid,
                     parallel=parallel, spawner=spawner, merge=merge
@@ -382,7 +382,7 @@
             drivers = yield gatherResults(
                 [spawner.spawnWithStore(UpgradeDriver(self),
                                         UpgradeHelperProcess)
-                 for x in xrange(parallel)]
+                 for _ignore_x in xrange(parallel)]
             )
             # Wait for all subprocesses to be fully configured before
             # continuing, but let them configure in any order.
@@ -467,7 +467,6 @@
             yield self.migrateOneHome(fileTxn, homeType, fileHome)
 
 
-
     def startService(self):
         """
         Start the service.

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/test/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/test/__init__.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/test/__init__.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -13,4 +13,3 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
-

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/test/test_migrate.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/test/test_migrate.py	2013-04-16 15:37:46 UTC (rev 11041)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/upgrade/test/test_migrate.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -37,7 +37,8 @@
 from txdav.carddav.datastore.test.common import CommonTests as ABCommonTests
 from txdav.common.datastore.file import CommonDataStore
 from txdav.common.datastore.sql_tables import schema
-from txdav.common.datastore.test.util import SQLStoreBuilder
+from txdav.common.datastore.test.util import SQLStoreBuilder, \
+    TestStoreDirectoryService
 from txdav.common.datastore.test.util import theStoreBuilder, \
     populateCalendarsFrom, StubNotifierFactory, resetCalendarMD5s, \
     populateAddressBooksFrom, resetAddressBookMD5s, deriveValue, \
@@ -149,7 +150,7 @@
         self.filesPath = CachingFilePath(self.mktemp())
         self.filesPath.createDirectory()
         fileStore = self.fileStore = CommonDataStore(
-            self.filesPath, StubNotifierFactory(), True, True
+            self.filesPath, StubNotifierFactory(), TestStoreDirectoryService(), True, True
         )
         self.sqlStore = yield theStoreBuilder.buildStore(
             self, StubNotifierFactory()

Added: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/idirectoryservice.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/idirectoryservice.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/idirectoryservice.py	2013-04-16 15:40:04 UTC (rev 11042)
@@ -0,0 +1,57 @@
+##
+# Copyright (c) 2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+Common directory service interfaces
+"""
+
+from zope.interface.interface import Interface, Attribute
+
+
+__all__ = [
+    "IStoreDirectoryService",
+    "IStoreDirectoryRecord",
+]
+
+class IStoreDirectoryService(Interface):
+    """
+    Directory Service for looking up users.
+    """
+
+    def recordWithUID(uid): #@NoSelf
+        """
+        Return the record for the specified store uid.
+
+        @return: the record.
+        @rtype: L{IStoreDirectoryRecord}
+        """
+
+
+
+class IStoreDirectoryRecord(Interface):
+    """
+    Directory record object
+
+    A record identifies a "user" in the system.
+    """
+
+    uid = Attribute("The record UID: C{str}")
+
+    shortNames = Attribute("Short names of the record: C{tuple}")
+
+    fullName = Attribute("Full name for the entity associated with the record: C{str}")
+
+    displayName = Attribute("Display name for entity associated with the record: C{str}")
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130416/665379e9/attachment-0001.html>


More information about the calendarserver-changes mailing list