[CalendarServer-changes] [4072] CalendarServer/branches/users/sagen/resource-delegates-4066

source_changes at macosforge.org source_changes at macosforge.org
Thu Apr 23 16:47:08 PDT 2009


Revision: 4072
          http://trac.macosforge.org/projects/calendarserver/changeset/4072
Author:   sagen at apple.com
Date:     2009-04-23 16:47:07 -0700 (Thu, 23 Apr 2009)
Log Message:
-----------
Merging branch forward

Modified Paths:
--------------
    CalendarServer/branches/users/sagen/resource-delegates-4066/bin/caldav_utility.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/calendarserver/tools/util.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/customxml.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingappleopendirectory.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingdirectory.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingxmlfile.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/calendaruserproxy.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/directory.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/idirectory.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/principal.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/sqldb.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/accounts.xml
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_cachedirectory.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_opendirectory.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_opendirectoryrecords.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_principal.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_proxyprincipalmembers.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_xmlfile.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/xmlaccountsparser.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/xmlfile.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/memcachepool.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/resource.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/scheduling/processing.py
    CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/test/test_memcachepool.py

Property Changed:
----------------
    CalendarServer/branches/users/sagen/resource-delegates-4066/
    CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-privatecomments.txt
    CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-privatecomments.xml
    CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-schedulingchanges.txt
    CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-schedulingchanges.xml


Property changes on: CalendarServer/branches/users/sagen/resource-delegates-4066
___________________________________________________________________
Added: svn:mergeinfo
   + /CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/bin/caldav_utility.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/bin/caldav_utility.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/bin/caldav_utility.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -18,6 +18,7 @@
 
 from __future__ import with_statement
 
+
 import sys
 
 if "PYTHONPATH" in globals():
@@ -37,53 +38,313 @@
 
 # sys.path.insert(0, "/usr/share/caldavd/lib/python")
 
-import os
-import itertools
-from code import interact
-
+from calendarserver.provision.root import RootResource
 from getopt import getopt, GetoptError
-from os.path import dirname, abspath
-
 from twisted.internet import reactor
+from twisted.internet.address import IPv4Address
 from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from twisted.python import log
 from twisted.python.reflect import namedClass
 from twisted.web2.dav import davxml
-# from twisted.web2.http import Request
-
-from twistedcaldav import ical
 from twistedcaldav import caldavxml
-from twistedcaldav.resource import isPseudoCalendarCollectionResource
-from twistedcaldav.static import CalDAVFile, CalendarHomeFile, CalendarHomeProvisioningFile
+from twistedcaldav import memcachepool
 from twistedcaldav.config import config, defaultConfigFile
+from twistedcaldav.customxml import calendarserver_namespace
 from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
 from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
+from twistedcaldav.log import setLogLevelForNamespace
+from twistedcaldav.notify import installNotificationClient
+from twistedcaldav.static import CalendarHomeProvisioningFile
+import itertools
+import os
 
-from calendarserver.provision.root import RootResource
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
+def usage(e=None):
+    if e:
+        print e
+        print ""
 
-from twistedcaldav import memcachepool
-from twistedcaldav.notify import installNotificationClient
-from twisted.internet.address import IPv4Address
+    name = os.path.basename(sys.argv[0])
+    print "usage: %s [options]" % (name,)
+    print ""
+    print "options:"
+    print "  -h --help: print this help and exit"
+    print "  -f --config <path>: Specify caldavd.plist configuration path"
+    print "  --resource <prinicpal-path>: path of the resource to use"
+    print "  --read-property: namespace-qualified DAV property to read, e.g. 'DAV:#group-member-set'"
+    print "  --list-read-delegates: list delegates with read-only access to the current resource"
+    print "  --list-write-delegates: list delegates with read-write access to the current resource"
+    print "  --add-read-delegate <prinicpal-path>: add argument as a read-only delegate to the current resource"
+    print "  --add-write-delegate <prinicpal-path>: add argument as a read-write delegate to the current resource"
+    print "  --remove-delegate <prinicpal-path>: strip argument of delegate status for the current resource"
+    print "  --set-auto-schedule [true|false] : determines whether the current resource auto-accepts invitations"
+    print "  --get-auto-schedule : returns the current resource's auto-schedule state"
 
-# This dictionary is a mapping of symbols that other modules might want
-# to use; it's populated by the @exportmethod decorator below.
-exportedSymbols = { }
+    if e:
+        sys.exit(64)
+    else:
+        sys.exit(0)
 
-def exportmethod(method):
-    """ Add the method to exportedSymbols """
-    global exportedSymbols
-    exportedSymbols[method.func_name] = method
-    return method
+class UsageError (StandardError):
+    pass
 
-def getExports(**kw):
-    """ Return a copy of exportedSymbols, with kw included """
-    exports = exportedSymbols.copy()
-    exports.update(**kw)
-    return exports
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
+def main():
+    try:
+        (optargs, args) = getopt(
+            sys.argv[1:], "hf:r:s", [
+                "config=",
+                "help",
+                "resource=",
+                "read-property=",
+                "list-read-delegates",
+                "list-write-delegates",
+                "add-read-delegate=",
+                "add-write-delegate=",
+                "remove-delegate=",
+                "set-auto-schedule=",
+                "get-auto-schedule",
+            ],
+        )
+    except GetoptError, e:
+        usage(e)
 
+    if args:
+        usage("Too many arguments: %s" % (" ".join(args),))
 
+    logFileName = "/dev/stdout"
+    observer = log.FileLogObserver(open(logFileName, "a"))
+    log.addObserver(observer.emit)
+
+    # First pass through the args:
+
+    configFileName = None
+
+    for opt, arg in optargs:
+        if opt in ("-h", "--help"):
+            usage()
+
+        elif opt in ("-f", "--config"):
+            configFileName = arg
+
+    loadConfig(configFileName)
+    directory, root = setup()
+    root = ResourceWrapper(root)
+
+    reactor.callLater(0, run, directory, root, optargs)
+    reactor.run()
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ at inlineCallbacks
+def run(directory, root, optargs):
+
+    resource = None
+
+    for opt, arg in optargs:
+
+        if opt in ("-r", "--resource",):
+            resource = root.getChild(arg)
+            if resource is not None:
+                print "Found resource %s at %s" % (resource.resource, arg)
+            else:
+                abort("Could not find resource at %s" % (arg,))
+
+        elif opt in ("--read-property",):
+            if resource is None: abort("No current resource.")
+
+            try:
+                namespace, name = arg.split("#")
+            except Exception, e:
+                abort("Can't parse --propertyToRead: %s" % (arg,))
+
+            result = (yield resource.readProperty((namespace, name)))
+            print result.toxml()
+
+        elif opt in ("--list-write-delegates", "--list-read-delegates"):
+            if resource is None: abort("No current resource.")
+
+            permission = "write" if "write" in opt else "read"
+            print "Delegates (%s) for %s:" % (permission, resource.resource)
+            paths = (yield resource.getDelegates(permission))
+            for path in paths:
+                delegate = root.getChild(path)
+                print delegate.resource
+
+        elif opt in ("--add-write-delegate", "--add-read-delegate"):
+            if resource is None: abort("No current resource.")
+
+            delegate = root.getChild(arg)
+            if delegate is None:
+                abort("No delegate found for %s" % (arg,))
+
+            permission = "write" if "write" in opt else "read"
+            result = (yield resource.addDelegate(delegate, permission))
+
+        elif opt == "--remove-delegate":
+            if resource is None: abort("No current resource.")
+
+            delegate = root.getChild(arg)
+            if delegate is None:
+                abort("No delegate found for %s" % (arg,))
+
+            result = (yield resource.removeDelegate(delegate, "read"))
+            result = (yield resource.removeDelegate(delegate, "write"))
+
+        elif opt == "--set-auto-schedule":
+            if resource is None: abort("No current resource.")
+
+            result = (yield resource.setAutoSchedule(arg.lower() in ("true", "1")))
+
+        elif opt == "--get-auto-schedule":
+            if resource is None: abort("No current resource.")
+
+            result = (yield resource.getAutoSchedule())
+            print "Auto-Schedule: %s" % ("True" if result else "False",)
+
+
+    # reactor.callLater(0, reactor.stop)
+    reactor.stop()
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+class ResourceWrapper(object):
+
+    def __init__(self, resource):
+        self.resource = resource
+
+    def readProperty(self, prop):
+        return self.resource.readProperty(prop, FakeRequest())
+
+    def writeProperty(self, prop):
+        return self.resource.writeProperty(prop, FakeRequest())
+
+    def getChild(self, path):
+        resource = self.resource
+        segments = path.strip("/").split("/")
+        for segment in segments:
+            resource = resource.getChild(segment)
+            if resource is None:
+                return None
+        return ResourceWrapper(resource)
+
+    @inlineCallbacks
+    def removeDelegate(self, delegate, permission):
+        subPrincipalName = "calendar-proxy-%s" % (permission,)
+        subPrincipal = self.getChild(subPrincipalName)
+        if subPrincipal is None:
+            abort("No proxy subprincipal found for %s" % (self.resource,))
+
+        namespace, name = davxml.dav_namespace, "group-member-set"
+        prop = (yield subPrincipal.readProperty((namespace, name)))
+        newChildren = []
+        for child in prop.children:
+            if str(child) != delegate.url():
+                newChildren.append(child)
+
+        if len(prop.children) == len(newChildren):
+            # Nothing to do -- the delegate wasn't there
+            returnValue(False)
+
+        newProp = davxml.GroupMemberSet(*newChildren)
+        result = (yield subPrincipal.writeProperty(newProp))
+        returnValue(result)
+
+    @inlineCallbacks
+    def addDelegate(self, delegate, permission):
+
+        opposite = "read" if permission == "write" else "write"
+        result = (yield self.removeDelegate(delegate, opposite))
+
+        subPrincipalName = "calendar-proxy-%s" % (permission,)
+        subPrincipal = self.getChild(subPrincipalName)
+        if subPrincipal is None:
+            abort("No proxy subprincipal found for %s" % (self.resource,))
+
+        namespace, name = davxml.dav_namespace, "group-member-set"
+        prop = (yield subPrincipal.readProperty((namespace, name)))
+        for child in prop.children:
+            if str(child) == delegate.url():
+                # delegate is already in the group
+                break
+        else:
+            # delegate is not already in the group
+            newChildren = list(prop.children)
+            newChildren.append(davxml.HRef(delegate.url()))
+            newProp = davxml.GroupMemberSet(*newChildren)
+            result = (yield subPrincipal.writeProperty(newProp))
+            returnValue(result)
+
+    @inlineCallbacks
+    def getDelegates(self, permission):
+
+        subPrincipalName = "calendar-proxy-%s" % (permission,)
+        subPrincipal = self.getChild(subPrincipalName)
+        if subPrincipal is None:
+            abort("No proxy subprincipal found for %s" % (self.resource,))
+
+        namespace, name = davxml.dav_namespace, "group-member-set"
+        prop = (yield subPrincipal.readProperty((namespace, name)))
+        result = []
+        for child in prop.children:
+            result.append(str(child))
+        returnValue(result)
+
+    def setAutoSchedule(self, autoSchedule):
+        return self.resource.setAutoSchedule(autoSchedule)
+
+    def getAutoSchedule(self):
+        return self.resource.getAutoSchedule()
+
+    def url(self):
+        return self.resource.url()
+
+class FakeRequest(object):
+    pass
+
+def abort(msg, errno=1):
+    print "ERROR:", msg
+    print "Exiting"
+    reactor.stop()
+    sys.exit(errno)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+def setup():
+
+    setLogLevelForNamespace(None, "warn")
+
+    directory = getDirectory()
+    if config.Memcached["ClientEnabled"]:
+        memcachepool.installPool(
+            IPv4Address(
+                'TCP',
+                config.Memcached["BindAddress"],
+                config.Memcached["Port"]
+            ),
+            config.Memcached["MaxClients"]
+        )
+    if config.Notifications["Enabled"]:
+        installNotificationClient(
+            config.Notifications["InternalNotificationHost"],
+            config.Notifications["InternalNotificationPort"],
+        )
+    principalCollection = directory.getPrincipalCollection()
+    root = RootResource(
+        config.DocumentRoot,
+        principalCollections=(principalCollection,),
+    )
+    root.putChild("principals", principalCollection)
+    calendarCollection = CalendarHomeProvisioningFile(
+        os.path.join(config.DocumentRoot, "calendars"),
+        directory, "/calendars/",
+    )
+    root.putChild("calendars", calendarCollection)
+
+    return (directory, root)
+
 def loadConfig(configFileName):
     if configFileName is None:
         configFileName = defaultConfigFile
