[CalendarServer-changes] [2563] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Mon Jun 16 17:25:11 PDT 2008


Revision: 2563
          http://trac.macosforge.org/projects/calendarserver/changeset/2563
Author:   wsanchez at apple.com
Date:     2008-06-16 17:25:09 -0700 (Mon, 16 Jun 2008)

Log Message:
-----------
/principals now has no on-disk presence.
Add EnablePrincipalListings option.

Modified Paths:
--------------
    CalendarServer/trunk/conf/caldavd-test.plist
    CalendarServer/trunk/conf/caldavd.plist
    CalendarServer/trunk/twistedcaldav/config.py
    CalendarServer/trunk/twistedcaldav/directory/calendar.py
    CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py
    CalendarServer/trunk/twistedcaldav/directory/principal.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_calendar.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_guidchange.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py
    CalendarServer/trunk/twistedcaldav/directory/util.py
    CalendarServer/trunk/twistedcaldav/extensions.py
    CalendarServer/trunk/twistedcaldav/tap.py
    CalendarServer/trunk/twistedcaldav/test/test_root.py
    CalendarServer/trunk/twistedcaldav/test/util.py
    CalendarServer/trunk/twistedcaldav/util.py

Added Paths:
-----------
    CalendarServer/trunk/lib-patches/Twisted/twisted.web2.static.patch

Modified: CalendarServer/trunk/conf/caldavd-test.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-test.plist	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/conf/caldavd-test.plist	2008-06-17 00:25:09 UTC (rev 2563)
@@ -225,7 +225,7 @@
     <key>Kerberos</key>
     <dict>
       <key>Enabled</key>
-      <true/>
+      <false/>
       <key>ServicePrincipal</key>
       <string></string>
     </dict>
@@ -253,6 +253,10 @@
 
   <key>LogLevels</key>
   <dict>
+<!--
+   <key>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</key>
+   <string>debug</string>
+ -->
   </dict>
 
   <!-- Accounting -->
@@ -389,6 +393,10 @@
   <key>ResponseCompression</key>
   <false/>
 
+  <!-- Enables directory listings for principals -->
+  <key>EnablePrincipalListings</key>
+  <true/>
+
   <!-- Support for Memcached -->
   <key>Memcached</key>
   <dict>
@@ -402,7 +410,7 @@
     <string>../memcached-1.2.5/_root/bin/memcached</string>
     <key>Options</key>
     <array>
-      <string>-vv</string>
+      <!--<string>-vv</string>-->
     </array>
   </dict>
 

Modified: CalendarServer/trunk/conf/caldavd.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd.plist	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/conf/caldavd.plist	2008-06-17 00:25:09 UTC (rev 2563)
@@ -254,6 +254,19 @@
 
 
   <!--
+    Miscellaneous items
+  -->
+
+  <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
+  <key>ResponseCompression</key>
+  <false/>
+
+  <!-- Enables directory listings for principals -->
+  <key>EnablePrincipalListings</key>
+  <true/>
+
+
+  <!--
     Non-standard CalDAV extensions
   -->
 

Added: CalendarServer/trunk/lib-patches/Twisted/twisted.web2.static.patch
===================================================================
--- CalendarServer/trunk/lib-patches/Twisted/twisted.web2.static.patch	                        (rev 0)
+++ CalendarServer/trunk/lib-patches/Twisted/twisted.web2.static.patch	2008-06-17 00:25:09 UTC (rev 2563)
@@ -0,0 +1,16 @@
+Index: twisted/web2/static.py
+===================================================================
+--- twisted/web2/static.py	(revision 19773)
++++ twisted/web2/static.py	(working copy)
+@@ -200,7 +200,10 @@
+         super(File, self).__init__()
+ 
+         self.putChildren = {}
+-        self.fp = filepath.FilePath(path)
++        if isinstance(path, FilePath):
++            self.fp = path
++        else:
++            self.fp = filepath.FilePath(path)
+         # Remove the dots from the path to split
+         self.defaultType = defaultType
+         self.ignoredExts = list(ignoredExts)

