[CalendarServer-changes] [5094] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu Feb 11 19:44:52 PST 2010


Revision: 5094
          http://trac.macosforge.org/projects/calendarserver/changeset/5094
Author:   cdaboo at apple.com
Date:     2010-02-11 19:44:52 -0800 (Thu, 11 Feb 2010)
Log Message:
-----------
guid -> uid in augments. Create a default augments file if none present. Add some useful
ElementTree functions.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tools/loadaugmentdb.py
    CalendarServer/trunk/calendarserver/tools/manageaugments.py
    CalendarServer/trunk/calendarserver/tools/test/gateway/augments.xml
    CalendarServer/trunk/conf/auth/augments-default.xml
    CalendarServer/trunk/conf/auth/augments-test.xml
    CalendarServer/trunk/conf/auth/augments.dtd
    CalendarServer/trunk/twistedcaldav/directory/augment.py
    CalendarServer/trunk/twistedcaldav/directory/test/augments-test-default.xml
    CalendarServer/trunk/twistedcaldav/directory/test/augments-test.xml
    CalendarServer/trunk/twistedcaldav/directory/test/augments.xml
    CalendarServer/trunk/twistedcaldav/directory/test/modify/augments.xml
    CalendarServer/trunk/twistedcaldav/directory/test/resources/augments.xml
    CalendarServer/trunk/twistedcaldav/directory/test/test_augment.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_cachedirectory.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py
    CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py

Added Paths:
-----------
    CalendarServer/trunk/twistedcaldav/xmlutil.py

Modified: CalendarServer/trunk/calendarserver/tools/loadaugmentdb.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/loadaugmentdb.py	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/calendarserver/tools/loadaugmentdb.py	2010-02-12 03:44:52 UTC (rev 5094)
@@ -160,23 +160,23 @@
 def run(dbxml):
     
     try:
-        guids = set((yield augment.AugmentService.getAllGUIDs()))
+        uids = set((yield augment.AugmentService.getAllUIDs()))
         added = 0
         updated = 0
         removed = 0
         if dbxml:
             for record in dbxml.db.values():
-                yield augment.AugmentService.addAugmentRecord(record, record.guid in guids)
-                if record.guid in guids:
+                yield augment.AugmentService.addAugmentRecord(record, record.uid in uids)
+                if record.uid in uids:
                     updated += 1
                 else:
                     added += 1
-            for guid in guids.difference(dbxml.db.keys()):
-                yield augment.AugmentService.removeAugmentRecord(guid)
+            for uid in uids.difference(dbxml.db.keys()):
+                yield augment.AugmentService.removeAugmentRecord(uid)
                 removed += 1
         else:
             yield augment.AugmentService.clean()
-            removed = len(guids)
+            removed = len(uids)
             
         print "Changes:"
         print "  Added: %d" % (added,)

Modified: CalendarServer/trunk/calendarserver/tools/manageaugments.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/manageaugments.py	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/calendarserver/tools/manageaugments.py	2010-02-12 03:44:52 UTC (rev 5094)
@@ -17,57 +17,23 @@
 
 from optparse import OptionParser
 from twistedcaldav.directory import xmlaugmentsparser
-from xml.etree.ElementTree import ElementTree, tostring, SubElement
-from xml.parsers.expat import ExpatError
+from xml.etree.ElementTree import tostring
 import sys
 import os
+from twistedcaldav.xmlutil import readXML, addSubElement, writeXML
 
 def error(s):
     print s
     sys.exit(1)
 
-def readXML(xmlfile):
+def doAdd(xmlfile, uid, host, enable_calendar, auto_schedule):
 
-    # Read in XML
     try:
-        tree = ElementTree(file=xmlfile)
-    except ExpatError, e:
-        error("Unable to parse file '%s' because: %s" % (xmlfile, e,))
+        _ignore_etree, augments_node = readXML(xmlfile)
+    except ValueError, e:
+        error("Could not read XML file: %s" % (e,))
 
-    # Verify that top-level element is correct
-    augments_node = tree.getroot()
-    if augments_node.tag != xmlaugmentsparser.ELEMENT_AUGMENTS:
-        error("Ignoring file '%s' because it is not a augments file" % (xmlfile,))
-
-    return augments_node
-
-def writeXML(xmlfile, root):
-    
-    data = """<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE augments SYSTEM "augments.dtd">
-
-""" + tostring(root)
-
-    with open(xmlfile, "w") as f:
-        f.write(data)
-
-def addSubElement(parent, tag, text=None, indent=0):
-    
-    child = SubElement(parent, tag)
-    child.text = text
-    child.tail = "\n" + " " * indent
-    return child
-
-def changeSubElementText(parent, tag, text):
-    
-    child = parent.find(tag)
-    child.text = text
-
-def doAdd(xmlfile, guid, host, enable_calendar, auto_schedule):
-
-    augments_node = readXML(xmlfile)
-
-    # Make sure GUID is not already present
+    # Make sure UID is not already present
     for child in augments_node.getchildren():
         
         if child.tag != xmlaugmentsparser.ELEMENT_RECORD:
