[CalendarServer-changes] [5095] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri Feb 12 08:12:34 PST 2010


Revision: 5095
          http://trac.macosforge.org/projects/calendarserver/changeset/5095
Author:   sagen at apple.com
Date:     2010-02-12 08:12:33 -0800 (Fri, 12 Feb 2010)
Log Message:
-----------
Landing branch with support for reading/writing resource "extra" data.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tools/gateway.py
    CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
    CalendarServer/trunk/twistedcaldav/directory/aggregate.py
    CalendarServer/trunk/twistedcaldav/directory/cachingdirectory.py
    CalendarServer/trunk/twistedcaldav/directory/directory.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_modify.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py
    CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py
    CalendarServer/trunk/twistedcaldav/directory/xmlfile.py

Property Changed:
----------------
    CalendarServer/trunk/


Property changes on: CalendarServer/trunk
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
   + /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093

Modified: CalendarServer/trunk/calendarserver/tools/gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/gateway.py	2010-02-12 03:44:52 UTC (rev 5094)
+++ CalendarServer/trunk/calendarserver/tools/gateway.py	2010-02-12 16:12:33 UTC (rev 5095)
@@ -125,6 +125,24 @@
     reactor.run()
 
 
+attrMap = {
+    'GeneratedUID' : { 'attr' : 'guid', },
+    'RealName' : { 'attr' : 'fullName', },
+    'RecordName' : { 'attr' : 'shortNames', },
+    'Comment' : { 'extras' : True, 'attr' : 'comment', },
+    'Description' : { 'extras' : True, 'attr' : 'description', },
+    'Type' : { 'extras' : True, 'attr' : 'type', },
+    'Capacity' : { 'extras' : True, 'attr' : 'capacity', },
+    'Building' : { 'extras' : True, 'attr' : 'building', },
+    'Floor' : { 'extras' : True, 'attr' : 'floor', },
+    'Street' : { 'extras' : True, 'attr' : 'street', },
+    'City' : { 'extras' : True, 'attr' : 'city', },
+    'State' : { 'extras' : True, 'attr' : 'state', },
+    'ZIP' : { 'extras' : True, 'attr' : 'zip', },
+    'Country' : { 'extras' : True, 'attr' : 'country', },
+    'Phone' : { 'extras' : True, 'attr' : 'phone', },
+}
+
 class Runner(object):
 
     def __init__(self, directory, commands):
@@ -160,16 +178,48 @@
 
     def command_createLocation(self, command):
 
+        kwargs = {}
+        for key, info in attrMap.iteritems():
+            if command.has_key(key):
+                kwargs[info['attr']] = command[key]
         try:
-            self.dir.createRecord("locations", guid=command['GeneratedUID'],
-                shortNames=command['RecordName'], fullName=command['RealName'])
+            self.dir.createRecord("locations", **kwargs)
         except DirectoryError, e:
             abort(str(e))
         respondWithRecordsOfType(self.dir, command, "locations")
 
+    def command_getLocationAttributes(self, command):
+        guid = command['GeneratedUID']
+        record = self.dir.recordWithGUID(guid)
+        recordDict = recordToDict(record)
+        # principal = principalForPrincipalID(guid, directory=self.dir)
+        # recordDict['AutoSchedule'] = principal.getAutoSchedule()
+        respond(command, recordDict)
+
+    def command_setLocationAttributes(self, command):
+
+        kwargs = {}
+        for key, info in attrMap.iteritems():
+            if command.has_key(key):
+                kwargs[info['attr']] = command[key]
+        try:
+            self.dir.updateRecord("locations", **kwargs)
+        except DirectoryError, e:
+            abort(str(e))
+
+        # principal = principalForPrincipalID(command['GeneratedUID'],
+        #     directory=self.dir)
+        # principal.setAutoSchedule(command.get('AutoSchedule', False))
+
+        respondWithRecordsOfType(self.dir, command, "locations")
+
     def command_deleteLocation(self, command):
+        kwargs = {}
+        for key, info in attrMap.iteritems():
+            if command.has_key(key):
+                kwargs[info['attr']] = command[key]
         try:
-            self.dir.destroyRecord("locations", guid=command['GeneratedUID'])
+            self.dir.destroyRecord("locations", **kwargs)
         except DirectoryError, e:
             abort(str(e))
         respondWithRecordsOfType(self.dir, command, "locations")
@@ -180,16 +230,48 @@
         respondWithRecordsOfType(self.dir, command, "resources")
 
     def command_createResource(self, command):
+        kwargs = {}
+        for key, info in attrMap.iteritems():
+            if command.has_key(key):
+                kwargs[info['attr']] = command[key]
         try:
-            self.dir.createRecord("resources", guid=command['GeneratedUID'],
-                shortNames=command['RecordName'], fullName=command['RealName'])
+            self.dir.createRecord("resources", **kwargs)
         except DirectoryError, e:
             abort(str(e))
         respondWithRecordsOfType(self.dir, command, "resources")
 
+    def command_getResourceAttributes(self, command):
+        guid = command['GeneratedUID']
+        record = self.dir.recordWithGUID(guid)
+        recordDict = recordToDict(record)
+        # principal = principalForPrincipalID(guid, directory=self.dir)
+        # recordDict['AutoSchedule'] = principal.getAutoSchedule()
+        respond(command, recordDict)
+
+    def command_setResourceAttributes(self, command):
+
+        kwargs = {}
+        for key, info in attrMap.iteritems():
+            if command.has_key(key):
+                kwargs[info['attr']] = command[key]
+        try:
+            self.dir.updateRecord("resources", **kwargs)
+        except DirectoryError, e:
+            abort(str(e))
+
+        # principal = principalForPrincipalID(command['GeneratedUID'],
+        #     directory=self.dir)
+        # principal.setAutoSchedule(command.get('AutoSchedule', False))
+
+        respondWithRecordsOfType(self.dir, command, "resources")
+
     def command_deleteResource(self, command):
+        kwargs = {}
+        for key, info in attrMap.iteritems():
+            if command.has_key(key):
+                kwargs[info['attr']] = command[key]
         try:
-            self.dir.destroyRecord("resources", guid=command['GeneratedUID'])
+            self.dir.destroyRecord("resources", **kwargs)
         except DirectoryError, e:
             abort(str(e))
         respondWithRecordsOfType(self.dir, command, "resources")
@@ -271,15 +353,24 @@
     })
 
 
+def recordToDict(record):
+    recordDict = {}
+    for key, info in attrMap.iteritems():
+        try:
+            if info.get('extras', False):
+                value = record.extras[info['attr']]
+            else:
+                value = getattr(record, info['attr'])
+            recordDict[key] = value
+        except KeyError:
+            pass
+    return recordDict
+
 def respondWithRecordsOfType(directory, command, recordType):
     result = []
     for record in directory.listRecords(recordType):
-        result.append( {
-            'GeneratedUID' : record.guid,
-            'RecordName' : [n for n in record.shortNames],
-            'RealName' : record.fullName,
-            'AutoSchedule' : record.autoSchedule,
-        } )
+        recordDict = recordToDict(record)
+        result.append(recordDict)
     respond(command, result)
 
 def respond(command, result):

Modified: CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_gateway.py	2010-02-12 03:44:52 UTC (rev 5094)
+++ CalendarServer/trunk/calendarserver/tools/test/test_gateway.py	2010-02-12 16:12:33 UTC (rev 5095)
@@ -185,27 +185,81 @@
         self.assertEquals(len(results['result']), 10)
 
     @inlineCallbacks
+    def test_getLocationAttributes(self):
+        results = yield self.runCommand(command_createLocation)
+        results = yield self.runCommand(command_getLocationAttributes)
+        self.assertEquals(results['result']['Building'], "Test Building")
+        self.assertEquals(results['result']['City'], "Cupertino")
+        self.assertEquals(results['result']['Capacity'], "40")
+        self.assertEquals(results['result']['Description'], "Test Description")
+        self.assertEquals(results['result']['ZIP'], "95014")
+        self.assertEquals(results['result']['Floor'], "First")
+        self.assertEquals(results['result']['RecordName'], ['createdlocation01'])
+        self.assertEquals(results['result']['State'], "CA")
+        self.assertEquals(results['result']['Street'], "1 Infinite Loop")
+        self.assertEquals(results['result']['RealName'], "Created Location 01")
+        self.assertEquals(results['result']['Comment'], "Test Comment")
+
+    @inlineCallbacks
     def test_getResourceList(self):
         results = yield self.runCommand(command_getResourceList)
         self.assertEquals(len(results['result']), 10)
 
     @inlineCallbacks