Modified: CalendarServer/trunk/twistedcaldav/config.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/config.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/config.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -152,6 +152,11 @@
     "EnableSACLs": False,
 
     #
+    # Enables directory listings for principals
+    #
+    "EnablePrincipalListings": True,
+
+    #
     # Non-standard CalDAV extensions
     #
     "EnableDropBox"         : False, # Calendar Drop Box
@@ -189,7 +194,6 @@
     # RFC2616 Section 3.5
     "ResponseCompression": True,
 
-
     # Set the maximum number of outstanding requests to this server.
     "MaxRequests": 600,
 

Modified: CalendarServer/trunk/twistedcaldav/directory/calendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/calendar.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/directory/calendar.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -154,11 +154,15 @@
         return self._parent.homeForDirectoryRecord(record)
 
     def listChildren(self):
-        return (
-            record.shortName
-            for record in self.directory.listRecords(self.recordType)
-            if record.enabledForCalendaring
-        )
+        if config.EnablePrincipalListings:
+            return (
+                record.shortName
+                for record in self.directory.listRecords(self.recordType)
+                if record.enabledForCalendaring
+            )
+        else:
+            # Not a listable collection
+            raise HTTPError(responsecode.FORBIDDEN)
 
     def createSimilarFile(self, path):
         raise HTTPError(responsecode.NOT_FOUND)

Modified: CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -25,19 +25,20 @@
 from twisted.internet.defer import returnValue
 from twisted.internet.defer import succeed, inlineCallbacks
 from twisted.web2 import responsecode
+from twisted.web2.http import HTTPError, StatusResponse
 from twisted.web2.dav import davxml
 from twisted.web2.dav.element.base import dav_namespace
 from twisted.web2.dav.util import joinURL
-from twisted.web2.http import HTTPError, StatusResponse
+from twisted.web2.dav.noneprops import NonePropertyStore
 
 from twistedcaldav.config import config
 from twistedcaldav.extensions import DAVFile, DAVPrincipalResource
 from twistedcaldav.extensions import ReadOnlyWritePropertiesResourceMixIn
 from twistedcaldav.memcacher import Memcacher
 from twistedcaldav.resource import CalDAVComplianceMixIn
+from twistedcaldav.directory.util import NotFilePath
 from twistedcaldav.sql import AbstractSQLDatabase
 from twistedcaldav.sql import db_prefix
-from twistedcaldav.static import AutoProvisioningFileMixIn
 
 import itertools
 import os
@@ -72,14 +73,12 @@
         # Permissions here are fixed, and are not subject to inherritance rules, etc.
         return succeed(self.defaultAccessControlList())
 
-class CalendarUserProxyPrincipalResource (CalDAVComplianceMixIn, AutoProvisioningFileMixIn, PermissionsMixIn, DAVPrincipalResource, DAVFile):
+class CalendarUserProxyPrincipalResource (CalDAVComplianceMixIn, PermissionsMixIn, DAVPrincipalResource, DAVFile):
     """
     Calendar user proxy principal resource.
     """
-
-    def __init__(self, path, parent, proxyType):
+    def __init__(self, parent, proxyType):
         """
-        @param path: the path to the file which will back this resource.
         @param parent: the parent of this resource.
         @param proxyType: a C{str} containing the name of the resource.
         """
@@ -90,7 +89,7 @@
 
         url = joinURL(parent.principalURL(), proxyType) + slash
 
-        super(CalendarUserProxyPrincipalResource, self).__init__(path, url)
+        super(CalendarUserProxyPrincipalResource, self).__init__(NotFilePath(isdir=True), url)
 
         self.parent      = parent
         self.proxyType   = proxyType
@@ -111,10 +110,6 @@
             if url.startswith("/")
         )
 
-        # Provision in __init__() because principals are used prior to request
-        # lookups.
-        self.provision()
-
     def __str__(self):
         return "%s [%s]" % (self.parent, self.proxyType)
 
@@ -127,7 +122,7 @@
 
         # The db is located in the principal collection root
         if not hasattr(self.pcollection, "calendar_user_proxy_db"):
