[CalendarServer-changes] [1635] CalendarServer/branches/release/CalendarServer-1.0-dev

source_changes at macosforge.org source_changes at macosforge.org
Mon Jul 2 13:29:36 PDT 2007


Revision: 1635
          http://trac.macosforge.org/projects/calendarserver/changeset/1635
Author:   wsanchez at apple.com
Date:     2007-07-02 13:29:35 -0700 (Mon, 02 Jul 2007)

Log Message:
-----------
Pull-up r1629 from trunk

Modified Paths:
--------------
    CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts-test.xml
    CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts.dtd
    CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts.xml
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/customxml.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/apache.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/calendaruserproxy.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/directory.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/principal.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/sqldb.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/sudo.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/accounts.xml
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_opendirectory.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_opendirectoryschema.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_principal.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_xmlfile.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlaccountsparser.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlfile.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/schedule.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/static.py

Added Paths:
-----------
    CalendarServer/branches/release/CalendarServer-1.0-dev/doc/User/

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts-test.xml
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts-test.xml	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts-test.xml	2007-07-02 20:29:35 UTC (rev 1635)
@@ -40,11 +40,16 @@
     <uid>location%02d</uid>
     <password>location%02d</password>
     <name>Room %02d</name>
+    <auto-schedule/>
   </location>
   <resource repeat="10">
     <uid>resource%02d</uid>
     <password>resource%02d</password>
     <name>Resource %02d</name>
+    <auto-schedule/>
+    <proxies>
+      <member type="users">user01</member>
+    </proxies>
   </resource>
   <group>
     <uid>group01</uid>

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts.dtd
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts.dtd	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts.dtd	2007-07-02 20:29:35 UTC (rev 1635)
@@ -16,31 +16,30 @@
 DRI: Cyrus Daboo, cdaboo at apple.com
  -->
 
-<!ELEMENT accounts (user*, group*, resource*) >
+<!ELEMENT accounts (user*, group*, resource*, location*) >
   <!ATTLIST accounts realm CDATA "">
 
-  <!ELEMENT user (uid, guid, password, name, cuaddr*, calendar*, quota?, autorespond?)>
+  <!ELEMENT user (uid, guid, password, name, cuaddr*)>
     <!ATTLIST user repeat CDATA "1">
 
-  <!ELEMENT group (uid, guid, password, name, members, cuaddr*, calendar*, quota?)>
+  <!ELEMENT group (uid, guid, password, name, members, cuaddr*)>
     <!ATTLIST group repeat CDATA "1">
 
-  <!ELEMENT resource (uid, guid, password, name, cuaddr*, calendar*, quota?, autorespond?)>
+  <!ELEMENT resource (uid, guid, password, name, cuaddr*, auto-schedule?, proxies?)>
     <!ATTLIST resource repeat CDATA "1">
 