@@ -152,152 +413,10 @@
     shortNames = ("dummy",),
     fullName = "Dummy McDummerson",
     calendarUserAddresses = set(),
-    autoSchedule = False,
+    # autoSchedule = False,
 )
 
-def setup():
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
-    directory = getDirectory()
-    if config.Memcached["ClientEnabled"]:
-        memcachepool.installPool(
-            IPv4Address(
-                'TCP',
-                config.Memcached["BindAddress"],
-                config.Memcached["Port"]
-            ),
-            config.Memcached["MaxClients"]
-        )
-    if config.Notifications["Enabled"]:
-        installNotificationClient(
-            config.Notifications["InternalNotificationHost"],
-            config.Notifications["InternalNotificationPort"],
-        )
-    principalCollection = directory.getPrincipalCollection()
-    root = RootResource(
-        config.DocumentRoot,
-        principalCollections=(principalCollection,),
-    )
-    root.putChild("principals", principalCollection)
-    calendarCollection = CalendarHomeProvisioningFile(
-        os.path.join(config.DocumentRoot, "calendars"),
-        directory, "/calendars/",
-    )
-    root.putChild("calendars", calendarCollection)
-
-    return (directory, root)
-
-
-
-class UsageError (StandardError):
-    pass
-
-def usage(e=None):
-    if e:
-        print e
-        print ""
-
-    name = os.path.basename(sys.argv[0])
-    print "usage: %s [options] [input_specifiers]" % (name,)
-    print ""
-    print "Change calendar user addresses"
-    print __doc__
-    print "options:"
-    print "  -h --help: print this help and exit"
-    print "  -f --config: Specify caldavd.plist configuration path"
-    print ""
-    print "input specifiers:"
-    print "  -c --changes: add all calendar homes"
-    print "  --dry-run: Don't actually change the data"
-
-    if e:
-        sys.exit(64)
-    else:
-        sys.exit(0)
-
-def loadChanges(fileName):
-    addresses = {}
-    with open(fileName) as input:
-        count = 1
-        for line in input:
-            line = line.strip()
-            if line and not line.startswith("#"):
-                try:
-                    oldAddr, newAddr = line.split()
-                    addresses[oldAddr] = newAddr
-                except Exception, e:
-                    print "Could not parse line %d: %s" % (count, line)
-                    sys.exit(2)
-            count += 1
-    return addresses
-
-
-
-
-class ResourceWrapper(object):
-
-    def __init__(self, resource):
-        self.resource = resource
-
- at exportmethod
-def byPath(root, path):
-    resource = root
-    segments = path.strip("/").split("/")
-    for segment in segments:
-        resource = resource.getChild(segment)
-    return ResourceWrapper(resource)
-
-def main():
-    try:
-        (optargs, args) = getopt(
-            sys.argv[1:], "hf:", [
-                "config=",
-                "help",
-                "dry-run",
-            ],
-        )
-    except GetoptError, e:
-        usage(e)
-
-    configFileName = None
-    logFileName = "/dev/stdout"
-    modifyData = True
-    changesFile = None
-
-    directory = None
-    calendarHomePaths = set()
-    calendarHomes = set()
-
-    def checkExists(resource):
-        if not resource.exists():
-            sys.stderr.write("No such file: %s\n" % (resource.fp.path,))
-            sys.exit(1)
-
-    for opt, arg in optargs:
-        if opt in ("-h", "--help"):
-            usage()
-
-        elif opt in ("-f", "--config"):
-            configFileName = arg
-
-        elif opt in ("--dry-run",):
-            modifyData = False
-
-    if args:
-        usage("Too many arguments: %s" % (" ".join(args),))
-
-    observer = log.FileLogObserver(open(logFileName, "a"))
-    log.addObserver(observer.emit)
-
-    loadConfig(configFileName)
-
-    directory, root = setup()
-    exportedSymbols['root'] = root
-    exportedSymbols['directory'] = directory
-
-    banner = "\nWelcome to calendar server\n"
-    interact(banner, None, getExports(__name__="__console__", __doc__=None))
-
-
 if __name__ == "__main__":
-
     main()

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/calendarserver/tools/util.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/calendarserver/tools/util.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/calendarserver/tools/util.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -105,5 +105,5 @@
     lastName = "McDummerson",
     emailAddresses = (),
     calendarUserAddresses = (),
-    autoSchedule = False,
+    # autoSchedule = False,
 )


Property changes on: CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-privatecomments.txt
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-privatecomments-00.txt:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-privatecomments-00.txt:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-privatecomments.txt:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-privatecomments-00.txt:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-privatecomments.txt:3574-3581
   + /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-privatecomments-00.txt:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-privatecomments-00.txt:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-privatecomments.txt:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-privatecomments-00.txt:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-privatecomments.txt:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-privatecomments.txt:4040-4067


Property changes on: CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-privatecomments.xml
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-privatecomments-00.xml:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-privatecomments-00.xml:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-privatecomments.xml:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-privatecomments-00.xml:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-privatecomments.xml:3574-3581
   + /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-privatecomments-00.xml:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-privatecomments-00.xml:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-privatecomments.xml:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-privatecomments-00.xml:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-privatecomments.xml:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-privatecomments.xml:4040-4067


Property changes on: CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-schedulingchanges.txt
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-schedulingchanges-01.txt:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-schedulingchanges-01.txt:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-schedulingchanges.txt:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-schedulingchanges-01.txt:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-schedulingchanges.txt:3574-3581
   + /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-schedulingchanges-01.txt:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-schedulingchanges-01.txt:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-schedulingchanges.txt:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-schedulingchanges-01.txt:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-schedulingchanges.txt:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-schedulingchanges.txt:4040-4067


Property changes on: CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-schedulingchanges.xml
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-schedulingchanges-01.xml:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-schedulingchanges-01.xml:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-schedulingchanges.xml:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-schedulingchanges-01.xml:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-schedulingchanges.xml:3574-3581
   + /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-schedulingchanges-01.xml:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-schedulingchanges-01.xml:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-schedulingchanges.xml:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-schedulingchanges-01.xml:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-schedulingchanges.xml:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-schedulingchanges.xml:4040-4067

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/customxml.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/customxml.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -575,9 +575,17 @@
     """
     Exposes the type of a record
     """
+    namespace = calendarserver_namespace
     name = "record-type"
     protected = True
 
+class AutoSchedule (davxml.WebDAVTextElement):
+    """
+    Whether the principal automatically accepts invitations
+    """
+    namespace = calendarserver_namespace
+    name = "auto-schedule"
+
 ##
 # Extensions to davxml.ResourceType
 ##

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/appleopendirectory.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/appleopendirectory.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -619,22 +619,6 @@
             else:
                 memberGUIDs = ()
 
-            # Special case for resources and locations
-            autoSchedule = False
-            proxyGUIDs = ()
-            readOnlyProxyGUIDs = ()
-            if recordType in (self.recordType_resources, self.recordType_locations):
-                resourceInfo = value.get(dsattributes.kDSNAttrResourceInfo)
-                if resourceInfo is not None:
-                    try:
-                        autoSchedule, proxy, read_only_proxy = self._parseResourceInfo(resourceInfo, recordGUID, recordType, recordShortName)
-                    except ValueError:
-                        continue
-                    if proxy:
-                        proxyGUIDs = (proxy,)
-                    if read_only_proxy:
-                        readOnlyProxyGUIDs = (read_only_proxy,)
-
             record = OpenDirectoryRecord(
                 service               = self,
                 recordType            = recordType,
@@ -647,11 +631,8 @@
                 lastName              = recordLastName,
                 emailAddresses        = recordEmailAddresses,
                 calendarUserAddresses = calendarUserAddresses,
-                autoSchedule          = autoSchedule,
                 enabledForCalendaring = enabledForCalendaring,
                 memberGUIDs           = memberGUIDs,
-                proxyGUIDs            = proxyGUIDs,
-                readOnlyProxyGUIDs    = readOnlyProxyGUIDs,
             )
 
             def disableGUID(guid, record):
@@ -709,11 +690,6 @@
                     if recordType == self.recordType_groups:
                         self._indexGroup(record, record._memberGUIDs, groupsForGUID)
 
