[CalendarServer-changes] [724] CalendarServer/trunk/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Wed Dec 6 00:32:08 PST 2006


Revision: 724
          http://trac.macosforge.org/projects/calendarserver/changeset/724
Author:   wsanchez at apple.com
Date:     2006-12-06 00:32:07 -0800 (Wed, 06 Dec 2006)

Log Message:
-----------
Move generic provisioning logic to new twistedcaldav.directory.calendar module.
File-based subclasses remain in twistedcaldav.static.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/static.py

Added Paths:
-----------
    CalendarServer/trunk/twistedcaldav/directory/calendar.py

Added: CalendarServer/trunk/twistedcaldav/directory/calendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/calendar.py	                        (rev 0)
+++ CalendarServer/trunk/twistedcaldav/directory/calendar.py	2006-12-06 08:32:07 UTC (rev 724)
@@ -0,0 +1,297 @@
+##
+# Copyright (c) 2006 Apple Computer, 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.
+#
+# DRI: Wilfredo Sanchez, wsanchez at apple.com
+##
+
+"""
+Implements a directory-backed calendar hierarchy.
+"""
+
+__all__ = [
+    "DirectoryCalendarHomeProvisioningResource",
+    "DirectoryCalendarHomeTypeProvisioningResource",
+    "DirectoryCalendarHomeResource",
+]
+
+from twisted.web2.dav import davxml
+from twisted.web2.dav.util import joinURL
+from twisted.web2.dav.resource import TwistedACLInheritable, TwistedQuotaRootProperty
+
+from twistedcaldav import caldavxml
+from twistedcaldav.extensions import ReadOnlyResourceMixIn, DAVResource
+from twistedcaldav.resource import CalDAVResource
+from twistedcaldav.schedule import ScheduleInboxResource, ScheduleOutboxResource
+from twistedcaldav.directory.idirectory import IDirectoryService
+
+class DirectoryCalendarHomeProvisioningResource (ReadOnlyResourceMixIn, DAVResource):
+    """
+    Resource which provisions calendar home collections as needed.    
+    """
+    def __init__(self, directory, url):
+        """
+        @param path: the path to the file which will back the resource.
+        @param directory: an L{IDirectoryService} to provision calendars from.
+        """
+        assert url.endswith("/"), "Collection URL must end in '/'"
+
+        DAVResource.__init__(self)
+
+        self.directory = IDirectoryService(directory)
+        self._url = url
+
+        # FIXME: Smells like a hack
+        directory.calendarHomesCollection = self
+
+    def provision(self):
+        if not self.putChildren:
+            # Create children
+            for recordType in self.directory.recordTypes():
+                self.putChild(recordType, self.provisionChild(recordType))
+
+    def provisionChild(self, recordType):
+        raise NotImplementedError("Subclass must implement provisionChild()")
+
+    def url(self):
+        return self._url
+
+    def getChild(self, name):
+        self.provision()
+
+        return self.putChildren.get(name, None)
+
+    def listChildren(self):
+        return self.directory.recordTypes()
+
+    def principalCollections(self):
+        # FIXME: directory.principalCollection smells like a hack
+        # See DirectoryPrincipalProvisioningResource.__init__()
+        return self.directory.principalCollection.principalCollections()
+
+    def homeForDirectoryRecord(self, record):
+        typeResource = self.getChild(record.recordType)
+        if typeResource is None:
+            return None
+        else:
+            return typeResource.getChild(record.shortName)
+
+    ##
+    # DAV
+    ##
+    
+    def isCollection(self):
+        return True
+
+    ##
+    # ACL
+    ##
+
+    def defaultAccessControlList(self):
+        return readOnlyACL
+
+class DirectoryCalendarHomeTypeProvisioningResource (ReadOnlyResourceMixIn, DAVResource):
+    """
+    Resource which provisions calendar home collections of a specific
+    record type as needed.
+    """
+    def __init__(self, parent, recordType):
+        """
+        @param path: the path to the file which will back the resource.
+        @param directory: an L{IDirectoryService} to provision calendars from.
+        @param recordType: the directory record type to provision.
+        """
+        DAVResource.__init__(self)
+
+        self.directory = parent.directory
+        self.recordType = recordType
+        self._parent = parent
+
+    def provision(self):
+        pass
+
+    def provisionChild(self, record):
+        raise NotImplementedError("Subclass must implement provisionChild()")
+
+    def url(self):
+        return joinURL(self._parent.url(), self.recordType)
+
+    def getChild(self, name, record=None):
+        self.provision()
+
+        if name == "":
+            return self
+
+        if record is None:
+            record = self.directory.recordWithShortName(self.recordType, name)
+            if record is None:
+                return None
+        else:
+            assert name is None
+            name = record.shortName
+
+        return self.provisionChild(record)
+
+    def listChildren(self):
+        return (record.shortName for record in self.directory.listRecords(self.recordType))
+
+    ##
+    # DAV
+    ##
+    
+    def isCollection(self):
+        return True
+
+    ##
+    # ACL
+    ##
+
+    def defaultAccessControlList(self):
+        return readOnlyACL
+
+    def principalCollections(self):
+        return self._parent.principalCollections()
+
+class DirectoryCalendarHomeResource (CalDAVResource):
+    """
+    Calendar home collection resource.
+    """
+    # A global quota limit for all calendar homes. Either a C{int} (size in bytes) to limit
+    # quota to that size, or C{None} for no limit.
+    quotaLimit = None
+
+    def __init__(self, parent, record):
+        """
+        @param path: the path to the file which will back the resource.
+        """
+        CalDAVResource.__init__(self)
+
+        self.record = record
+        self._parent = parent
+
+        # Cache children which must be of a specific type
+        for name, cls in (
+            ("inbox" , ScheduleInboxResource ),
+            ("outbox", ScheduleOutboxResource),
+        ):
+            child = self.provisionChild(name)
+            assert isinstance(child, cls), "Child %r is not a %s: %r" % (name, cls.__name__, child)
+            self.putChild(name, child)
+
+    def provision(self):
+        # Create a calendar collection
+
+        childName = "calendar"
+        childURL = joinURL(self.url(), childName)
+        child = self.provisionChild(childName)
+        assert isinstance(child, CalDAVResource), "Child %r is not a %s: %r" % (childName, CalDAVResource.__name__, child)
+
+        def setupChild(_):
+            # Grant read-free-busy access to authenticated users
+            child.setAccessControlList(
+                davxml.ACL(
+                    davxml.ACE(
+                        davxml.Principal(davxml.Authenticated()),
+                        davxml.Grant(davxml.Privilege(caldavxml.ReadFreeBusy())),
+                        TwistedACLInheritable(),
+                    ),
+                )
+            )
+
+            # Set calendar-free-busy-set on inbox
+            inbox = self.getChild("inbox")
+            inbox.provision()
+            inbox.writeDeadProperty(caldavxml.CalendarFreeBusySet(davxml.HRef(childURL)))
+
+        d = child.createCalendarCollection()
+        d.addCallback(setupChild)
+        return d
+
+    def provisionChild(self, name):
+        raise NotImplementedError("Subclass must implement provisionChild()")
+
+    def url(self):
+        return joinURL(self._parent.url(), self.record.shortName)
+
+    def locateChild(self, path, segments):
+        d = self.provision()
+        d.addCallback(lambda _: super(DirectoryCalendarHomeResource, self).locateChild(path, segments))
+        return d
+
+    ##
+    # DAV
+    ##
+    
+    def isCollection(self):
+        return True
+
+    ##
+    # ACL
+    ##
+
+    def defaultAccessControlList(self):
+        # FIXME: directory.principalCollection smells like a hack
+        # See DirectoryPrincipalProvisioningResource.__init__()
+        myPrincipal = self._parent._parent.directory.principalCollection.principalForRecord(self.record)
+
+        return davxml.ACL(
+            # DAV:read access for authenticated users.
+            davxml.ACE(
+                davxml.Principal(davxml.Authenticated()),
+                davxml.Grant(davxml.Privilege(davxml.Read())),
+            ),
+            # Inheritable DAV:all access for the resource's associated principal.
+            davxml.ACE(
+                davxml.Principal(davxml.HRef(myPrincipal.principalURL())),
+                davxml.Grant(davxml.Privilege(davxml.All())),
+                davxml.Protected(),
+                TwistedACLInheritable(),
+            ),
+        )
+
+    def principalCollections(self):
+        return self._parent.principalCollections()
+
+    ##
+    # Quota
+    ##
+
+    def hasQuotaRoot(self, request):
+        """
+        @return: a C{True} if this resource has quota root, C{False} otherwise.
+        """
+        return self.hasDeadProperty(TwistedQuotaRootProperty) or DirectoryCalendarHomeResource.quotaLimit is not None
+    
+    def quotaRoot(self, request):
+        """
+        @return: a C{int} containing the maximum allowed bytes if this collection
+            is quota-controlled, or C{None} if not quota controlled.
+        """
+        if self.hasDeadProperty(TwistedQuotaRootProperty):
+            return int(str(self.readDeadProperty(TwistedQuotaRootProperty)))
+        else:
+            return DirectoryCalendarHomeResource.quotaLimit
+
+##
+# Utilities
+##
+
+# DAV:read access for authenticated users.
+readOnlyACL = davxml.ACL(
+    davxml.ACE(
+        davxml.Principal(davxml.Authenticated()),
+        davxml.Grant(davxml.Privilege(davxml.Read())),
+        davxml.Protected(),
+    ),
+)