-            setattr(self.pcollection, "calendar_user_proxy_db", CalendarUserProxyDatabase(self.pcollection.fp.path))
+            setattr(self.pcollection, "calendar_user_proxy_db", CalendarUserProxyDatabase(config.DataRoot))
         return self.pcollection.calendar_user_proxy_db
 
     def resourceType(self):
@@ -141,6 +136,14 @@
     def isCollection(self):
         return True
 
+    def etag(self):
+        return None
+
+    def deadProperties(self):
+        if not hasattr(self, "_dead_properties"):
+            self._dead_properties = NonePropertyStore(self)
+        return self._dead_properties
+
     def writeProperty(self, property, request):
         assert isinstance(property, davxml.WebDAVElement)
 
@@ -332,7 +335,7 @@
     """
 
     dbType = "CALENDARUSERPROXY"
-    dbFilename = db_prefix + "calendaruserproxy"
+    dbFilename = "calendaruserproxy.sqlite"
     dbFormatVersion = "4"
 
     class ProxyDBMemcacher(Memcacher):

Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -40,6 +40,7 @@
 from twisted.web2.dav import davxml
 from twisted.web2.dav.element.base import twisted_private_namespace
 from twisted.web2.dav.util import joinURL
+from twisted.web2.dav.noneprops import NonePropertyStore
 
 from twistedcaldav.config import config
 from twistedcaldav.cache import DisabledCacheNotifier, PropfindCacheMixin
@@ -47,9 +48,9 @@
 from twistedcaldav.directory.calendaruserproxy import CalendarUserProxyDatabase
 from twistedcaldav.directory.calendaruserproxy import CalendarUserProxyPrincipalResource
 from twistedcaldav.directory.directory import DirectoryService
+from twistedcaldav.directory.util import NotFilePath
 from twistedcaldav.extensions import ReadOnlyResourceMixIn, DAVFile, DAVPrincipalResource
 from twistedcaldav.resource import CalendarPrincipalCollectionResource, CalendarPrincipalResource
-from twistedcaldav.static import AutoProvisioningFileMixIn
 from twistedcaldav.directory.idirectory import IDirectoryService
 from twistedcaldav.log import Logger
 
@@ -70,25 +71,38 @@
         # Permissions here are fixed, and are not subject to inherritance rules, etc.
         return succeed(self.defaultAccessControlList())
 
+
 class DirectoryProvisioningResource (
-    AutoProvisioningFileMixIn,
     PermissionsMixIn,
     CalendarPrincipalCollectionResource,
     DAVFile,
 ):
-    def __init__(self, path, url, directory):
+    def __init__(self, url, directory):
         """
-        @param path: the path to the file which will back the resource.
         @param url: the canonical URL for the resource.
         @param directory: an L{IDirectoryService} to provision principals from.
         """
         assert url.endswith("/"), "Collection URL must end in '/'"
 
         CalendarPrincipalCollectionResource.__init__(self, url)
-        DAVFile.__init__(self, path)
+        DAVFile.__init__(self, NotFilePath(isdir=True))
 
         self.directory = IDirectoryService(directory)
 
+    def locateChild(self, req, segments):
+        child = self.getChild(segments[0])
+        if child is not None:
+            return (child, segments[1:])
+        return (None, ())
+
+    def deadProperties(self):
+        if not hasattr(self, "_dead_properties"):
+            self._dead_properties = NonePropertyStore(self)
+        return self._dead_properties
+
+    def etag(self):
+        return None
+
     def principalForShortName(self, recordType, name):
         return self.principalForRecord(self.directory.recordWithShortName(recordType, name))
 
@@ -113,8 +127,8 @@
     """
     Collection resource which provisions directory principals as its children.
     """
-    def __init__(self, path, url, directory):
-        DirectoryProvisioningResource.__init__(self, path, url, directory)
+    def __init__(self, url, directory):
+        DirectoryProvisioningResource.__init__(self, url, directory)
 
         # FIXME: Smells like a hack
         self.directory.principalCollection = self
@@ -205,8 +219,6 @@
         raise HTTPError(responsecode.NOT_FOUND)
 
     def getChild(self, name):
-        self.provision()
-
         if name == "":
             return self
         else:
@@ -229,13 +241,11 @@
     """
     def __init__(self, parent, recordType):
         """
-        @param path: the path to the file which will back the resource.
         @param parent: the parent L{DirectoryPrincipalProvisioningResource}.
         @param recordType: the directory record type to provision.
         """
         DirectoryProvisioningResource.__init__(
             self,
-            parent.fp.child(recordType).path,
             joinURL(parent.principalCollectionURL(), recordType) + "/",
             parent.directory
         )