-                    # Do proxy indexing if needed
-                    elif recordType in (self.recordType_resources, self.recordType_locations):
-                        self._indexGroup(record, record._proxyGUIDs, proxiesForGUID)
-                        self._indexGroup(record, record._readOnlyProxyGUIDs, readOnlyProxiesForGUID)
-
                     # Index non-duplicate shortNames
                     def disableName(shortName, record):
                         self.log_warn("Short name %s disabled due to conflict for record: %s"
@@ -957,8 +933,8 @@
     def __init__(
         self, service, recordType, guid, nodeName, shortNames, authIDs, fullName,
         firstName, lastName, emailAddresses,
-        calendarUserAddresses, autoSchedule, enabledForCalendaring,
-        memberGUIDs, proxyGUIDs, readOnlyProxyGUIDs,
+        calendarUserAddresses, enabledForCalendaring,
+        memberGUIDs,
     ):
         super(OpenDirectoryRecord, self).__init__(
             service               = service,
@@ -971,13 +947,10 @@
             lastName              = lastName,
             emailAddresses        = emailAddresses,
             calendarUserAddresses = calendarUserAddresses,
-            autoSchedule          = autoSchedule,
             enabledForCalendaring = enabledForCalendaring,
         )
         self.nodeName = nodeName
         self._memberGUIDs = tuple(memberGUIDs)
-        self._proxyGUIDs = tuple(proxyGUIDs)
-        self._readOnlyProxyGUIDs = tuple(readOnlyProxyGUIDs)
 
     def __repr__(self):
         if self.service.realmName == self.nodeName:
@@ -1007,48 +980,6 @@
     def groups(self):
         return self.service.groupsForGUID(self.guid)
 
-    def proxies(self):
-        if self.recordType not in (self.service.recordType_resources, self.service.recordType_locations):
-            return
-
-        for guid in self._proxyGUIDs:
-            proxyRecord = self.service.recordWithGUID(guid)
-            if proxyRecord is None:
-                self.log_error("No record for proxy in (%s)%s with GUID %s" % (
-                    self.recordType,
-                    self.shortNames[0],
-                    guid,
-                ))
-            else:
-                yield proxyRecord
-
-    def proxyFor(self):
-        result = set()
-        result.update(self.service.proxiesForGUID(self.service.recordType_resources, self.guid))
-        result.update(self.service.proxiesForGUID(self.service.recordType_locations, self.guid))
-        return result
-
-    def readOnlyProxies(self):
-        if self.recordType not in (self.service.recordType_resources, self.service.recordType_locations):
-            return
-
-        for guid in self._readOnlyProxyGUIDs:
-            proxyRecord = self.service.recordWithGUID(guid)
-            if proxyRecord is None:
-                self.log_error("No record for proxy in (%s)%s with GUID %s" % (
-                    self.recordType,
-                    self.shortNames[0],
-                    guid,
-                ))
-            else:
-                yield proxyRecord
-
-    def readOnlyProxyFor(self):
-        result = set()
-        result.update(self.service.readOnlyProxiesForGUID(self.service.recordType_resources, self.guid))
-        result.update(self.service.readOnlyProxiesForGUID(self.service.recordType_locations, self.guid))
-        return result
-
     def verifyCredentials(self, credentials):
         if isinstance(credentials, UsernamePassword):
             # Check cached password

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingappleopendirectory.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingappleopendirectory.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingappleopendirectory.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -477,10 +477,12 @@
     
             elif recordType == DirectoryService.recordType_locations:
                 listRecordTypes.append(dsattributes.kDSStdRecordTypePlaces)
+                # MOR: possibly can be removed
                 attrs.append(dsattributes.kDSNAttrResourceInfo)
             
             elif recordType == DirectoryService.recordType_resources:
                 listRecordTypes.append(dsattributes.kDSStdRecordTypeResources)
+                # MOR: possibly can be removed
                 attrs.append(dsattributes.kDSNAttrResourceInfo)
             
             else:
@@ -657,22 +659,6 @@
             else:
                 memberGUIDs = ()
 
-            # Special case for resources and locations
-            autoSchedule = False
-            proxyGUIDs = ()
-            readOnlyProxyGUIDs = ()
-            if recordType in (self.recordType_resources, self.recordType_locations):
-                resourceInfo = value.get(dsattributes.kDSNAttrResourceInfo)
-                if resourceInfo is not None:
-                    try:
-                        autoSchedule, proxy, read_only_proxy = self._parseResourceInfo(resourceInfo, recordGUID, recordType, recordShortName)
-                    except ValueError:
-                        continue
-                    if proxy:
-                        proxyGUIDs = (proxy,)
-                    if read_only_proxy:
-                        readOnlyProxyGUIDs = (read_only_proxy,)
-
             record = OpenDirectoryRecord(
                 service               = self,
                 recordType            = recordType,
@@ -685,11 +671,8 @@
                 lastName              = recordLastName,
                 emailAddresses        = recordEmailAddresses,
                 calendarUserAddresses = calendarUserAddresses,
-                autoSchedule          = autoSchedule,
                 enabledForCalendaring = enabledForCalendaring,
                 memberGUIDs           = memberGUIDs,
-                proxyGUIDs            = proxyGUIDs,
-                readOnlyProxyGUIDs    = readOnlyProxyGUIDs,
             )
             self.recordCacheForType(recordType).addRecord(record)
 
@@ -701,8 +684,9 @@
     def __init__(
         self, service, recordType, guid, nodeName, shortNames, authIDs,
         fullName, firstName, lastName, emailAddresses,
-        calendarUserAddresses, autoSchedule, enabledForCalendaring,
-        memberGUIDs, proxyGUIDs, readOnlyProxyGUIDs,
+        calendarUserAddresses,
+        enabledForCalendaring,
+        memberGUIDs,
     ):
         super(OpenDirectoryRecord, self).__init__(
             service               = service,
@@ -715,13 +699,10 @@
             lastName              = lastName,
             emailAddresses        = emailAddresses,
             calendarUserAddresses = calendarUserAddresses,
-            autoSchedule          = autoSchedule,
             enabledForCalendaring = enabledForCalendaring,
         )
         self.nodeName = nodeName
         self._memberGUIDs = tuple(memberGUIDs)
-        self._proxyGUIDs = tuple(proxyGUIDs)
-        self._readOnlyProxyGUIDs = tuple(readOnlyProxyGUIDs)
         
         self._groupMembershipGUIDs = None
 
@@ -759,48 +740,6 @@
             if record:
                 yield record
 
-    def proxies(self):
-        if self.recordType not in (self.service.recordType_resources, self.service.recordType_locations):
-            return
-
-        for guid in self._proxyGUIDs:
-            proxyRecord = self.service.recordWithGUID(guid)
-            if proxyRecord is None:
-                self.log_error("No record for proxy in (%s)%s with GUID %s" % (
-                    self.recordType,
-                    self.shortNames[0],
-                    guid,
-                ))
-            else:
-                yield proxyRecord
-
-    def proxyFor(self):
-        result = set()
-        result.update(self.service.proxiesForGUID(self.service.recordType_resources, self.guid))
-        result.update(self.service.proxiesForGUID(self.service.recordType_locations, self.guid))
-        return result
-
-    def readOnlyProxies(self):
-        if self.recordType not in (self.service.recordType_resources, self.service.recordType_locations):
-            return
-
-        for guid in self._readOnlyProxyGUIDs:
-            proxyRecord = self.service.recordWithGUID(guid)
-            if proxyRecord is None:
-                self.log_error("No record for proxy in (%s)%s with GUID %s" % (
-                    self.recordType,
-                    self.shortNames[0],
-                    guid,
-                ))
-            else:
-                yield proxyRecord
-
-    def readOnlyProxyFor(self):
-        result = set()
-        result.update(self.service.readOnlyProxiesForGUID(self.service.recordType_resources, self.guid))
-        result.update(self.service.readOnlyProxiesForGUID(self.service.recordType_locations, self.guid))
-        return result
-
     def verifyCredentials(self, credentials):
         if isinstance(credentials, UsernamePassword):
             # Check cached password

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingdirectory.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingdirectory.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingdirectory.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -319,7 +319,7 @@
     def __init__(
         self, service, recordType, guid, shortNames=(), authIDs=set(),
         fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        calendarUserAddresses=set(), autoSchedule=False,
+        calendarUserAddresses=set(),
         enabledForCalendaring=None, uid=None,
     ):
         super(CachingDirectoryRecord, self).__init__(
@@ -333,7 +333,6 @@
             lastName              = lastName,
             emailAddresses        = emailAddresses,
             calendarUserAddresses = calendarUserAddresses,
-            autoSchedule          = autoSchedule,
             enabledForCalendaring = enabledForCalendaring,
             uid                   = uid,
         )

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingxmlfile.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingxmlfile.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/cachingxmlfile.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -183,17 +183,12 @@
             lastName              = xmlPrincipal.lastName,
             emailAddresses        = xmlPrincipal.emailAddresses,
             calendarUserAddresses = xmlPrincipal.calendarUserAddresses,
-            autoSchedule          = xmlPrincipal.autoSchedule,
             enabledForCalendaring = xmlPrincipal.enabledForCalendaring,
         )
 
         self.password          = xmlPrincipal.password
         self._members          = xmlPrincipal.members
         self._groups           = xmlPrincipal.groups
-        self._proxies          = xmlPrincipal.proxies
-        self._proxyFor         = xmlPrincipal.proxyFor
-        self._readOnlyProxies  = xmlPrincipal.readOnlyProxies
-        self._readOnlyProxyFor = xmlPrincipal.readOnlyProxyFor
 
     def members(self):
         for recordType, shortName in self._members:
@@ -203,22 +198,6 @@
         for shortName in self._groups:
             yield self.service.recordWithShortName(DirectoryService.recordType_groups, shortName)
 
-    def proxies(self):
-        for recordType, shortName in self._proxies:
-            yield self.service.recordWithShortName(recordType, shortName)
-
-    def proxyFor(self, read_write=True):
-        for recordType, shortName in self._proxyFor:
-            yield self.service.recordWithShortName(recordType, shortName)
-
-    def readOnlyProxies(self):
-        for recordType, shortName in self._readOnlyProxies:
-            yield self.service.recordWithShortName(recordType, shortName)
-
-    def readOnlyProxyFor(self, read_write=True):
-        for recordType, shortName in self._readOnlyProxyFor:
-            yield self.service.recordWithShortName(recordType, shortName)
-
     def verifyCredentials(self, credentials):
         if isinstance(credentials, UsernamePassword):
             return credentials.password == self.password

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/calendaruserproxy.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/calendaruserproxy.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -161,15 +161,7 @@
         assert isinstance(property, davxml.WebDAVElement)
 
         if property.qname() == (dav_namespace, "group-member-set"):
-            if self.hasEditableMembership():
-                return self.setGroupMemberSet(property, request)
-            else:
-                raise HTTPError(
-                    StatusResponse(
-                        responsecode.FORBIDDEN,
-                        "Proxies cannot be changed."
-                    )
-                )
+            return self.setGroupMemberSet(property, request)
 
         return super(CalendarUserProxyPrincipalResource, self).writeProperty(property, request)
 