@@ -75,28 +41,31 @@
 
         for node in child.getchildren():
             
-            if node.tag == xmlaugmentsparser.ELEMENT_GUID and node.text == guid:
-                error("Cannot add guid '%s' because it already exists in augment file: '%s'" % (guid, xmlfile,))
+            if node.tag == xmlaugmentsparser.ELEMENT_UID and node.text == uid:
+                error("Cannot add uid '%s' because it already exists in augment file: '%s'" % (uid, xmlfile,))
     
     # Create new record
     if len(augments_node.getchildren()):
         augments_node.getchildren()[-1].tail = "\n  "
-    record = addSubElement(augments_node, xmlaugmentsparser.ELEMENT_RECORD, "\n    ")
-    addSubElement(record, xmlaugmentsparser.ELEMENT_GUID, guid, 4)
-    addSubElement(record, xmlaugmentsparser.ELEMENT_ENABLE, "true", 4)
-    addSubElement(record, xmlaugmentsparser.ELEMENT_HOSTEDAT, host, 4)
-    addSubElement(record, xmlaugmentsparser.ELEMENT_ENABLECALENDAR, "true" if enable_calendar else "false", 4)
-    addSubElement(record, xmlaugmentsparser.ELEMENT_AUTOSCHEDULE, "true" if auto_schedule else "false", 2)
+    record = addSubElement(augments_node, xmlaugmentsparser.ELEMENT_RECORD)
+    addSubElement(record, xmlaugmentsparser.ELEMENT_UID, uid)
+    addSubElement(record, xmlaugmentsparser.ELEMENT_ENABLE, "true")
+    addSubElement(record, xmlaugmentsparser.ELEMENT_HOSTEDAT, host)
+    addSubElement(record, xmlaugmentsparser.ELEMENT_ENABLECALENDAR, "true" if enable_calendar else "false")
+    addSubElement(record, xmlaugmentsparser.ELEMENT_AUTOSCHEDULE, "true" if auto_schedule else "false")
     
     # Modify xmlfile
     writeXML(xmlfile, augments_node)
-    print "Added guid '%s' in augment file: '%s'" % (guid, xmlfile,)
+    print "Added uid '%s' in augment file: '%s'" % (uid, xmlfile,)
     
-def doModify(xmlfile, guid, host, enable_calendar, auto_schedule):
+def doModify(xmlfile, uid, host, enable_calendar, auto_schedule):
 
-    augments_node = readXML(xmlfile)
+    try:
+        _ignore_etree, augments_node = readXML(xmlfile)
+    except ValueError, e:
+        error("Could not read XML file: %s" % (e,))
 
-    # Make sure GUID is present
+    # Make sure UID is present
     for child in augments_node.getchildren():
         
         if child.tag != xmlaugmentsparser.ELEMENT_RECORD:
@@ -104,13 +73,13 @@
 
         for node in child.getchildren():
             
-            if node.tag == xmlaugmentsparser.ELEMENT_GUID and node.text == guid:
+            if node.tag == xmlaugmentsparser.ELEMENT_UID and node.text == uid:
                 break
         else:
             continue
         break
     else:
-        error("Cannot modify guid '%s' because it does not exist in augment file: '%s'" % (guid, xmlfile,))
+        error("Cannot modify uid '%s' because it does not exist in augment file: '%s'" % (uid, xmlfile,))
     
     # Modify record
     if host is not None:
@@ -120,13 +89,16 @@
     
     # Modify xmlfile
     writeXML(xmlfile, augments_node)
-    print "Modified guid '%s' in augment file: '%s'" % (guid, xmlfile,)
+    print "Modified uid '%s' in augment file: '%s'" % (uid, xmlfile,)
 
-def doRemove(xmlfile, guid):
+def doRemove(xmlfile, uid):
 
-    augments_node = readXML(xmlfile)
+    try:
+        _ignore_etree, augments_node = readXML(xmlfile)
+    except ValueError, e:
+        error("Could not read XML file: %s" % (e,))
 
-    # Make sure GUID is present
+    # Make sure UID is present
     for child in augments_node.getchildren():
         
         if child.tag != xmlaugmentsparser.ELEMENT_RECORD:
@@ -134,23 +106,26 @@
 
         for node in child.getchildren():
             
-            if node.tag == xmlaugmentsparser.ELEMENT_GUID and node.text == guid:
+            if node.tag == xmlaugmentsparser.ELEMENT_UID and node.text == uid:
                 break
         else:
             continue
         augments_node.remove(child)
         break
     else:
-        error("Cannot remove guid '%s' because it does not exist in augment file: '%s'" % (guid, xmlfile,))
+        error("Cannot remove uid '%s' because it does not exist in augment file: '%s'" % (uid, xmlfile,))
     
     # Modify xmlfile
     writeXML(xmlfile, augments_node)
-    print "Removed guid '%s' from augment file: '%s'" % (guid, xmlfile,)
+    print "Removed uid '%s' from augment file: '%s'" % (uid, xmlfile,)
     
 def doPrint(xmlfile):
 
     # Read in XML