Modified: CalendarServer/trunk/twistedcaldav/static.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/static.py	2006-12-06 06:35:01 UTC (rev 723)
+++ CalendarServer/trunk/twistedcaldav/static.py	2006-12-06 08:32:07 UTC (rev 724)
@@ -41,22 +41,22 @@
 from twisted.web2.dav.fileop import mkcollection, rmdir
 from twisted.web2.dav.http import ErrorResponse
 from twisted.web2.dav.idav import IDAVResource
-from twisted.web2.dav.resource import TwistedACLInheritable, TwistedQuotaRootProperty, davPrivilegeSet
-from twisted.web2.dav.util import parentForURL, joinURL, bindMethods
+from twisted.web2.dav.resource import davPrivilegeSet
+from twisted.web2.dav.util import parentForURL, bindMethods
 
 from twistedcaldav import caldavxml
 from twistedcaldav import customxml
-from twistedcaldav.extensions import ReadOnlyResourceMixIn
 from twistedcaldav.ical import Component as iComponent
 from twistedcaldav.ical import Property as iProperty
 from twistedcaldav.icaldav import ICalDAVResource
 from twistedcaldav.index import Index, IndexSchedule, db_basename
-from twistedcaldav.resource import CalDAVResource
-from twistedcaldav.resource import isCalendarCollectionResource
+from twistedcaldav.resource import CalDAVResource, isCalendarCollectionResource
 from twistedcaldav.schedule import ScheduleInboxResource, ScheduleOutboxResource
 from twistedcaldav.extensions import DAVFile
 from twistedcaldav.dropbox import DropBox