@@ -269,8 +261,7 @@
                 """Principal UID: %s\n"""          % (self.principalUID(),),
                 """Principal URL: %s\n"""          % (format_link(self.principalURL()),),
                 """\nAlternate URIs:\n"""          , format_list(format_link(u) for u in self.alternateURIs()),
-                """\nGroup members (%s):\n""" % ({False:"Locked", True:"Editable"}[self.hasEditableMembership()])
-                                                   , format_principals(closure["members"]),
+                """\nGroup members:\n"""           , format_principals(closure["members"]),
                 """\nGroup memberships:\n"""       , format_principals(closure["memberships"]),
                 """</pre></blockquote></div>""",
                 closure["output"]
@@ -321,7 +312,8 @@
                 for member in members:
                     if member.principalUID() not in uids:
                         relatives.add(member)
-                        yield self._expandMemberUIDs(member.principalUID(), relatives, uids, infinity=infinity)
+                        if infinity:
+                            yield self._expandMemberUIDs(member.principalUID(), relatives, uids, infinity=infinity)
             elif isinstance(principal, DirectoryPrincipalResource):
                 if infinity:
                     members = yield principal.expandedGroupMembers()
@@ -333,35 +325,28 @@
 
     @inlineCallbacks
     def _directGroupMembers(self):
-        if self.hasEditableMembership():
-            # Get member UIDs from database and map to principal resources
-            members = yield self._index().getMembers(self.uid)
-            found = []
-            missing = []
-            for uid in members:
-                p = self.pcollection.principalForUID(uid)
-                if p:
-                    found.append(p)
-                    # Make sure any outstanding deletion timer entries for
-                    # existing principals are removed
-                    yield self._index()._memcacher.clearDeletionTimer(uid)
-                else:
-                    missing.append(uid)
+        # Get member UIDs from database and map to principal resources
+        members = yield self._index().getMembers(self.uid)
+        found = []
+        missing = []
+        for uid in members:
+            p = self.pcollection.principalForUID(uid)
+            if p:
+                found.append(p)
+                # Make sure any outstanding deletion timer entries for
+                # existing principals are removed
+                yield self._index()._memcacher.clearDeletionTimer(uid)
+            else:
+                missing.append(uid)
 
-            # Clean-up ones that are missing
-            for uid in missing:
-                cacheTimeout = config.DirectoryService.params.get("cacheTimeout", 30) * 60 # in seconds
+        # Clean-up ones that are missing
+        for uid in missing:
+            cacheTimeout = config.DirectoryService.params.get("cacheTimeout", 30) * 60 # in seconds
 
-                yield self._index().removePrincipal(uid,
-                    delay=cacheTimeout*2)
+            yield self._index().removePrincipal(uid,
+                delay=cacheTimeout*2)
 
-            returnValue(found)
-        else:
-            # Fixed proxies
-            if self.proxyType == "calendar-proxy-write":
-                returnValue(self.parent.proxies())
-            else:
-                returnValue(self.parent.readOnlyProxies())
+        returnValue(found)
 
     def groupMembers(self):
         return self._expandMemberUIDs()
@@ -375,9 +360,6 @@
         memberships = yield self._index().getMemberships(self.uid)
         returnValue([p for p in [self.pcollection.principalForUID(uid) for uid in memberships] if p])
 
-    def hasEditableMembership(self):
-        return self.parent.hasEditableProxyMembership()
-
 class CalendarUserProxyDatabase(AbstractSQLDatabase, LoggingMixIn):
     """
     A database to maintain calendar user proxy group memberships.
@@ -481,11 +463,8 @@
             current_members = ()
         current_members = set(current_members)
 
-        # Remove what is there, then add it back.
-        self._delete_from_db(principalUID)
-        self._add_to_db(principalUID, members)
-        self._db_commit()
-        
+        self.setGroupMembersInDatabase(principalUID, members)
+
         # Update cache
         update_members = set(members)
         
@@ -495,6 +474,18 @@
             _ignore = yield self._memcacher.deleteMembership(member)
         _ignore = yield self._memcacher.deleteMember(principalUID)
 
+    def setGroupMembersInDatabase(self, principalUID, members):
+        """
+        A blocking call to add a group membership record in the database.
+
+        @param principalUID: the UID of the group principal to add.
+        @param members: a list UIDs of principals that are members of this group.
+        """
+        # Remove what is there, then add it back.
+        self._delete_from_db(principalUID)
+        self._add_to_db(principalUID, members)
+        self._db_commit()
+        
     @inlineCallbacks
     def removeGroup(self, principalUID):
         """
@@ -550,7 +541,7 @@
         for suffix in ("calendar-proxy-read", "calendar-proxy-write",):
             groupUID = "%s#%s" % (principalUID, suffix,)
             self._delete_from_db(groupUID)
-            
+
             # Update cache
             members = yield self.getMembers(groupUID)
             if members:
@@ -561,7 +552,7 @@
         memberships = (yield self.getMemberships(principalUID))
         for groupUID in memberships:
             yield self._memcacher.deleteMember(groupUID)
-            
+
         self._delete_from_db_member(principalUID)
         yield self._memcacher.deleteMembership(principalUID)
         self._db_commit()
@@ -571,7 +562,7 @@
     def getMembers(self, principalUID):
         """
         Return the list of group member UIDs for the specified principal.
-        
+
         @return: a deferred returning a C{set} of members.
         """
 