-    augments_node = readXML(xmlfile)
+    try:
+        _ignore_etree, augments_node = readXML(xmlfile)
+    except ValueError, e:
+        error("Could not read XML file: %s" % (e,))
 
     print tostring(augments_node)
 
@@ -173,45 +148,47 @@
 
     parser.add_option("-f", "--file", dest="xmlfilename",
                       help="XML augment file to manipulate", metavar="FILE")
-    parser.add_option("-g", "--guid", dest="guid",
-                      help="OD GUID to manipulate", metavar="GUID")
-    parser.add_option("-i", "--guidfile", dest="guidfile",
-                      help="File containing a list of GUIDs to manipulate", metavar="GUIDFILE")
+    parser.add_option("-u", "--uid", dest="uid",
+                      help="OD GUID to manipulate", metavar="UID")
+    parser.add_option("-i", "--uidfile", dest="uidfile",
+                      help="File containing a list of GUIDs to manipulate", metavar="UIDFILE")
     parser.add_option("-n", "--node", dest="node",
-                      help="Partition node to assign to GUID", metavar="NODE")
+                      help="Partition node to assign to UID", metavar="NODE")
     parser.add_option("-c", "--enable-calendar", action="store_true", dest="enable_calendar",
-                      default=True, help="Enable calendaring for this GUID: %default")
+                      default=True, help="Enable calendaring for this UID: %default")
     parser.add_option("-a", "--auto-schedule", action="store_true", dest="auto_schedule",
-                      default=False, help="Enable auto-schedule for this GUID: %default")
+                      default=False, help="Enable auto-schedule for this UID: %default")
 
     (options, args) = parser.parse_args()
 
     if len(args) != 1:
         parser.error("incorrect number of arguments")
 
-    guids = []
-    if options.guid:
-        guids.append(options.guid)
-    elif options.guidfile:
-        if not os.path.exists(options.guidfile):
-            parser.error("File containing list of GUIDs does not exist")
-        with open(options.guidfile) as f:
+    uids = []
+    if options.uid:
+        uids.append(options.uid)
+    elif options.uidfile:
+        if not os.path.exists(options.uidfile):
+            parser.error("File containing list of UIDs does not exist")
+        with open(options.uidfile) as f:
             for line in f:
-                guids.append(line[:-1])
+                uids.append(line[:-1])
         
     if args[0] == "add":
         if not options.node:
             parser.error("Partition node must be specified when adding")
-        for guid in guids:
-            doAdd(options.xmlfilename, guid, options.node, options.enable_calendar, options.auto_schedule)
+        for uid in uids:
+            doAdd(options.xmlfilename, uid, options.node, options.enable_calendar, options.auto_schedule)
     elif args[0] == "modify":
-        for guid in guids:
-            doModify(options.xmlfilename, guid, options.node, options.enable_calendar, options.auto_schedule)
+        for uid in uids:
+            doModify(options.xmlfilename, uid, options.node, options.enable_calendar, options.auto_schedule)
     elif args[0] == "remove":
-        for guid in guids:
-            doRemove(options.xmlfilename, guid)
+        for uid in uids:
+            doRemove(options.xmlfilename, uid)
     elif args[0] == "print":
         doPrint(options.xmlfilename)
+    else:
+        parser.error("Unknown argument")
 
 if __name__ == '__main__':
     main()

Modified: CalendarServer/trunk/calendarserver/tools/test/gateway/augments.xml
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/gateway/augments.xml	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/calendarserver/tools/test/gateway/augments.xml	2010-02-12 03:44:52 UTC (rev 5094)
@@ -20,17 +20,17 @@
 
 <augments>
   <record>
-    <guid>user01</guid>
+    <uid>user01</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>user02</guid>
+    <uid>user02</uid>
     <enable>false</enable>
     <enable-calendar>false</enable-calendar>
   </record>
   <record>
-    <guid>location01</guid>
+    <uid>location01</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>

Modified: CalendarServer/trunk/conf/auth/augments-default.xml
===================================================================
--- CalendarServer/trunk/conf/auth/augments-default.xml	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/conf/auth/augments-default.xml	2010-02-12 03:44:52 UTC (rev 5094)
@@ -20,7 +20,7 @@
 
 <augments>
   <record>
-    <guid>Default</guid>
+    <uid>Default</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>

Modified: CalendarServer/trunk/conf/auth/augments-test.xml
===================================================================
--- CalendarServer/trunk/conf/auth/augments-test.xml	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/conf/auth/augments-test.xml	2010-02-12 03:44:52 UTC (rev 5094)
@@ -20,31 +20,31 @@
 
 <augments>
   <record>
-    <guid>Default</guid>
+    <uid>Default</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
     <enable-addressbook>true</enable-addressbook>
   </record>
   <record repeat="10">
-    <guid>location%02d</guid>
+    <uid>location%02d</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
     <enable-addressbook>true</enable-addressbook>
     <auto-schedule>true</auto-schedule>
   </record>
   <record repeat="10">