+    def test_getResourceAttributes(self):
+        results = yield self.runCommand(command_createResource)
+        results = yield self.runCommand(command_getResourceAttributes)
+        self.assertEquals(results['result']['Comment'], "Test Comment")
+
+    @inlineCallbacks
     def test_createLocation(self):
         directory = getDirectory()
 
-        record = directory.recordWithUID("createdlocation01")
+        record = directory.recordWithUID("836B1B66-2E9A-4F46-8B1C-3DD6772C20B2")
         self.assertEquals(record, None)
 
         yield self.runCommand(command_createLocation)
 
         directory.flushCaches()
-        record = directory.recordWithUID("createdlocation01")
+        record = directory.recordWithUID("836B1B66-2E9A-4F46-8B1C-3DD6772C20B2")
         self.assertNotEquals(record, None)
 
+        self.assertEquals(record.extras['comment'], "Test Comment")
+        self.assertEquals(record.extras['building'], "Test Building")
+        self.assertEquals(record.extras['floor'], "First")
+        self.assertEquals(record.extras['capacity'], "40")
+        self.assertEquals(record.extras['street'], "1 Infinite Loop")
+        self.assertEquals(record.extras['city'], "Cupertino")
+        self.assertEquals(record.extras['state'], "CA")
+        self.assertEquals(record.extras['zip'], "95014")
+        self.assertEquals(record.extras['country'], "USA")
+        self.assertEquals(record.extras['phone'], "(408) 555-1212")
+
     @inlineCallbacks
-    def test_destroyRecord(self):
+    def test_setLocationAttributes(self):
         directory = getDirectory()
 
+        yield self.runCommand(command_createLocation)
+        record = directory.recordWithUID("836B1B66-2E9A-4F46-8B1C-3DD6772C20B2")
+        yield self.runCommand(command_setLocationAttributes)
+        directory.flushCaches()
+        record = directory.recordWithUID("836B1B66-2E9A-4F46-8B1C-3DD6772C20B2")
+
+        self.assertEquals(record.extras['comment'], "Updated Test Comment")
+        self.assertEquals(record.extras['building'], "Updated Test Building")
+        self.assertEquals(record.extras['floor'], "Second")
+        self.assertEquals(record.extras['capacity'], "41")
+        self.assertEquals(record.extras['street'], "2 Infinite Loop")
+        self.assertEquals(record.extras['city'], "Updated Cupertino")
+        self.assertEquals(record.extras['state'], "Updated CA")
+        self.assertEquals(record.extras['zip'], "95015")
+        self.assertEquals(record.extras['country'], "Updated USA")
+        self.assertEquals(record.extras['phone'], "(408) 555-1213")
+
+    @inlineCallbacks
+    def test_destroyLocation(self):
+        directory = getDirectory()
+
         record = directory.recordWithUID("location01")
         self.assertNotEquals(record, None)
 
@@ -216,6 +270,46 @@
         self.assertEquals(record, None)
 
     @inlineCallbacks
+    def test_createResource(self):
+        directory = getDirectory()
+
+        record = directory.recordWithUID("AF575A61-CFA6-49E1-A0F6-B5662C9D9801")
+        self.assertEquals(record, None)
+
+        yield self.runCommand(command_createResource)
+
+        directory.flushCaches()
+        record = directory.recordWithUID("AF575A61-CFA6-49E1-A0F6-B5662C9D9801")
+        self.assertNotEquals(record, None)
+
+    @inlineCallbacks
+    def test_setResourceAttributes(self):
+        directory = getDirectory()
+
+        yield self.runCommand(command_createResource)
+        record = directory.recordWithUID("AF575A61-CFA6-49E1-A0F6-B5662C9D9801")
+        self.assertEquals(record.fullName, "Laptop 1")
+
+        yield self.runCommand(command_setResourceAttributes)
+
+        directory.flushCaches()
+        record = directory.recordWithUID("AF575A61-CFA6-49E1-A0F6-B5662C9D9801")
+        self.assertEquals(record.fullName, "Updated Laptop 1")
+
+    @inlineCallbacks
+    def test_destroyResource(self):
+        directory = getDirectory()
+
+        record = directory.recordWithUID("resource01")
+        self.assertNotEquals(record, None)
+
+        yield self.runCommand(command_deleteResource)
+
+        directory.flushCaches()
+        record = directory.recordWithUID("resource01")
+        self.assertEquals(record, None)
+
+    @inlineCallbacks
     def test_addWriteProxy(self):
         results = yield self.runCommand(command_addWriteProxy)
         self.assertEquals(len(results['result']['Proxies']), 1)