@@ -695,6 +686,8 @@
                 """
             )
 
+
+
 ##
 # Utilities
 ##

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/directory.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/directory.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -269,7 +269,8 @@
     def __init__(
         self, service, recordType, guid, shortNames=(), authIDs=set(), fullName=None,
         firstName=None, lastName=None, emailAddresses=set(),
-        calendarUserAddresses=set(), autoSchedule=False, enabledForCalendaring=None,
+        calendarUserAddresses=set(),
+        enabledForCalendaring=None,
         uid=None,
     ):
         assert service.realmName is not None
@@ -309,7 +310,6 @@
         self.emailAddresses        = emailAddresses
         self.enabledForCalendaring = enabledForCalendaring
         self.calendarUserAddresses = calendarUserAddresses
-        self.autoSchedule          = autoSchedule
 
     def __cmp__(self, other):
         if not isinstance(other, DirectoryRecord):
@@ -335,20 +335,8 @@
     def groups(self):
         return ()
 
-    def proxies(self):
-        return ()
-
-    def proxyFor(self):
-        return ()
-
-    def readOnlyProxies(self):
-        return ()
-
-    def readOnlyProxyFor(self):
-        return ()
-
     def hasEditableProxyMembership(self):
-        return self.recordType in (DirectoryService.recordType_users, DirectoryService.recordType_groups)
+        return True
 
     def verifyCredentials(self, credentials):
         return False

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/idirectory.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/idirectory.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/idirectory.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -101,7 +101,6 @@
     lastName              = Attribute("The last name of this record.")
     emailAddress          = Attribute("The email address of this record.")
     calendarUserAddresses = Attribute("A set of calendar user addresses for this record.")
-    autoSchedule          = Attribute("Principal identified by this record should automatically accept/deny meetings.")
     enabledForCalendaring = Attribute("Determines whether this record should be provisioned with a calendar home.")
 
     def members():

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/principal.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/principal.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -582,11 +582,10 @@
         members = (yield self.groupMembers())
         
         memberships = (yield self.groupMemberships())
-        
+
         proxyFor = (yield self.proxyFor(True))
-        
         readOnlyProxyFor = (yield self.proxyFor(False))
-
+        
         returnValue("".join((
             """<div class="directory-listing">"""
             """<h1>Principal Details</h1>"""
@@ -662,6 +661,32 @@
     def url(self):
         return self.principalURL()
 
+    @inlineCallbacks
+    def proxyFor(self, read_write, resolve_memberships=True):
+        proxyFors = set()
+
+        if resolve_memberships:
+            memberships = self._getRelatives("groups", infinity=True)
+            for membership in memberships:
+                results = (yield membership.proxyFor(read_write, False))
+                proxyFors.update(results)
+
+        if config.EnableProxyPrincipals:
+            # Get proxy group UIDs and map to principal resources
+            proxies = []
+            memberships = (yield self._calendar_user_proxy_index().getMemberships(self.principalUID()))
+            for uid in memberships:
+                subprincipal = self.parent.principalForUID(uid)
+                if subprincipal:
+                    if subprincipal.isProxyType(read_write):
+                        proxies.append(subprincipal.parent)
+                else:
+                    yield self._calendar_user_proxy_index().removeGroup(uid)
+
+            proxyFors.update(proxies)
+
+        returnValue(proxyFors)
+
     def _getRelatives(self, method, record=None, relatives=None, records=None, proxy=None, infinity=False):
         if record is None:
             record = self.record
@@ -703,8 +728,9 @@
 
         if config.EnableProxyPrincipals:
             # Get any directory specified proxies
-            groups.update(self._getRelatives("proxyFor", proxy='read-write', infinity=infinity))
-            groups.update(self._getRelatives("readOnlyProxyFor", proxy='read-only', infinity=infinity))
+            # MOR:
+            # groups.update(self._getRelatives("proxyFor", proxy='read-write', infinity=infinity))
+            # groups.update(self._getRelatives("readOnlyProxyFor", proxy='read-only', infinity=infinity))
 
             # Get proxy group UIDs and map to principal resources
             proxies = []
@@ -723,40 +749,6 @@
     def expandedGroupMemberships(self):
         return self.groupMemberships(infinity=True)
 
-
-    @inlineCallbacks
-    def proxyFor(self, read_write, resolve_memberships=True):
-        proxyFors = set()
-
-        if resolve_memberships:
-            memberships = self._getRelatives("groups", infinity=True)
-            for membership in memberships:
-                results = (yield membership.proxyFor(read_write, False))
-                proxyFors.update(results)
-
-        if config.EnableProxyPrincipals:
-            # Get any directory specified proxies
-            if read_write:
-                directoryProxies = self._getRelatives("proxyFor", proxy='read-write', infinity=True)
-            else:
-                directoryProxies = self._getRelatives("readOnlyProxyFor", proxy='read-only', infinity=True)
-            proxyFors.update([subprincipal.parent for subprincipal in directoryProxies])
-
-            # Get proxy group UIDs and map to principal resources
-            proxies = []
-            memberships = (yield self._calendar_user_proxy_index().getMemberships(self.principalUID()))
-            for uid in memberships:
-                subprincipal = self.parent.principalForUID(uid)
-                if subprincipal:
-                    if subprincipal.isProxyType(read_write):
-                        proxies.append(subprincipal.parent)
-                else:
-                    yield self._calendar_user_proxy_index().removeGroup(uid)
-
-            proxyFors.update(proxies)
-
-        returnValue(proxyFors)
-
     def principalCollections(self):
         return self.parent.principalCollections()
 
@@ -837,9 +829,6 @@
 
         return addresses
 
-    def autoSchedule(self):
-        return self.record.autoSchedule
-
     def enabledAsOrganizer(self):
         if self.record.recordType == DirectoryService.recordType_users:
             return True
@@ -852,15 +841,6 @@
         else:
             return False
 
-    def proxies(self):
-        return self._getRelatives("proxies", infinity=True)
-
-    def readOnlyProxies(self):
-        return self._getRelatives("readOnlyProxies", infinity=True)
-
-    def hasEditableProxyMembership(self):
-        return self.record.hasEditableProxyMembership()
-
     def scheduleInbox(self, request):
         home = self.calendarHome()
         if home is None:
@@ -907,6 +887,7 @@
         else:
             return None
 
+
     ##
     # Static
     ##

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/sqldb.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/sqldb.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/sqldb.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -101,10 +101,7 @@
             # Get calendar user addresses
             calendarUserAddresses = self.calendarUserAddresses(shortName)
             
-            # TODO: need this for Resources and Locations
-            autoSchedule = False
-                
-            yield shortName, guid, password, name, members, groups, calendarUserAddresses, autoSchedule
+            yield shortName, guid, password, name, members, groups, calendarUserAddresses
 
     def getRecord(self, recordType, shortName):
         # Get individual account record
@@ -129,11 +126,8 @@
         # Get calendar user addresses
         calendarUserAddresses = self.calendarUserAddresses(shortName)
         
-        # TODO: need this for Resources and Locations
-        autoSchedule = False
+        return shortName, guid, password, name, members, groups, calendarUserAddresses
             
-        return shortName, guid, password, name, members, groups, calendarUserAddresses, autoSchedule
-            
     def members(self, shortName):
         members = set()
         for member in self._db_execute(
@@ -318,7 +312,6 @@
                 members               = result[4],
                 groups                = result[5],
                 calendarUserAddresses = result[6],
-                autoSchedule          = result[7],
             )
 
     def recordWithShortName(self, recordType, shortName):
@@ -334,7 +327,6 @@
                 members               = result[4],
                 groups                = result[5],
                 calendarUserAddresses = result[6],
-                autoSchedule          = result[7],
             )
 
         return None
@@ -343,7 +335,7 @@
     """
     XML based implementation implementation of L{IDirectoryRecord}.
     """
-    def __init__(self, service, recordType, shortName, guid, password, name, members, groups, calendarUserAddresses, autoSchedule):
+    def __init__(self, service, recordType, shortName, guid, password, name, members, groups, calendarUserAddresses):
         super(SQLDirectoryRecord, self).__init__(
             service               = service,
             recordType            = recordType,
@@ -351,7 +343,6 @@
             shortNames            = (shortName,),
             fullName              = name,
             calendarUserAddresses = calendarUserAddresses,
-            autoSchedule          = autoSchedule,
         )
 
         self.password = password

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/accounts.xml
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/accounts.xml	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/accounts.xml	2009-04-23 23:47:07 UTC (rev 4072)
@@ -67,6 +67,7 @@
   </user>
   <user repeat="2">
     <uid>user%02d</uid>
+    <guid>user%02d</guid>
     <password>%02duser</password>
     <name>User %02d</name>
   </user>
@@ -81,6 +82,7 @@
   </group>
   <group>
     <uid>admin</uid>
+    <guid>admin</guid>
     <password>admin</password>
     <name>Administrators</name>
     <members>
@@ -89,6 +91,7 @@
   </group>
   <group>
     <uid>grunts</uid>
+    <guid>grunts</guid>
     <password>grunts</password>
     <name>We do all the work</name>
     <members>
@@ -99,6 +102,7 @@
   </group>
   <group>
     <uid>right_coast</uid>
+    <guid>right_coast</guid>
     <password>right_coast</password>
     <name>East Coast</name>
     <members>
@@ -107,6 +111,7 @@
   </group>
   <group>
     <uid>left_coast</uid>
+    <guid>left_coast</guid>
     <password>left_coast</password>
     <name>West Coast</name>
     <members>
@@ -117,6 +122,7 @@
   </group>
   <group>
     <uid>both_coasts</uid>
+    <guid>both_coasts</guid>
     <password>both_coasts</password>
     <name>Both Coasts</name>
     <members>
@@ -126,6 +132,7 @@
   </group>
   <group>
     <uid>recursive1_coasts</uid>
+    <guid>recursive1_coasts</guid>
     <password>recursive1_coasts</password>
     <name>Recursive1 Coasts</name>
     <members>
@@ -135,6 +142,7 @@
   </group>
   <group>
     <uid>recursive2_coasts</uid>
+    <guid>recursive2_coasts</guid>
     <password>recursive2_coasts</password>
     <name>Recursive2 Coasts</name>
     <members>
@@ -144,6 +152,7 @@
   </group>
   <group>
     <uid>non_calendar_group</uid>
+    <guid>non_calendar_group</guid>
     <password>non_calendar_group</password>
     <name>Non-calendar group</name>
     <members>
@@ -154,6 +163,7 @@
   </group>
   <location>
     <uid>mercury</uid>
+    <guid>mercury</guid>
     <password>mercury</password>
     <name>Mecury Seven</name>
     <email-address>mercury at example.com</email-address>
@@ -164,6 +174,7 @@
   </location>
   <location>
     <uid>gemini</uid>
+    <guid>gemini</guid>
     <password>gemini</password>
     <name>Gemini Twelve</name>
     <email-address>gemini at example.com</email-address>
@@ -175,6 +186,7 @@
   </location>
   <location>
     <uid>apollo</uid>
+    <guid>apollo</guid>
     <password>apollo</password>
     <name>Apollo Eleven</name>
     <email-address>apollo at example.com</email-address>
@@ -185,6 +197,7 @@
   </location>
   <location>
     <uid>orion</uid>
+    <guid>orion</guid>
     <password>orion</password>
     <name>Orion</name>
     <email-address>orion at example.com</email-address>
@@ -195,6 +208,7 @@
   </location>
   <resource>
     <uid>transporter</uid>
+    <guid>transporter</guid>
     <password>transporter</password>
     <name>Mass Transporter</name>
     <email-address>transporter at example.com</email-address>
@@ -202,6 +216,7 @@
   </resource>
   <resource>
     <uid>ftlcpu</uid>
+    <guid>ftlcpu</guid>
     <password>ftlcpu</password>
     <name>Faster-Than-Light Microprocessor</name>
     <email-address>ftlcpu at example.com</email-address>

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_cachedirectory.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_cachedirectory.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_cachedirectory.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -61,7 +61,6 @@
                         lastName              = "",
                         emailAddresses        = record.get("email"),
                         calendarUserAddresses = record.get("email"),
-                        autoSchedule          = False,
                         enabledForCalendaring = True,
                     ) 
                     self.recordCacheForType(recordType).addRecord(cacheRecord)

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_opendirectory.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_opendirectory.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -72,11 +72,8 @@
                 lastName              = "User",
                 emailAddresses        = set(("someuser at example.com",)),
                 calendarUserAddresses = set(("mailtoguid at example.com",)),
-                autoSchedule          = False,
                 enabledForCalendaring = True,
                 memberGUIDs           = [],
-                proxyGUIDs            = (),
-                readOnlyProxyGUIDs    = (),
             )
 
             digestFields = {}

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_opendirectoryrecords.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_opendirectoryrecords.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_opendirectoryrecords.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -692,58 +692,8 @@
             self.verifyQuery(self.service.recordWithEmailAddress, "location05 at example.com")
             self.verifyNoQuery(self.service.recordWithEmailAddress, "location05 at example.com")
 
-        def test_resourceinfo(self):
-            good_plist = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-    <key>com.apple.WhitePagesFramework</key>
-    <dict>
-        <key>AutoAcceptsInvitation</key>
-        <true/>
-        <key>Label</key>
-        <string>Location</string>
-        <key>CalendaringDelegate</key>
-        <string></string>
-        <key>ReadOnlyCalendaringDelegate</key>
-        <string></string>
-    </dict>
-</dict>
-</plist>
-"""
 
-            bad_plist = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-    <key>com.apple.WhitePagesFramework</key>
-    <string>bogus</string>
-</dict>
-</plist>
-"""
 
-            invalid_xml = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-    <key>com.apple.WhitePagesFramework</key>
-    <string>R&D</string>
-</dict>
-</plist>
-"""
-
-            self.loadRecords({
-                DirectoryService.recordType_locations: [
-                    fakeODRecord("Location 01", resourceInfo=good_plist),
-                    fakeODRecord("Location 02", resourceInfo=bad_plist),
-                    fakeODRecord("Location 03", resourceInfo=invalid_xml),
-                ],
-            })
-
-            self.verifyRecords(DirectoryService.recordType_locations, ("location01",))
-            self.verifyDisabledRecords(DirectoryService.recordType_locations, (), ())
-
-
 def fakeODRecord(fullName, shortName=None, guid=None, email=None, members=None, resourceInfo=None):
     if shortName is None:
         shortName = shortNameForFullName(fullName)

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_principal.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_principal.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -39,11 +39,6 @@
 
 import twistedcaldav.test.util
 
-directoryServices = (
-    BasicDirectoryService(digestRealm, basicUserFile, groupFile),
-    DigestDirectoryService(digestRealm, digestUserFile, groupFile),
-    XMLDirectoryService(xmlFile),
-)
 
 class ProvisionedPrincipals (twistedcaldav.test.util.TestCase):
     """
@@ -52,9 +47,15 @@
     def setUp(self):
         super(ProvisionedPrincipals, self).setUp()
 
+        self.directoryServices = (
+            BasicDirectoryService(digestRealm, basicUserFile, groupFile),
+            DigestDirectoryService(digestRealm, digestUserFile, groupFile),
+            XMLDirectoryService(xmlFile),
+        )
+
         # Set up a principals hierarchy for each service we're testing with
         self.principalRootResources = {}
-        for directory in directoryServices:
+        for directory in self.directoryServices:
             name = directory.__class__.__name__
             url = "/" + name + "/"
 
@@ -78,7 +79,7 @@
 
         DirectoryPrincipalResource.principalURL(),
         """
-        for directory in directoryServices:
+        for directory in self.directoryServices:
             #print "\n -> %s" % (directory.__class__.__name__,)
             provisioningResource = self.principalRootResources[directory.__class__.__name__]
 
@@ -140,7 +141,7 @@
         """
         DirectoryPrincipalProvisioningResource.principalForUser()
         """
-        for directory in directoryServices:
+        for directory in self.directoryServices:
             provisioningResource = self.principalRootResources[directory.__class__.__name__]
 
             for user in directory.listRecords(DirectoryService.recordType_users):
