[CalendarServer-changes] [12084] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Wed Mar 12 11:21:48 PDT 2014
Revision: 12084
http://trac.calendarserver.org//changeset/12084
Author: sagen at apple.com
Date: 2013-12-13 09:37:30 -0800 (Fri, 13 Dec 2013)
Log Message:
-----------
Adds structure location support in the xmlfile directory implementation
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/tools/gateway.py
CalendarServer/trunk/calendarserver/tools/principals.py
CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist
CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
CalendarServer/trunk/conf/auth/augments-test.xml
CalendarServer/trunk/conf/auth/resources-test.xml
CalendarServer/trunk/twistedcaldav/datafilters/peruserdata.py
CalendarServer/trunk/twistedcaldav/directory/augment.py
CalendarServer/trunk/twistedcaldav/directory/directory.py
CalendarServer/trunk/twistedcaldav/directory/test/test_aggregate.py
CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py
CalendarServer/trunk/twistedcaldav/directory/test/util.py
CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py
CalendarServer/trunk/twistedcaldav/directory/xmlfile.py
CalendarServer/trunk/twistedcaldav/stdconfig.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
CalendarServer/trunk/txdav/caldav/datastore/test/util.py
CalendarServer/trunk/txdav/common/datastore/test/util.py
CalendarServer/trunk/txdav/common/idirectoryservice.py
Modified: CalendarServer/trunk/calendarserver/tools/gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/gateway.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/calendarserver/tools/gateway.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -32,7 +32,9 @@
principalForPrincipalID, proxySubprincipal, addProxy, removeProxy,
ProxyError, ProxyWarning, autoDisableMemcached
)
-from calendarserver.tools.principals import getProxies, setProxies, updateRecord
+from calendarserver.tools.principals import (
+ getProxies, setProxies, updateRecord, attrMap
+)
from calendarserver.tools.purge import WorkerService, PurgeOldEventsService, DEFAULT_BATCH_SIZE, DEFAULT_RETAIN_DAYS
from calendarserver.tools.cmdline import utilityMain
@@ -140,26 +142,6 @@
utilityMain(configFileName, RunnerService, verbose=debug)
-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', },
- 'Geo' : { 'extras' : True, 'attr' : 'geo', },
- 'AutoSchedule' : { 'attr' : 'autoSchedule', },
- 'AutoAcceptGroup' : { 'attr' : 'autoAcceptGroup', },
-}
class Runner(object):
@@ -218,9 +200,9 @@
self.respondWithError("Command failed: '%s'" % (str(e),))
raise
+
# Locations
-
def command_getLocationList(self, command):
self.respondWithRecordsOfTypes(self.dir, command, ["locations"])
@@ -266,6 +248,7 @@
command_getResourceAttributes = command_getLocationAttributes
+
@inlineCallbacks
def command_setLocationAttributes(self, command):
@@ -306,9 +289,9 @@
return
self.respondWithRecordsOfTypes(self.dir, command, ["locations"])
+
# Resources
-
def command_getResourceList(self, command):
self.respondWithRecordsOfTypes(self.dir, command, ["resources"])
@@ -379,6 +362,67 @@
self.respondWithRecordsOfTypes(self.dir, command, ["locations", "resources"])
+ # Addresses
+
+ def command_getAddressList(self, command):
+ self.respondWithRecordsOfTypes(self.dir, command, ["addresses"])
+
+
+ @inlineCallbacks
+ def command_createAddress(self, command):
+ kwargs = {}
+ for key, info in attrMap.iteritems():
+ if key in command:
+ kwargs[info['attr']] = command[key]
+
+ try:
+ yield updateRecord(True, self.dir, "addresses", **kwargs)
+ except DirectoryError, e:
+ self.respondWithError(str(e))
+ return
+
+ self.respondWithRecordsOfTypes(self.dir, command, ["addresses"])
+
+
+ def command_getAddressAttributes(self, command):
+ guid = command['GeneratedUID']
+ record = self.dir.recordWithGUID(guid)
+ if record is None:
+ self.respondWithError("Principal not found: %s" % (guid,))
+ return
+ recordDict = recordToDict(record)
+ self.respond(command, recordDict)
+ return succeed(None)
+
+
+ @inlineCallbacks
+ def command_setAddressAttributes(self, command):
+ kwargs = {}
+ for key, info in attrMap.iteritems():
+ if key in command:
+ kwargs[info['attr']] = command[key]
+ try:
+ yield updateRecord(False, self.dir, "addresses", **kwargs)
+ except DirectoryError, e:
+ self.respondWithError(str(e))
+ return
+
+ yield self.command_getAddressAttributes(command)
+
+
+ def command_deleteAddress(self, command):
+ kwargs = {}
+ for key, info in attrMap.iteritems():
+ if key in command:
+ kwargs[info['attr']] = command[key]
+ try:
+ self.dir.destroyRecord("addresses", **kwargs)
+ except DirectoryError, e:
+ self.respondWithError(str(e))
+ return
+ self.respondWithRecordsOfTypes(self.dir, command, ["addresses"])
+
+
# Config
def command_readConfig(self, command):
Modified: CalendarServer/trunk/calendarserver/tools/principals.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/principals.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/calendarserver/tools/principals.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -87,8 +87,14 @@
print(" --get-auto-schedule-mode: read auto-schedule mode")
print(" --set-auto-accept-group=principal: set auto-accept-group")
print(" --get-auto-accept-group: read auto-accept-group")
- print(" --add {locations|resources} 'full name' [record name] [GUID]: add a principal")
+ print(" --add {locations|resources|addresses} 'full name' [record name] [GUID]: add a principal")
print(" --remove: remove a principal")
+ print(" --set-geo=url: set the geo: url for an address (e.g. geo:37.331741,-122.030333)")
+ print(" --get-geo: get the geo: url for an address")
+ print(" --set-street-address=streetaddress: set the street address string for an address")
+ print(" --get-street-address: get the street address string for an address")
+ print(" --set-address=guid: associate principal with an address (by guid)")
+ print(" --get-address: get the associated address's guid")
if e:
sys.exit(64)
@@ -116,8 +122,29 @@
directory = rootResource.getDirectory()
yield self.function(rootResource, directory, self.store, *self.params)
+attrMap = {
+ 'GeneratedUID' : { 'attr' : 'guid', },
+ 'RealName' : { 'attr' : 'fullName', },
+ 'RecordName' : { 'attr' : 'shortNames', },
+ 'AutoSchedule' : { 'attr' : 'autoSchedule', },
+ 'AutoAcceptGroup' : { 'attr' : 'autoAcceptGroup', },
+ 'Comment' : { 'extras' : True, 'attr' : 'comment', },
+ 'Description' : { 'extras' : True, 'attr' : 'description', },
+ 'Type' : { 'extras' : True, 'attr' : 'type', },
+ # For "Locations", i.e. scheduled spaces
+ 'Capacity' : { 'extras' : True, 'attr' : 'capacity', },
+ 'Floor' : { 'extras' : True, 'attr' : 'floor', },
+ 'AssociatedAddress' : { 'extras' : True, 'attr' : 'associatedAddress', },
+
+ # For "Addresses", i.e. nonscheduled areas containing Locations
+ 'AbbreviatedName' : { 'extras' : True, 'attr' : 'abbreviatedName', },
+ 'StreetAddress' : { 'extras' : True, 'attr' : 'streetAddress', },
+ 'Geo' : { 'extras' : True, 'attr' : 'geo', },
+}
+
+
def main():
try:
(optargs, args) = getopt(
@@ -142,6 +169,12 @@
"get-auto-schedule-mode",
"set-auto-accept-group=",
"get-auto-accept-group",
+ "set-geo=",
+ "get-geo",
+ "set-address=",
+ "get-address",
+ "set-street-address=",
+ "get-street-address",
"verbose",
],
)
@@ -258,6 +291,24 @@
elif opt in ("", "--get-auto-accept-group"):
principalActions.append((action_getAutoAcceptGroup,))
+ elif opt in ("", "--set-geo"):
+ principalActions.append((action_setValue, "Geo", arg))
+
+ elif opt in ("", "--get-geo"):
+ principalActions.append((action_getValue, "Geo"))
+
+ elif opt in ("", "--set-street-address"):
+ principalActions.append((action_setValue, "StreetAddress", arg))
+
+ elif opt in ("", "--get-street-address"):
+ principalActions.append((action_getValue, "StreetAddress"))
+
+ elif opt in ("", "--set-address"):
+ principalActions.append((action_setValue, "AssociatedAddress", arg))
+
+ elif opt in ("", "--get-address"):
+ principalActions.append((action_getValue, "AssociatedAddress"))
+
else:
raise NotImplementedError(opt)
@@ -274,7 +325,7 @@
elif addType:
try:
- addType = matchStrings(addType, ["locations", "resources"])
+ addType = matchStrings(addType, ["locations", "resources", "addresses"])
except ValueError, e:
print(e)
return
@@ -296,7 +347,7 @@
elif listPrincipals:
try:
listPrincipals = matchStrings(listPrincipals, ["users", "groups",
- "locations", "resources"])
+ "locations", "resources", "addresses"])
except ValueError, e:
print(e)
return
@@ -393,6 +444,7 @@
"groups" : "Group",
"locations" : "Place",
"resources" : "Resource",
+ "addresses" : "Address",
}.get(record.recordType),
))
print(" GUID: %s" % (record.guid,))
@@ -667,7 +719,30 @@
print("No auto-accept-group assigned to %s" % (prettyPrincipal(principal),))
+ at inlineCallbacks
+def action_setValue(rootResource, directory, store, principal, name, value):
+ print("Setting %s to %s for %s" % (
+ name, value, prettyPrincipal(principal),
+ ))
+ principal.record.extras[attrMap[name]["attr"]] = value
+ (yield updateRecord(False, directory,
+ principal.record.recordType,
+ guid=principal.record.guid,
+ shortNames=principal.record.shortNames,
+ fullName=principal.record.fullName,
+ **principal.record.extras
+ ))
+
+
+def action_getValue(rootResource, directory, store, principal, name):
+ print("%s for %s is %s" % (
+ name,
+ prettyPrincipal(principal),
+ principal.record.extras[attrMap[name]["attr"]]
+ ))
+
+
def abort(msg, status=1):
sys.stdout.write("%s\n" % (msg,))
try:
Modified: CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist 2013-12-13 17:37:30 UTC (rev 12084)
@@ -176,6 +176,7 @@
<array>
<string>resources</string>
<string>locations</string>
+ <string>addresses</string>
</array>
</dict>
</dict>
Modified: CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_gateway.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/calendarserver/tools/test/test_gateway.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -127,15 +127,9 @@
def test_getLocationAttributes(self):
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 %s %s" % (unichr(208), u"\ud83d\udca3"))
self.assertEquals(results["result"]["Comment"], "Test Comment")
@@ -162,6 +156,43 @@
@inlineCallbacks
+ def test_createAddress(self):
+ directory = getDirectory()
+
+ record = directory.recordWithUID("C701069D-9CA1-4925-A1A9-5CD94767B74B")
+ self.assertEquals(record, None)
+ yield self.runCommand(command_createAddress)
+
+ directory.flushCaches()
+
+ record = directory.recordWithUID("C701069D-9CA1-4925-A1A9-5CD94767B74B")
+ self.assertEquals(record.fullName.decode("utf-8"),
+ "Created Address 01 %s %s" % (unichr(208), u"\ud83d\udca3"))
+
+ self.assertNotEquals(record, None)
+
+ self.assertEquals(record.extras["abbreviatedName"], "Addr1")
+ self.assertEquals(record.extras["streetAddress"], "1 Infinite Loop\nCupertino, 95014\nCA")
+ self.assertEquals(record.extras["geo"], "geo:37.331,-122.030")
+
+ results = yield self.runCommand(command_getAddressList)
+ self.assertEquals(len(results["result"]), 1)
+
+ results = yield self.runCommand(command_getAddressAttributes)
+ self.assertEquals(results["result"]["RealName"], u'Created Address 01 \xd0 \U0001f4a3')
+
+ results = yield self.runCommand(command_setAddressAttributes)
+
+ results = yield self.runCommand(command_getAddressAttributes)
+ self.assertEquals(results["result"]["RealName"], u'Updated Address')
+
+ results = yield self.runCommand(command_deleteAddress)
+
+ results = yield self.runCommand(command_getAddressList)
+ self.assertEquals(len(results["result"]), 0)
+
+
+ @inlineCallbacks
def test_createLocation(self):
directory = getDirectory()
@@ -184,16 +215,8 @@
self.assertEquals(record.autoSchedule, True)
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")
- self.assertEquals(record.extras["geo"], "geo:37.331,-122.030")
results = yield self.runCommand(command_getLocationAttributes)
self.assertEquals(set(results["result"]["ReadProxies"]), set(['user03', 'user04']))
@@ -216,15 +239,9 @@
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")
+ self.assertEquals(record.extras["streetAddress"], "2 Infinite Loop\nCupertino, 95014\nCA")
self.assertEquals(record.autoSchedule, True)
self.assertEquals(record.autoAcceptGroup, "F5A6142C-4189-4E9E-90B0-9CD0268B314B")
@@ -385,6 +402,31 @@
</plist>
"""
+command_createAddress = """<?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>createAddress</string>
+ <key>GeneratedUID</key>
+ <string>C701069D-9CA1-4925-A1A9-5CD94767B74B</string>
+ <key>RealName</key>
+ <string>Created Address 01 %s %s</string>
+ <key>AbbreviatedName</key>
+ <string>Addr1</string>
+ <key>RecordName</key>
+ <array>
+ <string>createdaddress01</string>
+ </array>
+ <key>StreetAddress</key>
+ <string>1 Infinite Loop\nCupertino, 95014\nCA</string>
+ <key>Geo</key>
+ <string>geo:37.331,-122.030</string>
+</dict>
+</plist>
+""" % (unichr(208), u"\ud83d\udca3")
+
+
command_createLocation = """<?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">
@@ -407,26 +449,12 @@
<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>
- <key>Geo</key>
- <string>geo:37.331,-122.030</string>
+ <key>AssociatedAddress</key>
+ <string>C701069D-9CA1-4925-A1A9-5CD94767B74B</string>
<key>ReadProxies</key>
<array>
<string>users:user03</string>
@@ -454,14 +482,16 @@
<string>AF575A61-CFA6-49E1-A0F6-B5662C9D9801</string>
<key>RealName</key>
<string>Laptop 1</string>
+ <key>Comment</key>
+ <string>Test Comment</string>
+ <key>Description</key>
+ <string>Test Description</string>
<key>Type</key>
<string>Computer</string>
<key>RecordName</key>
<array>
<string>laptop1</string>
</array>
- <key>Comment</key>
- <string>Test Comment</string>
<key>ReadProxies</key>
<array>
<string>users:user03</string>
@@ -500,6 +530,19 @@
</plist>
"""
+
+command_deleteAddress = """<?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>deleteAddress</string>
+ <key>GeneratedUID</key>
+ <string>C701069D-9CA1-4925-A1A9-5CD94767B74B</string>
+</dict>
+</plist>
+"""
+
command_getLocationAndResourceList = """<?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">
@@ -530,6 +573,17 @@
</plist>
"""
+command_getAddressList = """<?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>getAddressList</string>
+</dict>
+</plist>
+"""
+
+
command_listReadProxies = """<?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">
@@ -604,24 +658,12 @@
<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>
+ <key>StreetAddress</key>
+ <string>2 Infinite Loop\nCupertino, 95014\nCA</string>
<key>ReadProxies</key>
<array>
<string>users:user03</string>
@@ -648,6 +690,33 @@
</plist>
"""
+command_getAddressAttributes = """<?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>getAddressAttributes</string>
+ <key>GeneratedUID</key>
+ <string>C701069D-9CA1-4925-A1A9-5CD94767B74B</string>
+</dict>
+</plist>
+"""
+
+command_setAddressAttributes = """<?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>setAddressAttributes</string>
+ <key>GeneratedUID</key>
+ <string>C701069D-9CA1-4925-A1A9-5CD94767B74B</string>
+ <key>RealName</key>
+ <string>Updated Address</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">
Modified: CalendarServer/trunk/conf/auth/augments-test.xml
===================================================================
--- CalendarServer/trunk/conf/auth/augments-test.xml 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/conf/auth/augments-test.xml 2013-12-13 17:37:30 UTC (rev 12084)
@@ -109,6 +109,7 @@
<enable-addressbook>true</enable-addressbook>
<enable-login>true</enable-login>
<auto-schedule>true</auto-schedule>
+ <auto-schedule-mode>default</auto-schedule-mode>
</record>
<record>
<uid>C38BEE7A-36EE-478C-9DCB-CBF4612AFE65</uid>
@@ -136,4 +137,49 @@
<enable-login>true</enable-login>
<auto-schedule>true</auto-schedule>
</record>
+ <record>
+ <uid>6F9EE33B-78F6-481B-9289-3D0812FF0D64</uid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ <enable-login>true</enable-login>
+ <auto-schedule>false</auto-schedule>
+ <auto-schedule-mode>default</auto-schedule-mode>
+ </record>
+ <record>
+ <uid>76E7ECA6-08BC-4AE7-930D-F2E7453993A5</uid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ <enable-login>true</enable-login>
+ <auto-schedule>false</auto-schedule>
+ <auto-schedule-mode>default</auto-schedule-mode>
+ </record>
+ <record>
+ <uid>63A2F949-2D8D-4C8D-B8A5-DCF2A94610F3</uid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ <enable-login>true</enable-login>
+ <auto-schedule>false</auto-schedule>
+ <auto-schedule-mode>default</auto-schedule-mode>
+ </record>
+ <record>
+ <uid>06E3BDCB-9C19-485A-B14E-F146A80ADDC6</uid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ <enable-login>true</enable-login>
+ <auto-schedule>true</auto-schedule>
+ <auto-schedule-mode>default</auto-schedule-mode>
+ </record>
+ <record>
+ <uid>4D66A20A-1437-437D-8069-2F14E8322234</uid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ <enable-login>true</enable-login>
+ <auto-schedule>true</auto-schedule>
+ <auto-schedule-mode>default</auto-schedule-mode>
+ </record>
</augments>
Modified: CalendarServer/trunk/conf/auth/resources-test.xml
===================================================================
--- CalendarServer/trunk/conf/auth/resources-test.xml 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/conf/auth/resources-test.xml 2013-12-13 17:37:30 UTC (rev 12084)
@@ -1,5 +1,13 @@
<accounts realm="Test Realm">
<location>
+ <uid>fantastic</uid>
+ <guid>4D66A20A-1437-437D-8069-2F14E8322234</guid>
+ <name>Fantastic Conference Room</name>
+ <extras>
+ <associatedAddress>63A2F949-2D8D-4C8D-B8A5-DCF2A94610F3</associatedAddress>
+ </extras>
+ </location>
+ <location>
<uid>jupiter</uid>
<guid>jupiter</guid>
<name>Jupiter Conference Room, Building 2, 1st Floor</name>
@@ -78,6 +86,9 @@
<uid>sharissroom</uid>
<guid>80689D41-DAF8-4189-909C-DB017B271892</guid>
<name>Shari's Room</name>
+ <extras>
+ <associatedAddress>6F9EE33B-78F6-481B-9289-3D0812FF0D64</associatedAddress>
+ </extras>
</location>
<location>
<uid>pluto</uid>
@@ -95,6 +106,14 @@
<name>Room 10</name>
</location>
<location>
+ <uid>pretend</uid>
+ <guid>06E3BDCB-9C19-485A-B14E-F146A80ADDC6</guid>
+ <name>Pretend Conference Room</name>
+ <extras>
+ <associatedAddress>76E7ECA6-08BC-4AE7-930D-F2E7453993A5</associatedAddress>
+ </extras>
+ </location>
+ <location>
<uid>neptune</uid>
<guid>neptune</guid>
<name>Neptune Conference Room, Building 2, 1st Floor</name>
@@ -224,4 +243,31 @@
<guid>resource09</guid>
<name>Resource 09</name>
</resource>
+ <address>
+ <uid>testaddress1</uid>
+ <guid>6F9EE33B-78F6-481B-9289-3D0812FF0D64</guid>
+ <name>Test Address One</name>
+ <extras>
+ <streetAddress>20300 Stevens Creek Blvd, Cupertino, CA 95014</streetAddress>
+ <geo>37.322281,-122.028345</geo>
+ </extras>
+ </address>
+ <address>
+ <uid>il2</uid>
+ <guid>63A2F949-2D8D-4C8D-B8A5-DCF2A94610F3</guid>
+ <name>IL2</name>
+ <extras>
+ <streetAddress>2 Infinite Loop, Cupertino, CA 95014</streetAddress>
+ <geo>37.332633,-122.030502</geo>
+ </extras>
+ </address>
+ <address>
+ <uid>il1</uid>
+ <guid>76E7ECA6-08BC-4AE7-930D-F2E7453993A5</guid>
+ <name>IL1</name>
+ <extras>
+ <streetAddress>1 Infinite Loop, Cupertino, CA 95014</streetAddress>
+ <geo>37.331741,-122.030333</geo>
+ </extras>
+ </address>
</accounts>
Modified: CalendarServer/trunk/twistedcaldav/datafilters/peruserdata.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/datafilters/peruserdata.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/twistedcaldav/datafilters/peruserdata.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -67,7 +67,7 @@
# X- properties that are ignored - by default all X- properties are treated as per-user except for the
# ones listed here
- IGNORE_X_PROPERTIES = (Component.HIDDEN_INSTANCE_PROPERTY,)
+ IGNORE_X_PROPERTIES = [Component.HIDDEN_INSTANCE_PROPERTY]
def __init__(self, uid):
"""
Modified: CalendarServer/trunk/twistedcaldav/directory/augment.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/augment.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/twistedcaldav/directory/augment.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -79,6 +79,7 @@
"groups" : "Group",
"locations" : "Location",
"resources" : "Resource",
+ "addresses" : "Address",
}
class AugmentDB(object):
Modified: CalendarServer/trunk/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/directory.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/twistedcaldav/directory/directory.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -86,6 +86,7 @@
recordType_groups = "groups"
recordType_locations = "locations"
recordType_resources = "resources"
+ recordType_addresses = "addresses"
searchContext_location = "location"
searchContext_resource = "resource"
Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_aggregate.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_aggregate.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_aggregate.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -56,6 +56,7 @@
groups = property(_records("groups"))
locations = property(_records("locations"))
resources = property(_records("resources"))
+ addresses = property(_records("addresses"))
recordTypePrefixes = tuple(s[0] for s in testServices)
Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -34,7 +34,8 @@
DirectoryService.recordType_users,
DirectoryService.recordType_groups,
DirectoryService.recordType_locations,
- DirectoryService.recordType_resources
+ DirectoryService.recordType_resources,
+ DirectoryService.recordType_addresses,
))
users = {
Modified: CalendarServer/trunk/twistedcaldav/directory/test/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/util.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/twistedcaldav/directory/test/util.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -44,6 +44,10 @@
# Subclass should init this to a dict of resourcenames keys and dict values.
resources = {}
+ # Subclass should init this to a dict of addressname keys and dict values.
+ addresses = {}
+
+
# Subclass should init this to an IDirectoryService implementation class.
def service(self):
"""
Modified: CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -41,6 +41,7 @@
ELEMENT_GROUP = "group"
ELEMENT_LOCATION = "location"
ELEMENT_RESOURCE = "resource"
+ELEMENT_ADDRESS = "address"
ELEMENT_SHORTNAME = "uid"
ELEMENT_GUID = "guid"
@@ -65,6 +66,7 @@
ELEMENT_GROUP : DirectoryService.recordType_groups,
ELEMENT_LOCATION : DirectoryService.recordType_locations,
ELEMENT_RESOURCE : DirectoryService.recordType_resources,
+ ELEMENT_ADDRESS : DirectoryService.recordType_addresses,
}
class XMLAccountsParser(object):
Modified: CalendarServer/trunk/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/xmlfile.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/twistedcaldav/directory/xmlfile.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -71,6 +71,7 @@
self.recordType_groups,
self.recordType_locations,
self.recordType_resources,
+ self.recordType_addresses,
),
'realmName' : '/Search',
'statSeconds' : 15,
@@ -373,6 +374,7 @@
'groups' : 'group',
'locations' : 'location',
'resources' : 'resource',
+ 'addresses' : 'address',
}
xmlType = xmlTypes[principal.recordType]
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -34,6 +34,7 @@
from twistedcaldav.util import getPasswordFromKeychain
from twistedcaldav.util import KeychainAccessError, KeychainPasswordNotFound
from twistedcaldav.util import computeProcessCount
+from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
from calendarserver.push.util import getAPNTopicFromCertificate
from twistedcaldav import ical
@@ -166,7 +167,7 @@
DEFAULT_RESOURCE_PARAMS = {
"twistedcaldav.directory.xmlfile.XMLDirectoryService": {
"xmlFile": "resources.xml",
- "recordTypes" : ("locations", "resources"),
+ "recordTypes" : ("locations", "resources", "addresses"),
},
"twistedcaldav.directory.appleopendirectory.OpenDirectoryService": {
"node": "/Search",
@@ -588,10 +589,13 @@
"Calendars" : {
"Enabled" : True, # Calendar on/off switch
+ "IgnorePerUserProperties" : [
+ "X-APPLE-STRUCTURED-LOCATION",
+ ],
},
"AddressBooks" : {
"Enabled" : False, # Address Books on/off switch
- }
+ },
},
"RestrictCalendarsToOneComponentType" : True, # Only allow calendars to be created with a single component type
@@ -1560,7 +1564,16 @@
(direction,))
+def _updateSharing(configDict, reloading=False):
+ #
+ # Sharing
+ #
+ # Transfer configured non-per-user property names to PerUserDataFilter
+ for propertyName in configDict.Sharing.Calendars.IgnorePerUserProperties:
+ PerUserDataFilter.IGNORE_X_PROPERTIES.append(propertyName)
+
+
def _updateServers(configDict, reloading=False):
from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers
if configDict.Servers.Enabled:
@@ -1638,6 +1651,7 @@
_updateNotifications,
_updateICalendar,
_updateScheduling,
+ _updateSharing,
_updateServers,
_updateCompliance,
)
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -1993,6 +1993,43 @@
self._componentChanged = True
+ def addStructuredLocation(self, component):
+ """
+ Scan the component for ROOM attendees; if any are associated with an
+ address record which has street address and geo coordinates, add an
+ X-APPLE-STRUCTURED-LOCATION property and update the LOCATION property
+ to contain the name and street address.
+ """
+ for sub in component.subcomponents():
+ for attendee in sub.getAllAttendeeProperties():
+ if attendee.parameterValue("CUTYPE") == "ROOM":
+ value = attendee.value()
+ if value.startswith("urn:uuid:"):
+ guid = value[9:]
+ loc = self.directoryService().recordWithGUID(guid)
+ if loc is not None:
+ guid = loc.extras.get("associatedAddress",
+ None)
+ if guid is not None:
+ addr = self.directoryService().recordWithGUID(guid)
+ if addr is not None:
+ street = addr.extras.get("streetAddress", "")
+ geo = addr.extras.get("geo", "")
+ if street and geo:
+ title = attendee.parameterValue("CN")
+ params = {
+ "X-ADDRESS" : street,
+ "X-APPLE-RADIUS" : "71",
+ "X-TITLE" : title,
+ }
+ structured = Property("X-APPLE-STRUCTURED-LOCATION",
+ "geo:%s" % (geo,), params=params,
+ valuetype=Value.VALUETYPE_URI)
+ sub.replaceProperty(structured)
+ sub.replaceProperty(Property("LOCATION",
+ "%s\n%s" % (title, street)))
+
+
@inlineCallbacks
def doImplicitScheduling(self, component, inserting, internal_state, split_details=None):
@@ -2205,6 +2242,9 @@
# Default/duplicate alarms
self.processAlarms(component, inserting)
+ # Process structured location
+ self.addStructuredLocation(component)
+
# Do scheduling
implicit_result = (yield self.doImplicitScheduling(component, inserting, internal_state))
if isinstance(implicit_result, int):
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -2077,7 +2077,82 @@
self.assertEqual(len(self.flushLoggedErrors(InvalidOverriddenInstanceError)), 1)
+ @inlineCallbacks
+ def test_setComponent_structuredLocation(self):
+ """
+ Verify ROOM attendees who have street address and geo information
+ within the directory will get X-APPLE-STRUCTURED-LOCATION properties
+ added, as well as updated LOCATION properties.
+ """
+ data = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Apple Inc.//Mac OS X 10.9.1//EN
+BEGIN:VEVENT
+UID:561F5DBB-3F38-4B3A-986F-DD05CBAF554F
+DTSTART;TZID=America/Los_Angeles:20131211T164500
+DTEND;TZID=America/Los_Angeles:20131211T174500
+ATTENDEE;CN=Conference Room One;CUTYPE=ROOM;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPAN
+ T;SCHEDULE-STATUS=2.0:urn:uuid:room1
+ATTENDEE;CN=User 01;CUTYPE=INDIVIDUAL;EMAIL=user01 at example.com;PARTSTAT=AC
+ CEPTED:urn:uuid:user01
+CREATED:20131211T221854Z
+DTSTAMP:20131211T230632Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RRULE:FREQ=DAILY;COUNT=5
+SEQUENCE:8
+SUMMARY:locations
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:561F5DBB-3F38-4B3A-986F-DD05CBAF554F
+RECURRENCE-ID;TZID=America/Los_Angeles:20131214T164500
+DTSTART;TZID=America/Los_Angeles:20131214T160000
+DTEND;TZID=America/Los_Angeles:20131214T170000
+ATTENDEE;CN=Conference Room Two;CUTYPE=ROOM;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPAN
+ T;SCHEDULE-STATUS=2.0:urn:uuid:room2
+ATTENDEE;CN=User 01;CUTYPE=INDIVIDUAL;EMAIL=user01 at example.com;PARTSTAT=AC
+ CEPTED:urn:uuid:user01
+CREATED:20131211T221854Z
+DTSTAMP:20131211T230632Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+SEQUENCE:8
+SUMMARY:locations
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+ calendar = yield self.calendarUnderTest(name="calendar", home="user01")
+ yield calendar.createCalendarObjectWithName("structured.ics",
+ Component.fromString(data))
+ cobj = yield self.calendarObjectUnderTest(name="structured.ics",
+ calendar_name="calendar", home="user01")
+ comp = yield cobj.component()
+ components = list(comp.subcomponents())
+
+ # Check first component
+ locProp = components[0].getProperty("LOCATION")
+ self.assertEquals(locProp.value(),
+ "Conference Room One\n1 Infinite Loop, Cupertino, CA 95014")
+ structProp = components[0].getProperty("X-APPLE-STRUCTURED-LOCATION")
+ self.assertEquals(structProp.value(),
+ "geo:37.331741,-122.030333")
+
+ # Check second component
+ locProp = components[1].getProperty("LOCATION")
+ self.assertEquals(locProp.value(),
+ "Conference Room Two\n2 Infinite Loop, Cupertino, CA 95014")
+ structProp = components[1].getProperty("X-APPLE-STRUCTURED-LOCATION")
+ self.assertEquals(structProp.value(),
+ "geo:37.332633,-122.030502")
+
+ yield self.commit()
+
+
+
+
class CalendarObjectSplitting(CommonCommonTests, unittest.TestCase):
"""
CalendarObject splitting tests
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/util.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/util.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -62,9 +62,11 @@
calendarUserAddresses,
cutype="INDIVIDUAL",
thisServer=True,
+ extras={},
):
- super(TestCalendarStoreDirectoryRecord, self).__init__(uid, shortNames, fullName)
+ super(TestCalendarStoreDirectoryRecord, self).__init__(uid, shortNames,
+ fullName, extras=extras)
self.uid = uid
self.shortNames = shortNames
self.fullName = fullName
@@ -158,6 +160,36 @@
for uid in homes:
directory.addRecord(buildDirectoryRecord(uid))
+ # Structured Locations
+ directory.addRecord(TestCalendarStoreDirectoryRecord(
+ "il1", ("il1",), "1 Infinite Loop", [],
+ extras={
+ "geo" : "37.331741,-122.030333",
+ "streetAddress" : "1 Infinite Loop, Cupertino, CA 95014",
+ }
+ ))
+ directory.addRecord(TestCalendarStoreDirectoryRecord(
+ "il2", ("il2",), "2 Infinite Loop", [],
+ extras={
+ "geo" : "37.332633,-122.030502",
+ "streetAddress" : "2 Infinite Loop, Cupertino, CA 95014",
+ }
+ ))
+ directory.addRecord(TestCalendarStoreDirectoryRecord(
+ "room1", ("room1",), "Conference Room One",
+ frozenset(("urn:uuid:room1",)),
+ extras={
+ "associatedAddress" : "il1",
+ }
+ ))
+ directory.addRecord(TestCalendarStoreDirectoryRecord(
+ "room2", ("room2",), "Conference Room Two",
+ frozenset(("urn:uuid:room2",)),
+ extras={
+ "associatedAddress" : "il2",
+ }
+ ))
+
return directory
Modified: CalendarServer/trunk/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/util.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/txdav/common/datastore/test/util.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -112,6 +112,11 @@
def recordWithUID(self, uid):
return self.records.get(uid)
+ def recordWithGUID(self, guid):
+ for record in self.records.itervalues():
+ if record.guid == guid:
+ return record
+ return None
def addRecord(self, record):
self.records[record.uid] = record
@@ -122,11 +127,13 @@
implements(IStoreDirectoryRecord)
- def __init__(self, uid, shortNames, fullName):
+ def __init__(self, uid, shortNames, fullName, extras={}):
self.uid = uid
+ self.guid = uid
self.shortNames = shortNames
self.fullName = fullName
self.displayName = self.fullName if self.fullName else self.shortNames[0]
+ self.extras = extras
Modified: CalendarServer/trunk/txdav/common/idirectoryservice.py
===================================================================
--- CalendarServer/trunk/txdav/common/idirectoryservice.py 2013-12-13 14:47:16 UTC (rev 12083)
+++ CalendarServer/trunk/txdav/common/idirectoryservice.py 2013-12-13 17:37:30 UTC (rev 12084)
@@ -39,8 +39,15 @@
@rtype: L{IStoreDirectoryRecord}
"""
+ def recordWithGUID(guid): #@NoSelf
+ """
+ Return the record for the specified store guid.
+ @return: the record.
+ @rtype: L{IStoreDirectoryRecord}
+ """
+
class IStoreDirectoryRecord(Interface):
"""
Directory record object
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140312/0ac3eb67/attachment.html>
More information about the calendarserver-changes
mailing list