<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[12982] CalendarServer/branches/users/sagen/move2who-4</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/12982">12982</a></dd>
<dt>Author</dt> <dd>sagen@apple.com</dd>
<dt>Date</dt> <dd>2014-03-19 20:28:55 -0700 (Wed, 19 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>csmp supports record updates, including autoschedule and autoacceptgroup</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssagenmove2who4calendarservertoolsprincipalspy">CalendarServer/branches/users/sagen/move2who-4/calendarserver/tools/principals.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who4twistedcaldavdirectoryprincipalpy">CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/principal.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2who4txdavwhoaugmentpy">CalendarServer/branches/users/sagen/move2who-4/txdav/who/augment.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserssagenmove2who4calendarservertoolsprincipalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-4/calendarserver/tools/principals.py (12981 => 12982)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-4/calendarserver/tools/principals.py        2014-03-20 01:36:19 UTC (rev 12981)
+++ CalendarServer/branches/users/sagen/move2who-4/calendarserver/tools/principals.py        2014-03-20 03:28:55 UTC (rev 12982)
</span><span class="lines">@@ -17,30 +17,36 @@
</span><span class="cx"> ##
</span><span class="cx"> from __future__ import print_function
</span><span class="cx"> 
</span><del>-import sys
-import os
-import operator
</del><span class="cx"> from getopt import getopt, GetoptError
</span><ins>+import operator
+import os
+import sys
</ins><span class="cx"> from uuid import UUID
</span><span class="cx"> 
</span><ins>+from calendarserver.tools.cmdline import utilityMain, WorkerService
+from calendarserver.tools.util import (
+    recordForPrincipalID, prettyRecord
+)
+from twext.who.directory import DirectoryRecord
+from twext.who.idirectory import RecordType, InvalidDirectoryRecordError
</ins><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue, succeed
</span><del>-from txdav.xml import element as davxml
</del><ins>+from twistedcaldav.config import config
</ins><span class="cx"> from txdav.who.delegates import addDelegate, removeDelegate
</span><ins>+from txdav.who.idirectory import AutoScheduleMode
</ins><span class="cx"> 
</span><span class="cx"> 
</span><del>-from twistedcaldav.config import config
-from txdav.who.groups import schedulePolledGroupCachingUpdate
</del><ins>+allowedAutoScheduleModes = {
+    &quot;default&quot;: None,
+    &quot;none&quot;: AutoScheduleMode.none,
+    &quot;accept-always&quot;: AutoScheduleMode.accept,
+    &quot;decline-always&quot;: AutoScheduleMode.decline,
+    &quot;accept-if-free&quot;: AutoScheduleMode.acceptIfFree,
+    &quot;decline-if-busy&quot;: AutoScheduleMode.declineIfBusy,
+    &quot;automatic&quot;: AutoScheduleMode.acceptIfFreeDeclineIfBusy,
+}
</ins><span class="cx"> 
</span><del>-from calendarserver.tools.util import (
-    booleanArgument, proxySubprincipal,
-    recordForPrincipalID, prettyPrincipal, prettyRecord, ProxyError
-)
-from twistedcaldav.directory.augment import allowedAutoScheduleModes
</del><span class="cx"> 
</span><del>-from calendarserver.tools.cmdline import utilityMain, WorkerService
-
-
</del><span class="cx"> def usage(e=None):
</span><span class="cx">     if e:
</span><span class="cx">         print(e)
</span><span class="lines">@@ -74,8 +80,6 @@
</span><span class="cx">     print(&quot;  --add-read-proxy=principal: add a read-only proxy&quot;)
</span><span class="cx">     print(&quot;  --add-write-proxy=principal: add a read-write proxy&quot;)
</span><span class="cx">     print(&quot;  --remove-proxy=principal: remove a proxy&quot;)
</span><del>-    print(&quot;  --set-auto-schedule={true|false}: set auto-accept state&quot;)
-    print(&quot;  --get-auto-schedule: read auto-schedule state&quot;)
</del><span class="cx">     print(&quot;  --set-auto-schedule-mode={default|none|accept-always|decline-always|accept-if-free|decline-if-busy|automatic}: set auto-schedule mode&quot;)
</span><span class="cx">     print(&quot;  --get-auto-schedule-mode: read auto-schedule mode&quot;)
</span><span class="cx">     print(&quot;  --set-auto-accept-group=principal: set auto-accept-group&quot;)
</span><span class="lines">@@ -95,7 +99,6 @@
</span><span class="cx">         sys.exit(0)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> class PrincipalService(WorkerService):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Executes principals-related functions in a context which has access to the store
</span><span class="lines">@@ -155,8 +158,6 @@
</span><span class="cx">                 &quot;add-read-proxy=&quot;,
</span><span class="cx">                 &quot;add-write-proxy=&quot;,
</span><span class="cx">                 &quot;remove-proxy=&quot;,
</span><del>-                &quot;set-auto-schedule=&quot;,
-                &quot;get-auto-schedule&quot;,
</del><span class="cx">                 &quot;set-auto-schedule-mode=&quot;,
</span><span class="cx">                 &quot;get-auto-schedule-mode&quot;,
</span><span class="cx">                 &quot;set-auto-accept-group=&quot;,
</span><span class="lines">@@ -177,7 +178,7 @@
</span><span class="cx">     # Get configuration
</span><span class="cx">     #
</span><span class="cx">     configFileName = None
</span><del>-    # addType = None
</del><ins>+    addType = None
</ins><span class="cx">     listPrincipalTypes = False
</span><span class="cx">     listPrincipals = None
</span><span class="cx">     searchPrincipals = None
</span><span class="lines">@@ -198,11 +199,11 @@
</span><span class="cx">         elif opt in (&quot;-f&quot;, &quot;--config&quot;):
</span><span class="cx">             configFileName = arg
</span><span class="cx"> 
</span><del>-        # elif opt in (&quot;-a&quot;, &quot;--add&quot;):
-        #     addType = arg
</del><ins>+        elif opt in (&quot;-a&quot;, &quot;--add&quot;):
+            addType = arg
</ins><span class="cx"> 
</span><del>-        # elif opt in (&quot;-r&quot;, &quot;--remove&quot;):
-        #     principalActions.append((action_removePrincipal,))
</del><ins>+        elif opt in (&quot;-r&quot;, &quot;--remove&quot;):
+            principalActions.append((action_removePrincipal,))
</ins><span class="cx"> 
</span><span class="cx">         elif opt in (&quot;&quot;, &quot;--list-principal-types&quot;):
</span><span class="cx">             listPrincipalTypes = True
</span><span class="lines">@@ -237,41 +238,26 @@
</span><span class="cx">         elif opt in (&quot;&quot;, &quot;--remove-proxy&quot;):
</span><span class="cx">             principalActions.append((action_removeProxy, arg))
</span><span class="cx"> 
</span><del>-        # elif opt in (&quot;&quot;, &quot;--set-auto-schedule&quot;):
-        #     try:
-        #         autoSchedule = booleanArgument(arg)
-        #     except ValueError, e:
-        #         abort(e)
</del><ins>+        elif opt in (&quot;&quot;, &quot;--set-auto-schedule-mode&quot;):
+            try:
+                if arg not in allowedAutoScheduleModes:
+                    raise ValueError(&quot;Unknown auto-schedule mode: {mode}&quot;.format(
+                        mode=arg))
+                autoScheduleMode = allowedAutoScheduleModes[arg]
+            except ValueError, e:
+                abort(e)
</ins><span class="cx"> 
</span><del>-        #     principalActions.append((action_setAutoSchedule, autoSchedule))
</del><ins>+            principalActions.append((action_setAutoScheduleMode, autoScheduleMode))
</ins><span class="cx"> 
</span><del>-        # elif opt in (&quot;&quot;, &quot;--get-auto-schedule&quot;):
-        #     principalActions.append((action_getAutoSchedule,))
</del><ins>+        elif opt in (&quot;&quot;, &quot;--get-auto-schedule-mode&quot;):
+            principalActions.append((action_getAutoScheduleMode,))
</ins><span class="cx"> 
</span><del>-        # elif opt in (&quot;&quot;, &quot;--set-auto-schedule-mode&quot;):
-        #     try:
-        #         if arg not in allowedAutoScheduleModes:
-        #             raise ValueError(&quot;Unknown auto-schedule mode: %s&quot; % (arg,))
-        #         autoScheduleMode = arg
-        #     except ValueError, e:
-        #         abort(e)
</del><ins>+        elif opt in (&quot;&quot;, &quot;--set-auto-accept-group&quot;):
+            principalActions.append((action_setAutoAcceptGroup, arg))
</ins><span class="cx"> 
</span><del>-        #     principalActions.append((action_setAutoScheduleMode, autoScheduleMode))
</del><ins>+        elif opt in (&quot;&quot;, &quot;--get-auto-accept-group&quot;):
+            principalActions.append((action_getAutoAcceptGroup,))
</ins><span class="cx"> 
</span><del>-        # elif opt in (&quot;&quot;, &quot;--get-auto-schedule-mode&quot;):
-        #     principalActions.append((action_getAutoScheduleMode,))
-
-        # elif opt in (&quot;&quot;, &quot;--set-auto-accept-group&quot;):
-        #     try:
-        #         yield recordForPrincipalID(arg, checkOnly=True)
-        #     except ValueError, e:
-        #         abort(e)
-
-        #     principalActions.append((action_setAutoAcceptGroup, arg))
-
-        # elif opt in (&quot;&quot;, &quot;--get-auto-accept-group&quot;):
-        #     principalActions.append((action_getAutoAcceptGroup,))
-
</del><span class="cx">         # elif opt in (&quot;&quot;, &quot;--set-geo&quot;):
</span><span class="cx">         #     principalActions.append((action_setValue, &quot;Geo&quot;, arg))
</span><span class="cx"> 
</span><span class="lines">@@ -303,28 +289,38 @@
</span><span class="cx">         function = runListPrincipalTypes
</span><span class="cx">         params = ()
</span><span class="cx"> 
</span><del>-    # elif addType:
</del><ins>+    elif addType:
</ins><span class="cx"> 
</span><del>-    #     try:
-    #         addType = matchStrings(addType, [&quot;locations&quot;, &quot;resources&quot;, &quot;addresses&quot;])
-    #     except ValueError, e:
-    #         print(e)
-    #         return
</del><ins>+        try:
+            addType = matchStrings(
+                addType,
+                [
+                    &quot;locations&quot;, &quot;resources&quot;, &quot;addresses&quot;, &quot;users&quot;, &quot;groups&quot;
+                ]
+            )
+        except ValueError, e:
+            print(e)
+            return
</ins><span class="cx"> 
</span><del>-    #     try:
-    #         fullName, shortName, guid = parseCreationArgs(args)
-    #     except ValueError, e:
-    #         print(e)
-    #         return
</del><ins>+        try:
+            fullName, shortName, uid = parseCreationArgs(args)
+        except ValueError, e:
+            print(e)
+            return
</ins><span class="cx"> 
</span><del>-    #     if shortName is not None:
-    #         shortNames = [shortName]
-    #     else:
-    #         shortNames = ()
</del><ins>+        if fullName is not None:
+            fullNames = [fullName]
+        else:
+            fullNames = ()
</ins><span class="cx"> 
</span><del>-    #     function = runAddPrincipal
-    #     params = (addType, guid, shortNames, fullName)
</del><ins>+        if shortName is not None:
+            shortNames = [shortName]
+        else:
+            shortNames = ()
</ins><span class="cx"> 
</span><ins>+        function = runAddPrincipal
+        params = (addType, uid, shortNames, fullNames)
+
</ins><span class="cx">     elif listPrincipals:
</span><span class="cx">         try:
</span><span class="cx">             listPrincipals = matchStrings(
</span><span class="lines">@@ -349,13 +345,6 @@
</span><span class="cx">         if not args:
</span><span class="cx">             usage(&quot;No principals specified.&quot;)
</span><span class="cx"> 
</span><del>-        # We don't have a directory yet
-        # for arg in args:
-        #     try:
-        #         yield recordForPrincipalID(arg, checkOnly=True)
-        #     except ValueError, e:
-        #         abort(e)
-
</del><span class="cx">         unicodeArgs = [a.decode(&quot;utf-8&quot;) for a in args]
</span><span class="cx">         function = runPrincipalActions
</span><span class="cx">         params = (unicodeArgs, principalActions)
</span><span class="lines">@@ -384,7 +373,7 @@
</span><span class="cx">             printRecordList(records)
</span><span class="cx">         else:
</span><span class="cx">             print(&quot;No records of type %s&quot; % (listPrincipals,))
</span><del>-    except UnknownRecordTypeError, e:
</del><ins>+    except InvalidDirectoryRecordError, e:
</ins><span class="cx">         usage(e)
</span><span class="cx">     returnValue(None)
</span><span class="cx"> 
</span><span class="lines">@@ -453,28 +442,33 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-# @inlineCallbacks
-# def runAddPrincipal(service, store, addType, guid, shortNames, fullName):
-#     directory = store.directoryService()
-#     try:
-#         # FIXME STOP USING GUID
-#         yield updateRecord(
-#             True, directory, addType, guid=guid,
-#             shortNames=shortNames, fullName=fullName
-#         )
-#         print(&quot;Added '%s'&quot; % (fullName,))
-#     except DirectoryError, e:
-#         print(e)
</del><ins>+@inlineCallbacks
+def runAddPrincipal(service, store, addType, uid, shortNames, fullNames):
+    directory = store.directoryService()
+    recordType = directory.oldNameToRecordType(addType)
+    fields = {
+        directory.fieldName.recordType: recordType,
+        directory.fieldName.uid: uid,
+        directory.fieldName.shortNames: shortNames,
+        directory.fieldName.fullNames: fullNames,
+    }
+    record = DirectoryRecord(directory, fields)
+    yield record.service.updateRecords([record], create=True)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-# def action_removePrincipal(store, record):
-#     directory = store.directoryService()
-#     fullName = record.displayName
-#     shortName = record.shortNames[0]
</del><ins>+@inlineCallbacks
+def action_removePrincipal(store, record):
+    directory = store.directoryService()
+    fullName = record.displayName
+    shortNames = &quot;,&quot;.join(record.shortNames)
</ins><span class="cx"> 
</span><del>-#     yield directory.destroyRecord(record.recordType, uid=record.uid)
-#     print(&quot;Removed '%s' %s %s&quot; % (fullName, shortName, record.uid))
</del><ins>+    yield directory.removeRecords([record.uid])
+    print(
+        &quot;Removed '{full}' {shorts} {uid}&quot;.format(
+            full=fullName, shorts=shortNames, uid=record.uid
+        )
+    )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -622,149 +616,122 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-# @inlineCallbacks
-# def action_setAutoSchedule(rootResource, directory, store, principal, autoSchedule):
-#     if principal.record.recordType == &quot;groups&quot;:
-#         print(&quot;Enabling auto-schedule for %s is not allowed.&quot; % (principal,))
</del><ins>+def action_getAutoScheduleMode(store, record):
+    print(
+        &quot;Auto-schedule mode for {record} is {mode}&quot;.format(
+            record=prettyRecord(record),
+            mode=(
+                record.autoScheduleMode.description if record.autoScheduleMode
+                else &quot;Default&quot;
+            )
+        )
+    )
</ins><span class="cx"> 
</span><del>-#     elif principal.record.recordType == &quot;users&quot; and not config.Scheduling.Options.AutoSchedule.AllowUsers:
-#         print(&quot;Enabling auto-schedule for %s is not allowed.&quot; % (principal,))
</del><span class="cx"> 
</span><del>-#     else:
-#         print(&quot;Setting auto-schedule to %s for %s&quot; % (
-#             {True: &quot;true&quot;, False: &quot;false&quot;}[autoSchedule],
-#             prettyPrincipal(principal),
-#         ))
</del><ins>+@inlineCallbacks
+def action_setAutoScheduleMode(store, record, autoScheduleMode):
+    if record.recordType == RecordType.group:
+        print(
+            &quot;Setting auto-schedule-mode for {record} is not allowed.&quot;.format(
+                record=prettyRecord(record)
+            )
+        )
</ins><span class="cx"> 
</span><del>-#         (yield updateRecord(False, directory,
-#             principal.record.recordType,
-#             guid=principal.record.guid,
-#             shortNames=principal.record.shortNames,
-#             fullName=principal.record.fullName,
-#             autoSchedule=autoSchedule,
-#             **principal.record.extras
-#         ))
</del><ins>+    elif (
+        record.recordType == RecordType.user and
+        not config.Scheduling.Options.AutoSchedule.AllowUsers
+    ):
+        print(
+            &quot;Setting auto-schedule-mode for {record} is not allowed.&quot;.format(
+                record=prettyRecord(record)
+            )
+        )
</ins><span class="cx"> 
</span><ins>+    else:
+        print(
+            &quot;Setting auto-schedule-mode to {mode} for {record}&quot;.format(
+                mode=autoScheduleMode.description,
+                record=prettyRecord(record),
+            )
+        )
</ins><span class="cx"> 
</span><ins>+        # Get original fields
+        newFields = record.fields.copy()
</ins><span class="cx"> 
</span><del>-# def action_getAutoSchedule(rootResource, directory, store, principal):
-#     autoSchedule = principal.getAutoSchedule()
-#     print(&quot;Auto-schedule for %s is %s&quot; % (
-#         prettyPrincipal(principal),
-#         {True: &quot;true&quot;, False: &quot;false&quot;}[autoSchedule],
-#     ))
</del><ins>+        # Set new values
+        newFields[record.service.fieldName.autoScheduleMode] = autoScheduleMode
</ins><span class="cx"> 
</span><ins>+        updatedRecord = DirectoryRecord(record.service, newFields)
+        yield record.service.updateRecords([updatedRecord], create=False)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><del>-# @inlineCallbacks
-# def action_setAutoScheduleMode(rootResource, directory, store, principal, autoScheduleMode):
-#     if principal.record.recordType == &quot;groups&quot;:
-#         print(&quot;Setting auto-schedule mode for %s is not allowed.&quot; % (principal,))
</del><ins>+@inlineCallbacks
+def action_setAutoAcceptGroup(store, record, autoAcceptGroup):
+    if record.recordType == RecordType.group:
+        print(
+            &quot;Setting auto-accept-group for {record} is not allowed.&quot;.format(
+                record=prettyRecord(record)
+            )
+        )
</ins><span class="cx"> 
</span><del>-#     elif principal.record.recordType == &quot;users&quot; and not config.Scheduling.Options.AutoSchedule.AllowUsers:
-#         print(&quot;Setting auto-schedule mode for %s is not allowed.&quot; % (principal,))
</del><ins>+    elif (
+        record.recordType == RecordType.user and
+        not config.Scheduling.Options.AutoSchedule.AllowUsers
+    ):
+        print(
+            &quot;Setting auto-accept-group for {record} is not allowed.&quot;.format(
+                record=prettyRecord(record)
+            )
+        )
</ins><span class="cx"> 
</span><del>-#     else:
-#         print(&quot;Setting auto-schedule mode to %s for %s&quot; % (
-#             autoScheduleMode,
-#             prettyPrincipal(principal),
-#         ))
</del><ins>+    else:
+        groupRecord = yield recordForPrincipalID(record.service, autoAcceptGroup)
+        if groupRecord is None or groupRecord.recordType != RecordType.group:
+            print(&quot;Invalid principal ID: {id}&quot;.format(id=autoAcceptGroup))
+        else:
+            print(&quot;Setting auto-accept-group to {group} for {record}&quot;.format(
+                group=prettyRecord(groupRecord),
+                record=prettyRecord(record),
+            ))
</ins><span class="cx"> 
</span><del>-#         (yield updateRecord(False, directory,
-#             principal.record.recordType,
-#             guid=principal.record.guid,
-#             shortNames=principal.record.shortNames,
-#             fullName=principal.record.fullName,
-#             autoScheduleMode=autoScheduleMode,
-#             **principal.record.extras
-#         ))
</del><ins>+            # Get original fields
+            newFields = record.fields.copy()
</ins><span class="cx"> 
</span><ins>+            # Set new values
+            newFields[record.service.fieldName.autoAcceptGroup] = groupRecord.uid
</ins><span class="cx"> 
</span><ins>+            updatedRecord = DirectoryRecord(record.service, newFields)
+            yield record.service.updateRecords([updatedRecord], create=False)
</ins><span class="cx"> 
</span><del>-# def action_getAutoScheduleMode(rootResource, directory, store, principal):
-#     autoScheduleMode = principal.getAutoScheduleMode()
-#     if not autoScheduleMode:
-#         autoScheduleMode = &quot;automatic&quot;
-#     print(&quot;Auto-schedule mode for %s is %s&quot; % (
-#         prettyPrincipal(principal),
-#         autoScheduleMode,
-#     ))
</del><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
+def action_getAutoAcceptGroup(store, record):
+    if record.autoAcceptGroup:
+        groupRecord = yield record.service.recordWithUID(
+            record.autoAcceptGroup
+        )
+        if groupRecord is not None:
+            print(
+                &quot;Auto-accept-group for {record} is {group}&quot;.format(
+                    record=prettyRecord(record),
+                    group=prettyRecord(groupRecord),
+                )
+            )
+        else:
+            print(
+                &quot;Invalid auto-accept-group assigned: {uid}&quot;.format(
+                    uid=record.autoAcceptGroup
+                )
+            )
+    else:
+        print(
+            &quot;No auto-accept-group assigned to {record}&quot;.format(
+                record=prettyRecord(record)
+            )
+        )
</ins><span class="cx"> 
</span><del>-# @inlineCallbacks
-# def action_setAutoAcceptGroup(rootResource, directory, store, principal, autoAcceptGroup):
-#     if principal.record.recordType == &quot;groups&quot;:
-#         print(&quot;Setting auto-accept-group for %s is not allowed.&quot; % (principal,))
</del><span class="cx"> 
</span><del>-#     elif principal.record.recordType == &quot;users&quot; and not config.Scheduling.Options.AutoSchedule.AllowUsers:
-#         print(&quot;Setting auto-accept-group for %s is not allowed.&quot; % (principal,))
-
-#     else:
-#         groupPrincipal = yield principalForPrincipalID(autoAcceptGroup, directory=directory)
-#         if groupPrincipal is None or groupPrincipal.record.recordType != &quot;groups&quot;:
-#             print(&quot;Invalid principal ID: %s&quot; % (autoAcceptGroup,))
-#         else:
-#             print(&quot;Setting auto-accept-group to %s for %s&quot; % (
-#                 prettyPrincipal(groupPrincipal),
-#                 prettyPrincipal(principal),
-#             ))
-
-#             (yield updateRecord(False, 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(rootResource, directory, store, principal):
-#     autoAcceptGroup = principal.getAutoAcceptGroup()
-#     if autoAcceptGroup:
-#         record = yield directory.recordWithGUID(autoAcceptGroup)
-#         if record is not None:
-#             groupPrincipal = yield directory.principalCollection.principalForUID(record.uid)
-#             if groupPrincipal is not None:
-#                 print(&quot;Auto-accept-group for %s is %s&quot; % (
-#                     prettyPrincipal(principal),
-#                     prettyPrincipal(groupPrincipal),
-#                 ))
-#                 return
-#         print(&quot;Invalid auto-accept-group assigned: %s&quot; % (autoAcceptGroup,))
-#     else:
-#         print(&quot;No auto-accept-group assigned to %s&quot; % (prettyPrincipal(principal),))
-
-
-
-# @inlineCallbacks
-# def action_setValue(rootResource, directory, store, principal, name, value):
-#     print(&quot;Setting %s to %s for %s&quot; % (
-#         name, value, prettyPrincipal(principal),
-#     ))
-
-#     principal.record.extras[attrMap[name][&quot;attr&quot;]] = 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(&quot;%s for %s is %s&quot; % (
-#         name,
-#         prettyPrincipal(principal),
-#         principal.record.extras[attrMap[name][&quot;attr&quot;]]
-#     ))
-
-
-
</del><span class="cx"> def abort(msg, status=1):
</span><span class="cx">     sys.stdout.write(&quot;%s\n&quot; % (msg,))
</span><span class="cx">     try:
</span><span class="lines">@@ -777,32 +744,19 @@
</span><span class="cx"> 
</span><span class="cx"> def parseCreationArgs(args):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    Look at the command line arguments for --add, and figure out which
-    one is the shortName and which one is the guid by attempting to make a
-    UUID object out of them.
</del><ins>+    Look at the command line arguments for --add, and simply assume the first
+    is full name, the second is short name, and the third is uid.  We can make
+    this fancier later.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    fullName = args[0]
-    shortName = None
-    guid = None
-    for arg in args[1:]:
-        if isUUID(arg):
-            if guid is not None:
-                # Both the 2nd and 3rd args are UUIDs.  The first one
-                # should be used for shortName.
-                shortName = guid
-            guid = arg
-        else:
-            shortName = arg
</del><ins>+    fullName = args[0].decode(&quot;utf-8&quot;)
+    shortName = args[1].decode(&quot;utf-8&quot;)
+    uid = args[2].decode(&quot;utf-8&quot;)
</ins><span class="cx"> 
</span><del>-    if len(args) == 3 and guid is None:
-        # both shortName and guid were specified but neither was a UUID
-        raise ValueError(&quot;Invalid value for guid&quot;)
</del><ins>+    return fullName, shortName, uid
</ins><span class="cx"> 
</span><del>-    return fullName, shortName, guid
</del><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> def isUUID(value):
</span><span class="cx">     try:
</span><span class="cx">         UUID(value)
</span><span class="lines">@@ -835,84 +789,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-@inlineCallbacks
-def updateRecord(create, directory, recordType, **kwargs):
-    &quot;&quot;&quot;
-    Create/update a record, including the extra work required to set the
-    autoSchedule bit in the augment record.
</del><span class="cx"> 
</span><del>-    If C{create} is true, the record is created, otherwise update the record
-    matching the guid in kwargs.
-    &quot;&quot;&quot;
</del><span class="cx"> 
</span><del>-    assignAutoSchedule = False
-    if &quot;autoSchedule&quot; in kwargs:
-        assignAutoSchedule = True
-        autoSchedule = kwargs[&quot;autoSchedule&quot;]
-        del kwargs[&quot;autoSchedule&quot;]
-    elif create:
-        assignAutoSchedule = True
-        autoSchedule = recordType in (&quot;locations&quot;, &quot;resources&quot;)
-
-    assignAutoScheduleMode = False
-    if &quot;autoScheduleMode&quot; in kwargs:
-        assignAutoScheduleMode = True
-        autoScheduleMode = kwargs[&quot;autoScheduleMode&quot;]
-        del kwargs[&quot;autoScheduleMode&quot;]
-    elif create:
-        assignAutoScheduleMode = True
-        autoScheduleMode = None
-
-    assignAutoAcceptGroup = False
-    if &quot;autoAcceptGroup&quot; in kwargs:
-        assignAutoAcceptGroup = True
-        autoAcceptGroup = kwargs[&quot;autoAcceptGroup&quot;]
-        del kwargs[&quot;autoAcceptGroup&quot;]
-    elif create:
-        assignAutoAcceptGroup = True
-        autoAcceptGroup = None
-
-    for key, value in kwargs.items():
-        if isinstance(value, unicode):
-            kwargs[key] = value.encode(&quot;utf-8&quot;)
-        elif isinstance(value, list):
-            newValue = [v.encode(&quot;utf-8&quot;) for v in value]
-            kwargs[key] = newValue
-
-    if create:
-        record = yield directory.createRecord(recordType, **kwargs)
-        kwargs['guid'] = record.guid
-    else:
-        try:
-            record = yield directory.updateRecord(recordType, **kwargs)
-        except NotImplementedError:
-            # Updating of directory information is not supported by underlying
-            # directory implementation, but allow augment information to be
-            # updated
-            record = yield directory.recordWithGUID(kwargs[&quot;guid&quot;])
-            pass
-
-    augmentService = directory.serviceForRecordType(recordType).augmentService
-    augmentRecord = (yield augmentService.getAugmentRecord(kwargs['guid'], recordType))
-
-    if assignAutoSchedule:
-        augmentRecord.autoSchedule = autoSchedule
-    if assignAutoScheduleMode:
-        augmentRecord.autoScheduleMode = autoScheduleMode
-    if assignAutoAcceptGroup:
-        augmentRecord.autoAcceptGroup = autoAcceptGroup
-    (yield augmentService.addAugmentRecords([augmentRecord]))
-    try:
-        yield directory.updateRecord(recordType, **kwargs)
-    except NotImplementedError:
-        # Updating of directory information is not supported by underlying
-        # directory implementation, but allow augment information to be
-        # updated
-        pass
-
-    returnValue(record)
-
-
-
</del><span class="cx"> if __name__ == &quot;__main__&quot;:
</span><span class="cx">     main()
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who4twistedcaldavdirectoryprincipalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/principal.py (12981 => 12982)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/principal.py        2014-03-20 01:36:19 UTC (rev 12981)
+++ CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/principal.py        2014-03-20 03:28:55 UTC (rev 12982)
</span><span class="lines">@@ -824,11 +824,10 @@
</span><span class="cx">         url = joinURL(parent.principalCollectionURL(), self.principalUID()) + slash
</span><span class="cx">         self._url = url
</span><span class="cx"> 
</span><del>-        # MOVE2WHO - hack: just adding an &quot;s&quot; using recordType.name (need a mapping)
</del><span class="cx">         self._alternate_urls = tuple([
</span><span class="cx">             joinURL(
</span><span class="cx">                 parent.parent.principalCollectionURL(),
</span><del>-                (record.recordType.name + &quot;s&quot;),
</del><ins>+                record.service.recordTypeToOldName(record.recordType),
</ins><span class="cx">                 quote(shortName.encode(&quot;utf-8&quot;))
</span><span class="cx">             ) + slash
</span><span class="cx">             for shortName in record.shortNames
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2who4txdavwhoaugmentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who-4/txdav/who/augment.py (12981 => 12982)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who-4/txdav/who/augment.py        2014-03-20 01:36:19 UTC (rev 12981)
+++ CalendarServer/branches/users/sagen/move2who-4/txdav/who/augment.py        2014-03-20 03:28:55 UTC (rev 12982)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> 
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx"> 
</span><ins>+from twistedcaldav.directory.augment import AugmentRecord
</ins><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from twext.who.directory import DirectoryRecord
</span><span class="cx"> from twext.who.directory import DirectoryService as BaseDirectoryService
</span><span class="lines">@@ -172,11 +173,77 @@
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def updateRecords(self, records, create=False):
</span><del>-        return self._directory.updateRecords(records, create=create)
</del><ins>+        &quot;&quot;&quot;
+        Pull out the augmented fields from each record, apply those to the
+        augments database, then update the base records.
+        &quot;&quot;&quot;
</ins><span class="cx"> 
</span><ins>+        baseRecords = []
+        augmentRecords = []
</ins><span class="cx"> 
</span><del>-    @inlineCallbacks
</del><ins>+        for record in records:
+
+            # Split out the base fields from the augment fields
+            baseFields, augmentFields = self._splitFields(record)
+
+            if augmentFields:
+                # Create an AugmentRecord
+                autoScheduleMode = {
+                    AutoScheduleMode.none: &quot;none&quot;,
+                    AutoScheduleMode.accept: &quot;accept-always&quot;,
+                    AutoScheduleMode.decline: &quot;decline-always&quot;,
+                    AutoScheduleMode.acceptIfFree: &quot;accept-if-free&quot;,
+                    AutoScheduleMode.declineIfBusy: &quot;decline-if-busy&quot;,
+                    AutoScheduleMode.acceptIfFreeDeclineIfBusy: &quot;automatic&quot;,
+                }.get(augmentFields.get(FieldName.autoScheduleMode, None), None)
+                augmentRecord = AugmentRecord(
+                    uid=record.uid,
+                    enabledForCalendaring=augmentFields[FieldName.hasCalendars],
+                    enabledForAddressBooks=augmentFields[FieldName.hasContacts],
+                    autoScheduleMode=autoScheduleMode,
+                    enabledForLogin=augmentFields[FieldName.loginAllowed],
+                    autoAcceptGroup=augmentFields[FieldName.autoAcceptGroup],
+                    serverID=augmentFields[FieldName.serviceNodeUID],
+                )
+                augmentRecords.append(augmentRecord)
+
+            # Create new base records:
+            baseRecords.append(DirectoryRecord(self._directory, baseFields))
+
+        # Apply the augment records
+        if augmentRecords:
+            yield self._augmentDB.addAugmentRecords(augmentRecords)
+
+        # Apply the base records
+        if baseRecords:
+            yield self._directory.updateRecords(baseRecords, create=create)
+
+
+    def _splitFields(self, record):
+        &quot;&quot;&quot;
+        Returns a tuple of two dictionaries; the first contains all the non
+        augment fields, and the second contains all the augment fields.
+        &quot;&quot;&quot;
+        if record is None:
+            return None
+
+        augmentFields = {}
+        baseFields = record.fields.copy()
+        for field in (
+            FieldName.loginAllowed,
+            FieldName.hasCalendars, FieldName.hasContacts,
+            FieldName.autoScheduleMode, FieldName.autoAcceptGroup,
+            FieldName.serviceNodeUID
+        ):
+            if field in baseFields:
+                augmentFields[field] = baseFields[field]
+                del baseFields[field]
+
+        return (baseFields, augmentFields)
+
+
</ins><span class="cx">     def removeRecords(self, uids):
</span><ins>+        self._augmentDB.removeAugmentRecords(uids)
</ins><span class="cx">         return self._directory.removeRecords(uids)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -185,6 +252,7 @@
</span><span class="cx">         fields[field] = value
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _augment(self, record):
</span><span class="cx">         if record is None:
</span><span class="lines">@@ -205,12 +273,12 @@
</span><span class="cx">         # print(&quot;Got augment record&quot;, augmentRecord)
</span><span class="cx"> 
</span><span class="cx">         if augmentRecord:
</span><del>-            # record.enabled = augmentRecord.enabled
-            # record.serverID = augmentRecord.serverID
</del><ins>+
</ins><span class="cx">             self._assignToField(
</span><span class="cx">                 fields, &quot;hasCalendars&quot;,
</span><span class="cx">                 augmentRecord.enabledForCalendaring
</span><span class="cx">             )
</span><ins>+
</ins><span class="cx">             self._assignToField(
</span><span class="cx">                 fields, &quot;hasContacts&quot;,
</span><span class="cx">                 augmentRecord.enabledForAddressBooks
</span><span class="lines">@@ -229,15 +297,22 @@
</span><span class="cx">                 fields, &quot;autoScheduleMode&quot;,
</span><span class="cx">                 autoScheduleMode
</span><span class="cx">             )
</span><ins>+
</ins><span class="cx">             self._assignToField(
</span><span class="cx">                 fields, &quot;autoAcceptGroup&quot;,
</span><del>-                unicode(augmentRecord.autoAcceptGroup)
</del><ins>+                augmentRecord.autoAcceptGroup.decode(&quot;utf-8&quot;)
</ins><span class="cx">             )
</span><ins>+
</ins><span class="cx">             self._assignToField(
</span><span class="cx">                 fields, &quot;loginAllowed&quot;,
</span><span class="cx">                 augmentRecord.enabledForLogin
</span><span class="cx">             )
</span><span class="cx"> 
</span><ins>+            self._assignToField(
+                fields, &quot;serviceNodeUID&quot;,
+                augmentRecord.serverID.decode(&quot;utf-8&quot;)
+            )
+
</ins><span class="cx">             if (
</span><span class="cx">                 (
</span><span class="cx">                     fields.get(
</span></span></pre>
</div>
</div>

</body>
</html>