@@ -152,7 +153,7 @@
         """
         DirectoryPrincipalProvisioningResource.principalForAuthID()
         """
-        for directory in directoryServices:
+        for directory in self.directoryServices:
             provisioningResource = self.principalRootResources[directory.__class__.__name__]
 
             for user in directory.listRecords(DirectoryService.recordType_users):
@@ -204,20 +205,6 @@
         self.failIf(provisioningResource.principalForCalendarUserAddress("/principals/users/nocalendar/") is not None)
         self.failIf(provisioningResource.principalForCalendarUserAddress("/principals/__uids__/543D28BA-F74F-4D5F-9243-B3E3A61171E5/") is not None)
 
-    def test_autoSchedule(self):
-        """
-        DirectoryPrincipalProvisioningResource.principalForCalendarUserAddress()
-        """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
-            principal = provisioningResource.principalForRecord(record)
-            self.failIf(principal is None)
-            if record.enabledForCalendaring:
-                self.assertEquals(record.autoSchedule, principal.autoSchedule())
-                if record.shortNames[0] == "gemini":
-                    self.assertTrue(principal.autoSchedule())
-                else:
-                    self.assertFalse(principal.autoSchedule())
-
     def test_enabledForCalendaring(self):
         """
         DirectoryPrincipalProvisioningResource.principalForCalendarUserAddress()
@@ -302,24 +289,6 @@
             memberships = yield recordResource.groupMemberships()
             self.failUnless(set(record.groups()).issubset(set(r.record for r in memberships if hasattr(r, "record"))))
 
-    def test_proxies(self):
-        """
-        DirectoryPrincipalResource.proxies()
-        """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
-            if record.enabledForCalendaring:
-                self.failUnless(set(record.proxies()).issubset(set(r.record for r in recordResource.proxies())))
-                self.assertEqual(record.hasEditableProxyMembership(), recordResource.hasEditableProxyMembership())
-
-    def test_read_only_proxies(self):
-        """
-        DirectoryPrincipalResource.proxies()
-        """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
-            if record.enabledForCalendaring:
-                self.failUnless(set(record.readOnlyProxies()).issubset(set(r.record for r in recordResource.readOnlyProxies())))
-                self.assertEqual(record.hasEditableProxyMembership(), recordResource.hasEditableProxyMembership())
-
     def test_principalUID(self):
         """
         DirectoryPrincipalResource.principalUID()
@@ -356,7 +325,7 @@
         # Need to create a calendar home provisioner for each service.
         calendarRootResources = {}
 
-        for directory in directoryServices:
+        for directory in self.directoryServices:
             url = "/homes_" + directory.__class__.__name__ + "/"
             path = os.path.join(self.docroot, url[1:])
 
@@ -412,7 +381,7 @@
         """
         Default access controls for principal provisioning resources.
         """
-        for directory in directoryServices:
+        for directory in self.directoryServices:
             #print "\n -> %s" % (directory.__class__.__name__,)
             provisioningResource = self.principalRootResources[directory.__class__.__name__]
 
@@ -472,7 +441,7 @@
             C{record} is the directory service record
             for each record in each directory in C{directoryServices}.
         """
-        for directory in directoryServices:
+        for directory in self.directoryServices:
             provisioningResource = self.principalRootResources[directory.__class__.__name__]
             for recordType in directory.recordTypes():
                 for record in directory.listRecords(recordType):

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_proxyprincipalmembers.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -28,7 +28,6 @@
 import twistedcaldav.test.util
 from twistedcaldav.config import config
 
-directoryService = XMLDirectoryService(xmlFile)
 
 class ProxyPrincipals (twistedcaldav.test.util.TestCase):
     """
@@ -37,19 +36,21 @@
     def setUp(self):
         super(ProxyPrincipals, self).setUp()
 
+        self.directoryService = XMLDirectoryService(xmlFile)
+
         # Set up a principals hierarchy for each service we're testing with
         self.principalRootResources = {}
-        name = directoryService.__class__.__name__
+        name = self.directoryService.__class__.__name__
         url = "/" + name + "/"
 
-        provisioningResource = DirectoryPrincipalProvisioningResource(url, directoryService)
+        provisioningResource = DirectoryPrincipalProvisioningResource(url, self.directoryService)
 
         self.site.resource.putChild(name, provisioningResource)
 
-        self.principalRootResources[directoryService.__class__.__name__] = provisioningResource
+        self.principalRootResources[self.directoryService.__class__.__name__] = provisioningResource
 
     def _getPrincipalByShortName(self, type, name):
-        provisioningResource = self.principalRootResources[directoryService.__class__.__name__]
+        provisioningResource = self.principalRootResources[self.directoryService.__class__.__name__]
         return provisioningResource.principalForShortName(type, name)
 
     def _groupMembersTest(self, recordType, recordName, subPrincipalName, expectedMembers):
@@ -252,7 +253,7 @@
                 return self.members
 
 
-        user = self._getPrincipalByShortName(directoryService.recordType_users,
+        user = self._getPrincipalByShortName(self.directoryService.recordType_users,
                                            "cdaboo")
 
         proxyGroup = user.getChild("calendar-proxy-write")
@@ -281,7 +282,7 @@
             def changed(self):
                 self.changedCount += 1
 
-        user = self._getPrincipalByShortName(directoryService.recordType_users, "cdaboo")
+        user = self._getPrincipalByShortName(self.directoryService.recordType_users, "cdaboo")
 
         proxyGroup = user.getChild("calendar-proxy-write")
 
@@ -376,7 +377,7 @@
         db._memcacher.theTime = theTime
 
 
-        for doMembershipFirst in (True, False,):
+        for doMembershipFirst in (True, False):
             for proxyType in ("calendar-proxy-read", "calendar-proxy-write"):
 
                 principal = self._getPrincipalByShortName(DirectoryService.recordType_users, "wsanchez")
@@ -419,7 +420,7 @@
                 self.assertEquals(len(members), 2)
 
                 # Remove the dreid user from the directory service
-                del directoryService._accounts()[DirectoryService.recordType_users]["dreid"]
+                del self.directoryService._accounts()[DirectoryService.recordType_users]["dreid"]
 
                 @inlineCallbacks
                 def _membershipTest():
@@ -451,8 +452,8 @@
                     self.assertEquals(len(members), 2)
 
                     # Restore removed user
-                    parser = XMLAccountsParser(directoryService.xmlFile)
-                    directoryService._parsedAccounts = parser.items
+                    parser = XMLAccountsParser(self.directoryService.xmlFile)
+                    self.directoryService._parsedAccounts = parser.items
 
                     # Trigger the proxy DB clean up, which will actually
                     # remove the deletion timer because the principal has been
@@ -464,7 +465,7 @@
                     self.assertEquals(result, None)
 
                     # Remove the dreid user from the directory service
-                    del directoryService._accounts()[DirectoryService.recordType_users]["dreid"]
+                    del self.directoryService._accounts()[DirectoryService.recordType_users]["dreid"]
 
                     # Trigger the proxy DB clean up, which won't actually
                     # remove anything because we haven't exceeded the timeout
@@ -493,8 +494,8 @@
                     yield _membershipTest()
 
                 # Restore removed user
-                parser = XMLAccountsParser(directoryService.xmlFile)
-                directoryService._parsedAccounts = parser.items
+                parser = XMLAccountsParser(self.directoryService.xmlFile)
+                self.directoryService._parsedAccounts = parser.items
 
                 self._clearProxy(principal, proxyType)
                 self._clearProxy(fakePrincipal, proxyType)

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_xmlfile.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_xmlfile.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/test/test_xmlfile.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -17,10 +17,14 @@
 import os
 
 from twisted.python.filepath import FilePath
+from twisted.internet.defer import inlineCallbacks
 
+from twistedcaldav.directory.calendaruserproxy import CalendarUserProxyDatabase
 from twistedcaldav.directory.directory import DirectoryService
 import twistedcaldav.directory.test.util
 from twistedcaldav.directory.xmlfile import XMLDirectoryService
+from twistedcaldav.resource import ResourceInfoDatabase
+from twistedcaldav.config import config
 
 xmlFile = FilePath(os.path.join(os.path.dirname(__file__), "accounts.xml"))
 
@@ -104,6 +108,7 @@
 <accounts realm="Test Realm">
   <user>
     <uid>admin</uid>
+    <guid>admin</guid>
     <password>nimda</password>
     <name>Super User</name>
   </user>
@@ -121,6 +126,7 @@
                 set(expectedRecords)
             )
 
+    @inlineCallbacks
     def test_okAutoSchedule(self):
         service = self.service()
 
@@ -130,6 +136,7 @@
 <accounts realm="Test Realm">
   <location>
     <uid>my office</uid>
+    <guid>myoffice</guid>
     <password>nimda</password>
     <name>Super User</name>
     <auto-schedule/>
@@ -147,30 +154,10 @@
                 set(r.shortNames[0] for r in service.listRecords(recordType)),
                 set(expectedRecords)
             )
-        self.assertTrue(service.recordWithShortName(DirectoryService.recordType_locations, "my office").autoSchedule)
+        resourceInfoDatabase = ResourceInfoDatabase(config.DataRoot)
+        self.assertTrue((yield resourceInfoDatabase.getAutoSchedule(service.recordWithShortName(DirectoryService.recordType_locations, "my office").guid)))
 
-    def test_badAutoSchedule(self):
-        service = self.service()
 
-        self.xmlFile().open("w").write(
-"""<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-<accounts realm="Test Realm">
-  <user>
-    <uid>my office</uid>
-    <password>nimda</password>
-    <name>Super User</name>
-    <auto-schedule/>
-  </user>
-</accounts>
-"""
-        )
-        
-        def _findRecords():
-            set(r.shortNames[0] for r in service.listRecords(DirectoryService.recordType_users))
-
-        self.assertRaises(ValueError, _findRecords)
-        
     def test_okDisableCalendar(self):
         service = self.service()
 
@@ -207,6 +194,7 @@
         self.assertFalse(service.recordWithShortName(DirectoryService.recordType_groups, "enabled").enabledForCalendaring)
         self.assertFalse(service.recordWithShortName(DirectoryService.recordType_groups, "disabled").enabledForCalendaring)
 
+    @inlineCallbacks
     def test_okProxies(self):
         service = self.service()
 
@@ -216,11 +204,13 @@
 <accounts realm="Test Realm">
   <user>
     <uid>test</uid>
+    <guid>test</guid>
     <password>nimda</password>
     <name>Test</name>
   </user>
   <location>
     <uid>my office</uid>
+    <guid>myoffice</guid>
     <password>nimda</password>
     <name>Super User</name>
     <auto-schedule/>