@@ -228,18 +322,6 @@
 
 
 
-command_deleteLocation = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-        <key>command</key>
-        <string>deleteLocation</string>
-        <key>GeneratedUID</key>
-        <string>guidoffice3</string>
-</dict>
-</plist>
-"""
-
 command_addReadProxy = """<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
@@ -277,17 +359,40 @@
         <key>AutoSchedule</key>
         <true/>
         <key>GeneratedUID</key>
-        <string>createdlocation01</string>
+        <string>836B1B66-2E9A-4F46-8B1C-3DD6772C20B2</string>
         <key>RealName</key>
         <string>Created Location 01</string>
         <key>RecordName</key>
         <array>
                 <string>createdlocation01</string>
         </array>
+        <key>Comment</key>
+        <string>Test Comment</string>
+        <key>Description</key>
+        <string>Test Description</string>
+        <key>Building</key>
+        <string>Test Building</string>
+        <key>Floor</key>
+        <string>First</string>
+        <key>Capacity</key>
+        <string>40</string>
+        <key>Street</key>
+        <string>1 Infinite Loop</string>
+        <key>City</key>
+        <string>Cupertino</string>
+        <key>State</key>
+        <string>CA</string>
+        <key>ZIP</key>
+        <string>95014</string>
+        <key>Country</key>
+        <string>USA</string>
+        <key>Phone</key>
+        <string>(408) 555-1212</string>
 </dict>
 </plist>
 """
 
+
 command_createResource = """<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
@@ -297,13 +402,15 @@
         <key>AutoSchedule</key>
         <true/>
         <key>GeneratedUID</key>
-        <string>guidlaptop1</string>
+        <string>AF575A61-CFA6-49E1-A0F6-B5662C9D9801</string>
         <key>RealName</key>
         <string>Laptop 1</string>
         <key>RecordName</key>
         <array>
                 <string>laptop1</string>
         </array>
+        <key>Comment</key>
+        <string>Test Comment</string>
 </dict>
 </plist>
 """
@@ -327,7 +434,7 @@
         <key>command</key>
         <string>deleteResource</string>
         <key>GeneratedUID</key>
-        <string>guidlaptop1</string>
+        <string>resource01</string>
 </dict>
 </plist>
 """