-from twistedcaldav.directory.idirectory import IDirectoryService
+from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource
+from twistedcaldav.directory.calendar import DirectoryCalendarHomeTypeProvisioningResource
+from twistedcaldav.directory.calendar import DirectoryCalendarHomeResource
 
 class CalDAVFile (CalDAVResource, DAVFile):
     """
@@ -429,7 +429,7 @@
     def __repr__(self):
         return "<%s (calendar outbox collection): %s>" % (self.__class__.__name__, self.fp.path)
 
-class CalendarHomeProvisioningFile (ReadOnlyResourceMixIn, DAVFile):
+class CalendarHomeProvisioningFile (DirectoryCalendarHomeProvisioningResource, DAVFile):
     """
     Resource which provisions calendar home collections as needed.    
     """
@@ -438,70 +438,21 @@
         @param path: the path to the file which will back the resource.
         @param directory: an L{IDirectoryService} to provision calendars from.
         """
-        assert url.endswith("/"), "Collection URL must end in '/'"
+        DAVFile.__init__(self, path)
+        DirectoryCalendarHomeProvisioningResource.__init__(self, directory, url)
 
-        super(CalendarHomeProvisioningFile, self).__init__(path)
-
-        self.directory = IDirectoryService(directory)
-        self._url = url
-
-        # FIXME: Smells like a hack
-        directory.calendarHomesCollection = self
-
     def provision(self):
         provisionFile(self)
 
-        if not self.putChildren:
-            # Create children
-            for recordType in self.directory.recordTypes():
-                self.putChild(recordType, CalendarHomeTypeProvisioningFile(self.fp.child(recordType).path, self, recordType))
+        super(CalendarHomeProvisioningFile, self).provision()
 