@@ -258,14 +268,17 @@
         raise HTTPError(responsecode.NOT_FOUND)
 
     def getChild(self, name):
-        self.provision()
         if name == "":
             return self
         else:
             return self.principalForShortName(self.recordType, name)
 
     def listChildren(self):
-        return (record.shortName for record in self.directory.listRecords(self.recordType))
+        if config.EnablePrincipalListings:
+            return (record.shortName for record in self.directory.listRecords(self.recordType))
+        else:
+            # Not a listable collection
+            raise HTTPError(responsecode.FORBIDDEN)
 
     ##
     # ACL
@@ -279,16 +292,13 @@
     Collection resource which provisions directory principals indexed
     by UID.
     """
-    # FIXME: Remove path argument
     def __init__(self, parent):
         """
-        @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.
         """
         DirectoryProvisioningResource.__init__(
             self,
-            parent.fp.child(uidsResourceName).path,
             joinURL(parent.principalCollectionURL(), uidsResourceName) + "/",
             parent.directory
         )
@@ -310,8 +320,6 @@
         raise HTTPError(responsecode.NOT_FOUND)
 
     def getChild(self, name):
-        self.provision()
-
         if name == "":
             return self
 
@@ -328,13 +336,10 @@
             log.err("No principal found for UID: %s" % (name,))
             return None
 
-        assert len(name) > 4
-        childPath = self.fp.child(name[0:2]).child(name[2:4]).child(name)
-
         if record.enabledForCalendaring:
-            primaryPrincipal = DirectoryCalendarPrincipalResource(childPath.path, self, record)
+            primaryPrincipal = DirectoryCalendarPrincipalResource(self, record)
         else:
-            primaryPrincipal = DirectoryPrincipalResource(childPath.path, self, record)
+            primaryPrincipal = DirectoryPrincipalResource(self, record)
 
         if subType is None:
             return primaryPrincipal
@@ -352,19 +357,18 @@
     def principalCollections(self):
         return self.parent.principalCollections()
 
-class DirectoryPrincipalResource (PropfindCacheMixin, AutoProvisioningFileMixIn, PermissionsMixIn, DAVPrincipalResource, DAVFile):
+class DirectoryPrincipalResource (PropfindCacheMixin, PermissionsMixIn, DAVPrincipalResource, DAVFile):
     """
     Directory principal resource.
     """
     cacheNotifierFactory = DisabledCacheNotifier
 
-    def __init__(self, path, parent, record):
+    def __init__(self, parent, record):
         """
