[CalendarServer-changes] [5827] CalendarServer/branches/new-store

source_changes at macosforge.org source_changes at macosforge.org
Thu Jul 1 09:20:10 PDT 2010


Revision: 5827
          http://trac.macosforge.org/projects/calendarserver/changeset/5827
Author:   cdaboo at apple.com
Date:     2010-07-01 09:20:10 -0700 (Thu, 01 Jul 2010)
Log Message:
-----------
Support for global addresses. This was implemented as a new directory server providing a new
/public/ record type. Within that are "global" and "directory" records. The global addressbook
is the default addressbook for the "global" user. static.py is hard coded to look for that
record type and then use the appropriate global address book class for the addressbook.

Modified Paths:
--------------
    CalendarServer/branches/new-store/calendarserver/tap/util.py
    CalendarServer/branches/new-store/twistedcaldav/directory/addressbook.py
    CalendarServer/branches/new-store/twistedcaldav/static.py
    CalendarServer/branches/new-store/twistedcaldav/storebridge.py

Added Paths:
-----------
    CalendarServer/branches/new-store/twistedcaldav/directory/internal.py

Modified: CalendarServer/branches/new-store/calendarserver/tap/util.py
===================================================================
--- CalendarServer/branches/new-store/calendarserver/tap/util.py	2010-07-01 02:45:32 UTC (rev 5826)
+++ CalendarServer/branches/new-store/calendarserver/tap/util.py	2010-07-01 16:20:10 UTC (rev 5827)
@@ -40,6 +40,7 @@
 from twistedcaldav.directory import augment, calendaruserproxy
 from twistedcaldav.directory.aggregate import AggregateDirectoryService
 from twistedcaldav.directory.digest import QopDigestCredentialFactory
+from twistedcaldav.directory.internal import InternalDirectoryService
 from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
 from twistedcaldav.directory.sudo import SudoDirectoryService
 from twistedcaldav.directory.util import NotFilePath
@@ -47,8 +48,7 @@
 from twistedcaldav.notify import installNotificationClient
 from twistedcaldav.resource import CalDAVResource, AuthenticationWrapper
 from twistedcaldav.simpleresource import SimpleResource
-from twistedcaldav.static import CalendarHomeProvisioningFile,\
-    GlobalAddressBookFile
+from twistedcaldav.static import CalendarHomeProvisioningFile
 from twistedcaldav.static import IScheduleInboxFile
 from twistedcaldav.static import TimezoneServiceFile
 from twistedcaldav.static import AddressBookHomeProvisioningFile, DirectoryBackedAddressBookFile
@@ -95,7 +95,6 @@
     webAdminResourceClass        = WebAdminResource
     addressBookResourceClass     = AddressBookHomeProvisioningFile
     directoryBackedAddressBookResourceClass = DirectoryBackedAddressBookFile
-    globalAddressBookResourceClass          = GlobalAddressBookFile
 
     #
     # Setup the Directory
@@ -154,6 +153,14 @@
         wikiDirectory.realmName = baseDirectory.realmName
         directories.append(wikiDirectory)
 
+    #
+    # Add internal directory service
+    # Right now we only use this for CardDAV
+    #
+    if config.EnableCardDAV:
+        internalDirectory = InternalDirectoryService(baseDirectory.realmName)
+        directories.append(internalDirectory)
+
     directory = AggregateDirectoryService(directories)
 
     if sudoDirectory:
@@ -311,24 +318,6 @@
                 if e.errno != errno.ENOENT:
                     log.error("Could not delete: %s : %r" %  (directoryPath, e,))
 