-  <!ELEMENT location (uid, guid, password, name, cuaddr*, calendar*, quota?, autorespond?)>
+  <!ELEMENT location (uid, guid, password, name, cuaddr*, auto-schedule?, proxies?)>
     <!ATTLIST location repeat CDATA "1">
 
   <!ELEMENT member (#PCDATA)>
     <!ATTLIST member type (users|groups|locations|resources) "users">
 
-  <!ELEMENT uid         (#PCDATA)>
-  <!ELEMENT guid        (#PCDATA)>
-  <!ELEMENT password    (#PCDATA)>
-  <!ELEMENT name        (#PCDATA)>
-  <!ELEMENT cuaddr      (#PCDATA)>
-  <!ELEMENT calendar    (#PCDATA)>
-  <!ELEMENT quota       (#PCDATA)>
-  <!ELEMENT autorespond EMPTY>
-  <!ELEMENT members     (member*)>
+  <!ELEMENT uid           (#PCDATA)>
+  <!ELEMENT guid          (#PCDATA)>
+  <!ELEMENT password      (#PCDATA)>
+  <!ELEMENT name          (#PCDATA)>
+  <!ELEMENT cuaddr        (#PCDATA)>
+  <!ELEMENT members       (member*)>
+  <!ELEMENT auto-schedule EMPTY>
+  <!ELEMENT proxies       (member*)>
 >

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts.xml
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts.xml	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts.xml	2007-07-02 20:29:35 UTC (rev 1635)
@@ -38,10 +38,14 @@
       <member type="users">test</member>
     </members>
   </group>
-  <resource>
+  <location>
     <uid>mercury</uid>
     <password>mercury</password>
     <name>Mecury Conference Room, Building 1, 2nd Floor</name>
-  </resource>
+    <auto-schedule/>
+    <proxies>
+      <member type="users">test</member>
+    </proxies>
+  </location>
 </accounts>
 

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/customxml.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/customxml.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -91,14 +91,6 @@
 
     allowed_children = { (twisted_dav_namespace, "guid"): (0, None) }
 
-class TwistedScheduleAutoRespond(davxml.WebDAVEmptyElement):
-    """
-    When set on an Inbox, scheduling requests are automatically handled.
-    """
-    namespace = twisted_dav_namespace
-    name = "schedule-auto-respond"
-    hidden = True
-
 class DropBoxHome (davxml.WebDAVEmptyElement):
     """
     Denotes a drop box home collection (a collection that will contain drop boxes).

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/apache.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/apache.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/apache.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -132,6 +132,7 @@
             shortName             = shortName,
             fullName              = None,
             calendarUserAddresses = set(),
+            autoSchedule          = False,
         )
 
 class AbstractUserRecord(AbstractDirectoryRecord):

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/appleopendirectory.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/appleopendirectory.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -25,6 +25,7 @@
     "OpenDirectoryInitError",
 ]
 
+import itertools
 import sys
 
 import opendirectory
@@ -329,6 +330,21 @@
                 
         return result
 
+    def _parseResourceInfo(self, plist):
+        """
+        Parse OD ResourceInfo attribute and extract information that the server needs.
+
+        @param plist: the plist that is the attribute value.
+        @type plist: str
+        @return: a C{tuple} of C{bool} for auto-accept and C{str} for proxy GUID.
+        """
+        plist = readPlistFromString(plist)
+        wpframework = plist.get("com.apple.WhitePagesFramework", {})
+        autoaccept = wpframework.get("AutoAcceptsInvitation", False)
+        proxy= wpframework.get("CalendaringDelegate")
+        
+        return (autoaccept, proxy,)
+
     def recordTypes(self):
         return (
             DirectoryService.recordType_users,
@@ -395,6 +411,7 @@
         elif recordType == DirectoryService.recordType_locations:
             listRecordType = dsattributes.kDSStdRecordTypeResources
             query = dsquery.match(dsattributes.kDSNAttrResourceType, "1", dsattributes.eDSExact)
+            attrs.append(dsattributes.kDSNAttrResourceInfo)
         
         elif recordType == DirectoryService.recordType_resources:
             listRecordType = dsattributes.kDSStdRecordTypeResources
@@ -405,6 +422,7 @@
                 dsquery.match(dsattributes.kDSNAttrResourceType, "4", dsattributes.eDSExact),
                 dsquery.match(dsattributes.kDSNAttrResourceType, "5", dsattributes.eDSExact),
             ))
+            attrs.append(dsattributes.kDSNAttrResourceInfo)
         
         else:
             raise UnknownRecordTypeError("Unknown Open Directory record type: %s"
@@ -484,6 +502,16 @@
             else:
                 memberGUIDs = ()
 
+            # Special case for resources and locations
+            autoSchedule = False
+            proxyGUIDs = ()
+            if recordType in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
+                resourceInfo = value.get(dsattributes.kDSNAttrResourceInfo)
+                if resourceInfo is not None:
+                    autoSchedule, proxy = self._parseResourceInfo(resourceInfo)
+                    if proxy:
+                        proxyGUIDs = (proxy,)
+
             records[recordShortName] = OpenDirectoryRecord(
                 service               = self,
                 recordType            = recordType,
@@ -492,6 +520,8 @@
                 fullName              = realName,
                 calendarUserAddresses = cuaddrset,
                 memberGUIDs           = memberGUIDs,
+                autoSchedule          = autoSchedule,
+                proxyGUIDs            = proxyGUIDs,
             )
 
             #log.debug("Populated record: %s" % (records[recordShortName],))
@@ -529,7 +559,7 @@
     """
     Open Directory implementation of L{IDirectoryRecord}.
     """
-    def __init__(self, service, recordType, guid, shortName, fullName, calendarUserAddresses, memberGUIDs):
+    def __init__(self, service, recordType, guid, shortName, fullName, calendarUserAddresses, memberGUIDs, autoSchedule, proxyGUIDs):
         super(OpenDirectoryRecord, self).__init__(
             service               = service,
             recordType            = recordType,
@@ -537,8 +567,10 @@
             shortName             = shortName,
             fullName              = fullName,
             calendarUserAddresses = calendarUserAddresses,
+            autoSchedule          = autoSchedule,
         )
         self._memberGUIDs = tuple(memberGUIDs)
+        self._proxyGUIDs = tuple(proxyGUIDs)
 
     def members(self):
         if self.recordType != DirectoryService.recordType_groups:
@@ -556,6 +588,25 @@
             if self.guid in groupRecord._memberGUIDs:
                 yield groupRecord
 
+    def proxies(self):
+        if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
+            return
+
+        for guid in self._proxyGUIDs:
+            proxyRecord = self.service.recordWithGUID(guid)
+            if proxyRecord is None:
+                log.err("No record for proxy in %s with GUID %s" % (self.shortName, guid))
+            else:
+                yield proxyRecord
+
+    def proxyFor(self):
+        for proxyRecord in itertools.chain(
+                                  self.service.recordsForType(DirectoryService.recordType_resources).itervalues(),
+                                  self.service.recordsForType(DirectoryService.recordType_locations).itervalues()
+                              ):
+            if self.guid in proxyRecord._proxyGUIDs:
+                yield proxyRecord
+
     def verifyCredentials(self, credentials):
         if isinstance(credentials, UsernamePassword):
             try:

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/calendaruserproxy.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/calendaruserproxy.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -33,6 +33,7 @@
 from twisted.web2.dav.util import joinURL
 from twisted.web2.http import HTTPError, StatusResponse
 
+from twistedcaldav.config import config
 from twistedcaldav.extensions import DAVFile, DAVPrincipalResource
 from twistedcaldav.extensions import ReadOnlyWritePropertiesResourceMixIn
 from twistedcaldav.sql import AbstractSQLDatabase
@@ -56,6 +57,14 @@
             ),
         )
         
+        # Add admins
+        aces += tuple([davxml.ACE(
+                    davxml.Principal(davxml.HRef(principal)),
+                    davxml.Grant(davxml.Privilege(davxml.All())),
+                    davxml.Protected(),
+                 ) for principal in config.AdminPrincipals
+                ])
+
         return davxml.ACL(*aces)
 
     def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
@@ -121,7 +130,15 @@
         assert isinstance(property, davxml.WebDAVElement)
 
         if property.qname() == (dav_namespace, "group-member-set"):
-            return self.setGroupMemberSet(property, request)
+            if self.hasEditableMembership():
+                return self.setGroupMemberSet(property, request)
+            else:
+                raise HTTPError(
+                    StatusResponse(
+                        responsecode.FORBIDDEN,
+                        "Proxies cannot be changed."
+                    )
+                )
 
         return super(CalendarUserProxyPrincipalResource, self).writeProperty(property, request)
 
@@ -202,7 +219,7 @@
                 """Principal UID: %s\n"""          % (self.principalUID(),),
                 """Principal URL: %s\n"""          % (link(self.principalURL()),),
                 """\nAlternate URIs:\n"""          , format_list(self.alternateURIs()),
-                """\nGroup members:\n"""           , format_list(link(p.principalURL()) for p in self.groupMembers()),
+                """\nGroup members (%s):\n"""      % ({False:"Locked", True:"Editable"}[self.hasEditableMembership()]), format_list(link(p.principalURL()) for p in self.groupMembers()),
                 """\nGroup memberships:\n"""       , format_list(link(p.principalURL()) for p in self.groupMemberships()),
                 """</pre></blockquote></div>""",
                 output
@@ -237,16 +254,25 @@
         return self.parent.principalCollections()
 
     def groupMembers(self):
-        # Get member GUIDs and map to principal resources
-        members = self._index().getMembers(self.guid)
-        return [self.pcollection.principalForGUID(guid) for guid in members]
+        if self.hasEditableMembership():
+            # Get member GUIDs from database and map to principal resources
+            members = self._index().getMembers(self.guid)
+            return [self.pcollection.principalForGUID(guid) for guid in members]
+        else:
+            # Fixed proxies are only for read-write - the read-only list is empty
+            if self.proxyType == "calendar-proxy-write":
+                return self.parent.proxies()
+            else:
+                return ()
 
     def groupMemberships(self):
         # Get membership GUIDs and map to principal resources
         memberships = self._index().getMemberships(self.guid)
         return [self.pcollection.principalForGUID(guid) for guid in memberships]
 
-
+    def hasEditableMembership(self):
+        return self.parent.hasEditableProxyMembership()
+        
 class CalendarUserProxyDatabase(AbstractSQLDatabase):
     """
     A database to maintain calendar user proxy group memberships.

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/directory.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/directory.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -155,7 +155,7 @@
             self.fullName
         )
 
-    def __init__(self, service, recordType, guid, shortName, fullName, calendarUserAddresses):
+    def __init__(self, service, recordType, guid, shortName, fullName, calendarUserAddresses, autoSchedule):
         assert service.realmName is not None
         assert recordType
         assert shortName
@@ -171,6 +171,7 @@
         self.shortName             = shortName
         self.fullName              = fullName
         self.calendarUserAddresses = calendarUserAddresses
+        self.autoSchedule          = autoSchedule
 
     def __cmp__(self, other):
         if not isinstance(other, DirectoryRecord):
@@ -194,6 +195,15 @@
     def groups(self):
         return ()
 
+    def proxies(self):
+        return ()
+
+    def proxyFor(self):
+        return ()
+
+    def hasEditableProxyMembership(self):
+        return self.recordType in (DirectoryService.recordType_users, DirectoryService.recordType_groups)
+
     def verifyCredentials(self, credentials):
         return False
 

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/principal.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/principal.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -359,7 +359,7 @@
     def principalURL(self):
         return self._url
 
-    def _getRelatives(self, method, record=None, relatives=None, records=None):
+    def _getRelatives(self, method, record=None, relatives=None, records=None, proxy=False):
         if record is None:
             record = self.record
         if relatives is None:
@@ -373,9 +373,14 @@
             for relative in getattr(record, method)():
                 if relative not in records:
                     if relative.recordType == myRecordType: 
-                        relatives.add(self.parent.getChild(None, record=relative))
+                        found = self.parent.getChild(None, record=relative)
                     else:
-                        relatives.add(self.parent.parent.getChild(relative.recordType).getChild(None, record=relative))
+                        found = self.parent.parent.getChild(relative.recordType).getChild(None, record=relative)
+                    
+                    if proxy:
+                        found = found.getChild("calendar-proxy-write")
+                    relatives.add(found)
+
                     self._getRelatives(method, relative, relatives, records)
 
         return relatives
@@ -426,10 +431,15 @@
 
     def groupMemberships(self):
         groups = self._getRelatives("groups")
+
         if config.EnableProxyPrincipals:
+            # Get any directory specified proxies
+            groups.update(self._getRelatives("proxyFor", proxy=True))
+
             # Get proxy group GUIDs and map to principal resources
             proxies = self._map_calendar_user_proxy_guids(self._calendar_user_proxy_index().getMemberships(self.principalUID()))
             groups.update(proxies)
+
         return groups
 
     def principalCollections(self):
@@ -455,6 +465,15 @@
         
         return addresses
 
+    def autoSchedule(self):
+        return self.record.autoSchedule
+    
+    def proxies(self):
+        return self._getRelatives("proxies")
+
+    def hasEditableProxyMembership(self):
+        return self.record.hasEditableProxyMembership()
+
     def scheduleInbox(self, request):
         home = self._calendarHome()
         if home is None:

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/sqldb.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/sqldb.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/sqldb.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -101,8 +101,11 @@
                 
             # 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
+            yield shortName, guid, password, name, members, groups, calendarUserAddresses, autoSchedule
 
     def getRecord(self, recordType, shortName):
         # Get individual account record
@@ -126,8 +129,11 @@
             
         # 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()
@@ -313,6 +319,7 @@
                 members               = result[4],
                 groups                = result[5],
                 calendarUserAddresses = result[6],
+                autoSchedule          = result[7],
             )
 
     def recordWithShortName(self, recordType, shortName):
@@ -328,6 +335,7 @@
                 members               = result[4],
                 groups                = result[5],
                 calendarUserAddresses = result[6],
+                autoSchedule          = result[7],
             )
 
         return None
@@ -336,7 +344,7 @@
     """
     XML based implementation implementation of L{IDirectoryRecord}.
     """
-    def __init__(self, service, recordType, shortName, guid, password, name, members, groups, calendarUserAddresses):
+    def __init__(self, service, recordType, shortName, guid, password, name, members, groups, calendarUserAddresses, autoSchedule):
         super(SQLDirectoryRecord, self).__init__(
             service               = service,
             recordType            = recordType,
@@ -344,6 +352,7 @@
             shortName             = shortName,
             fullName              = name,
             calendarUserAddresses = calendarUserAddresses,
+            autoSchedule          = autoSchedule,
         )
 
         self.password = password

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/sudo.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/sudo.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/sudo.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -130,7 +130,8 @@
             guid=None,
             shortName=shortName,
             fullName=shortName,
-            calendarUserAddresses=set())
+            calendarUserAddresses=set(),
+            autoSchedule=False)
 
         self.password = entry['password']
 

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/accounts.xml
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/accounts.xml	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/accounts.xml	2007-07-02 20:29:35 UTC (rev 1635)
@@ -122,6 +122,10 @@
     <password>gemini</password>
     <name>Gemini Twelve</name>
     <cuaddr>mailto:gemini at example.com</cuaddr>
+    <auto-schedule/>
+    <proxies>
+      <member>wsanchez</member>
+    </proxies>
   </location>
   <location>
     <uid>apollo</uid>

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_opendirectory.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_opendirectory.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -68,7 +68,9 @@
                 "guidify",
                 "GUID",
                 set("mailtoguid at example.com",),
-                []
+                [],
+                False,
+                ()
             )
 
             digestFields = {}

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_opendirectoryschema.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_opendirectoryschema.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_opendirectoryschema.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -903,7 +903,7 @@
             dsattributes.kDSNAttrRecordName       : "computer1.apple.com",
             dsattributes.kDS1AttrXMLPlist         : PlistParse.plist_good,
             dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/127.0.0.1",
-       })
+        })
         record_good_other = ("computer2.apple.com", {
             dsattributes.kDS1AttrGeneratedUID     : "GUID1",
             dsattributes.kDSNAttrRecordName       : "computer2.apple.com",
@@ -989,3 +989,100 @@
 
             for recordlist, title, guid in records:
                 _doParseRecords(recordlist, title, guid)
+
+    class ODResourceInfoParse (twisted.trial.unittest.TestCase):
+
+        plist_good_false = """<?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>
+        <false/>
+        <key>Label</key>
+        <string>Location</string>
+        <key>CalendaringDelegate</key>
+        <string>1234-GUID-5678</string>
+    </dict>
+</dict>
+</plist>
+"""
+
+        plist_good_true = """<?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>
+    </dict>
+</dict>
+</plist>
+"""
+
+        plist_good_missing = """<?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>Label</key>
+        <string>Location</string>
+    </dict>
+</dict>
+</plist>
+"""
+
+        plist_bad = """<?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>
+"""
+
+        plist_wrong = """<?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.YellowPagesFramework</key>
+    <dict>
+        <key>AutoAcceptsInvitation</key>
+        <true/>
+        <key>Label</key>
+        <string>Location</string>
+        <key>CalendaringDelegate</key>
+        <string>1234-GUID-5678</string>
+    </dict>
+</dict>
+</plist>
+"""
+
+        test_bool = (
+            (plist_good_false, False, "1234-GUID-5678"),
+            (plist_good_true, True, ""),
+            (plist_good_missing, False, None),
+            (plist_wrong, False, None),
+        )
+
+        test_exception = (
+            (plist_bad, AttributeError),
+        )
+
+        def test_plists(self):
+            service = OpenDirectoryService(node="/Search", dosetup=False)
+            
+            for item in ODResourceInfoParse.test_bool:
+                self.assertEqual(service._parseResourceInfo(item[0])[0], item[1])
+                self.assertEqual(service._parseResourceInfo(item[0])[1], item[2])
+            
+            for item in ODResourceInfoParse.test_exception:
+                self.assertRaises(item[1], service._parseResourceInfo, item[0])

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_principal.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_principal.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -191,6 +191,17 @@
                 self.failIf(principal is None)
                 self.assertEquals(record, principal.record)
 
+    def test_autoSchedule(self):
+        """
+        DirectoryPrincipalProvisioningResource.principalForCalendarUserAddress()
+        """
+        for provisioningResource, recordType, recordResource, record in self._allRecords():
+            principal = provisioningResource.principalForRecord(record)
+            self.failIf(principal is None)
+            self.assertEquals(record.autoSchedule, principal.autoSchedule())
+            if record.shortName == "gemini":
+                self.assertTrue(principal.autoSchedule())
+
     # FIXME: Run DirectoryPrincipalProvisioningResource tests on DirectoryPrincipalTypeResource also
 
     ##
@@ -216,8 +227,16 @@
         DirectoryPrincipalResource.groupMemberships()
         """
         for provisioningResource, recordType, recordResource, record in self._allRecords():
-            self.failUnless(set(record.groups()).issubset(set(r.record for r in recordResource.groupMemberships())))
+            self.failUnless(set(record.groups()).issubset(set(r.record for r in recordResource.groupMemberships() if hasattr(r, "record"))))
 
+    def test_proxies(self):
+        """
+        DirectoryPrincipalResource.proxies()
+        """
+        for provisioningResource, recordType, recordResource, record in self._allRecords():
+            self.failUnless(set(record.proxies()).issubset(set(r.record for r in recordResource.proxies())))
+            self.assertEqual(record.hasEditableProxyMembership(), recordResource.hasEditableProxyMembership())
+
     def test_principalUID(self):
         """
         DirectoryPrincipalResource.principalUID()

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_xmlfile.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_xmlfile.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_xmlfile.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -113,3 +113,114 @@
                 set(r.shortName for r in service.listRecords(recordType)),
                 set(expectedRecords)
             )
+
+    def test_okAutoSchedule(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">
+  <location>
+    <uid>my office</uid>
+    <password>nimda</password>
+    <name>Super User</name>
+    <auto-schedule/>
+  </location>
+</accounts>
+"""
+        )
+        for recordType, expectedRecords in (
+            ( DirectoryService.recordType_users     , ()             ),
+            ( DirectoryService.recordType_groups    , ()             ),
+            ( DirectoryService.recordType_locations , ("my office",) ),
+            ( DirectoryService.recordType_resources , ()             ),
+        ):
+            self.assertEquals(
+                set(r.shortName for r in service.listRecords(recordType)),
+                set(expectedRecords)
+            )
+        self.assertTrue(service.recordWithShortName(DirectoryService.recordType_locations, "my office").autoSchedule)
+
+    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.shortName for r in service.listRecords(DirectoryService.recordType_users))
+
+        self.assertRaises(ValueError, _findRecords)
+
+    def test_okProxies(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>test</uid>
+    <password>nimda</password>
+    <name>Test</name>
+  </user>
+  <location>
+    <uid>my office</uid>
+    <password>nimda</password>
+    <name>Super User</name>
+    <auto-schedule/>
+    <proxies>
+        <member>test</member>
+    </proxies>
+  </location>
+</accounts>
+"""
+        )
+        for recordType, expectedRecords in (
+            ( DirectoryService.recordType_users     , ("test",)      ),
+            ( DirectoryService.recordType_groups    , ()             ),
+            ( DirectoryService.recordType_locations , ("my office",) ),
+            ( DirectoryService.recordType_resources , ()             ),
+        ):
+            self.assertEquals(
+                set(r.shortName 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)
+
+    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.shortName for r in service.listRecords(DirectoryService.recordType_users))
+
+        self.assertRaises(ValueError, _findRecords)

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlaccountsparser.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlaccountsparser.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlaccountsparser.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -44,6 +44,8 @@
 ELEMENT_MEMBERS      = "members"
 ELEMENT_MEMBER       = "member"
 ELEMENT_CUADDR       = "cuaddr"
+ELEMENT_AUTOSCHEDULE = "auto-schedule"
+ELEMENT_PROXIES      = "proxies"
 
 ATTRIBUTE_REALM      = "realm"
 ATTRIBUTE_REPEAT     = "repeat"
@@ -101,6 +103,13 @@
                 if item is not None:
                     item.groups.add(group.shortName)
 
+        def updateProxyFor(proxier):
+            # Update proxy membership
+            for recordType, shortName in proxier.proxies:
+                item = self.items[recordType].get(shortName, None)
+                if item is not None:
+                    item.proxyFor.add((proxier.recordType, proxier.shortName))
+
         for child in node._get_childNodes():
             child_name = child._get_localName()
             if child_name is None:
@@ -123,9 +132,11 @@
                     newprincipal = principal.repeat(i)
                     self.items[recordType][newprincipal.shortName] = newprincipal
                     updateMembership(newprincipal)
+                    updateProxyFor(newprincipal)
             else:
                 self.items[recordType][principal.shortName] = principal
                 updateMembership(principal)
+                updateProxyFor(principal)
 
 class XMLAccountRecord (object):
     """
@@ -143,6 +154,9 @@
         self.members = set()
         self.groups = set()
         self.calendarUserAddresses = set()
+        self.autoSchedule = False
+        self.proxies = set()
+        self.proxyFor = set()
 
     def repeat(self, ctr):
         """
@@ -175,6 +189,8 @@
         result.name = name
         result.members = self.members
         result.calendarUserAddresses = calendarUserAddresses
+        result.autoSchedule = self.autoSchedule
+        result.proxies = self.proxies
         return result
 
     def parseXML(self, node):
@@ -195,14 +211,24 @@
                 if child.firstChild is not None:
                     self.name = child.firstChild.data.encode("utf-8")
             elif child_name == ELEMENT_MEMBERS:
-                self._parseMembers(child)
+                self._parseMembers(child, self.members)
             elif child_name == ELEMENT_CUADDR:
                 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_PROXIES:
+                # 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._parseMembers(child, self.proxies)
             else:
                 raise RuntimeError("Unknown account attribute: %s" % (child_name,))
 
-    def _parseMembers(self, node):
+    def _parseMembers(self, node, addto):
         for child in node._get_childNodes():
             if child._get_localName() == ELEMENT_MEMBER:
                 if child.hasAttribute(ATTRIBUTE_RECORDTYPE):
@@ -210,4 +236,4 @@
                 else:
                     recordType = DirectoryService.recordType_users
                 if child.firstChild is not None:
-                    self.members.add((recordType, child.firstChild.data.encode("utf-8")))
+                    addto.add((recordType, child.firstChild.data.encode("utf-8")))

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlfile.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlfile.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -107,12 +107,15 @@
             guid                  = xmlPrincipal.guid,
             shortName             = shortName,
             fullName              = xmlPrincipal.name,
-            calendarUserAddresses = xmlPrincipal.calendarUserAddresses
+            calendarUserAddresses = xmlPrincipal.calendarUserAddresses,
+            autoSchedule          = xmlPrincipal.autoSchedule,
         )
 
-        self.password = xmlPrincipal.password
-        self._members = xmlPrincipal.members
-        self._groups  = xmlPrincipal.groups
+        self.password     = xmlPrincipal.password
+        self._members     = xmlPrincipal.members
+        self._groups      = xmlPrincipal.groups
+        self._proxies     = xmlPrincipal.proxies
+        self._proxyFor    = xmlPrincipal.proxyFor
 
     def members(self):
         for recordType, shortName in self._members:
@@ -122,6 +125,14 @@
         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):
+        for recordType, shortName in self._proxyFor:
+            yield self.service.recordWithShortName(recordType, shortName)
+
     def verifyCredentials(self, credentials):
         if isinstance(credentials, UsernamePassword):
             return credentials.password == self.password

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/schedule.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/schedule.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/schedule.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -35,10 +35,9 @@
 from twisted.web2.dav import davxml
 from twisted.web2.dav.http import ErrorResponse, errorForFailure, messageForFailure, statusForFailure
 from twisted.web2.dav.resource import AccessDeniedError
-from twisted.web2.dav.util import joinURL, parentForURL
+from twisted.web2.dav.util import joinURL
 
 from twistedcaldav import caldavxml
-from twistedcaldav import customxml
 from twistedcaldav import itip
 from twistedcaldav.resource import CalDAVResource
 from twistedcaldav.caldavxml import caldav_namespace, TimeRange
@@ -481,8 +480,8 @@
                         # Store CALDAV:schedule-state property
                         child.writeDeadProperty(caldavxml.ScheduleState(caldavxml.NotProcessed()))
                     
-                        # Look for auto-respond option
-                        if inbox.hasDeadProperty(customxml.TwistedScheduleAutoRespond):
+                        # Look for auto-schedule option
+                        if principal.autoSchedule():
                             autoresponses.append((principal, inbox, child))
                     except:
                         log.err("Could not store data in Inbox : %s" % (inbox,))

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/static.py	2007-06-29 22:01:47 UTC (rev 1634)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/static.py	2007-07-02 20:29:35 UTC (rev 1635)
@@ -575,12 +575,6 @@
 
     def provision(self):
         if self.provisionFile():
-            # FIXME: This should probably be a directory record option that
-            # maps to the property value directly without the need to store one.
-            if self.parent.record.recordType in (DirectoryService.recordType_locations, DirectoryService.recordType_resources,):
-                # Resources should have autorespond turned on by default,
-                # since they typically don't have someone responding for them.
-                self.writeDeadProperty(customxml.TwistedScheduleAutoRespond())
 
             # Initialize CTag on the calendar collection
             self.updateCTag()

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


More information about the calendarserver-changes mailing list