@@ -403,3 +510,89 @@
 </dict>
 </plist>
 """
+
+command_setLocationAttributes = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>setLocationAttributes</string>
+        <key>AutoSchedule</key>
+        <false/>
+        <key>GeneratedUID</key>
+        <string>836B1B66-2E9A-4F46-8B1C-3DD6772C20B2</string>
+        <key>RealName</key>
+        <string>Updated Location 01</string>
+        <key>RecordName</key>
+        <array>
+                <string>createdlocation01</string>
+        </array>
+        <key>Comment</key>
+        <string>Updated Test Comment</string>
+        <key>Description</key>
+        <string>Updated Test Description</string>
+        <key>Building</key>
+        <string>Updated Test Building</string>
+        <key>Floor</key>
+        <string>Second</string>
+        <key>Capacity</key>
+        <string>41</string>
+        <key>Street</key>
+        <string>2 Infinite Loop</string>
+        <key>City</key>
+        <string>Updated Cupertino</string>
+        <key>State</key>
+        <string>Updated CA</string>
+        <key>ZIP</key>
+        <string>95015</string>
+        <key>Country</key>
+        <string>Updated USA</string>
+        <key>Phone</key>
+        <string>(408) 555-1213</string>
+</dict>
+</plist>
+"""
+
+command_getLocationAttributes = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>getLocationAttributes</string>
+        <key>GeneratedUID</key>
+        <string>836B1B66-2E9A-4F46-8B1C-3DD6772C20B2</string>
+</dict>
+</plist>
+"""
+
+command_setResourceAttributes = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>setResourceAttributes</string>
+        <key>AutoSchedule</key>
+        <false/>
+        <key>GeneratedUID</key>
+        <string>AF575A61-CFA6-49E1-A0F6-B5662C9D9801</string>
+        <key>RealName</key>
+        <string>Updated Laptop 1</string>
+        <key>RecordName</key>
+        <array>
+                <string>laptop1</string>
+        </array>
+</dict>
+</plist>
+"""
+
+command_getResourceAttributes = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>command</key>
+        <string>getResourceAttributes</string>
+        <key>GeneratedUID</key>
+        <string>AF575A61-CFA6-49E1-A0F6-B5662C9D9801</string>
+</dict>
+</plist>
+"""

Modified: CalendarServer/trunk/twistedcaldav/directory/aggregate.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/aggregate.py	2010-02-12 03:44:52 UTC (rev 5094)
+++ CalendarServer/trunk/twistedcaldav/directory/aggregate.py	2010-02-12 16:12:33 UTC (rev 5095)
@@ -192,25 +192,26 @@
 
     def createRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
         fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None, password=None, **kwds):
+        uid=None, password=None, **kwargs):
         service = self.serviceForRecordType(recordType)
         return service.createRecord(recordType, guid=guid,
             shortNames=shortNames, authIDs=authIDs, fullName=fullName,
             firstName=firstName, lastName=lastName,
-            emailAddresses=emailAddresses, uid=uid, password=password, **kwds)
+            emailAddresses=emailAddresses, uid=uid, password=password, **kwargs)
 
-    def updateRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+    def updateRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
         fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None, password=None, **kwds):
+        uid=None, password=None, **kwargs):
         service = self.serviceForRecordType(recordType)
-        return service.updateRecord(recordType, guid, shortNames=shortNames,
+        return service.updateRecord(recordType, guid=guid,
+            shortNames=shortNames,
             authIDs=authIDs, fullName=fullName, firstName=firstName,
             lastName=lastName, emailAddresses=emailAddresses, uid=uid,
-            password=password, **kwds)
+            password=password, **kwargs)
 
-    def destroyRecord(self, recordType, guid):
+    def destroyRecord(self, recordType, guid=None):
         service = self.serviceForRecordType(recordType)
-        return service.destroyRecord(recordType, guid)
+        return service.destroyRecord(recordType, guid=guid)
 
 class DuplicateRecordTypeError(DirectoryError):
     """

Modified: CalendarServer/trunk/twistedcaldav/directory/cachingdirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/cachingdirectory.py	2010-02-12 03:44:52 UTC (rev 5094)
+++ CalendarServer/trunk/twistedcaldav/directory/cachingdirectory.py	2010-02-12 16:12:33 UTC (rev 5095)
@@ -357,12 +357,12 @@
         self, service, recordType, guid,
         shortNames=(), authIDs=set(),
         fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None,
+        uid=None, **kwargs
     ):
         super(CachingDirectoryRecord, self).__init__(
-            service               = service,
-            recordType            = recordType,
-            guid                  = guid,
+            service,
+            recordType,
+            guid,
             shortNames            = shortNames,
             authIDs               = authIDs,
             fullName              = fullName,
@@ -370,6 +370,7 @@
             lastName              = lastName,
             emailAddresses        = emailAddresses,
             uid                   = uid,
+            **kwargs
         )
         
         self.cachedTime = time.time()

Modified: CalendarServer/trunk/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/directory.py	2010-02-12 03:44:52 UTC (rev 5094)
+++ CalendarServer/trunk/twistedcaldav/directory/directory.py	2010-02-12 16:12:33 UTC (rev 5095)
@@ -299,21 +299,21 @@
 
     def createRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
         fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None, password=None, **kwds):