-        @param path: them path to the file which will back this resource.
         @param parent: the parent of this resource.
         @param record: the L{IDirectoryRecord} that this resource represents.
         """
-        super(DirectoryPrincipalResource, self).__init__(path)
+        super(DirectoryPrincipalResource, self).__init__(NotFilePath(isdir=True))
 
         self.cacheNotifier = self.cacheNotifierFactory(self)
 
@@ -373,7 +377,7 @@
         else:
             slash = ""
 
-        assert record is not None, "Principal must have a directory record: %s" % (path,)
+        assert record is not None, "Principal must have a directory record"
 
         url = joinURL(parent.principalCollectionURL(), record.guid) + slash
 
@@ -385,10 +389,6 @@
             joinURL(parent.parent.principalCollectionURL(), record.recordType, record.shortName) + slash,
         )
 
-        # Provision in __init__() because principals are used prior to request
-        # lookups.
-        self.provision()
-
     def __str__(self):
         return "(%s) %s" % (self.record.recordType, self.record.shortName)
 
@@ -399,6 +399,14 @@
             self.writeDeadProperty(RecordTypeProperty(self.record.recordType))
         return result
 
+    def deadProperties(self):
+        if not hasattr(self, "_dead_properties"):
+            self._dead_properties = NonePropertyStore(self)
+        return self._dead_properties
+
+    def etag(self):
+        return None
+
     ##
     # HTTP
     ##
@@ -471,7 +479,7 @@
 
         # The db is located in the principal collection root
         if not hasattr(pcollection, "calendar_user_proxy_db"):
-            setattr(pcollection, "calendar_user_proxy_db", CalendarUserProxyDatabase(pcollection.fp.path))
+            setattr(pcollection, "calendar_user_proxy_db", CalendarUserProxyDatabase(config.DataRoot))
         return pcollection.calendar_user_proxy_db
 
     def alternateURIs(self):
@@ -538,7 +546,6 @@
 
         yield groups
 
-
     def principalCollections(self):
         return self.parent.principalCollections()
 
@@ -563,8 +570,6 @@
         return ()
 
 
-
-
 class DirectoryCalendarPrincipalResource (DirectoryPrincipalResource, CalendarPrincipalResource):
     """
     Directory calendar principal resource.
@@ -698,7 +703,7 @@
             return self
 
         if config.EnableProxyPrincipals and name in ("calendar-proxy-read", "calendar-proxy-write"):
-            return CalendarUserProxyPrincipalResource(self.fp.child(name).path, self, name)
+            return CalendarUserProxyPrincipalResource(self, name)
         else:
             return None
 

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_calendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_calendar.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_calendar.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -44,14 +44,9 @@
         # Set up a principals hierarchy for each service we're testing with
         name = "principals"
         url = "/" + name + "/"
-        path = os.path.join(self.docroot, url[1:])
 
-        if os.path.exists(path):
-            rmdir(path)
-        os.mkdir(path)
+        provisioningResource = DirectoryPrincipalProvisioningResource(url, self.directoryService)
 
-        provisioningResource = DirectoryPrincipalProvisioningResource(path, url, self.directoryService)
-
         self.site.resource.putChild("principals", provisioningResource)
 
         self.setupCalendars()

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_guidchange.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_guidchange.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_guidchange.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -47,14 +47,9 @@
         # Set up a principals hierarchy for each service we're testing with
         name = "principals"
         url = "/" + name + "/"
-        path = os.path.join(self.docroot, url[1:])
 
-        if os.path.exists(path):
-            rmdir(path)
-        os.mkdir(path)
+        provisioningResource = DirectoryPrincipalProvisioningResource(url, self.directoryService)
 
-        provisioningResource = DirectoryPrincipalProvisioningResource(path, url, self.directoryService)
-
         self.site.resource.putChild("principals", provisioningResource)
 
         self.setupCalendars()

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -56,14 +56,9 @@
         for directory in directoryServices:
             name = directory.__class__.__name__
             url = "/" + name + "/"
-            path = os.path.join(self.docroot, url[1:])
 
-            if os.path.exists(path):
-                rmdir(path)
-            os.mkdir(path)
+            provisioningResource = DirectoryPrincipalProvisioningResource(url, directory)
 
-            provisioningResource = DirectoryPrincipalProvisioningResource(path, url, directory)
-
             self.site.resource.putChild(name, provisioningResource)
 
             self.principalRootResources[directory.__class__.__name__] = provisioningResource

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -41,14 +41,9 @@
         self.principalRootResources = {}
         name = directoryService.__class__.__name__
         url = "/" + name + "/"
-        path = os.path.join(self.docroot, url[1:])
 
-        if os.path.exists(path):
-            rmdir(path)
-        os.mkdir(path)
+        provisioningResource = DirectoryPrincipalProvisioningResource(url, directoryService)
 