@@ -241,29 +231,9 @@
                 set(r.shortNames[0] for r in service.listRecords(recordType)),
                 set(expectedRecords)
             )
-        self.assertEqual(set([("users", "test",)],), service.recordWithShortName(DirectoryService.recordType_locations, "my office")._proxies)
-        self.assertEqual(set([("locations", "my office",)],), service.recordWithShortName(DirectoryService.recordType_users, "test")._proxyFor)
+        calendarUserProxyDatabase = CalendarUserProxyDatabase(config.DataRoot)
+        members = (yield calendarUserProxyDatabase.getMembers("myoffice#calendar-proxy-write"))
+        self.assertTrue("test" in members)
+        members = (yield calendarUserProxyDatabase.getMemberships("test"))
+        self.assertTrue("myoffice#calendar-proxy-write" in members)
 
-    def test_badProxies(self):
-        service = self.service()
-
-        self.xmlFile().open("w").write(
-"""<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-<accounts realm="Test Realm">
-  <user>
-    <uid>my office</uid>
-    <password>nimda</password>
-    <name>Super User</name>
-    <proxies>
-        <member>12345-GUID-67890</member>
-    </proxies>
-  </user>
-</accounts>
-"""
-        )
-        
-        def _findRecords():
-            set(r.shortNames[0] for r in service.listRecords(DirectoryService.recordType_users))
-
-        self.assertRaises(ValueError, _findRecords)

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/xmlaccountsparser.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/xmlaccountsparser.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/xmlaccountsparser.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -28,8 +28,11 @@
 
 from twisted.python.filepath import FilePath
 
+from twistedcaldav.config import config
 from twistedcaldav.directory.directory import DirectoryService
 from twistedcaldav.log import Logger
+from twistedcaldav.resource import ResourceInfoDatabase
+from twistedcaldav.directory.calendaruserproxy import CalendarUserProxyDatabase
 
 log = Logger()
 
@@ -73,6 +76,7 @@
         return "<%s %r>" % (self.__class__.__name__, self.xmlFile)
 
     def __init__(self, xmlFile):
+
         if type(xmlFile) is str:
             xmlFile = FilePath(xmlFile)
 
@@ -94,7 +98,43 @@
             self.log("Ignoring file %r because it is not a repository builder file" % (self.xmlFile,))
             return
         self._parseXML(accounts_node)
-        
+        self._updateExternalDatabases()
+
+    def _updateExternalDatabases(self):
+        resourceInfoDatabase = ResourceInfoDatabase(config.DataRoot)
+
+        calendarUserProxyDatabase = CalendarUserProxyDatabase(config.DataRoot)
+
+        for records in self.items.itervalues():
+            for principal in records.itervalues():
+
+                resourceInfoDatabase.setAutoScheduleInDatabase(principal.guid,
+                    principal.autoSchedule)
+
+                if principal.proxies:
+                    proxies = []
+                    for recordType, uid in principal.proxies:
+                        record = self.items[recordType].get(uid)
+                        if record is not None:
+                            proxies.append(record.guid)
+
+                    calendarUserProxyDatabase.setGroupMembersInDatabase(
+                        "%s#calendar-proxy-write" % (principal.guid,),
+                        proxies
+                    )
+
+                if principal.readOnlyProxies:
+                    readOnlyProxies = []
+                    for recordType, uid in principal.readOnlyProxies:
+                        record = self.items[recordType].get(uid)
+                        if record is not None:
+                            readOnlyProxies.append(record.guid)
+
+                    calendarUserProxyDatabase.setGroupMembersInDatabase(
+                        "%s#calendar-proxy-read" % (principal.guid,),
+                        readOnlyProxies
+                    )
+
     def _parseXML(self, node):
         """
         Parse the XML root node from the accounts configuration document.
@@ -280,9 +320,6 @@
                 if child.firstChild is not None:
                     self.calendarUserAddresses.add(child.firstChild.data.encode("utf-8"))
             elif child_name == ELEMENT_AUTOSCHEDULE:
-                # Only Resources & Locations
-                if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
-                    raise ValueError("<auto-schedule> element only allowed for Resources and Locations: %s" % (child_name,))
                 self.autoSchedule = True
             elif child_name == ELEMENT_DISABLECALENDAR:
                 # FIXME: Not sure I see why this restriction is needed. --wsanchez
@@ -291,14 +328,8 @@
                 #    raise ValueError("<disable-calendar> element only allowed for Users: %s" % (child_name,))
                 self.enabledForCalendaring = False
             elif child_name == ELEMENT_PROXIES:
-                # Only Resources & Locations
-                if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
-                    raise ValueError("<proxies> element only allowed for Resources and Locations: %s" % (child_name,))
                 self._parseMembers(child, self.proxies)
             elif child_name == ELEMENT_READ_ONLY_PROXIES:
-                # Only Resources & Locations
-                if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
-                    raise ValueError("<read-only-proxies> element only allowed for Resources and Locations: %s" % (child_name,))
                 self._parseMembers(child, self.readOnlyProxies)
             else:
                 raise RuntimeError("Unknown account attribute: %s" % (child_name,))

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/xmlfile.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/directory/xmlfile.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -104,6 +104,7 @@
                 self._fileInfo = fileInfo
         return self._parsedAccounts
 
+
 class XMLDirectoryRecord(DirectoryRecord):
     """
     XML based implementation implementation of L{IDirectoryRecord}.
@@ -119,17 +120,12 @@
             lastName              = xmlPrincipal.lastName,
             emailAddresses        = xmlPrincipal.emailAddresses,
             calendarUserAddresses = xmlPrincipal.calendarUserAddresses,
-            autoSchedule          = xmlPrincipal.autoSchedule,
             enabledForCalendaring = xmlPrincipal.enabledForCalendaring,
         )
 
         self.password          = xmlPrincipal.password
         self._members          = xmlPrincipal.members
         self._groups           = xmlPrincipal.groups
-        self._proxies          = xmlPrincipal.proxies
-        self._proxyFor         = xmlPrincipal.proxyFor
-        self._readOnlyProxies  = xmlPrincipal.readOnlyProxies
-        self._readOnlyProxyFor = xmlPrincipal.readOnlyProxyFor
 
     def members(self):
         for recordType, shortName in self._members:
@@ -139,22 +135,6 @@
         for shortName in self._groups:
             yield self.service.recordWithShortName(DirectoryService.recordType_groups, shortName)
 
-    def proxies(self):
-        for recordType, shortName in self._proxies:
-            yield self.service.recordWithShortName(recordType, shortName)
-
-    def proxyFor(self, read_write=True):
-        for recordType, shortName in self._proxyFor:
-            yield self.service.recordWithShortName(recordType, shortName)
-
-    def readOnlyProxies(self):
-        for recordType, shortName in self._readOnlyProxies:
-            yield self.service.recordWithShortName(recordType, shortName)
-
-    def readOnlyProxyFor(self, read_write=True):
-        for recordType, shortName in self._readOnlyProxyFor:
-            yield self.service.recordWithShortName(recordType, shortName)
-
     def verifyCredentials(self, credentials):
         if isinstance(credentials, UsernamePassword):
             return credentials.password == self.password

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/memcachepool.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/memcachepool.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/memcachepool.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -67,6 +67,11 @@
         """
         Notify the connectionPool that we've lost our connection.
         """
+
+        if self.connectionPool.shutdown_requested:
+            # The reactor is stopping; don't reconnect
+            return
+
         self.log_error("MemCache connection lost: %s" % (reason,))
         if self._protocolInstance is not None:
             self.connectionPool.clientBusy(self._protocolInstance)
@@ -90,7 +95,6 @@
             connector,
             reason)
 
-
     def buildProtocol(self, addr):
         """
         Attach the C{self.connectionPool} to the protocol so it can tell it,
@@ -138,15 +142,31 @@
 
         if reactor is None:
             from twisted.internet import reactor
-
         self._reactor = reactor
 
+        self.shutdown_deferred = None
+        self.shutdown_requested = False
+        reactor.addSystemEventTrigger('before', 'shutdown', self._shutdownCallback)
+
         self._busyClients = set([])
         self._freeClients = set([])
         self._pendingConnects = 0
         self._commands = []
 
+    def _isIdle(self):
+        return (
+            len(self._busyClients) == 0 and
+            len(self._commands) == 0 and
+            self._pendingConnects == 0
+        )
 
+    def _shutdownCallback(self):
+        self.shutdown_requested = True
+        if self._isIdle():
+            return None
+        self.shutdown_deferred = Deferred()
+        return self.shutdown_deferred
+
     def _newClientConnection(self):
         """
         Create a new client connection.
@@ -165,6 +185,7 @@
             return client
 
         factory = self.clientFactory()
+        factory.noisy = False
 
         factory.connectionPool = self
 
@@ -222,6 +243,7 @@
 
         @return: A L{Deferred} that fires with the result of the given command.
         """
+
         if len(self._freeClients) > 0:
             client = self._freeClients.pop()
 
@@ -274,6 +296,7 @@
 
         @param client: An instance of C{self.clientFactory}
         """
+
         if client in self._freeClients:
             self._freeClients.remove(client)
 
@@ -294,6 +317,9 @@
 
         self._freeClients.add(client)
 
+        if self.shutdown_deferred and self._isIdle():
+            self.shutdown_deferred.callback(None)
+
         if len(self._commands) > 0:
             d, command, args, kwargs = self._commands.pop(0)
 

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/resource.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/resource.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -27,6 +27,7 @@
     "isPseudoCalendarCollectionResource",
 ]
 
+import os
 import urllib
 
 from zope.interface import implements
@@ -36,13 +37,12 @@
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
+from twisted.web2.dav.davxml import dav_namespace
+from twisted.web2.dav.http import ErrorResponse
 from twisted.web2.dav.idav import IDAVPrincipalCollectionResource
 from twisted.web2.dav.resource import AccessDeniedError, DAVPrincipalCollectionResource
-from twisted.web2.dav.davxml import dav_namespace
-from twisted.web2.dav.http import ErrorResponse
 from twisted.web2.dav.resource import TwistedACLInheritable
-from twisted.web2.dav.util import joinURL, parentForURL, unimplemented,\
-    normalizeURL
+from twisted.web2.dav.util import joinURL, parentForURL, unimplemented, normalizeURL
 from twisted.web2.http import HTTPError, RedirectResponse, StatusResponse, Response
 from twisted.web2.http_headers import MimeType
 from twisted.web2.iweb import IResponse
@@ -51,16 +51,18 @@
 
 import twistedcaldav
 from twistedcaldav import caldavxml, customxml
+from twistedcaldav.caldavxml import caldav_namespace
 from twistedcaldav.config import config
 from twistedcaldav.customxml import TwistedCalendarAccessProperty