+        uid=None, password=None, **kwargs):
         """
         Create/persist a directory record based on the given values
         """
         raise NotImplementedError("Subclass must implement createRecord")
 
-    def updateRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+    def updateRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
         fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None, password=None, **kwds):
+        uid=None, password=None, **kwargs):
         """
         Update/persist a directory record based on the given values
         """
         raise NotImplementedError("Subclass must implement updateRecord")
 
-    def destroyRecord(self, recordType, guid):
+    def destroyRecord(self, recordType, guid=None):
         """
         Remove a directory record from the directory
         """
@@ -342,6 +342,7 @@
         calendarUserAddresses=set(), autoSchedule=False, enabledForCalendaring=None,
         enabledForAddressBooks=None,
         uid=None,
+        **kwargs
     ):
         assert service.realmName is not None
         assert recordType
@@ -356,23 +357,25 @@
         if fullName is None:
             fullName = ""
 
-        self.service               = service
-        self.recordType            = recordType
-        self.guid                  = guid
-        self.uid                   = uid
-        self.enabled               = False
-        self.hostedAt              = ""
-        self.shortNames            = shortNames
-        self.authIDs               = authIDs
-        self.fullName              = fullName
-        self.firstName             = firstName
-        self.lastName              = lastName
-        self.emailAddresses        = emailAddresses
-        self.enabledForCalendaring = enabledForCalendaring
-        self.autoSchedule          = autoSchedule
+        self.service                = service
+        self.recordType             = recordType
+        self.guid                   = guid
+        self.uid                    = uid
+        self.enabled                = False
+        self.hostedAt               = ""
+        self.shortNames             = shortNames
+        self.authIDs                = authIDs
+        self.fullName               = fullName
+        self.firstName              = firstName
+        self.lastName               = lastName
+        self.emailAddresses         = emailAddresses
+        self.enabledForCalendaring  = enabledForCalendaring
+        self.autoSchedule           = autoSchedule
         self.enabledForAddressBooks = enabledForAddressBooks