-        provisioningResource = DirectoryPrincipalProvisioningResource(path, url, directoryService)
-
         self.site.resource.putChild(name, provisioningResource)
 
         self.principalRootResources[directoryService.__class__.__name__] = provisioningResource
@@ -240,8 +235,7 @@
             def changed(self):
                 self.changedCount += 1
 
-        user = self._getPrincipalByShortName(directoryService.recordType_users,
-                                          "cdaboo")
+        user = self._getPrincipalByShortName(directoryService.recordType_users, "cdaboo")
 
         proxyGroup = user.getChild("calendar-proxy-write")
 

Modified: CalendarServer/trunk/twistedcaldav/directory/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/util.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/directory/util.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -62,3 +62,144 @@
     # Convert from long integer to string representation
     uuid = "%032x" % (uuid,)
     return "%s-%s-%s-%s-%s" % (uuid[:8], uuid[8:12], uuid[12:16], uuid[16:20], uuid[20:])
+
+import errno
+import time
+from twisted.python.filepath import FilePath
+
+class NotFilePath(FilePath):
+    """
+    Dummy placeholder for FilePath for when we don't actually want a file.
+    Pretends to be an empty file or directory.
+    """
+    def __init__(self, isfile=False, isdir=False, islink=False):
+        assert isfile or isdir or islink
+
+        self._isfile = isfile
+        self._isdir  = isdir
+        self._islink = islink
+
+        self._time = time.time()
+
+    def __cmp__(self, other):
+        if not isinstance(other, self.__class__):
+            return NotImplemented
+        return cmp(
+            ( self.isdir(),  self.isfile(),  self.islink()),
+            (other.isdir(), other.isfile(), other.islink()),
+        )
+
+    def __repr__(self):
+        types = []
+        if self.isdir():
+            types.append("dir")
+        if self.isfile():
+            types.append("file")
+        if self.islink():
+            types.append("link")
+        if types:
+            return "<%s (%s)>" % (self.__class__.__name__, ",".join(types))
+        else:
+            return "<%s>" % (self.__class__.__name__,)
+
+    def _unimplemented(self, *args):
+        try:
+            raise NotImplementedError("NotFilePath isn't really a FilePath: psych!")
+        except NotImplementedError:
+            from twisted.python.failure import Failure
+            Failure().printTraceback()
+            raise
+
+    child                  = _unimplemented
+    preauthChild           = _unimplemented
+    siblingExtensionSearch = _unimplemented
+    siblingExtension       = _unimplemented
+    open                   = _unimplemented
+    clonePath              = _unimplemented # Cuz I think it's dumb
+
+    def childSearchPreauth(self, *paths):
+        return ()
+
+    def splitext(self):
+        return ("", "")
+
+    def basename(self):
+        return ""
+
+    def dirname(self):
+        return ""
+
+    def restat(self, reraise=True):
+        pass
+
+    def getsize(self):
+        return 0
+
+    def _time(self):
+        return self._time
+
+    # FIXME: Maybe we should have separate ctime, mtime, atime. Meh.
+    getModificationTime = _time
+    getStatusChangeTime = _time
+    getAccessTime       = _time
+
+    def exists(self):
+        return True
+
+    def isdir(self):
+        return self._isdir
+
+    def isfile(self):
+        return self._isfile
+
+    def islink(self):
+        return self._islink
+
+    def isabs(self):
+        return True
+
+    def listdir(self):
+        return ()
+
+    def touch(self):
+        self._time = time.time()
+
+    def _notAllowed(self):
+        raise OSError(errno.EACCES, "Permission denied")
+
+    remove     = _notAllowed
+    setContent = _notAllowed
+
+    def globChildren(self, pattern):
+        return ()
+
+    def parent(self):
+        return self.__class__(isdir=True)
+
+    def createDirectory(self):
+        if self.isdir():
+            raise OSError(errno.EEXIST, "File exists")
+        else:
+            return self._notAllowed
+
+    makedirs = createDirectory
+
+    def create(self):
+        if self.isfile():
+            raise OSError(errno.EEXIST, "File exists")
+        else:
+            return self._notAllowed
+
+    def temporarySibling(self):
+        return self.__class__(isfile=True)
+
+    def copyTo(self, destination):
+        if self.isdir():
+            if not destination.isdir():
+                destination.createDirectory()
+        elif self.isfile():
+            destination.open("w").close()
+        else:
+            raise NotImplementedError()
+
+    moveTo = _notAllowed