-    def url(self):
-        return self._url
+    def provisionChild(self, recordType):
+        return CalendarHomeTypeProvisioningFile(self.fp.child(recordType).path, self, recordType)
 
     def createSimilarFile(self, path):
         raise HTTPError(responsecode.NOT_FOUND)
 
-    def getChild(self, name):
-        self.provision()
-
-        children = self.putChildren
-        if name not in children and name.lower() in (x.lower() for x in children):
-            # This avoids finding case variants of put children on case-insensitive filesystems.
-            return None
-        else:
-            return children.get(name, None)
-
-    def listChildren(self):
-        return self.directory.recordTypes()
-
-    def principalCollections(self):
-        # FIXME: directory.principalCollection smells like a hack
-        # See DirectoryPrincipalProvisioningResource.__init__()
-        return self.directory.principalCollection.principalCollections()
-
-    def homeForDirectoryRecord(self, record):
-        typeResource = self.getChild(record.recordType)
-        if typeResource is None:
-            return None
-        else:
-            return typeResource.getChild(record.shortName)
-
-    ##
-    # DAV
-    ##
-    
-    def isCollection(self):
-        return True
-
-    ##
-    # ACL
-    ##
-
-    def defaultAccessControlList(self):
-        return readOnlyACL
-
-class CalendarHomeTypeProvisioningFile (ReadOnlyResourceMixIn, DAVFile):
+class CalendarHomeTypeProvisioningFile (DirectoryCalendarHomeTypeProvisioningResource, DAVFile):
     """
     Resource which provisions calendar home collections of a specific
     record type as needed.
@@ -512,74 +463,31 @@
         @param directory: an L{IDirectoryService} to provision calendars from.
         @param recordType: the directory record type to provision.
         """
-        super(CalendarHomeTypeProvisioningFile, self).__init__(path)
+        DAVFile.__init__(self, path)
+        DirectoryCalendarHomeTypeProvisioningResource.__init__(self, parent, recordType)
 
-        self.directory = parent.directory
-        self.recordType = recordType
-        self._parent = parent
-
     def provision(self):
         provisionFile(self, self._parent)
 
-    def url(self):
-        return joinURL(self._parent.url(), self.recordType)
+        return super(CalendarHomeTypeProvisioningFile, self).provision()
 
+    def provisionChild(self, record):
+        return CalendarHomeFile(self.fp.child(record.shortName).path, self, record)
+
     def createSimilarFile(self, path):
         raise HTTPError(responsecode.NOT_FOUND)
 
-    def getChild(self, name, record=None):
-        self.provision()
-
-        if name == "":
-            return self
-
-        if record is None:
-            record = self.directory.recordWithShortName(self.recordType, name)
-            if record is None:
-                return None
-        else:
-            assert name is None
-            name = record.shortName
-
-        return CalendarHomeFile(self.fp.child(name).path, self, record)
-
-    def listChildren(self):
-        return (record.shortName for record in self.directory.listRecords(self.recordType))
-
-    ##
-    # DAV
-    ##
-    
-    def isCollection(self):
-        return True
-
-    ##
-    # ACL
-    ##
-
-    def defaultAccessControlList(self):
-        return readOnlyACL
-
-    def principalCollections(self):
-        return self._parent.principalCollections()
-
-class CalendarHomeFile (CalDAVFile):
+class CalendarHomeFile (DirectoryCalendarHomeResource, CalDAVFile):
     """
     Calendar home collection resource.
     """
-    # A global quota limit for all calendar homes. Either a C{int} (size in bytes) to limit
-    # quota to that size, or C{None} for no limit.
-    quotaLimit = None
-
     def __init__(self, path, parent, record):
         """
         @param path: the path to the file which will back the resource.
         """
-        super(CalendarHomeFile, self).__init__(path)
+        CalDAVFile.__init__(self, path)
+        DirectoryCalendarHomeResource.__init__(self, parent, record)
 