+        self.extras                 = kwargs
 
 
+
     def get_calendarUserAddresses(self):
         """
         Dynamically construct a calendarUserAddresses attribute which describes

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_modify.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_modify.py	2010-02-12 03:44:52 UTC (rev 5094)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_modify.py	2010-02-12 16:12:33 UTC (rev 5095)
@@ -52,13 +52,19 @@
         record = directory.recordWithUID("resource01")
         self.assertEquals(record, None)
 
-        directory.createRecord("resources", "resource01", shortNames=("resource01",), uid="resource01")
+        directory.createRecord("resources", guid="resource01",
+            shortNames=("resource01",), uid="resource01",
+            emailAddresses=("res1 at example.com", "res2 at example.com"),
+            comment="Test Comment")
 
         record = directory.recordWithUID("resource01")
         self.assertNotEquals(record, None)
 
-        directory.createRecord("resources", "resource02", shortNames=("resource02",), uid="resource02")
+        self.assertEquals(len(record.emailAddresses), 2)
+        self.assertEquals(record.extras['comment'], "Test Comment")
 
+        directory.createRecord("resources", guid="resource02", shortNames=("resource02",), uid="resource02")
+
         record = directory.recordWithUID("resource02")
         self.assertNotEquals(record, None)
 
@@ -74,12 +80,12 @@
         record = directory.recordWithUID("resource01")
         self.assertEquals(record, None)
 
-        directory.createRecord("resources", "resource01", shortNames=("resource01",), uid="resource01")
+        directory.createRecord("resources", guid="resource01", shortNames=("resource01",), uid="resource01")
 
         record = directory.recordWithUID("resource01")
         self.assertNotEquals(record, None)
 
-        directory.destroyRecord("resources", "resource01")
+        directory.destroyRecord("resources", guid="resource01")
 
         record = directory.recordWithUID("resource01")
         self.assertEquals(record, None)
@@ -91,17 +97,18 @@
     def test_updateRecord(self):
         directory = getDirectory()
 
-        directory.createRecord("resources", "resource01",
+        directory.createRecord("resources", guid="resource01",
             shortNames=("resource01",), uid="resource01",
             fullName="Resource number 1")
 
         record = directory.recordWithUID("resource01")
         self.assertEquals(record.fullName, "Resource number 1")
 
-        directory.updateRecord("resources", "resource01",
+        directory.updateRecord("resources", guid="resource01",
             shortNames=("resource01", "r01"), uid="resource01",
             fullName="Resource #1", firstName="First", lastName="Last",
-            emailAddresses=("resource01 at example.com", "r01 at example.com"))
+            emailAddresses=("resource01 at example.com", "r01 at example.com"),
+            comment="Test Comment")
 
         record = directory.recordWithUID("resource01")
         self.assertEquals(record.fullName, "Resource #1")
@@ -110,6 +117,7 @@
         self.assertEquals(set(record.shortNames), set(["resource01", "r01"]))
         self.assertEquals(record.emailAddresses,
             set(["resource01 at example.com", "r01 at example.com"]))
+        self.assertEquals(record.extras['comment'], "Test Comment")
 
         # Make sure old records are still there:
         record = directory.recordWithUID("location01")
@@ -118,5 +126,5 @@
     def test_createDuplicateRecord(self):
         directory = getDirectory()
 
-        directory.createRecord("resources", "resource01", shortNames=("resource01",), uid="resource01")
-        self.assertRaises(DirectoryError, directory.createRecord, "resources", "resource01", shortNames=("resource01",), uid="resource01")
+        directory.createRecord("resources", guid="resource01", shortNames=("resource01",), uid="resource01")
+        self.assertRaises(DirectoryError, directory.createRecord, "resources", guid="resource01", shortNames=("resource01",), uid="resource01")

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py	2010-02-12 03:44:52 UTC (rev 5094)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py	2010-02-12 16:12:33 UTC (rev 5095)
@@ -229,6 +229,71 @@
         self.assertFalse(service.recordWithShortName(DirectoryService.recordType_groups, "disabled").enabledForCalendaring)
 
 
+    def test_readExtras(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>
+    <guid>myoffice</guid>
+    <name>My Office</name>
+    <extras>
+        <comment>This is the comment</comment>
+        <capacity>40</capacity>
+    </extras>
+  </location>
+</accounts>
+"""
+        )
+        
+        record = service.recordWithShortName(
+            DirectoryService.recordType_locations, "my office")
+        self.assertEquals(record.guid, "myoffice")
+        self.assertEquals(record.extras["comment"], "This is the comment")
+        self.assertEquals(record.extras["capacity"], "40")
+
+    def test_writeExtras(self):
+        service = self.service()
+
+        service.createRecord(DirectoryService.recordType_locations, "newguid",
+            shortNames=("New office",),
+            fullName="My New Office",
+            address="1 Infinite Loop, Cupertino, CA",
+            capacity="10",
+            comment="Test comment",
+        )
+
+        record = service.recordWithShortName(
+            DirectoryService.recordType_locations, "New office")
+        self.assertEquals(record.extras["comment"], "Test comment")
+        self.assertEquals(record.extras["capacity"], "10")
+
+
+        service.updateRecord(DirectoryService.recordType_locations, "newguid",
+            shortNames=("New office",),
+            fullName="My Newer Office",
+            address="2 Infinite Loop, Cupertino, CA",
+            capacity="20",
+            comment="Test comment updated",
+        )
+
+        record = service.recordWithShortName(
+            DirectoryService.recordType_locations, "New office")
+        self.assertEquals(record.fullName, "My Newer Office")
+        self.assertEquals(record.extras["address"], "2 Infinite Loop, Cupertino, CA")
+        self.assertEquals(record.extras["comment"], "Test comment updated")
+        self.assertEquals(record.extras["capacity"], "20")
+
+        service.destroyRecord(DirectoryService.recordType_locations, "newguid")
+
+        record = service.recordWithShortName(
+            DirectoryService.recordType_locations, "New office")
+        self.assertEquals(record, None)
+
+
 class XMLFileSubset (XMLFileBase, TestCase):
     """
     Test the recordTypes subset feature of XMLFile service.

Modified: CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py	2010-02-12 03:44:52 UTC (rev 5094)
+++ CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py	2010-02-12 16:12:33 UTC (rev 5095)
@@ -48,6 +48,7 @@
 ELEMENT_EMAIL_ADDRESS     = "email-address"
 ELEMENT_MEMBERS           = "members"
 ELEMENT_MEMBER            = "member"
+ELEMENT_EXTRAS            = "extras"
 
 ATTRIBUTE_REALM           = "realm"
 ATTRIBUTE_REPEAT          = "repeat"
@@ -156,6 +157,7 @@
         self.emailAddresses = set()
         self.members = set()
         self.groups = set()
+        self.extras = {}
 
     def repeat(self, ctr):
         """
