[CalendarServer-changes] [3000] CalendarServer/branches/users/sagen/principal-property-search-2995
source_changes at macosforge.org
source_changes at macosforge.org
Tue Sep 16 11:37:35 PDT 2008
Revision: 3000
http://trac.macosforge.org/projects/calendarserver/changeset/3000
Author: sagen at apple.com
Date: 2008-09-16 11:37:34 -0700 (Tue, 16 Sep 2008)
Log Message:
-----------
Checkpoint of progress: when doing a principal-property-search REPORT, the search now takes advantage of any properties that map to directory record fields; also added email-address DAV property.
Modified Paths:
--------------
CalendarServer/branches/users/sagen/principal-property-search-2995/lib-patches/Twisted/twisted.web2.dav.method.report_principal_property_search.patch
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/customxml.py
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/aggregate.py
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/apache.py
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/appleopendirectory.py
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/directory.py
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/idirectory.py
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/principal.py
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/sudo.py
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/xmlaccountsparser.py
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/xmlfile.py
CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/resource.py
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/lib-patches/Twisted/twisted.web2.dav.method.report_principal_property_search.patch
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/lib-patches/Twisted/twisted.web2.dav.method.report_principal_property_search.patch 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/lib-patches/Twisted/twisted.web2.dav.method.report_principal_property_search.patch 2008-09-16 18:37:34 UTC (rev 3000)
@@ -2,7 +2,7 @@
===================================================================
--- twisted/web2/dav/method/report_principal_property_search.py (revision 19773)
+++ twisted/web2/dav/method/report_principal_property_search.py (working copy)
-@@ -46,17 +46,23 @@
+@@ -46,17 +46,24 @@
Generate a principal-property-search REPORT. (RFC 3744, section 9.4)
"""
@@ -16,6 +16,7 @@
+ testMode = principal_property_search.attributes.get("test", "allof")
+ if testMode not in ("allof", "anyof"):
+ raise ValueError("Unknown value for test attribute: %s" % (testMode,))
++ operand = "and" if testMode == "allof" else "or"
+
# Only handle Depth: 0
depth = request.headers.getHeader("depth", "0")
@@ -27,18 +28,18 @@
# Get a single DAV:prop element from the REPORT request body
propertiesForResource = None
propElement = None
-@@ -93,31 +99,53 @@
+@@ -93,73 +100,157 @@
else:
return False
- def propertySearch(resource, request):
-+ def propertySearch(resource, request, testMode):
++ def propertySearch(resource, request, operand):
"""
Test the resource to see if it contains properties matching the
property-search specification in this report.
@param resource: the L{DAVFile} for the resource to test.
@param request: the current request.
-+ @param testMode: "allof" (AND operation) or "anyof" (OR operation)
++ @param operand: "and" or "or"
@return: True if the resource has matching properties, False otherwise.
"""
- for props, match in propertySearches:
@@ -50,7 +51,7 @@
- propvalue = propvalue.getResult()
- if propvalue and not nodeMatch(propvalue, match):
+
-+ if testMode == "allof": # "AND"
++ if operand == "and":
+ for props, match in propertySearches:
+ # Test each property
+ for prop in props:
@@ -75,7 +76,7 @@
+ # we hit on every property
+ yield True
-+ else: # "OR"
++ else: # "or"
+ for props, match in propertySearches:
+ # Test each property
+ for prop in props:
@@ -95,60 +96,146 @@
+
propertySearch = deferredGenerator(propertySearch)
++
# Run report
-@@ -127,13 +155,8 @@
+ try:
+- resources = []
+- responses = []
++ # propertySearches is a list of tuple(tuple(props), match)
++
++ # TODO: should this mapping live elsewhere?
++ # See if there are any that are recognized by record fields
++ cs_ns = "http://calendarserver.org/ns/"
++ fieldMap = {
++ "<{%s}%s>" % (cs_ns, "first-name") : "firstName",
++ "<{%s}%s>" % (cs_ns, "last-name") : "lastName",
++ "<{%s}%s>" % (cs_ns, "email-address") : "emailAddress",
++ }
++
++ fields = []
++ nonDirectorySearches = []
++ for props, match in propertySearches:
++ nonDirectoryProps = []
++ for prop in props:
++ uri = repr(prop)
++ if fieldMap.has_key(uri):
++ fieldName = fieldMap[uri]
++ fields.append((fieldName, match))
++ else:
++ nonDirectoryProps.append(prop)
++ if nonDirectoryProps:
++ nonDirectorySearches.append((nonDirectoryProps, match))
++
++ matchingResources = []
matchcount = 0
- if applyTo:
+- if applyTo:
- # Get the principal collection set
- pset = waitForDeferred(self.principalCollections(request))
- yield pset
- pset = pset.getResult()
--
++ if (
++ (operand == "or" and nonDirectorySearches) or
++ (operand == "and" and nonDirectorySearches and not fields)
++ ):
++ # These are the situations in which we need to iterate all the
++ # resources
++ resources = []
+
- for phref in pset:
- uri = str(phref)
-+ for principalCollection in self.principalCollections():
-+ uri = principalCollection.principalCollectionURL()
- resource = waitForDeferred(request.locateResource(uri))
- yield resource
- resource = resource.getResult()
-@@ -142,42 +165,67 @@
- else:
- resources.append((self, request.uri))
+- resource = waitForDeferred(request.locateResource(uri))
+- yield resource
+- resource = resource.getResult()
+- if resource:
+- resources.append((resource, uri))
+- else:
+- resources.append((self, request.uri))
++ if applyTo:
++ for principalCollection in self.principalCollections():
++ uri = principalCollection.principalCollectionURL()
++ resource = waitForDeferred(request.locateResource(uri))
++ yield resource
++ resource = resource.getResult()
++ if resource:
++ resources.append((resource, uri))
++ else:
++ resources.append((self, request.uri))
- # Loop over all collections and principal resources within
- for resource, ruri in resources:
-+ if True:
++ # Loop over all collections and principal resources within
++ for resource, ruri in resources:
- # Do some optimisation of access control calculation by determining any inherited ACLs outside of
- # the child resource loop and supply those to the checkPrivileges on each child.
- filteredaces = waitForDeferred(resource.inheritedACEsforChildren(request))
- yield filteredaces
- filteredaces = filteredaces.getResult()
-+ for record in self.directory.recordsStartingWith("mor"):
-+ resource = self.principalForRecord(record)
-+ url = resource.url()
++ # Do some optimisation of access control calculation by determining any inherited ACLs outside of
++ # the child resource loop and supply those to the checkPrivileges on each child.
++ filteredaces = waitForDeferred(resource.inheritedACEsforChildren(request))
++ yield filteredaces
++ filteredaces = filteredaces.getResult()
- children = []
- d = waitForDeferred(resource.findChildren("infinity", request, lambda x, y: children.append((x,y)),
- privileges=(davxml.Read(),), inherited_aces=filteredaces))
- yield d
- d.getResult()
-+ # Check size of results is within limit
-+ matchcount += 1
-+ if matchcount > max_number_of_matches:
-+ raise NumberOfMatchesWithinLimits
++ children = []
++ d = waitForDeferred(resource.findChildren("infinity", request,
++ lambda x, y: children.append((x,y)),
++ privileges=(davxml.Read(),), inherited_aces=filteredaces))
++ yield d
++ d.getResult()
- for child, uri in children:
- if isPrincipalResource(child):
- d = waitForDeferred(propertySearch(child, request))
-- yield d
-- d = d.getResult()
-- if d:
-- # Check size of results is within limit
-- matchcount += 1
-- if matchcount > max_number_of_matches:
-- raise NumberOfMatchesWithinLimits
++ for child, uri in children:
++ if isPrincipalResource(child):
++ d = waitForDeferred(propertySearch(child, request,
++ operand))
++ yield d
++ d = d.getResult()
++ if d:
++ # Check size of results is within limit
++ matchcount += 1
++ if matchcount > max_number_of_matches:
++ raise NumberOfMatchesWithinLimits
++
++ matchingResources.append(child)
++
++
++ elif fields: # search the directory
++ for record in self.directory.recordsMatchingFields(fields,
++ operand=operand):
++
++ resource = self.principalForRecord(record)
++ url = resource.url()
++
++ if not nonDirectorySearches:
++ # We've determined this is a matching resource
++
++ matchcount += 1
++ if matchcount > max_number_of_matches:
++ raise NumberOfMatchesWithinLimits
++ matchingResources.append(resource)
++
++ elif operand == "and":
++ # Further narrowing down needs to take place by examining
++ # the resource's DAV properties
++
++ d = waitForDeferred(propertySearch(resource, request,
++ operand))
+ yield d
+ d = d.getResult()
+ if d:
+@@ -167,18 +258,26 @@
+ matchcount += 1
+ if matchcount > max_number_of_matches:
+ raise NumberOfMatchesWithinLimits
-
- d = waitForDeferred(prop_common.responseForHref(
- request,
@@ -158,57 +245,28 @@
- propertiesForResource,
- propElement
- ))
-+ d = waitForDeferred(prop_common.responseForHref(
-+ request,
-+ responses,
-+ davxml.HRef.fromString(url),
-+ resource,
-+ propertiesForResource,
-+ propElement
-+ ))
-+ yield d
-+ d.getResult()
+- yield d
+- d.getResult()
++ matchingResources.append(resource)
+
+
-+ else:
+
-+ # Loop over all collections and principal resources within
-+ for resource, ruri in resources:
++ # Generate the response
++ responses = []
++ for resource in matchingResources:
++ url = resource.url()
++ d = waitForDeferred(prop_common.responseForHref(
++ request,
++ responses,
++ davxml.HRef.fromString(url),
++ resource,
++ propertiesForResource,
++ propElement
++ ))
++ yield d
++ d.getResult()
+
-+ # Do some optimisation of access control calculation by determining any inherited ACLs outside of
-+ # the child resource loop and supply those to the checkPrivileges on each child.
-+ filteredaces = waitForDeferred(resource.inheritedACEsforChildren(request))
-+ yield filteredaces
-+ filteredaces = filteredaces.getResult()
+
-+ children = []
-+ # d = waitForDeferred(resource.findChildren("infinity", request, lambda x, y: children.append((x,y)),
-+ d = waitForDeferred(resource.findChildren("1", request, lambda x, y: children.append((x,y)),
-+ privileges=(davxml.Read(),), inherited_aces=filteredaces))
-+ yield d
-+ d.getResult()
-+
-+ for child, uri in children:
-+ if isPrincipalResource(child):
-+ d = waitForDeferred(propertySearch(child, request, testMode))
- yield d
-- d.getResult()
-+ d = d.getResult()
-+ if d:
-+ # Check size of results is within limit
-+ matchcount += 1
-+ if matchcount > max_number_of_matches:
-+ raise NumberOfMatchesWithinLimits
-+
-+ d = waitForDeferred(prop_common.responseForHref(
-+ request,
-+ responses,
-+ davxml.HRef.fromString(uri),
-+ child,
-+ propertiesForResource,
-+ propElement
-+ ))
-+ yield d
-+ d.getResult()
-
except NumberOfMatchesWithinLimits:
log.err("Too many matching components in prinicpal-property-search report")
+ raise HTTPError(ErrorResponse(
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/customxml.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/customxml.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -306,6 +306,14 @@
name = "last-name"
protected = True
+class EMailProperty (davxml.WebDAVTextElement):
+ """
+ A property representing email address of a principal
+ """
+ namespace = calendarserver_namespace
+ name = "email-address"
+ protected = True
+
class IScheduleInbox (davxml.WebDAVEmptyElement):
"""
Denotes the resourcetype of a iSchedule Inbox.
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/aggregate.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/aggregate.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/aggregate.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -106,6 +106,10 @@
def recordsStartingWith(self, str):
return self._queryAll("recordsStartingWith", str)
+ def recordsMatchingFields(self, fields, caseInsensitive=True, operand="or"):
+ return self._queryAll("recordsMatchingFields", fields,
+ caseInsensitive=caseInsensitive, operand=operand)
+
def serviceForRecordType(self, recordType):
try:
return self._recordTypes[recordType]
@@ -123,9 +127,9 @@
*[a[len(service.recordTypePrefix):] for a in args]
)
- def _queryAll(self, query, *args):
+ def _queryAll(self, query, *args, **kwds):
for service in self._recordTypes.values():
- record = getattr(service, query)(*args)
+ record = getattr(service, query)(*args, **kwds)
if record is not None:
return record
else:
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/apache.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/apache.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/apache.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -131,6 +131,7 @@
fullName = None,
firstName = None,
lastName = None,
+ emailAddress = None,
calendarUserAddresses = set(),
autoSchedule = False,
)
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/appleopendirectory.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/appleopendirectory.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -512,10 +512,75 @@
except Exception, e:
print e
import pdb; pdb.set_trace()
-
+
return returning
+ _ODFields = {
+ 'firstName' : dsattributes.kDS1AttrFirstName,
+ 'lastName' : dsattributes.kDS1AttrLastName,
+ 'emailAddress' : dsattributes.kDSNAttrEMailAddress,
+ }
+ def recordsMatchingFields(self, fields, caseInsensitive=True, operand="or"):
+
+ comparison = dsattributes.eDSStartsWith
+ operand = (dsquery.expression.OR if operand == "or"
+ else dsquery.expression.AND)
+
+ expressions = []
+ for field, value in fields:
+ if field in self._ODFields:
+ ODField = self._ODFields[field]
+ expressions.append(dsquery.match(ODField, value, comparison))
+
+
+ results = opendirectory.queryRecordsWithAttributes(
+ self.directory,
+ dsquery.expression(operand, expressions).generate(),
+ caseInsensitive,
+ dsattributes.kDSStdRecordTypeUsers,
+ [
+ dsattributes.kDS1AttrGeneratedUID,
+ dsattributes.kDS1AttrFirstName,
+ dsattributes.kDS1AttrLastName,
+ dsattributes.kDSNAttrEMailAddress,
+ dsattributes.kDS1AttrDistinguishedName,
+ dsattributes.kDSNAttrMetaNodeLocation,
+ ]
+ )
+ returning = []
+ for key, val in results.iteritems():
+ try:
+ calendarUserAddresses = set()
+ enabledForCalendaring = False
+ if val.has_key(dsattributes.kDSNAttrEMailAddress):
+ enabledForCalendaring = True
+ calendarUserAddresses.add(val[dsattributes.kDSNAttrEMailAddress])
+ rec = OpenDirectoryRecord(
+ service = self,
+ recordType = DirectoryService.recordType_users,
+ guid = val[dsattributes.kDS1AttrGeneratedUID],
+ nodeName = val[dsattributes.kDSNAttrMetaNodeLocation],
+ shortName = key,
+ fullName = val[dsattributes.kDS1AttrDistinguishedName],
+ firstName = val[dsattributes.kDS1AttrFirstName],
+ lastName = val[dsattributes.kDS1AttrLastName],
+ emailAddress = val.get(dsattributes.kDSNAttrEMailAddress, ""),
+ calendarUserAddresses = calendarUserAddresses,
+ autoSchedule = False,
+ enabledForCalendaring = enabledForCalendaring,
+ memberGUIDs = (),
+ proxyGUIDs = (),
+ readOnlyProxyGUIDs = (),
+ )
+ returning.append(rec)
+ except Exception, e:
+ print e
+ import pdb; pdb.set_trace()
+
+ return returning
+
+
def reloadCache(self, recordType, shortName=None, guid=None):
if shortName:
self.log_info("Faulting record %s into %s record cache" % (shortName, recordType))
@@ -591,6 +656,7 @@
recordFullName = value.get(dsattributes.kDS1AttrDistinguishedName)
recordFirstName = value.get(dsattributes.kDS1AttrFirstName)
recordLastName = value.get(dsattributes.kDS1AttrLastName)
+ recordEmailAddress = value.get(dsattributes.kDSNAttrEMailAddress)
recordNodeName = value.get(dsattributes.kDSNAttrMetaNodeLocation)
if not recordGUID:
@@ -641,6 +707,7 @@
fullName = recordFullName,
firstName = recordFirstName,
lastName = recordLastName,
+ emailAddress = recordEmailAddress,
calendarUserAddresses = calendarUserAddresses,
autoSchedule = autoSchedule,
enabledForCalendaring = enabledForCalendaring,
@@ -889,7 +956,7 @@
"""
def __init__(
self, service, recordType, guid, nodeName, shortName, fullName,
- firstName, lastName,
+ firstName, lastName, emailAddress,
calendarUserAddresses, autoSchedule, enabledForCalendaring,
memberGUIDs, proxyGUIDs, readOnlyProxyGUIDs,
):
@@ -901,6 +968,7 @@
fullName = fullName,
firstName = firstName,
lastName = lastName,
+ emailAddress = emailAddress,
calendarUserAddresses = calendarUserAddresses,
autoSchedule = autoSchedule,
enabledForCalendaring = enabledForCalendaring,
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/directory.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/directory.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -138,6 +138,8 @@
for record in self.listRecords(recordType):
yield record
+ def recordsMatchingFields(self, fields, caseInsensitive=True, operand="or"):
+ return None
class DirectoryRecord(LoggingMixIn):
implements(IDirectoryRecord)
@@ -155,7 +157,7 @@
def __init__(
self, service, recordType, guid, shortName, fullName,
- firstName, lastName,
+ firstName, lastName, emailAddress,
calendarUserAddresses, autoSchedule, enabledForCalendaring=True,
):
assert service.realmName is not None
@@ -177,6 +179,7 @@
self.fullName = fullName
self.firstName = firstName
self.lastName = lastName
+ self.emailAddress = emailAddress
self.enabledForCalendaring = enabledForCalendaring
self.calendarUserAddresses = calendarUserAddresses
self.autoSchedule = autoSchedule
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/idirectory.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/idirectory.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/idirectory.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -79,7 +79,13 @@
insensitive)
"""
+ def recordsMatchingFields(fields):
+ """
+ @return: a sequence of L{IDirectoryRecord}s which match the given
+ fields.
+ """
+
class IDirectoryRecord(Interface):
"""
Directory Record
@@ -91,6 +97,7 @@
fullName = Attribute("The full name of this record.")
firstName = Attribute("The first name of this record.")
lastName = Attribute("The last name of this record.")
+ emailAddress = Attribute("The email address of this record.")
calendarUserAddresses = Attribute("A set of calendar user addresses for this record.")
autoSchedule = Attribute("Principal identified by this record should automatically accept/deny meetings.")
enabledForCalendaring = Attribute("Determines whether this record should be provisioned with a calendar home.")
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/principal.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/principal.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -439,6 +439,7 @@
"""Full name: %s\n""" % (self.record.fullName,),
"""First name: %s\n""" % (self.record.firstName,),
"""Last name: %s\n""" % (self.record.lastName,),
+ """Email address: %s\n""" % (self.record.emailAddress,),
"""Principal UID: %s\n""" % (self.principalUID(),),
"""Principal URL: %s\n""" % (format_link(self.principalURL()),),
"""\nAlternate URIs:\n""" , format_list(format_link(u) for u in self.alternateURIs()),
@@ -601,6 +602,7 @@
"""Full name: %s\n""" % (self.record.fullName,),
"""First name: %s\n""" % (self.record.firstName,),
"""Last name: %s\n""" % (self.record.lastName,),
+ """Email address: %s\n""" % (self.record.emailAddress,),
"""Principal UID: %s\n""" % (self.principalUID(),),
"""Principal URL: %s\n""" % (format_link(self.principalURL()),),
"""\nAlternate URIs:\n""" , format_list(format_link(u) for u in self.alternateURIs()),
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/sudo.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/sudo.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/sudo.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -137,6 +137,7 @@
fullName=shortName,
firstName="",
lastName="",
+ emailAddress="",
calendarUserAddresses=set(),
autoSchedule=False,
enabledForCalendaring=False)
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/xmlaccountsparser.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/xmlaccountsparser.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/xmlaccountsparser.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -45,6 +45,7 @@
ELEMENT_NAME = "name"
ELEMENT_FIRST_NAME = "first-name"
ELEMENT_LAST_NAME = "last-name"
+ELEMENT_EMAIL_ADDRESS = "email-address"
ELEMENT_MEMBERS = "members"
ELEMENT_MEMBER = "member"
ELEMENT_CUADDR = "cuaddr"
@@ -167,6 +168,7 @@
self.name = None
self.firstName = None
self.lastName = None
+ self.emailAddress = None
self.members = set()
self.groups = set()
self.calendarUserAddresses = set()
@@ -207,6 +209,10 @@
lastName = self.lastName % ctr
else:
lastName = self.lastName
+ if self.emailAddress and self.emailAddress.find("%") != -1:
+ emailAddress = self.emailAddress % ctr
+ else:
+ emailAddress = self.emailAddress
calendarUserAddresses = set()
for cuaddr in self.calendarUserAddresses:
if cuaddr.find("%") != -1:
@@ -221,6 +227,7 @@
result.name = name
result.firstName = firstName
result.lastName = lastName
+ result.emailAddress = emailAddress
result.members = self.members
result.calendarUserAddresses = calendarUserAddresses
result.autoSchedule = self.autoSchedule
@@ -257,6 +264,9 @@
elif child_name == ELEMENT_LAST_NAME:
if child.firstChild is not None:
self.lastName = child.firstChild.data.encode("utf-8")
+ elif child_name == ELEMENT_EMAIL_ADDRESS:
+ if child.firstChild is not None:
+ self.emailAddress = child.firstChild.data.encode("utf-8")
elif child_name == ELEMENT_MEMBERS:
self._parseMembers(child, self.members)
elif child_name == ELEMENT_CUADDR:
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/xmlfile.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/directory/xmlfile.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -110,6 +110,7 @@
fullName = xmlPrincipal.name,
firstName = xmlPrincipal.firstName,
lastName = xmlPrincipal.lastName,
+ emailAddress = xmlPrincipal.emailAddress,
calendarUserAddresses = xmlPrincipal.calendarUserAddresses,
autoSchedule = xmlPrincipal.autoSchedule,
enabledForCalendaring = xmlPrincipal.enabledForCalendaring,
Modified: CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/resource.py 2008-09-15 19:28:14 UTC (rev 2999)
+++ CalendarServer/branches/users/sagen/principal-property-search-2995/twistedcaldav/resource.py 2008-09-16 18:37:34 UTC (rev 3000)
@@ -707,6 +707,7 @@
(caldav_namespace, "schedule-outbox-URL" ),
(calendarserver_namespace, "first-name" ),
(calendarserver_namespace, "last-name" ),
+ (calendarserver_namespace, "email-address" ),
)
@classmethod
@@ -776,7 +777,14 @@
else:
return None
+ if name == "email-address":
+ emailAddress = self.record.emailAddress
+ if emailAddress:
+ return customxml.EMailProperty(emailAddress)
+ else:
+ return None
+
return super(CalendarPrincipalResource, self).readProperty(property, request)
return maybeDeferred(defer)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080916/6ef8bf60/attachment-0001.html
More information about the calendarserver-changes
mailing list