-    <guid>resource%02d</guid>
+    <uid>resource%02d</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
     <enable-addressbook>true</enable-addressbook>
     <auto-schedule>true</auto-schedule>
   </record>
   <record repeat="4">
-    <guid>group%02d</guid>
+    <uid>group%02d</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>disabledgroup</guid>
+    <uid>disabledgroup</uid>
     <enable>false</enable>
   </record>
 </augments>

Modified: CalendarServer/trunk/conf/auth/augments.dtd
===================================================================
--- CalendarServer/trunk/conf/auth/augments.dtd	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/conf/auth/augments.dtd	2010-02-12 03:44:52 UTC (rev 5094)
@@ -19,7 +19,7 @@
   <!ELEMENT record (guid, enable, hosted-at?, enable-calendar?, auto-schedule?)>
     <!ATTLIST record repeat CDATA "1">
 
-  <!ELEMENT guid              (#PCDATA)>
+  <!ELEMENT uid               (#PCDATA)>
   <!ELEMENT enable            (#PCDATA)>
   <!ELEMENT hosted-at         (#PCDATA)>
   <!ELEMENT enable-calendar   (#PCDATA)>

Modified: CalendarServer/trunk/twistedcaldav/directory/augment.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/augment.py	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/twistedcaldav/directory/augment.py	2010-02-12 03:44:52 UTC (rev 5094)
@@ -24,6 +24,10 @@
 from twistedcaldav.database import AbstractADBAPIDatabase, ADBAPISqliteMixin,\
     ADBAPIPostgreSQLMixin
 from twistedcaldav.directory.xmlaugmentsparser import XMLAugmentsParser
+import os
+from twistedcaldav.directory import xmlaugmentsparser
+from twistedcaldav.xmlutil import newElementTreeWithRoot, addSubElement,\
+    writeXML
 
 
 log = Logger()
@@ -35,14 +39,14 @@
 
     def __init__(
         self,
-        guid,
+        uid,
         enabled=False,
         hostedAt="",
         enabledForCalendaring=False,
         autoSchedule=False,
         enabledForAddressBooks=False,
     ):
-        self.guid = guid
+        self.uid = uid
         self.enabled = enabled
         self.hostedAt = hostedAt
         self.enabledForCalendaring = enabledForCalendaring
@@ -58,41 +62,41 @@
         pass
     
     @inlineCallbacks
-    def getAugmentRecord(self, guid):
+    def getAugmentRecord(self, uid):
         """
-        Get an AugmentRecord for the specified GUID or the default.
+        Get an AugmentRecord for the specified UID or the default.
 
-        @param guid: directory GUID to lookup
-        @type guid: C{str}
+        @param uid: directory UID to lookup
+        @type uid: C{str}
         
         @return: L{Deferred}
         """
         
-        result = (yield self._lookupAugmentRecord(guid))
+        result = (yield self._lookupAugmentRecord(uid))
         if result is None:
             if not hasattr(self, "_defaultRecord"):
                 self._defaultRecord = (yield self._lookupAugmentRecord("Default"))
             if self._defaultRecord is not None:
                 result = copy.deepcopy(self._defaultRecord)
-                result.guid = guid
+                result.uid = uid
         returnValue(result)
 
     @inlineCallbacks
-    def getAllGUIDs(self):
+    def getAllUIDs(self):
         """
-        Get all AugmentRecord GUIDs.
+        Get all AugmentRecord UIDs.
 
         @return: L{Deferred}
         """
         
         raise NotImplementedError("Child class must define this.")
 
-    def _lookupAugmentRecord(self, guid):
+    def _lookupAugmentRecord(self, uid):
         """
-        Get an AugmentRecord for the specified GUID.
+        Get an AugmentRecord for the specified UID.
 
-        @param guid: directory GUID to lookup
-        @type guid: C{str}
+        @param uid: directory UID to lookup
+        @type uid: C{str}
         
         @return: L{Deferred}
         """
@@ -120,6 +124,28 @@
         self.lastCached = 0
         self.db = {}
         
+        # Preflight existence of files
+        missing = list()
+        for xmlFile in self.xmlFiles:
+            if not os.path.exists(xmlFile):
+                missing.append(xmlFile)
+                
+        # For each missing one create an empty xml file
+        if missing:
+            # If all files are missing, then create one augment file that defaults
+            # to all records being enabled
+            doDefault = (len(missing) == len(self.xmlFiles))
+            for missedFile in missing:
+                
+                _ignore_etree, root = newElementTreeWithRoot(xmlaugmentsparser.ELEMENT_AUGMENTS)
+                if doDefault:
+                    record = addSubElement(root, xmlaugmentsparser.ELEMENT_RECORD)
+                    addSubElement(record, xmlaugmentsparser.ELEMENT_UID, "Default")
+                    addSubElement(record, xmlaugmentsparser.ELEMENT_ENABLE, "true")
+                    addSubElement(record, xmlaugmentsparser.ELEMENT_ENABLECALENDAR, "true")
+                    doDefault = False
+                writeXML(missedFile, root)
+            
         try:
             self.db = self._parseXML()
         except RuntimeError:
@@ -129,21 +155,21 @@
         self.lastCached = time.time()
 
     @inlineCallbacks
-    def getAllGUIDs(self):
+    def getAllUIDs(self):
         """
-        Get all AugmentRecord GUIDs.
+        Get all AugmentRecord UIDs.
 
         @return: L{Deferred}
         """
         
         return succeed(self.db.keys())
 
-    def _lookupAugmentRecord(self, guid):
+    def _lookupAugmentRecord(self, uid):
         """
-        Get an AugmentRecord for the specified GUID.
+        Get an AugmentRecord for the specified UID.
 
-        @param guid: directory GUID to lookup
-        @type guid: C{str}
+        @param uid: directory UID to lookup
+        @type uid: C{str}
         
         @return: L{Deferred}
         """
@@ -152,7 +178,7 @@
         if self.lastCached + self.cacheTimeout <= time.time():
             self.refresh()
             
-        return succeed(self.db.get(guid))
+        return succeed(self.db.get(uid))
 
     def refresh(self):
         """
@@ -191,37 +217,37 @@
         AbstractADBAPIDatabase.__init__(self, dbID, dbapiName, dbapiArgs, True, **kwargs)
         
     @inlineCallbacks
-    def getAllGUIDs(self):
+    def getAllUIDs(self):
         """
-        Get all AugmentRecord GUIDs.
+        Get all AugmentRecord UIDs.
 
         @return: L{Deferred}
         """
         
         # Query for the record information
-        results = (yield self.queryList("select GUID from AUGMENTS", ()))
+        results = (yield self.queryList("select UID from AUGMENTS", ()))
         returnValue(results)
 
     @inlineCallbacks
-    def _lookupAugmentRecord(self, guid):
+    def _lookupAugmentRecord(self, uid):
         """
-        Get an AugmentRecord for the specified GUID.
+        Get an AugmentRecord for the specified UID.
 
-        @param guid: directory GUID to lookup
-        @type guid: C{str}
+        @param uid: directory UID to lookup
+        @type uid: C{str}
 
         @return: L{Deferred}
         """
         
         # Query for the record information
-        results = (yield self.query("select GUID, ENABLED, PARTITIONID, CALENDARING, AUTOSCHEDULE from AUGMENTS where GUID = :1", (guid,)))
+        results = (yield self.query("select UID, ENABLED, PARTITIONID, CALENDARING, AUTOSCHEDULE from AUGMENTS where UID = :1", (uid,)))
         if not results:
             returnValue(None)
         else:
-            guid, enabled, partitionid, enabdledForCalendaring, autoSchedule = results[0]
+            uid, enabled, partitionid, enabdledForCalendaring, autoSchedule = results[0]
             
             record = AugmentRecord(
-                guid = guid,
+                uid = uid,
                 enabled = enabled == "T",
                 hostedAt = (yield self._getPartition(partitionid)),
                 enabledForCalendaring = enabdledForCalendaring == "T",
@@ -238,24 +264,24 @@
         if update:
             yield self.execute(
                 """update AUGMENTS set
-                (GUID, ENABLED, PARTITIONID, CALENDARING, AUTOSCHEDULE) =
-                (:1, :2, :3, :4, :5) where GUID = :6""",
+                (UID, ENABLED, PARTITIONID, CALENDARING, AUTOSCHEDULE) =
+                (:1, :2, :3, :4, :5) where UID = :6""",
                 (
-                    record.guid,
+                    record.uid,
                     "T" if record.enabled else "F",
                     partitionid,
                     "T" if record.enabledForCalendaring else "F",
                     "T" if record.autoSchedule else "F",
-                    record.guid,
+                    record.uid,
                 )
             )
         else:
             yield self.execute(
                 """insert into AUGMENTS
-                (GUID, ENABLED, PARTITIONID, CALENDARING, AUTOSCHEDULE)
+                (UID, ENABLED, PARTITIONID, CALENDARING, AUTOSCHEDULE)
                 values (:1, :2, :3, :4, :5)""",
                 (
-                    record.guid,
+                    record.uid,
                     "T" if record.enabled else "F",
                     partitionid,
                     "T" if record.enabledForCalendaring else "F",
@@ -263,9 +289,9 @@
                 )
             )
 
-    def removeAugmentRecord(self, guid):
+    def removeAugmentRecord(self, uid):
 
-        return self.query("delete from AUGMENTS where GUID = :1", (guid,))
+        return self.query("delete from AUGMENTS where UID = :1", (uid,))
 
     @inlineCallbacks
     def _getPartitionID(self, hostedat, createIfMissing=True):
@@ -318,7 +344,7 @@
         # TESTTYPE table
         #
         yield self._create_table("AUGMENTS", (
-            ("GUID",         "text unique"),
+            ("UID",          "text unique"),
             ("ENABLED",      "text(1)"),
             ("PARTITIONID",  "text"),
             ("CALENDARING",  "text(1)"),

Modified: CalendarServer/trunk/twistedcaldav/directory/test/augments-test-default.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/augments-test-default.xml	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/twistedcaldav/directory/test/augments-test-default.xml	2010-02-12 03:44:52 UTC (rev 5094)
@@ -20,41 +20,41 @@
 
 <augments>
   <record>
-    <guid>Default</guid>
+    <uid>Default</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
     <hosted-at>00001</hosted-at>
   </record>
   <record>
-    <guid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</guid>
+    <uid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>6423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
+    <uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>5A985493-EE2C-4665-94CF-4DFEA3A89500</guid>
+    <uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
     <enable>false</enable>
   </record>
   <record>
-    <guid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</guid>
+    <uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</uid>
     <enable>true</enable>
     <enable-calendar>false</enable-calendar>
   </record>
   <record>
-    <guid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</guid>
+    <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
     <enable>true</enable>
     <hosted-at>00001</hosted-at>
   </record>
   <record>
-    <guid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</guid>
+    <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
     <enable>true</enable>
     <hosted-at>00002</hosted-at>
   </record>
   <record>
-    <guid>6A73326A-F781-47E7-A9F8-AF47364D4152</guid>
+    <uid>6A73326A-F781-47E7-A9F8-AF47364D4152</uid>
     <enable>true</enable>
     <hosted-at>00002</hosted-at>
     <enable>true</enable>

Modified: CalendarServer/trunk/twistedcaldav/directory/test/augments-test.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/augments-test.xml	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/twistedcaldav/directory/test/augments-test.xml	2010-02-12 03:44:52 UTC (rev 5094)
@@ -20,35 +20,35 @@
 
 <augments>
   <record>
-    <guid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</guid>
+    <uid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>6423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
+    <uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>5A985493-EE2C-4665-94CF-4DFEA3A89500</guid>
+    <uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
     <enable>false</enable>
   </record>
   <record>
-    <guid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</guid>
+    <uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</uid>
     <enable>true</enable>
     <enable-calendar>false</enable-calendar>
   </record>
   <record>
-    <guid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</guid>
+    <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
     <enable>true</enable>
     <hosted-at>00001</hosted-at>
   </record>
   <record>
-    <guid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</guid>
+    <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
     <enable>true</enable>
     <hosted-at>00002</hosted-at>
   </record>
   <record>
-    <guid>6A73326A-F781-47E7-A9F8-AF47364D4152</guid>
+    <uid>6A73326A-F781-47E7-A9F8-AF47364D4152</uid>
     <enable>true</enable>
     <hosted-at>00002</hosted-at>
     <enable>true</enable>

Modified: CalendarServer/trunk/twistedcaldav/directory/test/augments.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/augments.xml	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/twistedcaldav/directory/test/augments.xml	2010-02-12 03:44:52 UTC (rev 5094)
@@ -20,108 +20,108 @@
 
 <augments realm="Test">
   <record>
-    <guid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</guid>
+    <uid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>6423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
+    <uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>5A985493-EE2C-4665-94CF-4DFEA3A89500</guid>
+    <uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</guid>
+    <uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</guid>
+    <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</guid>
+    <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
     <enable>true</enable>
     <enable-calendar>false</enable-calendar>
   </record>
   <record repeat="2">
-    <guid>user%02d</guid>
+    <uid>user%02d</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</guid>
+    <uid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>admin</guid>
+    <uid>admin</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>grunts</guid>
+    <uid>grunts</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>right_coast</guid>
+    <uid>right_coast</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>left_coast</guid>
+    <uid>left_coast</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>both_coasts</guid>
+    <uid>both_coasts</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>recursive1_coasts</guid>
+    <uid>recursive1_coasts</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>recursive2_coasts</guid>
+    <uid>recursive2_coasts</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>non_calendar_group</guid>
+    <uid>non_calendar_group</uid>
     <enable>true</enable>
   </record>
   <record>
-    <guid>mercury</guid>
+    <uid>mercury</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>gemini</guid>
+    <uid>gemini</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>apollo</guid>
+    <uid>apollo</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>orion</guid>
+    <uid>orion</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>transporter</guid>
+    <uid>transporter</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>ftlcpu</guid>
+    <uid>ftlcpu</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>non_calendar_proxy</guid>
+    <uid>non_calendar_proxy</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>

Modified: CalendarServer/trunk/twistedcaldav/directory/test/modify/augments.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/modify/augments.xml	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/twistedcaldav/directory/test/modify/augments.xml	2010-02-12 03:44:52 UTC (rev 5094)
@@ -20,12 +20,12 @@
 
 <augments>
   <record>
-    <guid>user01</guid>
+    <uid>user01</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>user02</guid>
+    <uid>user02</uid>
     <enable>false</enable>
     <enable-calendar>false</enable-calendar>
   </record>

Modified: CalendarServer/trunk/twistedcaldav/directory/test/resources/augments.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/resources/augments.xml	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/twistedcaldav/directory/test/resources/augments.xml	2010-02-12 03:44:52 UTC (rev 5094)
@@ -20,23 +20,23 @@
 
 <augments>
   <record>
-    <guid>user01</guid>
+    <uid>user01</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
   </record>
   <record>
-    <guid>user02</guid>
+    <uid>user02</uid>
     <enable>false</enable>
     <enable-calendar>false</enable-calendar>
   </record>
   <record>
-    <guid>resource01</guid>
+    <uid>resource01</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
     <auto-schedule>true</auto-schedule>
   </record>
   <record>
-    <guid>resource02</guid>
+    <uid>resource02</uid>
     <enable>false</enable>
     <enable-calendar>false</enable-calendar>
     <auto-schedule>false</auto-schedule>

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_augment.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_augment.py	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_augment.py	2010-02-12 03:44:52 UTC (rev 5094)
@@ -26,32 +26,32 @@
 xmlFileDefault = os.path.join(os.path.dirname(__file__), "augments-test-default.xml")
 
 testRecords = (
-    {"guid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "enabled":True,  "hostedAt":"", "enabledForCalendaring":False, "autoSchedule":False},
-    {"guid":"6423F94A-6B76-4A3A-815B-D52CFD77935D", "enabled":True,  "hostedAt":"", "enabledForCalendaring":True, "autoSchedule":False},
-    {"guid":"5A985493-EE2C-4665-94CF-4DFEA3A89500", "enabled":False, "hostedAt":"", "enabledForCalendaring":False, "autoSchedule":False},
-    {"guid":"8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "enabled":True,  "hostedAt":"", "enabledForCalendaring":False, "autoSchedule":False},
-    {"guid":"5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "enabled":True,  "hostedAt":"00001", "enabledForCalendaring":False, "autoSchedule":False},
-    {"guid":"543D28BA-F74F-4D5F-9243-B3E3A61171E5", "enabled":True,  "hostedAt":"00002", "enabledForCalendaring":False, "autoSchedule":False},
-    {"guid":"6A73326A-F781-47E7-A9F8-AF47364D4152", "enabled":True,  "hostedAt":"00002", "enabledForCalendaring":True, "autoSchedule":True},
+    {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "enabled":True,  "hostedAt":"", "enabledForCalendaring":False, "autoSchedule":False},
+    {"uid":"6423F94A-6B76-4A3A-815B-D52CFD77935D", "enabled":True,  "hostedAt":"", "enabledForCalendaring":True, "autoSchedule":False},
+    {"uid":"5A985493-EE2C-4665-94CF-4DFEA3A89500", "enabled":False, "hostedAt":"", "enabledForCalendaring":False, "autoSchedule":False},
+    {"uid":"8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "enabled":True,  "hostedAt":"", "enabledForCalendaring":False, "autoSchedule":False},
+    {"uid":"5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "enabled":True,  "hostedAt":"00001", "enabledForCalendaring":False, "autoSchedule":False},
+    {"uid":"543D28BA-F74F-4D5F-9243-B3E3A61171E5", "enabled":True,  "hostedAt":"00002", "enabledForCalendaring":False, "autoSchedule":False},
+    {"uid":"6A73326A-F781-47E7-A9F8-AF47364D4152", "enabled":True,  "hostedAt":"00002", "enabledForCalendaring":True, "autoSchedule":True},
 )
 
-testRecordDefault = {"guid":"A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True,  "hostedAt":"00001", "enabledForCalendaring":True, "autoSchedule":False}
+testRecordDefault = {"uid":"A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True,  "hostedAt":"00001", "enabledForCalendaring":True, "autoSchedule":False}
 
 class AugmentTests(TestCase):
 
     @inlineCallbacks
     def _checkRecord(self, db, items):
         
-        record = (yield db.getAugmentRecord(items["guid"]))
+        record = (yield db.getAugmentRecord(items["uid"]))
         self.assertTrue(record is not None)
         
         for k,v in items.iteritems():
             self.assertEqual(getattr(record, k), v)
 
     @inlineCallbacks
-    def _checkNoRecord(self, db, guid):
+    def _checkNoRecord(self, db, uid):
         
-        record = (yield db.getAugmentRecord(guid))
+        record = (yield db.getAugmentRecord(uid))
         self.assertTrue(record is None)
 
 class AugmentXMLTests(AugmentTests):
@@ -99,7 +99,7 @@
 """), db)
         self.assertRaises(RuntimeError, XMLAugmentsParser, cStringIO.StringIO("""<?xml version="1.0" encoding="utf-8"?>
   <record>
-    <guid>admin</guid>
+    <uid>admin</uid>
     <enable>true</enable>
     <foo/>
   </record>

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_cachedirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_cachedirectory.py	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_cachedirectory.py	2010-02-12 03:44:52 UTC (rev 5094)
@@ -68,7 +68,7 @@
                     ) 
                     
                     augmentRecord = AugmentRecord(
-                        guid = cacheRecord.guid,
+                        uid = cacheRecord.guid,
                         enabled=True,
                         enabledForCalendaring = True,
                     )

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py	2010-02-12 03:44:52 UTC (rev 5094)
@@ -159,7 +159,7 @@
 <!DOCTYPE accounts SYSTEM "accounts.dtd">
 <augments>
   <record>
-    <guid>myoffice</guid>
+    <uid>myoffice</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
     <auto-schedule>true</auto-schedule>

Modified: CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py	2010-02-12 03:08:03 UTC (rev 5093)
+++ CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py	2010-02-12 03:44:52 UTC (rev 5094)
@@ -33,7 +33,7 @@
 ELEMENT_AUGMENTS          = "augments"
 ELEMENT_RECORD            = "record"
 
-ELEMENT_GUID              = "guid"
+ELEMENT_UID               = "uid"
 ELEMENT_ENABLE            = "enable"
 ELEMENT_HOSTEDAT          = "hosted-at"
 ELEMENT_ENABLEADDRESSBOOK = "enable-addressbook"
@@ -46,7 +46,7 @@
 VALUE_FALSE               = "false"
 
 ELEMENT_AUGMENTRECORD_MAP = {
-    ELEMENT_GUID:              "guid",
+    ELEMENT_UID:               "uid",
     ELEMENT_ENABLE:            "enabled",
     ELEMENT_HOSTEDAT:          "hostedAt",
     ELEMENT_ENABLECALENDAR:    "enabledForCalendaring",
@@ -95,7 +95,7 @@
             for node in child.getchildren():
                 
                 if node.tag in (
-                    ELEMENT_GUID,
+                    ELEMENT_UID,
                     ELEMENT_HOSTEDAT,
                 ):
                     fields[node.tag] = node.text
@@ -109,9 +109,9 @@
                 else:
                     log.error("Invalid element '%s' in augment file: '%s'" % (node.tag, self.xmlFile,), raiseException=RuntimeError)
                     
-            # Must have at least a guid
-            if ELEMENT_GUID not in fields:
-                log.error("Invalid record '%s' without a guid in augment file: '%s'" % (child, self.xmlFile,), raiseException=RuntimeError)
+            # Must have at least a uid
+            if ELEMENT_UID not in fields:
+                log.error("Invalid record '%s' without a uid in augment file: '%s'" % (child, self.xmlFile,), raiseException=RuntimeError)
                 
             if repeat > 1:
                 for i in xrange(1, repeat+1):
@@ -137,4 +137,4 @@
             actualFields[ELEMENT_AUGMENTRECORD_MAP[k]] = expandCount(v, count)
 
         record = AugmentRecord(**actualFields)
-        self.items[record.guid] = record
+        self.items[record.uid] = record

Added: CalendarServer/trunk/twistedcaldav/xmlutil.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/xmlutil.py	                        (rev 0)
+++ CalendarServer/trunk/twistedcaldav/xmlutil.py	2010-02-12 03:44:52 UTC (rev 5094)
@@ -0,0 +1,99 @@
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from xml.etree.ElementTree import Element, ElementTree, SubElement, tostring
+from xml.parsers.expat import ExpatError
+
+# Utilities for working with ElementTree
+
+def readXML(xmlfile, expectedRootTag=None):
+    """
+    Read in XML data from a file and parse into ElementTree. Optionally verify
+    the root node is what we expect.
+    
+    @param xmlfile: file to read from
+    @type xmlfile: C{File}
+    @param expectedRootTag: root tag (qname) to test or C{None}
+    @type expectedRootTag: C{str}
+    @return: C{tuple} of C{ElementTree}, C{Element}
+    """
+
+    # Read in XML
+    try:
+        etree = ElementTree(file=xmlfile)
+    except ExpatError, e:
+        ValueError("Unable to parse file '%s' because: %s" % (xmlfile, e,))
+
+    if expectedRootTag:
+        root = etree.getroot()
+        if root.tag != expectedRootTag:
+            ValueError("Ignoring file '%s' because it is not a %s file" % (xmlfile, expectedRootTag,))
+    
+    return etree, etree.getroot()
+
+def writeXML(xmlfile, root):
+    
+    data = """<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE %s SYSTEM "%s.dtd">
+
+""" % (root.tag, root.tag)
+
+    INDENT = 2
+
+    # Generate indentation
+    def _indentNode(node, level=0):
+        
+        if node.text is not None and node.text.strip():
+            return
+        elif len(node.getchildren()):
+            indent = "\n" + " " * (level + 1) * INDENT
+            node.text = indent
+            for child in node.getchildren():
+                child.tail = indent
+                _indentNode(child, level + 1)
+            if len(node.getchildren()):
+                node.getchildren()[-1].tail = "\n" + " " * level * INDENT
+
+    _indentNode(root, 0)
+    data += tostring(root) + "\n"
+
+    with open(xmlfile, "w") as f:
+        f.write(data)
+
+def newElementTreeWithRoot(roottag):
+
+    root = createElement(roottag)
+    etree = ElementTree(root)
+    
+    return etree, root
+
+def createElement(tag, text=None):
+
+    child = Element(tag)
+    child.text = text
+    return child
+
+def addSubElement(parent, tag, text=None):
+
+    child = SubElement(parent, tag)
+    child.text = text
+    return child
+
+def changeSubElementText(parent, tag, text):
+    
+    child = parent.find(tag)
+    child.text = text
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100211/76534e44/attachment-0001.html>


More information about the calendarserver-changes mailing list