[CalendarServer-changes] [10008] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Thu Nov 8 15:22:14 PST 2012
Revision: 10008
http://trac.calendarserver.org//changeset/10008
Author: sagen at apple.com
Date: 2012-11-08 15:22:14 -0800 (Thu, 08 Nov 2012)
Log Message:
-----------
Auto-accept group feature added. You can now assign an auto-accept group to a principal, and that principal will then automatically accept invites from members of that group (or automatically decline if there is a conflict).
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/tools/gateway.py
CalendarServer/trunk/calendarserver/tools/principals.py
CalendarServer/trunk/calendarserver/tools/test/gateway/users-groups.xml
CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
CalendarServer/trunk/doc/calendarserver_manage_principals.8
CalendarServer/trunk/twistedcaldav/directory/aggregate.py
CalendarServer/trunk/twistedcaldav/directory/augment.py
CalendarServer/trunk/twistedcaldav/directory/directory.py
CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py
CalendarServer/trunk/twistedcaldav/directory/principal.py
CalendarServer/trunk/twistedcaldav/directory/test/augments.xml
CalendarServer/trunk/twistedcaldav/directory/test/test_directory.py
CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py
CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py
CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py
CalendarServer/trunk/twistedcaldav/scheduling/processing.py
Modified: CalendarServer/trunk/calendarserver/tools/gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/gateway.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/calendarserver/tools/gateway.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -171,6 +171,7 @@
'Country' : { 'extras' : True, 'attr' : 'country', },
'Phone' : { 'extras' : True, 'attr' : 'phone', },
'AutoSchedule' : { 'attr' : 'autoSchedule', },
+ 'AutoAcceptGroup' : { 'attr' : 'autoAcceptGroup', },
}
class Runner(object):
@@ -249,6 +250,7 @@
respondWithError("Principal not found: %s" % (guid,))
return
recordDict['AutoSchedule'] = principal.getAutoSchedule()
+ recordDict['AutoAcceptGroup'] = principal.getAutoAcceptGroup()
recordDict['ReadProxies'], recordDict['WriteProxies'] = (yield getProxies(principal))
respond(command, recordDict)
@@ -262,6 +264,7 @@
principal = principalForPrincipalID(command['GeneratedUID'],
directory=self.dir)
(yield principal.setAutoSchedule(command.get('AutoSchedule', False)))
+ (yield principal.setAutoAcceptGroup(command.get('AutoAcceptGroup', "")))
kwargs = {}
for key, info in attrMap.iteritems():
@@ -325,6 +328,7 @@
principal = principalForPrincipalID(command['GeneratedUID'],
directory=self.dir)
(yield principal.setAutoSchedule(command.get('AutoSchedule', False)))
+ (yield principal.setAutoAcceptGroup(command.get('AutoAcceptGroup', "")))
kwargs = {}
for key, info in attrMap.iteritems():
Modified: CalendarServer/trunk/calendarserver/tools/principals.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/principals.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/calendarserver/tools/principals.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -88,6 +88,8 @@
print " --get-auto-schedule: read auto-schedule state"
print " --set-auto-schedule-mode={default|none|accept-always|decline-always|accept-if-free|decline-if-busy|automatic}: set auto-schedule mode"
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 " --remove: remove a principal"
@@ -118,6 +120,8 @@
"get-auto-schedule",
"set-auto-schedule-mode=",
"get-auto-schedule-mode",
+ "set-auto-accept-group=",
+ "get-auto-accept-group",
"verbose",
],
)
@@ -223,6 +227,18 @@
elif opt in ("", "--get-auto-schedule-mode"):
principalActions.append((action_getAutoScheduleMode,))
+ elif opt in ("", "--set-auto-accept-group"):
+ try:
+ principalForPrincipalID(arg, checkOnly=True)
+ except ValueError, e:
+ abort(e)
+
+ principalActions.append((action_setAutoAcceptGroup, arg))
+
+ elif opt in ("", "--get-auto-accept-group"):
+ principalActions.append((action_getAutoAcceptGroup,))
+
+
else:
raise NotImplementedError(opt)
@@ -768,7 +784,50 @@
autoScheduleMode,
)
+ at inlineCallbacks
+def action_setAutoAcceptGroup(principal, autoAcceptGroup):
+ if principal.record.recordType == "groups":
+ print "Setting auto-accept-group for %s is not allowed." % (principal,)
+ elif principal.record.recordType == "users" and not config.Scheduling.Options.AutoSchedule.AllowUsers:
+ print "Setting auto-accept-group for %s is not allowed." % (principal,)
+
+ else:
+ groupPrincipal = principalForPrincipalID(autoAcceptGroup)
+ if groupPrincipal is None or groupPrincipal.record.recordType != "groups":
+ print "Invalid principal ID: %s" % (autoAcceptGroup,)
+ else:
+ print "Setting auto-accept-group to %s for %s" % (
+ prettyPrincipal(groupPrincipal),
+ prettyPrincipal(principal),
+ )
+
+ (yield updateRecord(False, config.directory,
+ principal.record.recordType,
+ guid=principal.record.guid,
+ shortNames=principal.record.shortNames,
+ fullName=principal.record.fullName,
+ autoAcceptGroup=groupPrincipal.record.guid,
+ **principal.record.extras
+ ))
+
+def action_getAutoAcceptGroup(principal):
+ autoAcceptGroup = principal.getAutoAcceptGroup()
+ if autoAcceptGroup:
+ record = config.directory.recordWithGUID(autoAcceptGroup)
+ if record is not None:
+ groupPrincipal = config.directory.principalCollection.principalForUID(record.uid)
+ if groupPrincipal is not None:
+ print "Auto-accept-group for %s is %s" % (
+ prettyPrincipal(principal),
+ prettyPrincipal(groupPrincipal),
+ )
+ return
+ print "Invalid auto-accept-group assigned: %s" % (autoAcceptGroup,)
+ else:
+ print "No auto-accept-group assigned to %s" % (prettyPrincipal(principal),)
+
+
def abort(msg, status=1):
sys.stdout.write("%s\n" % (msg,))
try:
@@ -856,18 +915,33 @@
matching the guid in kwargs.
"""
+ assignAutoSchedule = False
if kwargs.has_key("autoSchedule"):
+ assignAutoSchedule = True
autoSchedule = kwargs["autoSchedule"]
del kwargs["autoSchedule"]
- else:
+ elif create:
+ assignAutoSchedule = True
autoSchedule = recordType in ("locations", "resources")
+ assignAutoScheduleMode = False
if kwargs.has_key("autoScheduleMode"):
+ assignAutoScheduleMode = True
autoScheduleMode = kwargs["autoScheduleMode"]
del kwargs["autoScheduleMode"]
- else:
+ elif create:
+ assignAutoScheduleMode = True
autoScheduleMode = None
+ assignAutoAcceptGroup = False
+ if kwargs.has_key("autoAcceptGroup"):
+ assignAutoAcceptGroup = True
+ autoAcceptGroup = kwargs["autoAcceptGroup"]
+ del kwargs["autoAcceptGroup"]
+ elif create:
+ assignAutoAcceptGroup = True
+ autoAcceptGroup = None
+
for key, value in kwargs.items():
if isinstance(value, unicode):
kwargs[key] = value.encode("utf-8")
@@ -890,8 +964,13 @@
augmentService = directory.serviceForRecordType(recordType).augmentService
augmentRecord = (yield augmentService.getAugmentRecord(kwargs['guid'], recordType))
- augmentRecord.autoSchedule = autoSchedule
- augmentRecord.autoScheduleMode = autoScheduleMode
+
+ if assignAutoSchedule:
+ augmentRecord.autoSchedule = autoSchedule
+ if assignAutoScheduleMode:
+ augmentRecord.autoScheduleMode = autoScheduleMode
+ if assignAutoAcceptGroup:
+ augmentRecord.autoAcceptGroup = autoAcceptGroup
(yield augmentService.addAugmentRecords([augmentRecord]))
try:
directory.updateRecord(recordType, **kwargs)
Modified: CalendarServer/trunk/calendarserver/tools/test/gateway/users-groups.xml
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/gateway/users-groups.xml 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/calendarserver/tools/test/gateway/users-groups.xml 2012-11-08 23:22:14 UTC (rev 10008)
@@ -37,4 +37,13 @@
<member type="users">user02</member>
</members>
</group>
+ <group>
+ <uid>testgroup2</uid>
+ <guid>f5a6142c-4189-4e9e-90b0-9cd0268b314b</guid>
+ <password>test</password>
+ <name>Group 02</name>
+ <members>
+ <member type="users">user01</member>
+ </members>
+ </group>
</accounts>
Modified: CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_gateway.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/calendarserver/tools/test/test_gateway.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -121,6 +121,7 @@
self.assertEquals(results["result"]["RealName"], "Created Location 01 %s" % unichr(208))
self.assertEquals(results["result"]["Comment"], "Test Comment")
self.assertEquals(results["result"]["AutoSchedule"], True)
+ self.assertEquals(results["result"]["AutoAcceptGroup"], "E5A6142C-4189-4E9E-90B0-9CD0268B314B")
self.assertEquals(set(results["result"]["ReadProxies"]), set(['user03', 'user04']))
self.assertEquals(set(results["result"]["WriteProxies"]), set(['user05', 'user06']))
@@ -202,9 +203,11 @@
self.assertEquals(record.extras["country"], "Updated USA")
self.assertEquals(record.extras["phone"], "(408) 555-1213")
self.assertEquals(record.autoSchedule, True)
+ self.assertEquals(record.autoAcceptGroup, "F5A6142C-4189-4E9E-90B0-9CD0268B314B")
results = yield self.runCommand(command_getLocationAttributes)
self.assertEquals(results["result"]["AutoSchedule"], True)
+ self.assertEquals(results["result"]["AutoAcceptGroup"], "F5A6142C-4189-4E9E-90B0-9CD0268B314B")
self.assertEquals(set(results["result"]["ReadProxies"]), set(['user03']))
self.assertEquals(set(results["result"]["WriteProxies"]), set(['user05', 'user06', 'user07']))
@@ -312,6 +315,8 @@
<string>createLocation</string>
<key>AutoSchedule</key>
<true/>
+ <key>AutoAcceptGroup</key>
+ <string>E5A6142C-4189-4E9E-90B0-9CD0268B314B</string>
<key>GeneratedUID</key>
<string>836B1B66-2E9A-4F46-8B1C-3DD6772C20B2</string>
<key>RealName</key>
@@ -495,6 +500,8 @@
<string>setLocationAttributes</string>
<key>AutoSchedule</key>
<true/>
+ <key>AutoAcceptGroup</key>
+ <string>F5A6142C-4189-4E9E-90B0-9CD0268B314B</string>
<key>GeneratedUID</key>
<string>836B1B66-2E9A-4F46-8B1C-3DD6772C20B2</string>
<key>RealName</key>
Modified: CalendarServer/trunk/doc/calendarserver_manage_principals.8
===================================================================
--- CalendarServer/trunk/doc/calendarserver_manage_principals.8 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/doc/calendarserver_manage_principals.8 2012-11-08 23:22:14 UTC (rev 10008)
@@ -38,6 +38,8 @@
.Op Fl -get-auto-schedule
.Op Fl -set-auto-schedule-mode Ar none|accept-always|decline-always|accept-if-free|decline-if-busy|automatic
.Op Fl -get-auto-schedule-mode
+.Op Fl -set-auto-accept-group Ar group
+.Op Fl -get-auto-accept-group
.Op Fl -add Ar locations|resources full-name [record-name] [GUID]
.Op Fl -remove
.Ar principal
@@ -123,6 +125,11 @@
Enable or disable automatic scheduling.
.It Fl -get-auto-schedule
Get the automatic scheduling state.
+.It Fl -set-auto-accept-group Ar group
+The principal will auto-accept any invites from any member of the group (as long
+as there are no conflicts).
+.It Fl -get-auto-accept-group
+Get the currently assigned auto-accept group for the principal.
.It Fl -add Ar locations|resources full-name [record-name] [GUID]
Add a new location or resource. Record name and GUID are optional. If
GUID is not specified, one will be generated. If record name is not
Modified: CalendarServer/trunk/twistedcaldav/directory/aggregate.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/aggregate.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/directory/aggregate.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -67,6 +67,8 @@
)
recordTypes[recordType] = service
+ service.aggregateService = self
+
self.realmName = realmName
self._recordTypes = recordTypes
Modified: CalendarServer/trunk/twistedcaldav/directory/augment.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/augment.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/directory/augment.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -60,6 +60,7 @@
enabledForCalendaring=False,
autoSchedule=False,
autoScheduleMode="default",
+ autoAcceptGroup="",
enabledForAddressBooks=False,
enabledForLogin=True,
):
@@ -72,6 +73,7 @@
self.enabledForLogin = enabledForLogin
self.autoSchedule = autoSchedule
self.autoScheduleMode = autoScheduleMode if autoScheduleMode in allowedAutoScheduleModes else "default"
+ self.autoAcceptGroup = autoAcceptGroup
self.clonedFromDefault = False
recordTypesMap = {
@@ -459,6 +461,8 @@
addSubElement(recordNode, xmlaugmentsparser.ELEMENT_AUTOSCHEDULE, "true" if record.autoSchedule else "false")
if record.autoScheduleMode:
addSubElement(recordNode, xmlaugmentsparser.ELEMENT_AUTOSCHEDULE_MODE, record.autoScheduleMode)
+ if record.autoAcceptGroup:
+ addSubElement(recordNode, xmlaugmentsparser.ELEMENT_AUTOACCEPTGROUP, record.autoAcceptGroup)
def refresh(self):
"""
@@ -570,11 +574,11 @@
"""
# Query for the record information
- results = (yield self.query("select UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, LOGINENABLED from AUGMENTS where UID = :1", (uid,)))
+ results = (yield self.query("select UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED from AUGMENTS where UID = :1", (uid,)))
if not results:
returnValue(None)
else:
- uid, enabled, serverid, partitionid, enabledForCalendaring, enabledForAddressBooks, autoSchedule, autoScheduleMode, enabledForLogin = results[0]
+ uid, enabled, serverid, partitionid, enabledForCalendaring, enabledForAddressBooks, autoSchedule, autoScheduleMode, autoAcceptGroup, enabledForLogin = results[0]
record = AugmentRecord(
uid = uid,
@@ -586,6 +590,7 @@
enabledForLogin = enabledForLogin == "T",
autoSchedule = autoSchedule == "T",
autoScheduleMode = autoScheduleMode,
+ autoAcceptGroup = autoAcceptGroup,
)
returnValue(record)
@@ -648,6 +653,7 @@
("ADDRESSBOOKS", "text(1)"),
("AUTOSCHEDULE", "text(1)"),
("AUTOSCHEDULEMODE", "text"),
+ ("AUTOACCEPTGROUP", "text"),
("LOGINENABLED", "text(1)"),
),
ifnotexists=True,
@@ -671,8 +677,8 @@
def _addRecord(self, record):
yield self.execute(
"""insert or replace into AUGMENTS
- (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, LOGINENABLED)
- values (:1, :2, :3, :4, :5, :6, :7, :8, :9)""",
+ (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
+ values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)""",
(
record.uid,
"T" if record.enabled else "F",
@@ -682,6 +688,7 @@
"T" if record.enabledForAddressBooks else "F",
"T" if record.autoSchedule else "F",
record.autoScheduleMode if record.autoScheduleMode else "",
+ record.autoAcceptGroup,
"T" if record.enabledForLogin else "F",
)
)
@@ -703,8 +710,8 @@
def _addRecord(self, record):
yield self.execute(
"""insert into AUGMENTS
- (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, LOGINENABLED)
- values (:1, :2, :3, :4, :5, :6, :7, :8, :9)""",
+ (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
+ values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)""",
(
record.uid,
"T" if record.enabled else "F",
@@ -714,6 +721,7 @@
"T" if record.enabledForAddressBooks else "F",
"T" if record.autoSchedule else "F",
record.autoScheduleMode if record.autoScheduleMode else "",
+ record.autoAcceptGroup,
"T" if record.enabledForLogin else "F",
)
)
@@ -722,8 +730,8 @@
def _modifyRecord(self, record):
yield self.execute(
"""update AUGMENTS set
- (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, LOGINENABLED) =
- (:1, :2, :3, :4, :5, :6, :7, :8, :9) where UID = :10""",
+ (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED) =
+ (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10) where UID = :11""",
(
record.uid,
"T" if record.enabled else "F",
@@ -733,6 +741,7 @@
"T" if record.enabledForAddressBooks else "F",
"T" if record.autoSchedule else "F",
record.autoScheduleMode if record.autoScheduleMode else "",
+ record.autoAcceptGroup,
"T" if record.enabledForLogin else "F",
record.uid,
)
Modified: CalendarServer/trunk/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/directory.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/directory/directory.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -82,6 +82,8 @@
searchContext_location = "location"
searchContext_attendee = "attendee"
+ aggregateService = None
+
def _generatedGUID(self):
if not hasattr(self, "_guid"):
realmName = self.realmName
@@ -477,6 +479,7 @@
autoaccept = wpframework.get("AutoAcceptsInvitation", False)
proxy = wpframework.get("CalendaringDelegate", None)
read_only_proxy = wpframework.get("ReadOnlyCalendaringDelegate", None)
+ autoAcceptGroup = wpframework.get("AutoAcceptGroup", "")
except (ExpatError, AttributeError), e:
self.log_error(
"Failed to parse ResourceInfo attribute of record (%s)%s (guid=%s): %s\n%s" %
@@ -484,7 +487,7 @@
)
raise ValueError("Invalid ResourceInfo")
- return (autoaccept, proxy, read_only_proxy,)
+ return (autoaccept, proxy, read_only_proxy, autoAcceptGroup)
def getExternalProxyAssignments(self):
@@ -1245,6 +1248,7 @@
firstName=None, lastName=None, emailAddresses=set(),
calendarUserAddresses=set(),
autoSchedule=False, autoScheduleMode=None,
+ autoAcceptGroup="",
enabledForCalendaring=None,
enabledForAddressBooks=None,
uid=None,
@@ -1280,6 +1284,7 @@
self.enabledForCalendaring = enabledForCalendaring
self.autoSchedule = autoSchedule
self.autoScheduleMode = autoScheduleMode
+ self.autoAcceptGroup = autoAcceptGroup
self.enabledForAddressBooks = enabledForAddressBooks
self.enabledForLogin = enabledForLogin
self.extProxies = extProxies
@@ -1353,6 +1358,7 @@
self.enabledForAddressBooks = augment.enabledForAddressBooks
self.autoSchedule = augment.autoSchedule
self.autoScheduleMode = augment.autoScheduleMode
+ self.autoAcceptGroup = augment.autoAcceptGroup
self.enabledForLogin = augment.enabledForLogin
if (self.enabledForCalendaring or self.enabledForAddressBooks) and self.recordType == self.service.recordType_groups:
@@ -1556,7 +1562,28 @@
return True
+ def autoAcceptMembers(self):
+ """
+ Return the list of GUIDs for which this record will automatically accept
+ invites from (assuming no conflicts). This list is based on the group
+ assigned to record.autoAcceptGroup. Cache the expanded group membership
+ within the record.
+ @return: the list of members of the autoAcceptGroup, or an empty list if
+ not assigned
+ @rtype: C{list} of GUID C{str}
+ """
+ if not hasattr(self, "_cachedAutoAcceptMembers"):
+ self._cachedAutoAcceptMembers = []
+ if self.autoAcceptGroup:
+ service = self.service.aggregateService or self.service
+ groupRecord = service.recordWithGUID(self.autoAcceptGroup)
+ if groupRecord is not None:
+ self._cachedAutoAcceptMembers = [m.guid for m in groupRecord.expandedMembers()]
+
+ return self._cachedAutoAcceptMembers
+
+
class DirectoryError(RuntimeError):
"""
Generic directory error.
Modified: CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -182,6 +182,7 @@
"autoScheduleEnabledValue": "yes",
"proxyAttr": None, # list of GUIDs
"readOnlyProxyAttr": None, # list of GUIDs
+ "autoAcceptGroupAttr": None, # single group GUID
},
"partitionSchema": {
"serverIdAttr": None, # maps to augments server-id
@@ -261,6 +262,8 @@
attrSet.add(self.resourceSchema["resourceInfoAttr"])
if self.resourceSchema["autoScheduleAttr"]:
attrSet.add(self.resourceSchema["autoScheduleAttr"])
+ if self.resourceSchema["autoAcceptGroupAttr"]:
+ attrSet.add(self.resourceSchema["autoAcceptGroupAttr"])
if self.resourceSchema["proxyAttr"]:
attrSet.add(self.resourceSchema["proxyAttr"])
if self.resourceSchema["readOnlyProxyAttr"]:
@@ -787,6 +790,7 @@
proxyGUIDs = ()
readOnlyProxyGUIDs = ()
autoSchedule = False
+ autoAcceptGroup = ""
memberGUIDs = []
# LDAP attribute -> principal matchings
@@ -836,7 +840,8 @@
(
autoSchedule,
proxy,
- readOnlyProxy
+ readOnlyProxy,
+ autoAcceptGroup
) = self.parseResourceInfo(
resourceInfo,
guid,
@@ -861,6 +866,9 @@
if self.resourceSchema["readOnlyProxyAttr"]:
readOnlyProxyGUIDs = set(self._getMultipleLdapAttributes(attrs,
self.resourceSchema["readOnlyProxyAttr"]))
+ if self.resourceSchema["autoAcceptGroupAttr"]:
+ autoAcceptGroup = self._getUniqueLdapAttribute(attrs,
+ self.resourceSchema["autoAcceptGroupAttr"])
serverID = partitionID = None
if self.partitionSchema["serverIdAttr"]:
@@ -906,6 +914,7 @@
partitionID=partitionID,
enabledForCalendaring=enabledForCalendaring,
autoSchedule=autoSchedule,
+ autoAcceptGroup=autoAcceptGroup,
enabledForAddressBooks=enabledForAddressBooks, # TODO: add to LDAP?
enabledForLogin=enabledForLogin,
)
Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -994,14 +994,20 @@
def getAutoSchedule(self):
return self.record.autoSchedule
- def canAutoSchedule(self):
+ def canAutoSchedule(self, organizer=None):
"""
Determine the auto-schedule state based on record state, type and config settings.
+
+ @param organizer: the CUA of the organizer trying to schedule this principal
+ @type organizer: C{str}
"""
-
+
if config.Scheduling.Options.AutoSchedule.Enabled:
- if config.Scheduling.Options.AutoSchedule.Always or self.getAutoSchedule():
- if self.getCUType() != "INDIVIDUAL" or config.Scheduling.Options.AutoSchedule.AllowUsers:
+ if (config.Scheduling.Options.AutoSchedule.Always or
+ self.getAutoSchedule() or
+ self.autoAcceptFromOrganizer(organizer)):
+ if (self.getCUType() != "INDIVIDUAL" or
+ config.Scheduling.Options.AutoSchedule.AllowUsers):
return True
return False
@@ -1012,9 +1018,65 @@
augmentRecord.autoScheduleMode = autoScheduleMode
(yield self.record.service.augmentService.addAugmentRecords([augmentRecord]))
- def getAutoScheduleMode(self):
- return self.record.autoScheduleMode
+ def getAutoScheduleMode(self, organizer=None):
+ """
+ Return the auto schedule mode value for the principal. If the optional
+ organizer is provided, and that organizer is a member of the principal's
+ auto-accept group, return "automatic" instead; this allows specifying a
+ priliveged group whose scheduling requests are automatically accepted or
+ declined, regardless of whether the principal is normally managed by a
+ delegate.
+ @param organizer: the CUA of the organizer scheduling this principal
+ @type organizer: C{str}
+ @return: auto schedule mode; one of: none, accept-always, decline-always,
+ accept-if-free, decline-if-busy, automatic (see stdconfig.py)
+ @rtype: C{str}
+ """
+ autoScheduleMode = self.record.autoScheduleMode
+ if self.autoAcceptFromOrganizer(organizer):
+ autoScheduleMode = "automatic"
+ return autoScheduleMode
+
+
+ @inlineCallbacks
+ def setAutoAcceptGroup(self, autoAcceptGroup):
+ """
+ Sets the group whose members can automatically schedule with this principal
+ even if this principal's auto-schedule is False (assuming no conflicts).
+
+ @param autoAcceptGroup: GUID of the group
+ @type autoAcceptGroup: C{str}
+ """
+ self.record.autoAcceptGroup = autoAcceptGroup
+ augmentRecord = (yield self.record.service.augmentService.getAugmentRecord(self.record.guid, self.record.recordType))
+ augmentRecord.autoAcceptGroup = autoAcceptGroup
+ (yield self.record.service.augmentService.addAugmentRecords([augmentRecord]))
+
+ def getAutoAcceptGroup(self):
+ """
+ Returns the GUID of the auto accept group assigned to this principal, or empty
+ string if not assigned
+ """
+ return self.record.autoAcceptGroup
+
+ def autoAcceptFromOrganizer(self, organizer):
+ """
+ Is the organizer a member of this principal's autoAcceptGroup?
+
+ @param organizer: CUA of the organizer
+ @type organizer: C{str}
+ @return: True if the autoAcceptGroup is assigned, and the organizer is a member
+ of that group. False otherwise.
+ @rtype: C{bool}
+ """
+ if organizer is not None and self.record.autoAcceptGroup is not None:
+ organizerPrincipal = self.parent.principalForCalendarUserAddress(organizer)
+ if organizerPrincipal is not None:
+ if organizerPrincipal.record.guid in self.record.autoAcceptMembers():
+ return True
+ return False
+
def getCUType(self):
return self.record.getCUType()
Modified: CalendarServer/trunk/twistedcaldav/directory/test/augments.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/augments.xml 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/directory/test/augments.xml 2012-11-08 23:22:14 UTC (rev 10008)
@@ -120,6 +120,7 @@
<enable>true</enable>
<enable-calendar>true</enable-calendar>
<enable-addressbook>true</enable-addressbook>
+ <auto-accept-group>both_coasts</auto-accept-group>
</record>
<record>
<uid>orion</uid>
Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_directory.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_directory.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -757,6 +757,30 @@
}
)
+ def test_autoAcceptMembers(self):
+ """
+ autoAcceptMembers( ) returns an empty list if no autoAcceptGroup is
+ assigned, or the expanded membership if assigned.
+ """
+
+ # No auto-accept-group for "orion" in augments.xml
+ orion = self.directoryService.recordWithGUID("orion")
+ self.assertEquals( orion.autoAcceptMembers(), [])
+
+ # "both_coasts" group assigned to "apollo" in augments.xml
+ apollo = self.directoryService.recordWithGUID("apollo")
+ self.assertEquals(
+ set(apollo.autoAcceptMembers()),
+ set([
+ "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0",
+ "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1",
+ "5A985493-EE2C-4665-94CF-4DFEA3A89500",
+ "6423F94A-6B76-4A3A-815B-D52CFD77935D",
+ "right_coast",
+ "left_coast",
+ ])
+ )
+
class RecordsMatchingTokensTests(TestCase):
@inlineCallbacks
Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -547,6 +547,7 @@
"autoScheduleAttr": None,
"proxyAttr": None,
"readOnlyProxyAttr": None,
+ "autoAcceptGroupAttr": None,
},
"partitionSchema": {
"serverIdAttr": "server-id", # maps to augments server-id
@@ -762,6 +763,7 @@
"autoScheduleAttr": None,
"proxyAttr": None,
"readOnlyProxyAttr": None,
+ "autoAcceptGroupAttr": None,
},
"partitionSchema": {
"serverIdAttr": "server-id", # maps to augments server-id
@@ -979,6 +981,7 @@
"autoScheduleAttr": None,
"proxyAttr": None,
"readOnlyProxyAttr": None,
+ "autoAcceptGroupAttr": None,
},
"partitionSchema": {
"serverIdAttr": "server-id", # maps to augments server-id
@@ -1192,6 +1195,7 @@
"autoScheduleAttr": None,
"proxyAttr": None,
"readOnlyProxyAttr": None,
+ "autoAcceptGroupAttr": None,
},
"partitionSchema": {
"serverIdAttr": "server-id", # maps to augments server-id
@@ -1363,7 +1367,7 @@
])
)
- # Resource with delegates and autoSchedule = True
+ # Resource with delegates, autoSchedule = True, and autoAcceptGroup
dn = "cn=odtestresource,cn=resources,dc=example,dc=com"
guid = 'D3094652-344B-4633-8DB8-09639FA00FB6'
@@ -1382,6 +1386,8 @@
<string>6C6CD280-E6E3-11DF-9492-0800200C9A66</string>
<key>ReadOnlyCalendaringDelegate</key>
<string>6AA1AE12-592F-4190-A069-547CD83C47C0</string>
+<key>AutoAcceptGroup</key>
+<string>77A8EB52-AA2A-42ED-8843-B2BEE863AC70</string>
</dict>
</dict>
</plist>"""]
@@ -1394,6 +1400,8 @@
self.assertEquals(record.externalReadOnlyProxies(),
set(['6AA1AE12-592F-4190-A069-547CD83C47C0']))
self.assertTrue(record.autoSchedule)
+ self.assertEquals(record.autoAcceptGroup,
+ '77A8EB52-AA2A-42ED-8843-B2BEE863AC70')
# Resource with no delegates and autoSchedule = False
@@ -1422,6 +1430,7 @@
self.assertEquals(record.externalReadOnlyProxies(),
set())
self.assertFalse(record.autoSchedule)
+ self.assertEquals(record.autoAcceptGroup, "")
# Now switch off the resourceInfoAttr and switch to individual
@@ -1432,6 +1441,7 @@
"autoScheduleEnabledValue" : "yes",
"proxyAttr" : "proxy",
"readOnlyProxyAttr" : "read-only-proxy",
+ "autoAcceptGroupAttr" : "auto-accept-group",
}
# Resource with delegates and autoSchedule = True
@@ -1444,6 +1454,7 @@
'auto-schedule' : ['yes'],
'proxy' : ['6C6CD280-E6E3-11DF-9492-0800200C9A66'],
'read-only-proxy' : ['6AA1AE12-592F-4190-A069-547CD83C47C0'],
+ 'auto-accept-group' : ['77A8EB52-AA2A-42ED-8843-B2BEE863AC70'],
}
record = self.service._ldapResultToRecord(dn, attrs,
self.service.recordType_resources)
@@ -1453,6 +1464,8 @@
self.assertEquals(record.externalReadOnlyProxies(),
set(['6AA1AE12-592F-4190-A069-547CD83C47C0']))
self.assertTrue(record.autoSchedule)
+ self.assertEquals(record.autoAcceptGroup,
+ '77A8EB52-AA2A-42ED-8843-B2BEE863AC70')
def test_listRecords(self):
"""
Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -560,7 +560,7 @@
"""
DirectoryPrincipalResource.canAutoSchedule()
"""
-
+
# Set all resources and locations to auto-schedule, plus one user
for provisioningResource, recordType, recordResource, record in self._allRecords():
if record.enabledForCalendaring:
@@ -590,6 +590,27 @@
if record.enabledForCalendaring:
self.assertFalse(recordResource.canAutoSchedule())
+
+ def test_canAutoScheduleAutoAcceptGroup(self):
+ """
+ DirectoryPrincipalResource.canAutoSchedule(organizer)
+ """
+
+ # Location "apollo" has an auto-accept group ("both_coasts") set in augments.xml,
+ # therefore any organizer in that group should be able to auto schedule
+
+ for provisioningResource, recordType, recordResource, record in self._allRecords():
+ if record.uid == "apollo":
+
+ # No organizer
+ self.assertFalse(recordResource.canAutoSchedule())
+
+ # Organizer in auto-accept group
+ self.assertTrue(recordResource.canAutoSchedule(organizer="mailto:wsanchez at example.com"))
+ # Organizer not in auto-accept group
+ self.assertFalse(recordResource.canAutoSchedule(organizer="mailto:a at example.com"))
+
+
@inlineCallbacks
def test_defaultAccessControlList_principals(self):
"""
Modified: CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -43,6 +43,7 @@
ELEMENT_ENABLELOGIN = "enable-login"
ELEMENT_AUTOSCHEDULE = "auto-schedule"
ELEMENT_AUTOSCHEDULE_MODE = "auto-schedule-mode"
+ELEMENT_AUTOACCEPTGROUP = "auto-accept-group"
ATTRIBUTE_REPEAT = "repeat"
@@ -60,6 +61,7 @@
ELEMENT_ENABLELOGIN: "enabledForLogin",
ELEMENT_AUTOSCHEDULE: "autoSchedule",
ELEMENT_AUTOSCHEDULE_MODE: "autoScheduleMode",
+ ELEMENT_AUTOACCEPTGROUP: "autoAcceptGroup",
}
class XMLAugmentsParser(object):
@@ -103,6 +105,7 @@
ELEMENT_PARTITIONID,
ELEMENT_HOSTEDAT,
ELEMENT_AUTOSCHEDULE_MODE,
+ ELEMENT_AUTOACCEPTGROUP,
):
fields[node.tag] = node.text if node.text else ""
elif node.tag in (
Modified: CalendarServer/trunk/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/processing.py 2012-11-08 04:53:48 UTC (rev 10007)
+++ CalendarServer/trunk/twistedcaldav/scheduling/processing.py 2012-11-08 23:22:14 UTC (rev 10008)
@@ -500,8 +500,12 @@
new_calendar = iTipProcessing.processNewRequest(self.message, self.recipient.cuaddr, creating=True)
# Handle auto-reply behavior
- if self.recipient.principal.canAutoSchedule():
- send_reply, store_inbox, partstat = (yield self.checkAttendeeAutoReply(new_calendar, self.recipient.principal.getAutoScheduleMode()))
+ organizer = normalizeCUAddr(self.message.getOrganizer())
+ if self.recipient.principal.canAutoSchedule(organizer=organizer):
+ # auto schedule mode can depend on who the organizer is
+ mode = self.recipient.principal.getAutoScheduleMode(organizer=organizer)
+ send_reply, store_inbox, partstat = (yield self.checkAttendeeAutoReply(new_calendar,
+ mode))
# Only store inbox item when reply is not sent or always for users
store_inbox = store_inbox or self.recipient.principal.getCUType() == "INDIVIDUAL"
@@ -533,8 +537,13 @@
if new_calendar:
# Handle auto-reply behavior
- if self.recipient.principal.canAutoSchedule():
- send_reply, store_inbox, partstat = (yield self.checkAttendeeAutoReply(new_calendar, self.recipient.principal.getAutoScheduleMode()))
+ x = self.recipient.principal.canAutoSchedule()
+ organizer = normalizeCUAddr(self.message.getOrganizer())
+ if self.recipient.principal.canAutoSchedule(organizer=organizer):
+ # auto schedule mode can depend on who the organizer is
+ mode = self.recipient.principal.getAutoScheduleMode(organizer=organizer)
+ send_reply, store_inbox, partstat = (yield self.checkAttendeeAutoReply(new_calendar,
+ mode))
# Only store inbox item when reply is not sent or always for users
store_inbox = store_inbox or self.recipient.principal.getCUType() == "INDIVIDUAL"
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20121108/61c33b90/attachment-0001.html>
More information about the calendarserver-changes
mailing list