@@ -205,6 +207,7 @@
         result.lastName = lastName
         result.emailAddresses = emailAddresses
         result.members = self.members
+        result.extras = self.extras
         return result
 
     def parseXML(self, node):
@@ -237,6 +240,8 @@
                     self.emailAddresses.add(child.firstChild.data.encode("utf-8").lower())
             elif child_name == ELEMENT_MEMBERS:
                 self._parseMembers(child, self.members)
+            elif child_name == ELEMENT_EXTRAS:
+                self._parseExtras(child, self.extras)
             else:
                 raise RuntimeError("Unknown account attribute: %s" % (child_name,))
 
@@ -252,3 +257,10 @@
                     recordType = DirectoryService.recordType_users
                 if child.firstChild is not None:
                     addto.add((recordType, child.firstChild.data.encode("utf-8")))
+
+    def _parseExtras(self, node, addto):
+        for child in node._get_childNodes():
+            key = child._get_localName()
+            if key:
+                value = child.firstChild.data.encode("utf-8")
+                addto[key.encode("utf-8")] = value

Modified: CalendarServer/trunk/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/xmlfile.py	2010-02-12 03:44:52 UTC (rev 5094)
+++ CalendarServer/trunk/twistedcaldav/directory/xmlfile.py	2010-02-12 16:12:33 UTC (rev 5095)
@@ -276,6 +276,10 @@
         ET.SubElement(element, "last-name").text = principal.lastName
         for value in principal.emailAddresses:
             ET.SubElement(element, "email-address").text = value
+        if principal.extras:
+            extrasElement = ET.SubElement(element, "extras")
+            for key, value in principal.extras.iteritems():
+                ET.SubElement(extrasElement, key).text = value
 
         return element
 
@@ -311,7 +315,7 @@
 
     def createRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
         fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None, password=None, **kwds):
+        uid=None, password=None, **kwargs):
         """
         Create and persist a record using the provided information.  In this
         XML-based implementation, the xml accounts are read in and converted
@@ -341,12 +345,13 @@
         xmlPrincipal.firstName = firstName
         xmlPrincipal.lastName = lastName
         xmlPrincipal.emailAddresses = emailAddresses
+        xmlPrincipal.extras = kwargs
         self._addElement(accountsElement, xmlPrincipal)
 
         self._persistRecords(accountsElement)
 
 
-    def destroyRecord(self, recordType, guid):
+    def destroyRecord(self, recordType, guid=None):
         """
         Remove the record matching guid.  In this XML-based implementation,
         the xml accounts are read in and those not matching the given guid are
@@ -368,9 +373,9 @@
         self._persistRecords(accountsElement)
 
 
-    def updateRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+    def updateRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
         fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None, password=None, **kwds):
+        uid=None, password=None, **kwargs):
         """
         Update the record matching guid.  In this XML-based implementation,
         the xml accounts are read in and converted to elementtree elements.
@@ -394,6 +399,7 @@
                     xmlPrincipal.firstName = firstName
                     xmlPrincipal.lastName = lastName
                     xmlPrincipal.emailAddresses = emailAddresses
+                    xmlPrincipal.extras = kwargs
                     self._addElement(accountsElement, xmlPrincipal)
                 else:
                     self._addElement(accountsElement, xmlPrincipal)
@@ -415,6 +421,7 @@
             firstName             = xmlPrincipal.firstName,
             lastName              = xmlPrincipal.lastName,
             emailAddresses        = xmlPrincipal.emailAddresses,
+            **xmlPrincipal.extras
         )
 
         self.password          = xmlPrincipal.password
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100212/3b1c55ec/attachment-0001.html>


More information about the calendarserver-changes mailing list