-        if config.GlobalAddressBook.Enabled:
-            log.info("Setting up global address book collection: %r" % (globalAddressBookResourceClass,))
-
-            globalAddressBookCollection = globalAddressBookResourceClass(
-                os.path.join(config.DocumentRoot, config.GlobalAddressBook.Name),
-                principalCollections=(principalCollection,)
-            )
-            if not globalAddressBookCollection.exists():
-                def createGlobalAddressBookIgnoreException():
-                    try:
-                        if not globalAddressBookCollection.exists():
-                            globalAddressBookCollection.createAddressBookCollection()
-                    except OSError, e:
-                        if e.errno != errno.EEXIST:
-                            raise
-
-                addSystemEventTrigger("after", "startup", createGlobalAddressBookIgnoreException)
-
     log.info("Setting up root resource: %r" % (rootResourceClass,))
 
     root = rootResourceClass(
@@ -348,8 +337,6 @@
         root.putChild('addressbooks', addressBookCollection)
         if config.DirectoryAddressBook.Enabled:
             root.putChild(config.DirectoryAddressBook.name, directoryBackedAddressBookCollection)
-        if config.GlobalAddressBook.Enabled:
-            root.putChild(config.GlobalAddressBook.Name, globalAddressBookCollection)            
 
     # /.well-known
     if config.EnableWellKnown:

Modified: CalendarServer/branches/new-store/twistedcaldav/directory/addressbook.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/directory/addressbook.py	2010-07-01 02:45:32 UTC (rev 5826)
+++ CalendarServer/branches/new-store/twistedcaldav/directory/addressbook.py	2010-07-01 16:20:10 UTC (rev 5827)
@@ -379,12 +379,6 @@
     def resourceType(self, request):
         return succeed(davxml.ResourceType.sharedaddressbook)
 
-    def url(self):
-        return joinURL("/", config.GlobalAddressBook.Name, "/")
-
-    def canonicalURL(self, request):
-        return succeed(self.url())
-
     def defaultAccessControlList(self):
 
         aces = (

Added: CalendarServer/branches/new-store/twistedcaldav/directory/internal.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/directory/internal.py	                        (rev 0)
+++ CalendarServer/branches/new-store/twistedcaldav/directory/internal.py	2010-07-01 16:20:10 UTC (rev 5827)
@@ -0,0 +1,125 @@
+##
+# Copyright (c) 2010 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.
+##
+
+"""
+Directory service implementation for internal purposes - e.g. public
+calendars, addressbooks, directory gateway, global address book.
+"""
+
+__all__ = [
+    "InternalDirectoryService",
+]
+
+from twext.web2.dav.auth import IPrincipalCredentials
+
+from twisted.cred.error import UnauthorizedLogin
+
+from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
+from twistedcaldav.directory.directory import UnknownRecordTypeError
+
+class InternalDirectoryService(DirectoryService):
+    """
+    L{IDirectoryService} implementation for internal record types.
+    """
+    baseGUID = "80DED344-B79F-46AF-B05B-E35D737BC19A"
+
+    realmName = None
+
+    plistFile = None
+
+    supportedRecordTypes = ("public",)
+
+    def __repr__(self):
+        return "<%s %r>" % (self.__class__.__name__, self.realmName)
+
+    def __init__(self, realm):
+        super(InternalDirectoryService, self).__init__()
+
+        self.realmName = realm
+        self._records()
+
+    def _records(self):
+        """
+        Build the list of records.
+        
+        Right now we want public/global and public/directory for
+        global and directory address books.
+        """
+        
+        if not hasattr(self, "_cachedRecords"):
+            self._cachedRecords = (
+                InternalDirectoryRecord(
+                    self,
+                    "public",
+                    "4F00E8BA-7B45-42E9-B9D1-F499B6A2E887",
+                    "global",
+                    False,
+                    True
+                ),
+                InternalDirectoryRecord(
+                    self,
+                    "public",
+                    "1BC554CC-DBD6-4454-8423-2637A9B681DC",
+                    "directory",
+                    False,
+                    True
+                ),
+            )
+        return self._cachedRecords
+
+    def recordTypes(self):
+        return InternalDirectoryService.supportedRecordTypes
+
+    def listRecords(self, recordType):
+        if recordType not in InternalDirectoryService.supportedRecordTypes:
+            raise UnknownRecordTypeError(recordType)
+
+        return self._records()
+
+    def recordWithShortName(self, recordType, shortName):
+        if recordType not in InternalDirectoryService.supportedRecordTypes:
+            raise UnknownRecordTypeError(recordType)
+
+        for record in self._records():
+            if shortName in record.shortNames:
+                return record
+
+    def requestAvatarId(self, credentials):
+        credentials = IPrincipalCredentials(credentials)
+        raise UnauthorizedLogin("No such user: %s" % (credentials.credentials.username,))
+
+
+class InternalDirectoryRecord(DirectoryRecord):
+    """
+    L{DirectoryRecord} implementation for internal records.
+    """
+
+    def __init__(self, service, recordType, guid, shortName,
+                enabledForCalendaring=None, enabledForAddressBooks=None,):
+        super(InternalDirectoryRecord, self).__init__(
+            service=service,
+            recordType=recordType,
+            guid=guid,
+            shortNames=(shortName,),
+            fullName=shortName,
+            enabledForCalendaring=enabledForCalendaring,
+            enabledForAddressBooks=enabledForAddressBooks,
+        )
+
+        self.enabled = True     # Explicitly enabled
+
+    def verifyCredentials(self, credentials):
+        return False

Modified: CalendarServer/branches/new-store/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/static.py	2010-07-01 02:45:32 UTC (rev 5826)
+++ CalendarServer/branches/new-store/twistedcaldav/static.py	2010-07-01 16:20:10 UTC (rev 5827)
@@ -75,6 +75,7 @@
 from twistedcaldav.config import config
 from twistedcaldav.customxml import TwistedCalendarAccessProperty, TwistedScheduleMatchETags
 from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
+from twistedcaldav.directory.internal import InternalDirectoryRecord
 from twistedcaldav.directory.util import NotFilePath
 from twistedcaldav.extensions import DAVFile, CachingPropertyStore
 from twistedcaldav.linkresource import LinkResource, LinkFollowerMixIn
@@ -1727,7 +1728,7 @@
             if config.GlobalAddressBook.Enabled:
                 self.putChild(
                     config.GlobalAddressBook.Name,
-                    LinkResource(self, joinURL("/", config.GlobalAddressBook.Name, "/")),
+                    LinkResource(self, "/addressbooks/public/global/addressbook/"),
                 )
             self._provisionedLinks = True
 
@@ -1755,20 +1756,32 @@
         else:
             if not isinstance(path, FilePath):
                 path = FilePath(path)
+
+            # Check for public/global path
+            from twistedcaldav.storebridge import (
+                AddressBookCollectionFile,
+                ProtoAddressBookCollectionFile,
+                GlobalAddressBookCollectionFile,
+                ProtoGlobalAddressBookCollectionFile,
+            )
+            mainCls = AddressBookCollectionFile
+            protoCls = ProtoAddressBookCollectionFile
+            if isinstance(self.record, InternalDirectoryRecord):
+                if "global" in self.record.shortNames:
+                    mainCls = GlobalAddressBookCollectionFile
+                    protoCls = ProtoGlobalAddressBookCollectionFile
+
             newAddressBook = self._newStoreAddressBookHome.addressbookWithName(
                 path.basename()
             )
             if newAddressBook is None:
                 # Local imports.due to circular dependency between modules.
-                from twistedcaldav.storebridge import (
-                     ProtoAddressBookCollectionFile)
-                similar = ProtoAddressBookCollectionFile(
+                similar = protoCls(
                     self._newStoreAddressBookHome,
                     path, principalCollections=self.principalCollections()
                 )
             else:
-                from twistedcaldav.storebridge import AddressBookCollectionFile
-                similar = AddressBookCollectionFile(
+                similar = mainCls(
                     newAddressBook, self._newStoreAddressBookHome,
                     path, principalCollections=self.principalCollections()
                 )

Modified: CalendarServer/branches/new-store/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/storebridge.py	2010-07-01 02:45:32 UTC (rev 5826)
+++ CalendarServer/branches/new-store/twistedcaldav/storebridge.py	2010-07-01 16:20:10 UTC (rev 5827)
@@ -41,7 +41,7 @@
 from twext.web2.http import HTTPError, StatusResponse
 
 from twistedcaldav.static import CalDAVFile, ScheduleInboxFile,\
-    NotificationCollectionFile, NotificationFile
+    NotificationCollectionFile, NotificationFile, GlobalAddressBookFile
 from twistedcaldav.vcard import Component as VCard
 
 from txdav.common.icommondatastore import NoSuchObjectResourceError,\
@@ -1097,7 +1097,103 @@
         return succeed(0)
 
 
+class GlobalAddressBookCollectionFile(_AddressBookChildHelper, GlobalAddressBookFile):
+    """
+    Wrapper around a L{txcarddav.iaddressbook.IAddressBook}.
+    """
 
+    def __init__(self, addressbook, home, *args, **kw):
+        """
+        Create a GlobalAddressBookCollectionFile from a L{txcarddav.iaddressbook.IAddressBook}
+        and the arguments required for L{CalDAVFile}.
+        """
+        super(GlobalAddressBookCollectionFile, self).__init__(*args, **kw)
+        self._initializeWithAddressBook(addressbook, home)
+
+
+    def isCollection(self):
+        return True
+
+    def isAddressBookCollection(self):
+        """
+        Yes, it is a calendar collection.
+        """
+        return True
+
+class ProtoGlobalAddressBookCollectionFile(GlobalAddressBookFile):
+    """
+    A resource representing an addressbook collection which hasn't yet been created.
+    """
+
+    def __init__(self, home, *args, **kw):
+        """
+        A placeholder resource for an addressbook collection which does not yet
+        exist, but will become a L{GlobalAddressBookCollectionFile}.
+
+        @param home: The addressbook home which will be this resource's parent,
+            when it exists.
+
+        @type home: L{txcarddav.iaddressbookstore.IAddressBookHome}
+        """
+        self._newStoreParentHome = home
+        super(ProtoGlobalAddressBookCollectionFile, self).__init__(*args, **kw)
+
+
+    def isCollection(self):
+        return True
+
+    def createSimilarFile(self, path):
+        # FIXME: this is necessary for 
+        # twistedcaldav.test.test_mkcol.
+        #     MKCOL.test_make_addressbook_no_parent - there should be a more
+        # structured way to refuse creation with a non-existent parent.
+        return NoParent(path)
+
+
+    def provisionFile(self):
+        """
+        Create an addressbook collection.
+        """
+        # FIXME: this should be done in the backend; provisionDefaultAddressBooks
+        # should go away.
+        return self.createAddressBookCollection()
+
+
+    def createAddressBookCollection(self):
+        """
+        Override C{createAddressBookCollection} to actually do the work.
+        """
+        d = succeed(CREATED)
+
+        Name = self.fp.basename()
+        self._newStoreParentHome.createAddressBookWithName(Name)
+        newStoreAddressBook = self._newStoreParentHome.addressbookWithName(
+            Name
+        )
+        GlobalAddressBookCollectionFile.transform(
+            self, newStoreAddressBook, self._newStoreParentHome
+        )
+        return d
+
+
+    def exists(self):
+        # FIXME: tests
+        return False
+
+
+    def provision(self):
+        """
+        This resource should do nothing if it's provisioned.
+        """
+        # FIXME: should be deleted, or raise an exception
+
+
+    def quotaSize(self, request):
+        # FIXME: tests, workingness
+        return succeed(0)
+
+
+
 class AddressBookObjectFile(CalDAVFile):
     """
     A resource wrapping a addressbook object.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100701/7109ba9f/attachment-0001.html>


More information about the calendarserver-changes mailing list