Modified: CalendarServer/trunk/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/extensions.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/extensions.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -50,7 +50,7 @@
 from twisted.web2.dav.xattrprops import xattrPropertyStore
 
 from twistedcaldav.log import Logger, LoggingMixIn
-from twistedcaldav.util import submodule, Alternator
+from twistedcaldav.util import submodule, Alternator, printTracebacks
 from twistedcaldav.directory.sudo import SudoDirectoryService
 from twistedcaldav.directory.directory import DirectoryService
 
@@ -560,6 +560,7 @@
         d.addCallback(gotBody)
         return d
 
+    @printTracebacks
     def renderDirectoryBody(self, request):
         """
         Generate a directory listing table in HTML.
@@ -606,10 +607,13 @@
         def gotProperties(qnames):
             ds = []
 
+            noneValue         = object()
+            accessDeniedValue = object()
+
             def gotProperty(property):
                 if property is None:
                     name = "{%s}%s" % qname
-                    value = "** None **"
+                    value = noneValue
                 else:
                     name = property.sname()
                     value = property.toxml()
@@ -628,7 +632,7 @@
                     return (name, None)
 
                 if code == responsecode.UNAUTHORIZED:
-                    return (name, "(access forbidden)")
+                    return (name, accessDeniedValue)
 
                 return f
 
@@ -641,19 +645,34 @@
             even = Alternator()
 
             def gotValues(items):
-                output.append("".join(
-                    """<tr class="%(even)s">"""
-                    """<td valign="top">%(name)s</td>"""
-                    """<td><pre>%(value)s</pre></td>"""
-                    """</tr>"""
-                    % {
-                        "even": even.state() and "even" or "odd",
-                        "name": name,
-                        "value": cgi.escape(value),
-                    }
-                    for result, (name, value) in items
-                    if result and value is not None
-                ))
+                for result, (name, value) in items:
+                    if not result:
+                        continue
+
+                    if value is None:
+                        # An AssertionError might be appropriate, but
+                        # we may as well continue rendering.
+                        log.err("Unexpected None value for property: %s" % (name,))
+                        continue
+                    elif value is noneValue:
+                        value = "<i>(no value)</i>"
+                    elif value is accessDeniedValue:
+                        value = "<i>(access forbidden)</i>"
+                    else:
+                        value = cgi.escape(value)
+
+                    output.append(
+                        """<tr class="%(even)s">"""
+                        """<td valign="top">%(name)s</td>"""
+                        """<td><pre>%(value)s</pre></td>"""
+                        """</tr>"""
+                        % {
+                            "even": even.state() and "even" or "odd",
+                            "name": name,
+                            "value": value,
+                        }
+                    )
+
                 output.append("</div>")
                 return "".join(output)
 

Modified: CalendarServer/trunk/twistedcaldav/tap.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/tap.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/tap.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -499,10 +499,7 @@
         log.info("Setting up document root at: %s" % (config.DocumentRoot,))
         log.info("Setting up principal collection: %r" % (self.principalResourceClass,))
 
-        principalCollection = self.principalResourceClass(
-            os.path.join(config.DocumentRoot, "principals"),
-            "/principals/", directory
-        )
+        principalCollection = self.principalResourceClass("/principals/", directory)
 
         log.info("Setting up calendar collection: %r" % (self.calendarResourceClass,))
 

Modified: CalendarServer/trunk/twistedcaldav/test/test_root.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_root.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/test/test_root.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -53,24 +53,17 @@
 class RootTests(TestCase):
     def setUp(self):
         self.docroot = self.mktemp()
+        os.mkdir(self.docroot)
 
         RootResource.CheckSACL = FakeCheckSACL(sacls={
                 'calendar': ['dreid']})
 
         directory = XMLDirectoryService(xmlFile)
 
-        principals = DirectoryPrincipalProvisioningResource(
-            os.path.join(self.docroot, 'principals'),
-            '/principals/',
-            directory)
+        principals = DirectoryPrincipalProvisioningResource('/principals/', directory)
 
-        # Otherwise the tests that never touch the root resource will
-        # fail on teardown.
-        principals.provision()
+        root = RootResource(self.docroot, principalCollections=[principals])
 
-        root = RootResource(self.docroot,
-                            principalCollections=[principals])
-
         root.putChild('principals',
                       principals)
 
@@ -103,9 +96,9 @@
         resrc, segments = resrc.locateChild(request, ['principals'])
 
         self.failUnless(
-            isinstance(resrc,
-                       DirectoryPrincipalProvisioningResource),
-            "Did not get a DirectoryPrincipalProvisioningResource: %s" % (resrc,))
+            isinstance(resrc, DirectoryPrincipalProvisioningResource),
+            "Did not get a DirectoryPrincipalProvisioningResource: %s" % (resrc,)
+        )
 
         self.assertEquals(segments, [])
 
@@ -131,9 +124,9 @@
 
         def _Cb((resrc, segments)):
             self.failUnless(
-                isinstance(resrc,
-                           DirectoryPrincipalProvisioningResource),
-                "Did not get a DirectoryPrincipalProvisioningResource: %s" % (resrc,))
+                isinstance(resrc, DirectoryPrincipalProvisioningResource),
+                "Did not get a DirectoryPrincipalProvisioningResource: %s" % (resrc,)
+            )
 
             self.assertEquals(segments, [])
 
@@ -163,8 +156,7 @@
                     'Authorization': ['basic', '%s' % (
                             'wsanchez:zehcnasw'.encode('base64'),)]}))
 
-        resrc, segments = self.root.locateChild(request,
-                                         ['principals'])
+        resrc, segments = self.root.locateChild(request, ['principals'])
 
         def _Eb(failure):
             failure.trap(HTTPError)

Modified: CalendarServer/trunk/twistedcaldav/test/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/util.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/test/util.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -14,18 +14,28 @@
 # limitations under the License.
 ##
 
-import twisted.web2.dav.test.util
-from twisted.web2.http import HTTPError, StatusResponse
+import os
 
 from twisted.internet.defer import succeed
+from twisted.web2.http import HTTPError, StatusResponse
 
+from twistedcaldav.config import config
 from twistedcaldav.static import CalDAVFile
 
+import twisted.web2.dav.test.util
 
+
 class TestCase(twisted.web2.dav.test.util.TestCase):
     resource_class = CalDAVFile
 
+    def setUp(self):
+        super(TestCase, self).setUp()
 
+        dataroot = self.mktemp()
+        os.mkdir(dataroot)
+        config.DataRoot = dataroot
+
+
 class InMemoryPropertyStore(object):
     def __init__(self):
         class _FauxPath(object):

Modified: CalendarServer/trunk/twistedcaldav/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/util.py	2008-06-16 21:41:40 UTC (rev 2562)
+++ CalendarServer/trunk/twistedcaldav/util.py	2008-06-17 00:25:09 UTC (rev 2563)
@@ -59,7 +59,7 @@
         raise NotImplementedError("getNCPU not supported on %s%s" % (sys.platform, msg))
 
 ##
-#
+# Module management
 ##
 
 def submodule(module, name):
@@ -75,6 +75,25 @@
 
     return submodule
 
+##
+# Tracebacks
+##
+
+from twisted.python.failure import Failure
+
+def printTracebacks(f):
+    def wrapper(*args, **kwargs):
+        try:
+            return f(*args, **kwargs)
+        except:
+            Failure().printTraceback()
+            raise
+    return wrapper
+
+##
+# Helpers
+##
+
 class Alternator (object):
     """
     Object that alternates between True and False states.
@@ -89,4 +108,3 @@
         state = self._state
         self._state = not state
         return state
-

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080616/c02c530a/attachment-0001.htm 


More information about the calendarserver-changes mailing list