+from twistedcaldav.customxml import calendarserver_namespace
 from twistedcaldav.extensions import DAVResource, DAVPrincipalResource
 from twistedcaldav.ical import Component
+from twistedcaldav.ical import Component as iComponent
+from twistedcaldav.ical import allowedComponents
 from twistedcaldav.icaldav import ICalDAVResource, ICalendarPrincipalResource
-from twistedcaldav.caldavxml import caldav_namespace
-from twistedcaldav.customxml import calendarserver_namespace
-from twistedcaldav.ical import allowedComponents
-from twistedcaldav.ical import Component as iComponent
 from twistedcaldav.log import LoggingMixIn
+from twistedcaldav.memcacher import Memcacher
+from twistedcaldav.sql import AbstractSQLDatabase, db_prefix
 
 from urlparse import urlsplit
 
@@ -828,6 +830,7 @@
         (caldav_namespace, "calendar-user-type"       ),
         (calendarserver_namespace, "calendar-proxy-read-for"  ),
         (calendarserver_namespace, "calendar-proxy-write-for" ),
+        (calendarserver_namespace, "auto-schedule" ),
     )
 
     @classmethod
@@ -898,9 +901,23 @@
                     *[davxml.HRef(principal.principalURL()) for principal in results]
                 ))
 
+            elif name == "auto-schedule":
+                autoSchedule = (yield self.getAutoSchedule())
+                returnValue(customxml.AutoSchedule("true" if autoSchedule else "false"))
+
         result = (yield super(CalendarPrincipalResource, self).readProperty(property, request))
         returnValue(result)
 
+    def writeProperty(self, property, request):
+        assert isinstance(property, davxml.WebDAVElement), (
+            "%r is not a WebDAVElement instance" % (property,)
+        )
+
+        if property.qname() == (caldav_namespace, "auto-schedule"):
+            self.setAutoSchedule(autoSchedule) # MOR: parse the value
+
+        return super(CalendarPrincipalResource, self).writeProperty(property, request)
+
     def calendarHomeURLs(self):
         if self.hasDeadProperty((caldav_namespace, "calendar-home-set")):
             home_set = self.readDeadProperty((caldav_namespace, "calendar-home-set"))
@@ -989,6 +1006,39 @@
         """
         return None
 
+    def setAutoSchedule(self, autoSchedule):
+        self._resource_info_index().setAutoSchedule(self.record.guid, autoSchedule)
+
+    def getAutoSchedule(self):
+        return self._resource_info_index().getAutoSchedule(self.record.guid)
+
+    def _resource_info_index(self):
+        """
+        Return the resource info SQL database for this calendar principal.
+
+        @return: the L{ResourceInfoDatabase} for the calendar principal.
+        """
+
+        # The db is located in the data root
+        self.pcollection = self.parent.parent # MOR: doesn't feel right
+        if not hasattr(self.pcollection, "resource_info_db"):
+            setattr(self.pcollection, "resource_info_db", ResourceInfoDatabase(config.DataRoot))
+        return self.pcollection.resource_info_db
+
+    def _calendar_user_proxy_index(self):
+        """
+        Return the calendar user proxy SQL database for this calendar principal.
+
+        @return: the L{CalendarUserProxyDatabase} for the calendar principal.
+        """
+
+        # The db is located in the data root
+        self.pcollection = self.parent.parent # MOR: doesn't feel right
+        if not hasattr(self.pcollection, "calendar_user_proxy_db"):
+            setattr(self.pcollection, "calendar_user_proxy_db", CalendarUserProxyDatabase(config.DataRoot))
+        return self.pcollection.calendar_user_proxy_db
+
+
 ##
 # Utilities
 ##
@@ -1008,3 +1058,146 @@
         return False
     else:
         return resource.isPseudoCalendarCollection()
+
+
+class ResourceInfoDatabase(AbstractSQLDatabase, LoggingMixIn):
+    """
+    A database to maintain resource (and location) information
+
+    SCHEMA:
+
+    Group Database:
+
+    ROW: GUID, AUTOSCHEDULE
+
+    """
+
+    dbType = "RESOURCEINFO"
+    dbFilename = "resourceinfo.sqlite"
+    dbOldFilename = db_prefix + "resourceinfo"
+    dbFormatVersion = "1"
+
+    class ResourceInfoDBMemcacher(Memcacher):
+
+        def setAutoSchedule(self, guid, autoSchedule):
+            return self.set("resourceinfo:%s" % (str(guid),), "1" if autoSchedule else "0")
+
+        @inlineCallbacks
+        def getAutoSchedule(self, guid):
+            result = (yield self.get("resourceinfo:%s" % (str(guid),)))
+            if result is not None:
+                autoSchedule = result == "1"
+            else:
+                autoSchedule = None
+            returnValue(autoSchedule)
+
+    def __init__(self, path):
+        path = os.path.join(path, ResourceInfoDatabase.dbFilename)
+        super(ResourceInfoDatabase, self).__init__(path, True)
+
+        self._memcacher = ResourceInfoDatabase.ResourceInfoDBMemcacher("resourceInfoDB")
+
+    @inlineCallbacks
+    def setAutoSchedule(self, guid, autoSchedule):
+        """
+        Set a resource/location's auto-Schedule boolean.
+
+        @param guid: the UID of the group principal to add.
+        @param autoSchedule: boolean
+        """
+        self.setAutoScheduleInDatabase(guid, autoSchedule)
+
+        # Update cache
+        _ignore = (yield self._memcacher.setAutoSchedule(guid, autoSchedule))
+
+    def setAutoScheduleInDatabase(self, guid, autoSchedule):
+        """
+        A blocking call to set a resource/location's auto-Schedule boolean
+        value in the database.
+
+        @param guid: the UID of the group principal to add.
+        @param autoSchedule: boolean
+        """
+        # Remove what is there, then add it back.
+        self._delete_from_db(guid)
+        self._add_to_db(guid, autoSchedule)
+        self._db_commit()
+
+    @inlineCallbacks
+    def getAutoSchedule(self, guid):
+        """
+        Return the auto-Schedule state for the resource/location specified by guid
+        """
+
+        # Pull from cache
+        autoSchedule = (yield self._memcacher.getAutoSchedule(guid))
+        if autoSchedule is None:
+            # Not in memcache, check local db
+            autoSchedule = self._db_value_for_sql("select AUTOSCHEDULE from RESOURCEINFO where GUID = :1", guid)
+            if autoSchedule is not None:
+                autoSchedule = autoSchedule == 1
+                result = (yield self._memcacher.setAutoSchedule(guid, autoSchedule))
+            else:
+                # Not in local db
+                # MOR: no value -- what to do, default to False?
+                autoSchedule = False
+
+        returnValue(autoSchedule)
+
+    def _add_to_db(self, guid, autoSchedule):
+        """
+        Insert the specified entry into the database.
+
+        @param guid: the guid of the resource/location
+        @param autoSchedule: a boolean
+        """
+        self._db_execute(
+            """
+            insert into RESOURCEINFO (GUID, AUTOSCHEDULE)
+            values (:1, :2)
+            """, guid, 1 if autoSchedule else 0
+        )
+
+    def _delete_from_db(self, guid):
+        """
+        Deletes the specified entry from the database.
+
+        @param guid: the guid of the resource/location to delete
+        """
+        self._db_execute("delete from RESOURCEINFO where GUID = :1", guid)
+
+    def _db_version(self):
+        """
+        @return: the schema version assigned to this index.
+        """
+        return ResourceInfoDatabase.dbFormatVersion
+
+    def _db_type(self):
+        """
+        @return: the collection type assigned to this index.
+        """
+        return ResourceInfoDatabase.dbType
+
+    def _db_init_data_tables(self, q):
+        """
+        Initialise the underlying database tables.
+        @param q:           a database cursor to use.
+        """
+
+        #
+        # RESOURCEINFO table
+        #
+        q.execute(
+            """
+            create table RESOURCEINFO (
+                GUID            text,
+                AUTOSCHEDULE    integer
+            )
+            """
+        )
+        q.execute(
+            """
+            create index RESOURCEGUIDS on RESOURCEINFO (GUID)
+            """
+        )
+

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/scheduling/processing.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/scheduling/processing.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -269,7 +269,7 @@
                 raise ImplicitProcessorException(iTIPRequestStatus.NO_USER_SUPPORT)
 
             log.debug("ImplicitProcessing - originator '%s' to recipient '%s' ignoring METHOD:REQUEST, UID: '%s' - new processed" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
-            autoprocessed = self.recipient.principal.autoSchedule()
+            autoprocessed = (yield self.recipient.principal.getAutoSchedule())
             new_calendar = iTipProcessing.processNewRequest(self.message, self.recipient.cuaddr, autoprocessing=autoprocessed)
             name =  md5(str(new_calendar) + str(time.time()) + default.fp.path).hexdigest() + ".ics"
             
@@ -292,7 +292,7 @@
             result = (True, autoprocessed, changes,)
         else:
             # Processing update to existing event
-            autoprocessed = self.recipient.principal.autoSchedule()
+            autoprocessed = (yield self.recipient.principal.getAutoSchedule())
             new_calendar, props_changed, rids = iTipProcessing.processRequest(self.message, self.recipient_calendar, self.recipient.cuaddr, autoprocessing=autoprocessed)
             if new_calendar:
      
@@ -351,7 +351,7 @@
         else:
             # Need to check for auto-respond attendees. These need to suppress the inbox message
             # if the cancel is processed.
-            autoprocessed = self.recipient.principal.autoSchedule()
+            autoprocessed = (yield self.recipient.principal.getAutoSchedule())
 
             # Check to see if this is a cancel of the entire event
             processed_message, delete_original, rids = iTipProcessing.processCancel(self.message, self.recipient_calendar, autoprocessing=autoprocessed)

Modified: CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/test/test_memcachepool.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/test/test_memcachepool.py	2009-04-23 19:53:23 UTC (rev 4071)
+++ CalendarServer/branches/users/sagen/resource-delegates-4066/twistedcaldav/test/test_memcachepool.py	2009-04-23 23:47:07 UTC (rev 4072)
@@ -40,8 +40,11 @@
     """
     def __init__(self):
         self.calls = []
+        self.shutdown_deferred = None
+        self.shutdown_requested = False
 
 
+
     def clientFree(self, client):
         """
         Record a C{'free'} call for C{client}.
@@ -100,7 +103,10 @@
         return StubConnector()
 
 
+    def addSystemEventTrigger(*args, **kwds):
+        pass
 
+
 class PooledMemCacheProtocolTests(TestCase):
     """
     Tests for the L{PooledMemCacheProtocol}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090423/de4e9430/attachment-0001.html>


More information about the calendarserver-changes mailing list