<!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>[12849] CalendarServer/branches/users/sagen/move2who</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/12849">12849</a></dd>
<dt>Author</dt> <dd>sagen@apple.com</dd>
<dt>Date</dt> <dd>2014-03-07 14:20:50 -0800 (Fri, 07 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Allow single-process configurations (like Single and Utility) to bypass the client/server/amp business, and just use an augmented directory service directly.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssagenmove2whocalendarservertapcaldavpy">CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whocalendarservertaptesttest_utilpy">CalendarServer/branches/users/sagen/move2who/calendarserver/tap/test/test_util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whocalendarservertaputilpy">CalendarServer/branches/users/sagen/move2who/calendarserver/tap/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whocalendarservertoolscalverifypy">CalendarServer/branches/users/sagen/move2who/calendarserver/tools/calverify.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whocalendarservertoolsprincipalspy">CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whocalendarservertoolstesttest_principalspy">CalendarServer/branches/users/sagen/move2who/calendarserver/tools/test/test_principals.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whocalendarservertoolsutilpy">CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotwistedcaldavtestutilpy">CalendarServer/branches/users/sagen/move2who/twistedcaldav/test/util.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotwistedcaldavupgradepy">CalendarServer/branches/users/sagen/move2who/twistedcaldav/upgrade.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavcaldavdatastoretesttest_attachmentspy">CalendarServer/branches/users/sagen/move2who/txdav/caldav/datastore/test/test_attachments.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavcommondatastoresqlpy">CalendarServer/branches/users/sagen/move2who/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavdpsclientpy">CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavdpsserverpy">CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavwhoaugmentpy">CalendarServer/branches/users/sagen/move2who/txdav/who/augment.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavwhodelegatespy">CalendarServer/branches/users/sagen/move2who/txdav/who/delegates.py</a></li>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavwhogroupspy">CalendarServer/branches/users/sagen/move2who/txdav/who/groups.py</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssagenmove2whotxdavwhodirectorypy">CalendarServer/branches/users/sagen/move2who/txdav/who/directory.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserssagenmove2whocalendarservertapcaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -86,7 +86,7 @@
</span><span class="cx"> from txdav.common.datastore.work.revision_cleanup import (
</span><span class="cx">     scheduleFirstFindMinRevision
</span><span class="cx"> )
</span><del>-from txdav.dps.server import DirectoryProxyServiceMaker
</del><ins>+from txdav.dps.server import directoryFromConfig
</ins><span class="cx"> from txdav.dps.client import DirectoryService as DirectoryProxyClientService
</span><span class="cx"> from txdav.who.groups import GroupCacher as NewGroupCacher
</span><span class="cx"> 
</span><span class="lines">@@ -927,10 +927,10 @@
</span><span class="cx">         CalDAV and CardDAV requests.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         pool, txnFactory = getDBPool(config)
</span><del>-        store = storeFromConfig(config, txnFactory)
</del><ins>+        directory = DirectoryProxyClientService(&quot;FIXME&quot;)
+        store = storeFromConfig(config, txnFactory, directory)
</ins><span class="cx">         logObserver = AMPCommonAccessLoggingObserver()
</span><span class="cx">         result = self.requestProcessingService(options, store, logObserver)
</span><del>-        directory = store.directoryService()
</del><span class="cx"> 
</span><span class="cx">         if pool is not None:
</span><span class="cx">             pool.setServiceParent(result)
</span><span class="lines">@@ -1011,7 +1011,7 @@
</span><span class="cx">                 namespace=config.GroupCaching.MemcachedPool,
</span><span class="cx">                 useExternalProxies=config.GroupCaching.UseExternalProxies,
</span><span class="cx">             )
</span><del>-            newGroupCacher = NewGroupCacher(DirectoryProxyClientService(None))
</del><ins>+            newGroupCacher = NewGroupCacher(directory)
</ins><span class="cx">         else:
</span><span class="cx">             groupCacher = None
</span><span class="cx">             newGroupCacher = None
</span><span class="lines">@@ -1312,6 +1312,13 @@
</span><span class="cx">             if store is None:
</span><span class="cx">                 raise StoreNotAvailable()
</span><span class="cx"> 
</span><ins>+            # Create a Directory Proxy &quot;Server&quot; service and hand it to the
+            # store.
+            # FIXME: right now the store passed *to* the directory is the
+            # calendar/contacts data store, but for a multi-server deployment
+            # it will need its own separate store.
+            store.setDirectoryService(directoryFromConfig(config, store=store))
+
</ins><span class="cx">             result = self.requestProcessingService(options, store, logObserver)
</span><span class="cx"> 
</span><span class="cx">             # Optionally set up push notifications
</span><span class="lines">@@ -1357,9 +1364,7 @@
</span><span class="cx">                     namespace=config.GroupCaching.MemcachedPool,
</span><span class="cx">                     useExternalProxies=config.GroupCaching.UseExternalProxies
</span><span class="cx">                 )
</span><del>-                newGroupCacher = NewGroupCacher(
-                    DirectoryProxyClientService(None)
-                )
</del><ins>+                newGroupCacher = NewGroupCacher(directory)
</ins><span class="cx">             else:
</span><span class="cx">                 groupCacher = None
</span><span class="cx">                 newGroupCacher = None
</span><span class="lines">@@ -1393,13 +1398,6 @@
</span><span class="cx">                         &quot;manhole_tap could not be imported&quot;
</span><span class="cx">                     )
</span><span class="cx"> 
</span><del>-            # Optionally enable Directory Proxy
-            if config.DirectoryProxy.Enabled:
-                dps = DirectoryProxyServiceMaker().makeService(
-                    None, store=store
-                )
-                dps.setServiceParent(result)
-
</del><span class="cx">             def decorateTransaction(txn):
</span><span class="cx">                 txn._pushDistributor = pushDistributor
</span><span class="cx">                 txn._rootResource = result.rootResource
</span><span class="lines">@@ -1445,7 +1443,7 @@
</span><span class="cx">                 Popen(memcachedArgv)
</span><span class="cx"> 
</span><span class="cx">         return self.storageService(
</span><del>-            slaveSvcCreator, logObserver, uid=uid, gid=gid
</del><ins>+            slaveSvcCreator, logObserver, uid=uid, gid=gid, directory=None
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1458,10 +1456,17 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         def toolServiceCreator(pool, store, ignored, storageService):
</span><ins>+            # Create a Directory Proxy &quot;Server&quot; service and hand it to the
+            # store
+            # FIXME: right now the store passed *to* the directory is the
+            # calendar/contacts data store, but for a multi-server deployment
+            # it will need its own separate store.
+            store.setDirectoryService(directoryFromConfig(config, store=store))
</ins><span class="cx">             return config.UtilityServiceClass(store)
</span><span class="cx"> 
</span><span class="cx">         uid, gid = getSystemIDs(config.UserName, config.GroupName)
</span><del>-        return self.storageService(toolServiceCreator, None, uid=uid, gid=gid)
</del><ins>+        return self.storageService(toolServiceCreator, None, uid=uid, gid=gid,
+                                   directory=None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def makeService_Agent(self, options):
</span><span class="lines">@@ -1509,7 +1514,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def storageService(
</span><del>-        self, createMainService, logObserver, uid=None, gid=None
</del><ins>+        self, createMainService, logObserver, uid=None, gid=None, directory=None
</ins><span class="cx">     ):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         If necessary, create a service to be started used for storage; for
</span><span class="lines">@@ -1535,6 +1540,9 @@
</span><span class="cx">             running as root (also the gid to chown Attachments to).
</span><span class="cx">         @type gid: C{int}
</span><span class="cx"> 
</span><ins>+        @param directory: The directory service to use.
+        @type directory: L{IStoreDirectoryService} or None
+
</ins><span class="cx">         @return: the appropriate a service to start.
</span><span class="cx">         @rtype: L{IService}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -1549,7 +1557,7 @@
</span><span class="cx">                     maxConnections=config.MaxDBConnectionsPerPool
</span><span class="cx">                 )
</span><span class="cx">                 cp.setServiceParent(ms)
</span><del>-                store = storeFromConfig(config, cp.connection)
</del><ins>+                store = storeFromConfig(config, cp.connection, directory)
</ins><span class="cx"> 
</span><span class="cx">                 pps = PreProcessingService(
</span><span class="cx">                     createMainService, cp, store, logObserver, storageService
</span><span class="lines">@@ -1674,7 +1682,7 @@
</span><span class="cx">                     &quot;Unknown database type {}&quot;.format(config.DBType)
</span><span class="cx">                 )
</span><span class="cx">         else:
</span><del>-            store = storeFromConfig(config, None)
</del><ins>+            store = storeFromConfig(config, None, directory)
</ins><span class="cx">             return createMainService(None, store, logObserver, None)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1941,7 +1949,7 @@
</span><span class="cx">                     namespace=config.GroupCaching.MemcachedPool,
</span><span class="cx">                     useExternalProxies=config.GroupCaching.UseExternalProxies
</span><span class="cx">                 )
</span><del>-                newGroupCacher = NewGroupCacher(DirectoryProxyClientService(None))
</del><ins>+                newGroupCacher = NewGroupCacher(directory)
</ins><span class="cx">             else:
</span><span class="cx">                 groupCacher = None
</span><span class="cx">                 newGroupCacher = None
</span><span class="lines">@@ -1957,7 +1965,10 @@
</span><span class="cx"> 
</span><span class="cx">             return multi
</span><span class="cx"> 
</span><del>-        ssvc = self.storageService(spawnerSvcCreator, None, uid, gid)
</del><ins>+        ssvc = self.storageService(
+            spawnerSvcCreator, None, uid, gid,
+            directory=DirectoryProxyClientService(&quot;FIXME&quot;)
+        )
</ins><span class="cx">         ssvc.setServiceParent(s)
</span><span class="cx">         return s
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whocalendarservertaptesttest_utilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tap/test/test_util.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/calendarserver/tap/test/test_util.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tap/test/test_util.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -14,13 +14,14 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx"> 
</span><del>-from calendarserver.tap.util import directoryFromConfig, MemoryLimitService, Stepper
</del><ins>+from calendarserver.tap.util import MemoryLimitService, Stepper
</ins><span class="cx"> from twistedcaldav.util import computeProcessCount
</span><span class="cx"> from twistedcaldav.test.util import TestCase
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.directory.augment import AugmentXMLDB
</span><span class="cx"> from twisted.internet.task import Clock
</span><span class="cx"> from twisted.internet.defer import succeed, inlineCallbacks
</span><ins>+from txdav.dps.server import directoryFromConfig
</ins><span class="cx"> 
</span><span class="cx"> class ProcessCountTestCase(TestCase):
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whocalendarservertaputilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tap/util.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/calendarserver/tap/util.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tap/util.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -223,7 +223,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-def storeFromConfig(config, txnFactory, directoryService=None):
</del><ins>+def storeFromConfig(config, txnFactory, directoryService):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Produce an L{IDataStore} from the given configuration, transaction factory,
</span><span class="cx">     and notifier factory.
</span><span class="lines">@@ -241,9 +241,6 @@
</span><span class="cx">     if config.EnableResponseCache and config.Memcached.Pools.Default.ClientEnabled:
</span><span class="cx">         notifierFactories[&quot;cache&quot;] = CacheStoreNotifierFactory()
</span><span class="cx"> 
</span><del>-    if directoryService is None:
-        directoryService = directoryFromConfig(config)
-
</del><span class="cx">     quota = config.UserQuota
</span><span class="cx">     if quota == 0:
</span><span class="cx">         quota = None
</span><span class="lines">@@ -286,14 +283,11 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-def directoryFromConfig(config):
</del><ins>+def REMOVEMEdirectoryFromConfig(config):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Create an L{AggregateDirectoryService} from the given configuration.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    # MOVE2WHO
-    return DirectoryProxyClientService(&quot;XYZZY&quot;)
-
</del><span class="cx">     #
</span><span class="cx">     # Setup the Augment Service
</span><span class="cx">     #
</span><span class="lines">@@ -470,7 +464,6 @@
</span><span class="cx">     directory = newStore.directoryService()
</span><span class="cx">     principalCollection = principalResourceClass(&quot;/principals/&quot;, directory)
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     #
</span><span class="cx">     # Setup the ProxyDB Service
</span><span class="cx">     #
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whocalendarservertoolscalverifypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tools/calverify.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/calendarserver/tools/calverify.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tools/calverify.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -431,7 +431,7 @@
</span><span class="cx">         configuration, creating one first if necessary.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if self._directory is None:
</span><del>-            self._directory = getDirectory(self.config) #directoryFromConfig(self.config)
</del><ins>+            self._directory = getDirectory(self.config)
</ins><span class="cx">         return self._directory
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whocalendarservertoolsprincipalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -119,6 +119,7 @@
</span><span class="cx">         if self.function is not None:
</span><span class="cx">             yield self.function(self.store, *self.params)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> attrMap = {
</span><span class="cx">     'GeneratedUID': {'attr': 'guid', },
</span><span class="cx">     'RealName': {'attr': 'fullName', },
</span><span class="lines">@@ -142,7 +143,6 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-@inlineCallbacks
</del><span class="cx"> def main():
</span><span class="cx">     try:
</span><span class="cx">         (optargs, args) = getopt(
</span><span class="lines">@@ -191,6 +191,10 @@
</span><span class="cx">     verbose = False
</span><span class="cx"> 
</span><span class="cx">     for opt, arg in optargs:
</span><ins>+
+        # Args come in as encoded bytes
+        arg = arg.decode(&quot;utf-8&quot;)
+
</ins><span class="cx">         if opt in (&quot;-h&quot;, &quot;--help&quot;):
</span><span class="cx">             usage()
</span><span class="cx"> 
</span><span class="lines">@@ -234,20 +238,9 @@
</span><span class="cx">                 proxyType = &quot;write&quot;
</span><span class="cx">             else:
</span><span class="cx">                 raise AssertionError(&quot;Unknown proxy type&quot;)
</span><del>-
-            try:
-                yield recordForPrincipalID(arg, checkOnly=True)
-            except ValueError, e:
-                abort(e)
-
</del><span class="cx">             principalActions.append((action_addProxy, proxyType, arg))
</span><span class="cx"> 
</span><span class="cx">         elif opt in (&quot;&quot;, &quot;--remove-proxy&quot;):
</span><del>-            try:
-                yield recordForPrincipalID(arg, checkOnly=True)
-            except ValueError, e:
-                abort(e)
-
</del><span class="cx">             principalActions.append((action_removeProxy, arg))
</span><span class="cx"> 
</span><span class="cx">         # elif opt in (&quot;&quot;, &quot;--set-auto-schedule&quot;):
</span><span class="lines">@@ -359,21 +352,19 @@
</span><span class="cx">         params = (searchPrincipals,)
</span><span class="cx"> 
</span><span class="cx">     else:
</span><del>-        #
-        # Do a quick sanity check that arguments look like principal
-        # identifiers.
-        #
</del><span class="cx">         if not args:
</span><span class="cx">             usage(&quot;No principals specified.&quot;)
</span><span class="cx"> 
</span><del>-        for arg in args:
-            try:
-                yield recordForPrincipalID(arg, checkOnly=True)
-            except ValueError, e:
-                abort(e)
</del><ins>+        # We don't have a directory yet
+        # for arg in args:
+        #     try:
+        #         yield recordForPrincipalID(arg, checkOnly=True)
+        #     except ValueError, e:
+        #         abort(e)
</ins><span class="cx"> 
</span><ins>+        unicodeArgs = [a.decode(&quot;utf-8&quot;) for a in args]
</ins><span class="cx">         function = runPrincipalActions
</span><del>-        params = (args, principalActions)
</del><ins>+        params = (unicodeArgs, principalActions)
</ins><span class="cx"> 
</span><span class="cx">     PrincipalService.function = function
</span><span class="cx">     PrincipalService.params = params
</span><span class="lines">@@ -411,9 +402,7 @@
</span><span class="cx">     for principalID in principalIDs:
</span><span class="cx">         # Resolve the given principal IDs to records
</span><span class="cx">         try:
</span><del>-            record = yield recordForPrincipalID(
-                principalID, directory=directory
-            )
</del><ins>+            record = yield recordForPrincipalID(directory, principalID)
</ins><span class="cx">         except ValueError:
</span><span class="cx">             record = None
</span><span class="cx"> 
</span><span class="lines">@@ -545,7 +534,7 @@
</span><span class="cx">     directory = store.directoryService()
</span><span class="cx">     readWrite = (proxyType == &quot;write&quot;)
</span><span class="cx">     for proxyID in proxyIDs:
</span><del>-        proxyRecord = yield recordForPrincipalID(proxyID, directory=directory)
</del><ins>+        proxyRecord = yield recordForPrincipalID(directory, proxyID)
</ins><span class="cx">         if proxyRecord is None:
</span><span class="cx">             print(&quot;Invalid principal ID: %s&quot; % (proxyID,))
</span><span class="cx">         else:
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whocalendarservertoolstesttest_principalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tools/test/test_principals.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/calendarserver/tools/test/test_principals.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tools/test/test_principals.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -17,22 +17,23 @@
</span><span class="cx"> import os
</span><span class="cx"> import sys
</span><span class="cx"> 
</span><ins>+from calendarserver.tools.principals import (
+    parseCreationArgs, matchStrings,
+    updateRecord, principalForPrincipalID, getProxies, setProxies
+)
</ins><span class="cx"> from twext.python.filepath import CachingFilePath as FilePath
</span><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
</span><del>-
</del><span class="cx"> from twistedcaldav.config import config
</span><ins>+from twistedcaldav.directory import calendaruserproxy
</ins><span class="cx"> from twistedcaldav.directory.directory import DirectoryError
</span><del>-from twistedcaldav.directory import calendaruserproxy
</del><ins>+from twistedcaldav.test.util import (
+    TestCase, CapturingProcessProtocol, ErrorOutput
+)
+from txdav.dps.server import directoryFromConfig
</ins><span class="cx"> 
</span><del>-from twistedcaldav.test.util import TestCase, CapturingProcessProtocol, \
-    ErrorOutput
</del><span class="cx"> 
</span><del>-from calendarserver.tap.util import directoryFromConfig
-from calendarserver.tools.principals import (parseCreationArgs, matchStrings,
-    updateRecord, principalForPrincipalID, getProxies, setProxies)
</del><span class="cx"> 
</span><del>-
</del><span class="cx"> class ManagePrincipalsTestCase(TestCase):
</span><span class="cx"> 
</span><span class="cx">     def setUp(self):
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whocalendarservertoolsutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -396,14 +396,8 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def recordForPrincipalID(principalID, checkOnly=False, directory=None):
</del><ins>+def recordForPrincipalID(directory, principalID, checkOnly=False):
</ins><span class="cx"> 
</span><del>-    # Allow a directory parameter to be passed in, but default to config.directory
-    # But config.directory isn't set right away, so only use it when we're doing more
-    # than checking.
-    if not checkOnly and not directory:
-        directory = config.directory
-
</del><span class="cx">     if principalID.startswith(&quot;/&quot;):
</span><span class="cx">         segments = principalID.strip(&quot;/&quot;).split(&quot;/&quot;)
</span><span class="cx">         if (len(segments) == 3 and
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotwistedcaldavtestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/twistedcaldav/test/util.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/twistedcaldav/test/util.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/twistedcaldav/test/util.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -50,11 +50,12 @@
</span><span class="cx"> from calendarserver.provision.root import RootResource
</span><span class="cx"> 
</span><span class="cx"> from twext.python.log import Logger
</span><del>-from txdav.caldav.datastore.test.util import buildCalendarStore
-from calendarserver.tap.util import getRootResource, directoryFromConfig
</del><ins>+from calendarserver.tap.util import getRootResource
</ins><span class="cx"> from txweb2.dav.test.util import SimpleRequest
</span><span class="cx"> from twistedcaldav.directory.util import transactionFromRequest
</span><span class="cx"> from twistedcaldav.directory.directory import DirectoryService
</span><ins>+from txdav.caldav.datastore.test.util import buildCalendarStore
+from txdav.dps.server import directoryFromConfig
</ins><span class="cx"> 
</span><span class="cx"> log = Logger()
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotwistedcaldavupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/twistedcaldav/upgrade.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/twistedcaldav/upgrade.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/twistedcaldav/upgrade.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -60,7 +60,7 @@
</span><span class="cx"> 
</span><span class="cx"> from twisted.protocols.amp import AMP, Command, String, Boolean
</span><span class="cx"> 
</span><del>-from calendarserver.tap.util import getRootResource, FakeRequest, directoryFromConfig
</del><ins>+from calendarserver.tap.util import getRootResource, FakeRequest
</ins><span class="cx"> from calendarserver.tools.util import getDirectory
</span><span class="cx"> 
</span><span class="cx"> from txdav.caldav.datastore.scheduling.imip.mailgateway import migrateTokensToStore
</span><span class="lines">@@ -1032,7 +1032,7 @@
</span><span class="cx">     def stepWithResult(self, result):
</span><span class="cx">         if self.doPostImport:
</span><span class="cx"> 
</span><del>-            directory = directoryFromConfig(self.config)
</del><ins>+            directory = self.store.directoryService()
</ins><span class="cx"> 
</span><span class="cx">             # Load proxy assignments from XML if specified
</span><span class="cx">             if self.config.ProxyLoadFromFile:
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavcaldavdatastoretesttest_attachmentspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/caldav/datastore/test/test_attachments.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/caldav/datastore/test/test_attachments.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/txdav/caldav/datastore/test/test_attachments.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -14,7 +14,7 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx"> 
</span><del>-from calendarserver.tap.util import directoryFromConfig
</del><ins>+from txdav.dps.server import directoryFromConfig
</ins><span class="cx"> 
</span><span class="cx"> from pycalendar.datetime import DateTime
</span><span class="cx"> from pycalendar.value import Value
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/common/datastore/sql.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/common/datastore/sql.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/txdav/common/datastore/sql.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -211,6 +211,10 @@
</span><span class="cx">         return self._directoryService
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def setDirectoryService(self, directoryService):
+        self._directoryService = directoryService
+
+
</ins><span class="cx">     def callWithNewTransactions(self, callback):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Registers a method to be called whenever a new transaction is
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavdpsclientpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -38,6 +38,9 @@
</span><span class="cx">     MembersCommand, GroupsCommand, SetMembersCommand,
</span><span class="cx">     VerifyPlaintextPasswordCommand, VerifyHTTPDigestCommand
</span><span class="cx"> )
</span><ins>+from txdav.who.directory import (
+    CalendarDirectoryRecordMixin, CalendarDirectoryServiceMixin
+)
</ins><span class="cx"> import txdav.who.delegates
</span><span class="cx"> import txdav.who.idirectory
</span><span class="cx"> from txweb2.auth.digest import DigestedCredentials
</span><span class="lines">@@ -59,7 +62,7 @@
</span><span class="cx"> ##    component.normalizeCalendarUserAddresses
</span><span class="cx"> 
</span><span class="cx"> @implementer(IDirectoryService, IStoreDirectoryService)
</span><del>-class DirectoryService(BaseDirectoryService):
</del><ins>+class DirectoryService(BaseDirectoryService, CalendarDirectoryServiceMixin):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Client side of directory proxy
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -83,16 +86,7 @@
</span><span class="cx">     def getGroups(self, guids=None):
</span><span class="cx">         return succeed(set())
</span><span class="cx"> 
</span><del>-    # Must maintain the hack for a bit longer:
-    def setPrincipalCollection(self, principalCollection):
-        &quot;&quot;&quot;
-        Set the principal service that the directory relies on for doing proxy tests.
</del><span class="cx"> 
</span><del>-        @param principalService: the principal service.
-        @type principalService: L{DirectoryProvisioningResource}
-        &quot;&quot;&quot;
-        self.principalCollection = principalCollection
-
</del><span class="cx">     guid = &quot;1332A615-4D3A-41FE-B636-FBE25BFB982E&quot;
</span><span class="cx"> 
</span><span class="cx">     # END MOVE2WHO
</span><span class="lines">@@ -293,8 +287,9 @@
</span><span class="cx">         return self.recordType.lookupByName(oldName[:-1])
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> @implementer(ICalendarStoreDirectoryRecord)
</span><del>-class DirectoryRecord(BaseDirectoryRecord):
</del><ins>+class DirectoryRecord(BaseDirectoryRecord, CalendarDirectoryRecordMixin):
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -384,132 +379,8 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    @property
-    def calendarUserAddresses(self):
-        if not self.hasCalendars:
-            return frozenset()
</del><span class="cx"> 
</span><del>-        try:
-            cuas = set(
-                [&quot;mailto:%s&quot; % (emailAddress,)
-                 for emailAddress in self.emailAddresses]
-            )
-        except AttributeError:
-            cuas = set()
</del><span class="cx"> 
</span><del>-        try:
-            if self.guid:
-                if isinstance(self.guid, uuid.UUID):
-                    guid = unicode(self.guid).upper()
-                else:
-                    guid = self.guid
-                cuas.add(&quot;urn:uuid:{guid}&quot;.format(guid=guid))
-        except AttributeError:
-            # No guid
-            pass
-        cuas.add(&quot;/principals/__uids__/{uid}/&quot;.format(uid=self.uid))
-        for shortName in self.shortNames:
-            cuas.add(&quot;/principals/{rt}/{sn}/&quot;.format(
-                rt=self.recordType.name + &quot;s&quot;, sn=shortName)
-            )
-        return frozenset(cuas)
-
-
-    def getCUType(self):
-        # Mapping from directory record.recordType to RFC2445 CUTYPE values
-        self._cuTypes = {
-            self.service.recordType.user: 'INDIVIDUAL',
-            self.service.recordType.group: 'GROUP',
-            self.service.recordType.resource: 'RESOURCE',
-            self.service.recordType.location: 'ROOM',
-        }
-
-        return self._cuTypes.get(self.recordType, &quot;UNKNOWN&quot;)
-
-
-    @property
-    def displayName(self):
-        return self.fullNames[0]
-
-
-    def cacheToken(self):
-        &quot;&quot;&quot;
-        Generate a token that can be uniquely used to identify the state of this record for use
-        in a cache.
-        &quot;&quot;&quot;
-        return hash((
-            self.__class__.__name__,
-            self.service.realmName,
-            self.recordType.name,
-            self.shortNames,
-            self.guid,
-            self.hasCalendars,
-        ))
-
-
-    def canonicalCalendarUserAddress(self):
-        &quot;&quot;&quot;
-            Return a CUA for this record, preferring in this order:
-            urn:uuid: form
-            mailto: form
-            first in calendarUserAddresses list
-        &quot;&quot;&quot;
-
-        cua = &quot;&quot;
-        for candidate in self.calendarUserAddresses:
-            # Pick the first one, but urn:uuid: and mailto: can override
-            if not cua:
-                cua = candidate
-            # But always immediately choose the urn:uuid: form
-            if candidate.startswith(&quot;urn:uuid:&quot;):
-                cua = candidate
-                break
-            # Prefer mailto: if no urn:uuid:
-            elif candidate.startswith(&quot;mailto:&quot;):
-                cua = candidate
-        return cua
-
-
-    def enabledAsOrganizer(self):
-        # MOVE2WHO FIXME TO LOOK AT CONFIG
-        if self.recordType == self.service.recordType.user:
-            return True
-        elif self.recordType == DirectoryService.recordType_groups:
-            return False  # config.Scheduling.Options.AllowGroupAsOrganizer
-        elif self.recordType == DirectoryService.recordType_locations:
-            return False  # config.Scheduling.Options.AllowLocationAsOrganizer
-        elif self.recordType == DirectoryService.recordType_resources:
-            return False  # config.Scheduling.Options.AllowResourceAsOrganizer
-        else:
-            return False
-
-
-    #MOVE2WHO
-    def thisServer(self):
-        return True
-
-
-    def isLoginEnabled(self):
-        return self.loginAllowed
-
-
-    #MOVE2WHO
-    def calendarsEnabled(self):
-        # In the old world, this *also* looked at config:
-        # return config.EnableCalDAV and self.enabledForCalendaring
-        return self.hasCalendars
-
-
-    def getAutoScheduleMode(self, organizer):
-        # MOVE2WHO Fix this to take organizer into account:
-        return self.autoScheduleMode
-
-
-    def canAutoSchedule(self, organizer=None):
-        # MOVE2WHO Fix this:
-        return True
-
-
</del><span class="cx">     # For scheduling/freebusy
</span><span class="cx">     # FIXME: doesn't this need to happen in the DPS?
</span><span class="cx">     @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavdpsserverpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -446,6 +446,89 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+def directoryFromConfig(config, store=None):
+    &quot;&quot;&quot;
+    Return a directory service based on the config
+    &quot;&quot;&quot;
+    directoryType = config.DirectoryProxy.DirectoryType
+    args = config.DirectoryProxy.Arguments
+    kwds = config.DirectoryProxy.Keywords
+
+    # FIXME: this needs to talk to its own separate database
+    pool, txnFactory = getDBPool(config)
+    if store is None:
+        store = storeFromConfig(config, txnFactory, None)
+
+    if directoryType == &quot;OD&quot;:
+        from twext.who.opendirectory import DirectoryService as ODDirectoryService
+        primaryDirectory = ODDirectoryService(*args, **kwds)
+
+    elif directoryType == &quot;LDAP&quot;:
+        authDN = kwds.pop(&quot;authDN&quot;, &quot;&quot;)
+        password = kwds.pop(&quot;password&quot;, &quot;&quot;)
+        if authDN and password:
+            creds = UsernamePassword(authDN, password)
+        else:
+            creds = None
+        kwds[&quot;credentials&quot;] = creds
+        debug = kwds.pop(&quot;debug&quot;, &quot;&quot;)
+        primaryDirectory = LDAPDirectoryService(
+            *args, _debug=debug, **kwds
+        )
+
+    elif directoryType == &quot;XML&quot;:
+        path = kwds.pop(&quot;path&quot;, &quot;&quot;)
+        if not path or not os.path.exists(path):
+            log.error(&quot;Path not found for XML directory: {p}&quot;, p=path)
+        fp = FilePath(path)
+        primaryDirectory = XMLDirectoryService(fp, *args, **kwds)
+
+    else:
+        log.error(&quot;Invalid DirectoryType: {dt}&quot;, dt=directoryType)
+
+    #
+    # Setup the Augment Service
+    #
+    if config.AugmentService.type:
+        augmentClass = namedClass(config.AugmentService.type)
+        log.info(
+            &quot;Configuring augment service of type: {augmentClass}&quot;,
+            augmentClass=augmentClass
+        )
+        try:
+            augmentService = augmentClass(**config.AugmentService.params)
+        except IOError:
+            log.error(&quot;Could not start augment service&quot;)
+            raise
+    else:
+        augmentService = None
+
+    delegateDirectory = DelegateDirectoryService(
+        primaryDirectory.realmName,
+        store
+    )
+
+    aggregateDirectory = AggregateDirectoryService(
+        primaryDirectory.realmName,
+        (primaryDirectory, delegateDirectory)
+    )
+    try:
+        augmented = AugmentedDirectoryService(
+            aggregateDirectory, store, augmentService
+        )
+
+        # The delegate directory needs a way to look up user/group records
+        # so hand it a reference to the augmented directory.
+        # FIXME: is there a better pattern to use here?
+        delegateDirectory.setMasterDirectory(augmented)
+
+    except Exception as e:
+        log.error(&quot;Could not create directory service&quot;, error=e)
+        raise
+
+    return augmented
+
+
</ins><span class="cx"> @implementer(IPlugin, service.IServiceMaker)
</span><span class="cx"> class DirectoryProxyServiceMaker(object):
</span><span class="cx"> 
</span><span class="lines">@@ -454,7 +537,7 @@
</span><span class="cx">     options = DirectoryProxyOptions
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def makeService(self, options, store=None):
</del><ins>+    def makeService(self, options):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Return a service
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -465,82 +548,20 @@
</span><span class="cx">         else:
</span><span class="cx">             setproctitle(&quot;CalendarServer Directory Proxy Service&quot;)
</span><span class="cx"> 
</span><del>-        directoryType = config.DirectoryProxy.DirectoryType
-        args = config.DirectoryProxy.Arguments
-        kwds = config.DirectoryProxy.Keywords
</del><ins>+        try:
+            print(&quot;XZZZY AAA&quot;)
+            directory = directoryFromConfig(config)
+            print(&quot;XZZZY BBB&quot;)
+        except Exception as e:
+            log.error(&quot;Failed to create directory service&quot;, error=e)
+            raise
</ins><span class="cx"> 
</span><del>-        # FIXME: this needs to talk to its own separate database
-        if store is None:
-            pool, txnFactory = getDBPool(config)
-            store = storeFromConfig(config, txnFactory)
</del><ins>+        log.info(&quot;Created directory service&quot;)
+        print(&quot;XZZZY CCCC&quot;)
</ins><span class="cx"> 
</span><del>-        if directoryType == &quot;OD&quot;:
-            from twext.who.opendirectory import DirectoryService as ODDirectoryService
-            primaryDirectory = ODDirectoryService(*args, **kwds)
-
-        elif directoryType == &quot;LDAP&quot;:
-            authDN = kwds.pop(&quot;authDN&quot;, &quot;&quot;)
-            password = kwds.pop(&quot;password&quot;, &quot;&quot;)
-            if authDN and password:
-                creds = UsernamePassword(authDN, password)
-            else:
-                creds = None
-            kwds[&quot;credentials&quot;] = creds
-            debug = kwds.pop(&quot;debug&quot;, &quot;&quot;)
-            primaryDirectory = LDAPDirectoryService(
-                *args, _debug=debug, **kwds
-            )
-
-        elif directoryType == &quot;XML&quot;:
-            path = kwds.pop(&quot;path&quot;, &quot;&quot;)
-            if not path or not os.path.exists(path):
-                log.error(&quot;Path not found for XML directory: {p}&quot;, p=path)
-            fp = FilePath(path)
-            primaryDirectory = XMLDirectoryService(fp, *args, **kwds)
-
-        else:
-            log.error(&quot;Invalid DirectoryType: {dt}&quot;, dt=directoryType)
-
-        desc = &quot;unix:{path}:mode=660&quot;.format(
-            path=config.DirectoryProxy.SocketPath
</del><ins>+        return strPortsService(
+            &quot;unix:{path}:mode=660&quot;.format(
+                path=config.DirectoryProxy.SocketPath
+            ),
+            DirectoryProxyAMPFactory(directory)
</ins><span class="cx">         )
</span><del>-        #
-        # Setup the Augment Service
-        #
-        if config.AugmentService.type:
-            augmentClass = namedClass(config.AugmentService.type)
-            log.info(
-                &quot;Configuring augment service of type: {augmentClass}&quot;,
-                augmentClass=augmentClass
-            )
-            try:
-                augmentService = augmentClass(**config.AugmentService.params)
-            except IOError:
-                log.error(&quot;Could not start augment service&quot;)
-                raise
-        else:
-            augmentService = None
-
-        delegateDirectory = DelegateDirectoryService(
-            primaryDirectory.realmName,
-            store
-        )
-
-        aggregateDirectory = AggregateDirectoryService(
-            primaryDirectory.realmName,
-            (primaryDirectory, delegateDirectory)
-        )
-        try:
-            augmented = AugmentedDirectoryService(
-                aggregateDirectory, store, augmentService
-            )
-
-            # The delegate directory needs a way to look up user/group records
-            # so hand it a reference to the augmented directory.
-            # FIXME: is there a better pattern to use here?
-            delegateDirectory.setMasterDirectory(augmented)
-
-        except Exception as e:
-            log.error(&quot;Could not create directory service&quot;, error=e)
-            raise
-        return strPortsService(desc, DirectoryProxyAMPFactory(augmented))
</del></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavwhoaugmentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/who/augment.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/who/augment.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/txdav/who/augment.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -23,6 +23,9 @@
</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="cx"> from twext.who.util import ConstantsContainer
</span><ins>+from txdav.who.directory import (
+    CalendarDirectoryRecordMixin, CalendarDirectoryServiceMixin
+)
</ins><span class="cx"> from txdav.who.idirectory import AutoScheduleMode, FieldName
</span><span class="cx"> from txdav.who.idirectory import RecordType as CalRecordType
</span><span class="cx"> from txdav.who.delegates import RecordType as DelegateRecordType
</span><span class="lines">@@ -33,7 +36,7 @@
</span><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class AugmentedDirectoryRecord(DirectoryRecord):
</del><ins>+class AugmentedDirectoryRecord(DirectoryRecord, CalendarDirectoryRecordMixin):
</ins><span class="cx"> 
</span><span class="cx">     def __init__(self, service, baseRecord, augmentedFields):
</span><span class="cx">         DirectoryRecord.__init__(self, service, augmentedFields)
</span><span class="lines">@@ -64,7 +67,8 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @implementer(IDirectoryService)
</span><del>-class AugmentedDirectoryService(BaseDirectoryService):
</del><ins>+class AugmentedDirectoryService(BaseDirectoryService,
+                                CalendarDirectoryServiceMixin):
</ins><span class="cx"> 
</span><span class="cx">     fieldName = ConstantsContainer((
</span><span class="cx">         BaseDirectoryService.fieldName,
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavwhodelegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/who/delegates.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/who/delegates.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/txdav/who/delegates.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -67,6 +67,7 @@
</span><span class="cx">         this record.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         parentUID, proxyType = self.uid.split(&quot;#&quot;)
</span><ins>+
</ins><span class="cx">         txn = self.service._store.newTransaction()
</span><span class="cx"> 
</span><span class="cx">         if self.recordType in (
</span><span class="lines">@@ -103,7 +104,6 @@
</span><span class="cx">         @param memberRecords: The new members of the group
</span><span class="cx">         @type memberRecords: iterable of L{iDirectoryRecord}s
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-
</del><span class="cx">         if self.recordType not in (
</span><span class="cx">             RecordType.readDelegateGroup, RecordType.writeDelegateGroup
</span><span class="cx">         ):
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavwhodirectorypy"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/sagen/move2who/txdav/who/directory.py (0 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/who/directory.py                                (rev 0)
+++ CalendarServer/branches/users/sagen/move2who/txdav/who/directory.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -0,0 +1,170 @@
</span><ins>+##
+# Copyright (c) 2006-2014 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+&quot;&quot;&quot;
+Calendar/Contacts specific methods for DirectoryRecord
+&quot;&quot;&quot;
+
+
+import uuid
+
+
+__all__ = [
+    &quot;CalendarDirectoryRecordMixin&quot;,
+    &quot;CalendarDirectoryServiceMixin&quot;,
+]
+
+
+class CalendarDirectoryServiceMixin(object):
+
+    # Must maintain the hack for a bit longer:
+    def setPrincipalCollection(self, principalCollection):
+        &quot;&quot;&quot;
+        Set the principal service that the directory relies on for doing proxy tests.
+
+        @param principalService: the principal service.
+        @type principalService: L{DirectoryProvisioningResource}
+        &quot;&quot;&quot;
+        self.principalCollection = principalCollection
+
+
+class CalendarDirectoryRecordMixin(object):
+
+
+    @property
+    def calendarUserAddresses(self):
+        if not self.hasCalendars:
+            return frozenset()
+
+        try:
+            cuas = set(
+                [&quot;mailto:%s&quot; % (emailAddress,)
+                 for emailAddress in self.emailAddresses]
+            )
+        except AttributeError:
+            cuas = set()
+
+        try:
+            if self.guid:
+                if isinstance(self.guid, uuid.UUID):
+                    guid = unicode(self.guid).upper()
+                else:
+                    guid = self.guid
+                cuas.add(&quot;urn:uuid:{guid}&quot;.format(guid=guid))
+        except AttributeError:
+            # No guid
+            pass
+        cuas.add(&quot;/principals/__uids__/{uid}/&quot;.format(uid=self.uid))
+        for shortName in self.shortNames:
+            cuas.add(&quot;/principals/{rt}/{sn}/&quot;.format(
+                rt=self.recordType.name + &quot;s&quot;, sn=shortName)
+            )
+        return frozenset(cuas)
+
+
+    def getCUType(self):
+        # Mapping from directory record.recordType to RFC2445 CUTYPE values
+        self._cuTypes = {
+            self.service.recordType.user: 'INDIVIDUAL',
+            self.service.recordType.group: 'GROUP',
+            self.service.recordType.resource: 'RESOURCE',
+            self.service.recordType.location: 'ROOM',
+        }
+
+        return self._cuTypes.get(self.recordType, &quot;UNKNOWN&quot;)
+
+
+    @property
+    def displayName(self):
+        return self.fullNames[0]
+
+
+    def cacheToken(self):
+        &quot;&quot;&quot;
+        Generate a token that can be uniquely used to identify the state of this record for use
+        in a cache.
+        &quot;&quot;&quot;
+        return hash((
+            self.__class__.__name__,
+            self.service.realmName,
+            self.recordType.name,
+            self.shortNames,
+            self.guid,
+            self.hasCalendars,
+        ))
+
+
+    def canonicalCalendarUserAddress(self):
+        &quot;&quot;&quot;
+            Return a CUA for this record, preferring in this order:
+            urn:uuid: form
+            mailto: form
+            first in calendarUserAddresses list
+        &quot;&quot;&quot;
+
+        cua = &quot;&quot;
+        for candidate in self.calendarUserAddresses:
+            # Pick the first one, but urn:uuid: and mailto: can override
+            if not cua:
+                cua = candidate
+            # But always immediately choose the urn:uuid: form
+            if candidate.startswith(&quot;urn:uuid:&quot;):
+                cua = candidate
+                break
+            # Prefer mailto: if no urn:uuid:
+            elif candidate.startswith(&quot;mailto:&quot;):
+                cua = candidate
+        return cua
+
+
+    def enabledAsOrganizer(self):
+        # MOVE2WHO FIXME TO LOOK AT CONFIG
+        if self.recordType == self.service.recordType.user:
+            return True
+        elif self.recordType == self.service.recordType.group:
+            return False  # config.Scheduling.Options.AllowGroupAsOrganizer
+        elif self.recordType == self.service.recordType.location:
+            return False  # config.Scheduling.Options.AllowLocationAsOrganizer
+        elif self.recordType == self.service.recordType.resource:
+            return False  # config.Scheduling.Options.AllowResourceAsOrganizer
+        else:
+            return False
+
+
+    #MOVE2WHO
+    def thisServer(self):
+        return True
+
+
+    def isLoginEnabled(self):
+        return self.loginAllowed
+
+
+    #MOVE2WHO
+    def calendarsEnabled(self):
+        # In the old world, this *also* looked at config:
+        # return config.EnableCalDAV and self.enabledForCalendaring
+        return self.hasCalendars
+
+
+    def getAutoScheduleMode(self, organizer):
+        # MOVE2WHO Fix this to take organizer into account:
+        return self.autoScheduleMode
+
+
+    def canAutoSchedule(self, organizer=None):
+        # MOVE2WHO Fix this:
+        return True
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssagenmove2whotxdavwhogroupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/move2who/txdav/who/groups.py (12848 => 12849)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/move2who/txdav/who/groups.py        2014-03-07 21:46:24 UTC (rev 12848)
+++ CalendarServer/branches/users/sagen/move2who/txdav/who/groups.py        2014-03-07 22:20:50 UTC (rev 12849)
</span><span class="lines">@@ -131,7 +131,6 @@
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def doWork(self):
</span><del>-        print(&quot;XYZZY IN GRW doWork&quot;, )
</del><span class="cx">         # Delete all other work items for this group
</span><span class="cx">         yield Delete(
</span><span class="cx">             From=self.table, Where=(self.table.GROUP_GUID == self.groupGuid)
</span><span class="lines">@@ -142,7 +141,7 @@
</span><span class="cx"> 
</span><span class="cx">             try:
</span><span class="cx">                 yield newGroupCacher.refreshGroup(
</span><del>-                    self.transaction, self.groupGuid
</del><ins>+                    self.transaction, self.groupGuid.decode(&quot;utf-8&quot;)
</ins><span class="cx">                 )
</span><span class="cx">             except Exception, e:
</span><span class="cx">                 log.error(
</span><span class="lines">@@ -471,4 +470,6 @@
</span><span class="cx">             ).on(txn)
</span><span class="cx">             attendeeGroupUIDs = set([row[0] for row in rows])
</span><span class="cx"> 
</span><ins>+        # FIXME: is this a good place to clear out unreferenced groups?
+
</ins><span class="cx">         returnValue(delegatedUIDs.union(attendeeGroupUIDs))
</span></span></pre>
</div>
</div>

</body>
</html>