-        self.record = record
-        self._parent = parent
-
         # Cache children which must be of a specific type
         for name, cls in (
             ("inbox" , ScheduleInboxFile),
@@ -591,32 +499,8 @@
         if not provisionFile(self, self._parent):
             return succeed(None)
 
-        # Create a calendar collection
+        d = super(CalendarHomeFile, self).provision()
 
-        child_name = "calendar"
-        childURL = joinURL(self.url(), child_name)
-        child = CalDAVFile(os.path.join(self.fp.path, child_name))
-
-        def setupChild(_):
-            # Grant read-free-busy access to authenticated users
-            child.setAccessControlList(
-                davxml.ACL(
-                    davxml.ACE(
-                        davxml.Principal(davxml.Authenticated()),
-                        davxml.Grant(davxml.Privilege(caldavxml.ReadFreeBusy())),
-                        TwistedACLInheritable(),
-                    ),
-                )
-            )
-
-            # Set calendar-free-busy-set on inbox
-            inbox = self.getChild("inbox")
-            inbox.provision()
-            inbox.writeDeadProperty(caldavxml.CalendarFreeBusySet(davxml.HRef(childURL)))
-
-        d = child.createCalendarCollection()
-        d.addCallback(setupChild)
-
         # FIXME: This should provision itself also
         # Provision a drop box
         if self.record.recordType == "user":
@@ -624,9 +508,17 @@
 
         return d
 
-    def url(self):
-        return joinURL(self._parent.url(), self.record.shortName)
+    def provisionChild(self, name):
+        cls = {
+            "inbox" : ScheduleInboxFile,
+            "outbox": ScheduleOutboxFile,
+        }.get(name, None)
 
+        if cls is not None:
+            return cls(self.fp.child(name).path, self)
+
+        return self.createSimilarFile(self.fp.child(name).path)
+
     def createSimilarFile(self, path):
         if path == self.fp.path:
             return self
@@ -640,65 +532,6 @@
 
         return super(CalendarHomeFile, self).getChild(name)
 
-    def locateChild(self, path, segments):
-        d = self.provision()
-        d.addCallback(lambda _: super(CalendarHomeFile, self).locateChild(path, segments))
-        return d
-
-    ##
-    # DAV
-    ##
-    
-    def isCollection(self):
-        return True
-
-    ##
-    # ACL
-    ##
-
-    def defaultAccessControlList(self):
-        # FIXME: directory.principalCollection smells like a hack
-        # See DirectoryPrincipalProvisioningResource.__init__()
-        myPrincipal = self._parent._parent.directory.principalCollection.principalForRecord(self.record)
-
-        return davxml.ACL(
-            # DAV:read access for authenticated users.
-            davxml.ACE(
-                davxml.Principal(davxml.Authenticated()),
-                davxml.Grant(davxml.Privilege(davxml.Read())),
-            ),
-            # Inheritable DAV:all access for the resource's associated principal.
-            davxml.ACE(
-                davxml.Principal(davxml.HRef(myPrincipal.principalURL())),
-                davxml.Grant(davxml.Privilege(davxml.All())),
-                davxml.Protected(),
-                TwistedACLInheritable(),
-            ),
-        )
-
-    def principalCollections(self):
-        return self._parent.principalCollections()
-
-    ##
-    # Quota
-    ##
-
-    def hasQuotaRoot(self, request):
-        """
-        @return: a C{True} if this resource has quota root, C{False} otherwise.
-        """
-        return self.hasDeadProperty(TwistedQuotaRootProperty) or CalendarHomeFile.quotaLimit is not None
-    
-    def quotaRoot(self, request):
-        """
-        @return: a C{int} containing the maximum allowed bytes if this collection
-            is quota-controlled, or C{None} if not quota controlled.
-        """
-        if self.hasDeadProperty(TwistedQuotaRootProperty):
-            return int(str(self.readDeadProperty(TwistedQuotaRootProperty)))
-        else:
-            return CalendarHomeFile.quotaLimit
-
 ##
 # Utilities
 ##
@@ -736,15 +569,6 @@
     # Otherwise, there is no child
     return (None, ())
 
-# DAV:read access for authenticated users.
-readOnlyACL = davxml.ACL(
-    davxml.ACE(
-        davxml.Principal(davxml.Authenticated()),
-        davxml.Grant(davxml.Privilege(davxml.Read())),
-        davxml.Protected(),
-    ),
-)
-
 def _schedulePrivilegeSet():
     edited = False
 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20061206/f4f13ccd/attachment.html


More information about the calendarserver-changes mailing list