<!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>[13362] CalendarServer/trunk</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/13362">13362</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2014-04-23 09:47:07 -0700 (Wed, 23 Apr 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Hide missing or invalid proxyFors. Fix some sharing issues related to missing or invalid users.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunktwistedcaldavdirectoryprincipalpy">CalendarServer/trunk/twistedcaldav/directory/principal.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavdirectorytestaugmentsxml">CalendarServer/trunk/twistedcaldav/directory/test/augments.xml</a></li>
<li><a href="#CalendarServertrunktwistedcaldavdirectorytesttest_principalpy">CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavresourcepy">CalendarServer/trunk/twistedcaldav/resource.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavsharingpy">CalendarServer/trunk/twistedcaldav/sharing.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavtesttest_sharingpy">CalendarServer/trunk/twistedcaldav/test/test_sharing.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavtestutilpy">CalendarServer/trunk/twistedcaldav/test/util.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoretestutilpy">CalendarServer/trunk/txdav/common/datastore/test/util.py</a></li>
<li><a href="#CalendarServertrunktxdavwhodirectorypy">CalendarServer/trunk/txdav/who/directory.py</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#CalendarServertrunktwistedcaldavdirectorytestutilpy">CalendarServer/trunk/twistedcaldav/directory/test/util.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunktwistedcaldavdirectoryprincipalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py (13361 => 13362)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/directory/principal.py        2014-04-23 00:49:34 UTC (rev 13361)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py        2014-04-23 16:47:07 UTC (rev 13362)
</span><span class="lines">@@ -986,7 +986,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def proxyFor(self, readWrite):
</del><ins>+    def proxyFor(self, readWrite, ignoreDisabled=True):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Returns the set of principals currently delegating to this principal
</span><span class="cx">         with the access indicated by the readWrite argument.  If readWrite is
</span><span class="lines">@@ -995,7 +995,9 @@
</span><span class="cx"> 
</span><span class="cx">         @param readWrite: Whether to look up read-write delegators, or
</span><span class="cx">             read-only delegators
</span><del>-        @type readWrite: C{bool}
</del><ins>+        @type readWrite: L{bool}
+        @param ignoreDisabled: If L{True} disabled delegators are not returned
+        @type ignoreDisabled: L{bool}
</ins><span class="cx"> 
</span><span class="cx">         @return: A Deferred firing with a set of principals
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -1021,7 +1023,7 @@
</span><span class="cx"> 
</span><span class="cx">                 for record in proxyForRecords:
</span><span class="cx">                     principal = yield self.parent.principalForRecord(record)
</span><del>-                    if principal is not None:
</del><ins>+                    if principal is not None and (not ignoreDisabled or principal.record.hasCalendars):
</ins><span class="cx">                         proxyFors.add(principal)
</span><span class="cx"> 
</span><span class="cx">         returnValue(proxyFors)
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavdirectorytestaugmentsxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/directory/test/augments.xml (13361 => 13362)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/directory/test/augments.xml        2014-04-23 00:49:34 UTC (rev 13361)
+++ CalendarServer/trunk/twistedcaldav/directory/test/augments.xml        2014-04-23 16:47:07 UTC (rev 13362)
</span><span class="lines">@@ -66,12 +66,37 @@
</span><span class="cx">     &lt;enable-calendar&gt;false&lt;/enable-calendar&gt;
</span><span class="cx">     &lt;enable-addressbook&gt;false&lt;/enable-addressbook&gt;
</span><span class="cx">   &lt;/record&gt;
</span><del>-  &lt;record repeat=&quot;100&quot;&gt;
-    &lt;uid&gt;user%02d&lt;/uid&gt;
</del><ins>+  &lt;record&gt;
+    &lt;uid&gt;user01&lt;/uid&gt;
</ins><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span><span class="cx">     &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
</span><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><ins>+    &lt;uid&gt;user02&lt;/uid&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+  &lt;/record&gt;
+  &lt;record&gt;
+    &lt;uid&gt;user03&lt;/uid&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+  &lt;/record&gt;
+  &lt;record&gt;
+    &lt;uid&gt;user04&lt;/uid&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+  &lt;/record&gt;
+  &lt;record&gt;
+    &lt;uid&gt;user05&lt;/uid&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+  &lt;/record&gt;
+  &lt;record&gt;
+    &lt;uid&gt;user06&lt;/uid&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+  &lt;/record&gt;
+  &lt;record&gt;
</ins><span class="cx">     &lt;uid&gt;right_coast&lt;/uid&gt;
</span><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span><span class="cx">     &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavdirectorytesttest_principalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py (13361 => 13362)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py        2014-04-23 00:49:34 UTC (rev 13361)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py        2014-04-23 16:47:07 UTC (rev 13362)
</span><span class="lines">@@ -15,18 +15,11 @@
</span><span class="cx"> ##
</span><span class="cx"> from __future__ import print_function
</span><span class="cx"> 
</span><del>-from urllib import quote
-from uuid import UUID
</del><ins>+from twext.who.idirectory import RecordType
</ins><span class="cx"> 
</span><ins>+from twisted.cred.credentials import UsernamePassword
</ins><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><del>-from twisted.cred.credentials import UsernamePassword
</del><span class="cx"> 
</span><del>-from txweb2.dav.resource import AccessDeniedError
-from txweb2.http import HTTPError
-from txweb2.test.test_server import SimpleRequest
-
-from txdav.xml import element as davxml
-
</del><span class="cx"> from twistedcaldav import carddavxml
</span><span class="cx"> from twistedcaldav.cache import DisabledCacheNotifier
</span><span class="cx"> from twistedcaldav.caldavxml import caldav_namespace
</span><span class="lines">@@ -38,10 +31,19 @@
</span><span class="cx">     DirectoryPrincipalTypeProvisioningResource,
</span><span class="cx"> )
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase
</span><ins>+
+from txdav.who.delegates import addDelegate
</ins><span class="cx"> from txdav.who.idirectory import AutoScheduleMode, RecordType as CalRecordType
</span><del>-from twext.who.idirectory import RecordType
</del><ins>+from txdav.xml import element as davxml
</ins><span class="cx"> 
</span><ins>+from txweb2.dav.resource import AccessDeniedError
+from txweb2.http import HTTPError
+from txweb2.test.test_server import SimpleRequest
</ins><span class="cx"> 
</span><ins>+from urllib import quote
+from uuid import UUID
+
+
</ins><span class="cx"> class ProvisionedPrincipals(StoreTestCase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Directory service provisioned principals.
</span><span class="lines">@@ -53,7 +55,6 @@
</span><span class="cx">         self.principalRootResource = self.actualRoot.getChild(&quot;principals&quot;)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_hierarchy(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -169,7 +170,7 @@
</span><span class="cx">         Test of a test routine...
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if True:  # user.enabled:
</span><span class="cx">                 self.assertEquals(recordResource.record, record)
</span><span class="lines">@@ -185,7 +186,7 @@
</span><span class="cx">         DirectoryPrincipalProvisioningResource.principalForShortName()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            provisioningResource, recordType, _ignore_recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             principal = yield provisioningResource.principalForShortName(
</span><span class="cx">                 recordType, record.shortNames[0]
</span><span class="lines">@@ -246,7 +247,7 @@
</span><span class="cx">         DirectoryPrincipalProvisioningResource.principalForUID()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            provisioningResource, _ignore_recordType, _ignore_recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             principal = yield provisioningResource.principalForUID(record.uid)
</span><span class="cx">             if True:  # user.enabled:
</span><span class="lines">@@ -262,7 +263,7 @@
</span><span class="cx">         DirectoryPrincipalProvisioningResource.principalForRecord()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            provisioningResource, _ignore_recordType, _ignore_recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             principal = yield provisioningResource.principalForRecord(record)
</span><span class="cx">             if True:  # user.enabled:
</span><span class="lines">@@ -279,7 +280,7 @@
</span><span class="cx">         .principalForCalendarUserAddress()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            provisioningResource, _ignore_recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx"> 
</span><span class="cx">             test_items = tuple(record.calendarUserAddresses)
</span><span class="lines">@@ -347,7 +348,7 @@
</span><span class="cx">         .principalForCalendarUserAddress()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            provisioningResource, _ignore_recordType, _ignore_recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             principal = yield provisioningResource.principalForRecord(record)
</span><span class="cx">             if True:  # user.enabled:
</span><span class="lines">@@ -465,7 +466,7 @@
</span><span class="cx">             self.directory.recordType.user,
</span><span class="cx">         )
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            provisioningResource, recordType, _ignore_recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx"> 
</span><span class="cx">             if record.hasCalendars:
</span><span class="lines">@@ -488,7 +489,7 @@
</span><span class="cx">             self.directory.recordType.resource,
</span><span class="cx">         )
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            provisioningResource, recordType, _ignore_recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if record.hasCalendars:
</span><span class="cx">                 principal = (
</span><span class="lines">@@ -515,7 +516,7 @@
</span><span class="cx">         that is an instance of DisabledCacheNotifier
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, _ignore_record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if True:  # user.enabled:
</span><span class="cx">                 self.failUnless(
</span><span class="lines">@@ -532,7 +533,7 @@
</span><span class="cx">         DirectoryPrincipalResource.displayName()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, _ignore_record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             self.failUnless(recordResource.displayName())
</span><span class="cx"> 
</span><span class="lines">@@ -543,7 +544,7 @@
</span><span class="cx">         DirectoryPrincipalResource.groupMembers()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             members = yield recordResource.groupMembers()
</span><span class="cx">             self.failUnless(
</span><span class="lines">@@ -559,7 +560,7 @@
</span><span class="cx">         DirectoryPrincipalResource.groupMemberships()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if True:  # user.enabled:
</span><span class="cx">                 memberships = yield recordResource.groupMemberships()
</span><span class="lines">@@ -579,7 +580,7 @@
</span><span class="cx">         DirectoryPrincipalResource.principalUID()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             self.assertEquals(record.uid, recordResource.principalUID())
</span><span class="cx"> 
</span><span class="lines">@@ -590,7 +591,7 @@
</span><span class="cx">         DirectoryPrincipalResource.calendarUserAddresses()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if record.hasCalendars:
</span><span class="cx">                 self.assertEqual(
</span><span class="lines">@@ -609,7 +610,7 @@
</span><span class="cx">         DirectoryPrincipalResource.canonicalCalendarUserAddress()
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if record.hasCalendars:
</span><span class="cx">                 self.failUnless(
</span><span class="lines">@@ -625,7 +626,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if record.hasContacts:
</span><span class="cx">                 homeURLs = tuple(recordResource.addressBookHomeURLs())
</span><span class="lines">@@ -684,7 +685,7 @@
</span><span class="cx"> 
</span><span class="cx">         # Calendar home provisioners should result in calendar homes.
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if record.hasCalendars:
</span><span class="cx">                 homeURLs = tuple(recordResource.calendarHomeURLs())
</span><span class="lines">@@ -746,7 +747,7 @@
</span><span class="cx"> 
</span><span class="cx">         # Default state - resources and locations, enabled, others not
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if record.hasCalendars:
</span><span class="cx">                 if recordType in (CalRecordType.location, CalRecordType.resource):
</span><span class="lines">@@ -757,7 +758,7 @@
</span><span class="cx">         # Set config to allow users
</span><span class="cx">         self.patch(config.Scheduling.Options.AutoSchedule, &quot;AllowUsers&quot;, True)
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if record.hasCalendars:
</span><span class="cx">                 if (
</span><span class="lines">@@ -777,7 +778,7 @@
</span><span class="cx">         # Set config to disallow all
</span><span class="cx">         self.patch(config.Scheduling.Options.AutoSchedule, &quot;Enabled&quot;, False)
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, recordType, recordResource, record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if record.hasCalendars:
</span><span class="cx">                 self.assertFalse((yield recordResource.canAutoSchedule()))
</span><span class="lines">@@ -829,7 +830,7 @@
</span><span class="cx">         Default access controls for principals.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for (
</span><del>-            provisioningResource, recordType, recordResource, record
</del><ins>+            _ignore_provisioningResource, _ignore_recordType, recordResource, _ignore_record
</ins><span class="cx">         ) in (yield self._allRecords()):
</span><span class="cx">             if True:  # user.enabled:
</span><span class="cx">                 for args in (
</span><span class="lines">@@ -1016,7 +1017,7 @@
</span><span class="cx"> def _authReadOnlyPrivileges(self, resource, url):
</span><span class="cx">     items = []
</span><span class="cx">     for (
</span><del>-        provisioningResource, recordType, recordResource, record
</del><ins>+        _ignore_provisioningResource, _ignore_recordType, recordResource, _ignore_record
</ins><span class="cx">     ) in (yield self._allRecords()):
</span><span class="cx">         if True:  # user.enabled:
</span><span class="cx">             items.append((
</span><span class="lines">@@ -1039,3 +1040,62 @@
</span><span class="cx">         results.append((resource, url, principal, privilege, allowed))
</span><span class="cx"> 
</span><span class="cx">     returnValue(results)
</span><ins>+
+
+
+class ProxyPrincipals(StoreTestCase):
+    &quot;&quot;&quot;
+    Directory service proxy principals.
+    &quot;&quot;&quot;
+    @inlineCallbacks
+    def setUp(self):
+        yield super(ProxyPrincipals, self).setUp()
+
+        self.principalRootResource = self.actualRoot.getChild(&quot;principals&quot;)
+
+
+    @inlineCallbacks
+    def test_hideDisabledProxies(self):
+        &quot;&quot;&quot;
+        Make sure users that are missing or not enabled for calendaring are removed
+        from the proxyFor list.
+        &quot;&quot;&quot;
+
+        # Check proxies empty right now
+        principal01 = yield self.principalRootResource.principalForUID((yield self.userUIDFromShortName(&quot;user01&quot;)))
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 0)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        principal02 = yield self.principalRootResource.principalForUID((yield self.userUIDFromShortName(&quot;user02&quot;)))
+        self.assertTrue(len((yield principal02.proxyFor(False))) == 0)
+        self.assertTrue(len((yield principal02.proxyFor(True))) == 0)
+
+        principal03 = yield self.principalRootResource.principalForUID((yield self.userUIDFromShortName(&quot;user03&quot;)))
+        self.assertTrue(len((yield principal03.proxyFor(False))) == 0)
+        self.assertTrue(len((yield principal03.proxyFor(True))) == 0)
+
+        # Make user01 a read-only proxy for user02 and user03
+        yield addDelegate(self.transactionUnderTest(), principal02.record, principal01.record, False)
+        yield addDelegate(self.transactionUnderTest(), principal03.record, principal01.record, False)
+        yield self.commit()
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 2)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        # Now disable user02
+        yield self.changeRecord(principal02.record, self.directory.fieldName.hasCalendars, False)
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 1)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        # Now enable user02
+        yield self.changeRecord(principal02.record, self.directory.fieldName.hasCalendars, True)
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 2)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        # Now remove user02
+        yield self.directory.removeRecords((principal02.record.uid,))
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 1)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
</ins></span></pre></div>
<a id="CalendarServertrunktwistedcaldavdirectorytestutilpy"></a>
<div class="delfile"><h4>Deleted: CalendarServer/trunk/twistedcaldav/directory/test/util.py (13361 => 13362)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/directory/test/util.py        2014-04-23 00:49:34 UTC (rev 13361)
+++ CalendarServer/trunk/twistedcaldav/directory/test/util.py        2014-04-23 16:47:07 UTC (rev 13362)
</span><span class="lines">@@ -1,386 +0,0 @@
</span><del>-##
-# Copyright (c) 2005-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.
-##
-
-from twisted.trial.unittest import SkipTest
-from twisted.cred.credentials import UsernamePassword
-from txweb2.auth.digest import DigestedCredentials, calcResponse, calcHA1
-
-from twistedcaldav.directory.directory import DirectoryService
-from twistedcaldav.directory.directory import UnknownRecordTypeError
-from twistedcaldav.directory.util import transactionFromRequest
-from twistedcaldav.test.util import TestCase
-
-# FIXME: Add tests for GUID hooey, once we figure out what that means here
-
-class DirectoryTestCase (TestCase):
-    &quot;&quot;&quot;
-    Tests a directory implementation.
-    &quot;&quot;&quot;
-    # Subclass should init this to a set of recordtypes.
-    recordTypes = set()
-
-    # Subclass should init this to a dict of username keys and dict values.
-    users = {}
-
-    # Subclass should init this to a dict of groupname keys and dict values.
-    groups = {}
-
-    # Subclass should init this to a dict of locationnames keys and dict values.
-    locations = {}
-
-    # Subclass should init this to a dict of resourcenames keys and dict values.
-    resources = {}
-
-    # Subclass should init this to a dict of addressname keys and dict values.
-    addresses = {}
-
-
-    # Subclass should init this to an IDirectoryService implementation class.
-    def service(self):
-        &quot;&quot;&quot;
-        Returns an IDirectoryService.
-        &quot;&quot;&quot;
-        raise NotImplementedError(&quot;Subclass needs to implement service()&quot;)
-
-    # For aggregator subclasses
-    recordTypePrefixes = (&quot;&quot;,)
-
-
-    def test_realm(self):
-        &quot;&quot;&quot;
-        IDirectoryService.realm
-        &quot;&quot;&quot;
-        self.failUnless(self.service().realmName)
-
-
-    def test_recordTypes(self):
-        &quot;&quot;&quot;
-        IDirectoryService.recordTypes()
-        &quot;&quot;&quot;
-        if not self.recordTypes:
-            raise SkipTest(&quot;No record types&quot;)
-
-        self.assertEquals(set(self.service().recordTypes()), self.recordTypes)
-
-
-    def test_recordWithShortName(self):
-        &quot;&quot;&quot;
-        IDirectoryService.recordWithShortName()
-        &quot;&quot;&quot;
-        for recordType, data in (
-            (DirectoryService.recordType_users    , self.users),
-            (DirectoryService.recordType_groups   , self.groups),
-            (DirectoryService.recordType_locations, self.locations),
-            (DirectoryService.recordType_resources, self.resources),
-        ):
-            if not data:
-                raise SkipTest(&quot;No %s&quot; % (recordType,))
-
-            service = self.service()
-            for shortName, info in data.iteritems():
-                record = service.recordWithShortName(info.get(&quot;prefix&quot;, &quot;&quot;) + recordType, shortName)
-                self.failUnless(record, &quot;No record (%s)%s&quot; % (info.get(&quot;prefix&quot;, &quot;&quot;) + recordType, shortName))
-                self.compare(record, shortName, data[shortName])
-
-            for prefix in self.recordTypePrefixes:
-                try:
-                    record = service.recordWithShortName(prefix + recordType, &quot;IDunnoWhoThisIsIReallyDont&quot;)
-                except UnknownRecordTypeError:
-                    continue
-                self.assertEquals(record, None)
-
-
-    def test_recordWithUID(self):
-        service = self.service()
-        record = None
-
-        for shortName, what in self.allEntries():
-            guid = what[&quot;guid&quot;]
-            if guid is not None:
-                record = service.recordWithUID(guid)
-                self.compare(record, shortName, what)
-
-        if record is None:
-            raise SkipTest(&quot;No GUIDs provided to test&quot;)
-
-
-    def test_recordWithCalendarUserAddress(self):
-        service = self.service()
-        record = None
-
-        for shortName, what in self.allEntries():
-            for address in what[&quot;addresses&quot;]:
-                record = service.recordWithCalendarUserAddress(address)
-                self.compare(record, shortName, what)
-
-        if record is None:
-            raise SkipTest(&quot;No calendar user addresses provided to test&quot;)
-
-
-    def test_groupMembers(self):
-        &quot;&quot;&quot;
-        IDirectoryRecord.members()
-        &quot;&quot;&quot;
-        if not self.groups:
-            raise SkipTest(&quot;No groups&quot;)
-
-        service = self.service()
-        for group, info in self.groups.iteritems():
-            prefix = info.get(&quot;prefix&quot;, &quot;&quot;)
-            groupRecord = service.recordWithShortName(prefix + DirectoryService.recordType_groups, group)
-            result = set((m.recordType, prefix + m.shortNames[0]) for m in groupRecord.members())
-            expected = set(self.groups[group][&quot;members&quot;])
-            self.assertEquals(
-                result, expected,
-                &quot;Wrong membership for group %r: %s != %s&quot; % (group, result, expected)
-            )
-
-
-    def test_groupMemberships(self):
-        &quot;&quot;&quot;
-        IDirectoryRecord.groups()
-        &quot;&quot;&quot;
-        if not self.users:
-            raise SkipTest(&quot;No users&quot;)
-        if not self.groups:
-            raise SkipTest(&quot;No groups&quot;)
-
-        for recordType, data in (
-            (DirectoryService.recordType_users , self.users),
-            (DirectoryService.recordType_groups, self.groups),
-        ):
-            service = self.service()
-            for shortName, info in data.iteritems():
-                prefix = info.get(&quot;prefix&quot;, &quot;&quot;)
-                record = service.recordWithShortName(prefix + recordType, shortName)
-                result = set(prefix + g.shortNames[0] for g in record.groups())
-                expected = set(g for g in self.groups if (record.recordType, shortName) in self.groups[g][&quot;members&quot;])
-                self.assertEquals(
-                    result, expected,
-                    &quot;Wrong groups for %s %r: %s != %s&quot; % (record.recordType, shortName, result, expected)
-                )
-
-
-    def recordNames(self, recordType):
-        service = self.service()
-        names = set()
-        for prefix in self.recordTypePrefixes:
-            try:
-                records = service.listRecords(prefix + recordType)
-            except UnknownRecordTypeError:
-                continue
-            assert records is not None, &quot;%r(%r) returned None&quot; % (service.listRecords, recordType)
-            for record in records:
-                names.add(prefix + record.shortNames[0])
-
-        return names
-
-
-    def allEntries(self):
-        for data, _ignore_recordType in (
-            (self.users, DirectoryService.recordType_users),
-            (self.groups, DirectoryService.recordType_groups),
-            (self.locations, DirectoryService.recordType_locations),
-            (self.resources, DirectoryService.recordType_resources),
-        ):
-            for item in data.iteritems():
-                yield item
-
-
-    def compare(self, record, shortName, data):
-        def value(key):
-            if key in data:
-                return data[key]
-            else:
-                return None
-
-        guid = value(&quot;guid&quot;)
-        if guid is not None:
-            guid = record.guid
-
-        addresses = set(value(&quot;addresses&quot;))
-        if record.hasCalendars:
-            addresses.add(&quot;urn:x-uid:%s&quot; % (record.uid,))
-            addresses.add(&quot;urn:uuid:%s&quot; % (record.guid,))
-            addresses.add(&quot;/principals/__uids__/%s/&quot; % (record.uid,))
-            addresses.add(&quot;/principals/%s/%s/&quot; % (record.recordType, record.shortNames[0],))
-
-        if hasattr(record.service, &quot;recordTypePrefix&quot;):
-            prefix = record.service.recordTypePrefix
-        else:
-            prefix = &quot;&quot;
-
-        self.assertEquals(prefix + record.shortNames[0], shortName)
-        self.assertEquals(set(record.calendarUserAddresses), addresses)
-
-        if value(&quot;guid&quot;):
-            self.assertEquals(record.guid, value(&quot;guid&quot;))
-
-        if value(&quot;name&quot;):
-            self.assertEquals(record.fullName, value(&quot;name&quot;))
-
-
-    def servicePrefix(self):
-        service = self.service()
-        if hasattr(service, &quot;recordTypePrefix&quot;):
-            return service.recordTypePrefix
-        else:
-            return &quot;&quot;
-
-
-
-class NonCachingTestCase (DirectoryTestCase):
-
-    def test_listRecords_user(self):
-        &quot;&quot;&quot;
-        IDirectoryService.listRecords(DirectoryService.recordType_users)
-        &quot;&quot;&quot;
-        if not self.users:
-            raise SkipTest(&quot;No users&quot;)
-
-        self.assertEquals(self.recordNames(DirectoryService.recordType_users), set(self.users.keys()))
-
-
-    def test_listRecords_group(self):
-        &quot;&quot;&quot;
-        IDirectoryService.listRecords(DirectoryService.recordType_groups)
-        &quot;&quot;&quot;
-        if not self.groups:
-            raise SkipTest(&quot;No groups&quot;)
-
-        self.assertEquals(self.recordNames(DirectoryService.recordType_groups), set(self.groups.keys()))
-
-
-    def test_listRecords_locations(self):
-        &quot;&quot;&quot;
-        IDirectoryService.listRecords(&quot;locations&quot;)
-        &quot;&quot;&quot;
-        if not self.resources:
-            raise SkipTest(&quot;No locations&quot;)
-
-        self.assertEquals(self.recordNames(DirectoryService.recordType_locations), set(self.locations.keys()))
-
-
-    def test_listRecords_resources(self):
-        &quot;&quot;&quot;
-        IDirectoryService.listRecords(&quot;resources&quot;)
-        &quot;&quot;&quot;
-        if not self.resources:
-            raise SkipTest(&quot;No resources&quot;)
-
-        self.assertEquals(self.recordNames(DirectoryService.recordType_resources), set(self.resources.keys()))
-
-
-
-class BasicTestCase (DirectoryTestCase):
-    &quot;&quot;&quot;
-    Tests a directory implementation with basic auth.
-    &quot;&quot;&quot;
-    def test_verifyCredentials_basic(self):
-        &quot;&quot;&quot;
-        IDirectoryRecord.verifyCredentials() with basic
-        &quot;&quot;&quot;
-        if not self.users:
-            raise SkipTest(&quot;No users&quot;)
-
-        service = self.service()
-        for user in self.users:
-            userRecord = service.recordWithShortName(DirectoryService.recordType_users, user)
-            self.failUnless(userRecord.verifyCredentials(UsernamePassword(user, self.users[user][&quot;password&quot;])))
-
-
-
-# authRequest = {
-#    username=&quot;username&quot;,
-#    realm=&quot;test realm&quot;,
-#    nonce=&quot;178288758716122392881254770685&quot;,
-#    uri=&quot;/write/&quot;,
-#    response=&quot;62f388be1cf678fbdfce87910871bcc5&quot;,
-#    opaque=&quot;1041524039&quot;,
-#    algorithm=&quot;md5&quot;,
-#    cnonce=&quot;29fc54aa1641c6fa0e151419361c8f23&quot;,
-#    nc=00000001,
-#    qop=&quot;auth&quot;,
-# }
-
-class DigestTestCase (DirectoryTestCase):
-    &quot;&quot;&quot;
-    Tests a directory implementation with digest auth.
-    &quot;&quot;&quot;
-    def test_verifyCredentials_digest(self):
-        &quot;&quot;&quot;
-        IDirectoryRecord.verifyCredentials() with digest
-        &quot;&quot;&quot;
-        if not self.users:
-            raise SkipTest(&quot;No users&quot;)
-
-        service = self.service()
-        for user in self.users:
-            for good in (True, True, False, False, True):
-                userRecord = service.recordWithShortName(DirectoryService.recordType_users, user)
-
-                # I'm glad this is so simple...
-                response = calcResponse(
-                    calcHA1(
-                        &quot;md5&quot;,
-                        user,
-                        service.realmName,
-                        self.users[user][&quot;password&quot;],
-                        &quot;booger&quot;,
-                        &quot;phlegm&quot;,
-                    ),
-                    &quot;md5&quot;,
-                    &quot;booger&quot;,
-                    None,
-                    &quot;phlegm&quot;,
-                    &quot;auth&quot;,
-                    &quot;GET&quot;,
-                    &quot;/&quot;,
-                    None,
-                )
-
-                if good:
-                    noise = &quot;&quot;
-                else:
-                    noise = &quot;blah&quot;
-
-                credentials = DigestedCredentials(
-                    user,
-                    &quot;GET&quot;,
-                    service.realmName,
-                    {
-                        &quot;response&quot;: response,
-                        &quot;uri&quot;: &quot;/&quot;,
-                        &quot;nonce&quot;: &quot;booger&quot; + noise,
-                        &quot;cnonce&quot;: &quot;phlegm&quot;,
-                        &quot;nc&quot;: None,
-                    },
-                )
-
-                if good:
-                    self.failUnless(userRecord.verifyCredentials(credentials))
-                else:
-                    self.failIf(userRecord.verifyCredentials(credentials))
-
-
-
-def maybeCommit(req):
-    class JustForCleanup(object):
-        def newTransaction(self, *whatever):
-            return self
-        def commit(self):
-            return
-    transactionFromRequest(req, JustForCleanup()).commit()
</del></span></pre></div>
<a id="CalendarServertrunktwistedcaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/resource.py (13361 => 13362)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/resource.py        2014-04-23 00:49:34 UTC (rev 13361)
+++ CalendarServer/trunk/twistedcaldav/resource.py        2014-04-23 16:47:07 UTC (rev 13362)
</span><span class="lines">@@ -2567,7 +2567,10 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _otherPrincipalHomeURL(self, otherUID):
</span><span class="cx">         ownerPrincipal = (yield self.principalForUID(otherUID))
</span><del>-        returnValue(ownerPrincipal.calendarHomeURLs()[0])
</del><ins>+        if ownerPrincipal and len(ownerPrincipal.calendarHomeURLs()):
+            returnValue(ownerPrincipal.calendarHomeURLs()[0])
+        else:
+            returnValue(None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -2822,7 +2825,7 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _otherPrincipalHomeURL(self, otherUID):
</span><span class="cx">         ownerPrincipal = (yield self.principalForUID(otherUID))
</span><del>-        returnValue(ownerPrincipal.addressBookHomeURLs()[0])
</del><ins>+        returnValue(ownerPrincipal.addressBookHomeURLs()[0] if ownerPrincipal else None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavsharingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/sharing.py (13361 => 13362)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/sharing.py        2014-04-23 00:49:34 UTC (rev 13361)
+++ CalendarServer/trunk/twistedcaldav/sharing.py        2014-04-23 16:47:07 UTC (rev 13362)
</span><span class="lines">@@ -91,12 +91,16 @@
</span><span class="cx">                     invitations = yield self.validateInvites(request, invitations)
</span><span class="cx"> 
</span><span class="cx">                     ownerPrincipal = yield self.principalForUID(self._newStoreObject.ownerHome().uid())
</span><del>-                    # FIXME:  use urn:x-uid in all cases
-                    if self.isCalendarCollection():
-                        owner = ownerPrincipal.principalURL()
</del><ins>+                    if ownerPrincipal is None:
+                        owner = &quot;invalid&quot;
+                        ownerCN = &quot;Invalid&quot;
</ins><span class="cx">                     else:
</span><del>-                        owner = &quot;urn:x-uid:&quot; + ownerPrincipal.principalUID()
-                    ownerCN = ownerPrincipal.displayName()
</del><ins>+                        # FIXME:  use urn:x-uid in all cases
+                        if self.isCalendarCollection():
+                            owner = ownerPrincipal.principalURL()
+                        else:
+                            owner = &quot;urn:x-uid:&quot; + ownerPrincipal.principalUID()
+                        ownerCN = ownerPrincipal.displayName()
</ins><span class="cx"> 
</span><span class="cx">                     returnValue(customxml.Invite(
</span><span class="cx">                         customxml.Organizer(
</span><span class="lines">@@ -435,12 +439,15 @@
</span><span class="cx">         # assert request
</span><span class="cx">         if invitations is None:
</span><span class="cx">             invitations = yield self._newStoreObject.allInvitations()
</span><ins>+        adjusted_invitations = []
</ins><span class="cx">         for invitation in invitations:
</span><span class="cx">             if invitation.status != _BIND_STATUS_INVALID:
</span><span class="cx">                 if not (yield self.validUserIDForShare(&quot;urn:x-uid:&quot; + invitation.shareeUID, request)):
</span><span class="cx">                     self.log.error(&quot;Invalid sharee detected: {uid}&quot;, uid=invitation.shareeUID)
</span><ins>+                    invitation = invitation._replace(status=_BIND_STATUS_INVALID)
+            adjusted_invitations.append(invitation)
</ins><span class="cx"> 
</span><del>-        returnValue(invitations)
</del><ins>+        returnValue(adjusted_invitations)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def inviteUserToShare(self, userid, cn, ace, summary, request):
</span><span class="lines">@@ -519,12 +526,18 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def uninviteSingleUserFromShare(self, userid, aces, request): #@UnusedVariable
</span><span class="cx"> 
</span><del>-        # Cancel invites - we'll just use whatever userid we are given
</del><ins>+        # Cancel invites - we'll just use whatever userid we are given. However, if we
+        # cannot find a matching principal, try to extract the uid from the userid
+        # and use that (to allow invalid principals to be removed).
</ins><span class="cx">         sharee = yield self.principalForCalendarUserAddress(userid)
</span><del>-        if not sharee:
</del><ins>+        if sharee is not None:
+            uid = sharee.principalUID()
+        elif userid.startswith(&quot;urn:x-uid:&quot;):
+            uid = userid[10:]
+        else:
</ins><span class="cx">             returnValue(False)
</span><span class="cx"> 
</span><del>-        result = (yield self._newStoreObject.uninviteUserFromShare(sharee.principalUID()))
</del><ins>+        result = (yield self._newStoreObject.uninviteUserFromShare(uid))
</ins><span class="cx"> 
</span><span class="cx">         returnValue(result)
</span><span class="cx"> 
</span><span class="lines">@@ -793,7 +806,7 @@
</span><span class="cx">         if child._newStoreObject is not None and not child._newStoreObject.owned():
</span><span class="cx">             ownerHomeURL = (yield self._otherPrincipalHomeURL(child._newStoreObject.ownerHome().uid()))
</span><span class="cx">             ownerView = yield child._newStoreObject.ownerView()
</span><del>-            child.setShare(joinURL(ownerHomeURL, ownerView.name()))
</del><ins>+            child.setShare(joinURL(ownerHomeURL, ownerView.name()) if ownerHomeURL else None)
</ins><span class="cx">             access = yield child._checkAccessControl()
</span><span class="cx">             if access is None:
</span><span class="cx">                 returnValue(None)
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavtesttest_sharingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/test/test_sharing.py (13361 => 13362)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/test/test_sharing.py        2014-04-23 00:49:34 UTC (rev 13361)
+++ CalendarServer/trunk/twistedcaldav/test/test_sharing.py        2014-04-23 16:47:07 UTC (rev 13362)
</span><span class="lines">@@ -23,7 +23,6 @@
</span><span class="cx"> 
</span><span class="cx"> from twistedcaldav import customxml
</span><span class="cx"> from twistedcaldav.config import config
</span><del>-from twistedcaldav.directory.principal import DirectoryCalendarPrincipalResource
</del><span class="cx"> from twistedcaldav.test.test_cache import StubResponseCacheResource
</span><span class="cx"> from twistedcaldav.test.util import norequest, StoreTestCase, SimpleStoreRequest
</span><span class="cx"> 
</span><span class="lines">@@ -35,7 +34,6 @@
</span><span class="cx"> from txdav.who.wiki import (
</span><span class="cx">     DirectoryRecord as WikiDirectoryRecord,
</span><span class="cx">     DirectoryService as WikiDirectoryService,
</span><del>-    RecordType as WikiRecordType,
</del><span class="cx">     WikiAccessLevel
</span><span class="cx"> )
</span><span class="cx"> 
</span><span class="lines">@@ -53,78 +51,6 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class FakeHome(object):
-    pass
-
-
-
-class FakeRecord(object):
-
-    def __init__(self, name, cuaddr):
-        self.fullName = name
-        self.guid = name
-        self.calendarUserAddresses = set((cuaddr,))
-        if name.startswith(WikiDirectoryService.uidPrefix):
-            recordType = WikiRecordType.macOSXServerWiki
-        else:
-            recordType = None
-        self.recordType = recordType
-        self.shortNames = [name]
-
-
-
-class FakePrincipal(DirectoryCalendarPrincipalResource):
-
-    invalid_names = set()
-
-    def __init__(self, cuaddr, test):
-        if cuaddr.startswith(&quot;mailto:&quot;):
-            name = cuaddr[7:].split('@')[0]
-        elif cuaddr.startswith(&quot;urn:x-uid:&quot;):
-            name = cuaddr[10:]
-        elif cuaddr.startswith(&quot;urn:uuid:&quot;):
-            name = cuaddr[9:]
-        else:
-            name = cuaddr
-
-        self.path = &quot;/principals/__uids__/%s&quot; % (name,)
-        self.homepath = &quot;/calendars/__uids__/%s&quot; % (name,)
-        self.displayname = name.upper()
-        self.record = FakeRecord(name, cuaddr)
-        self._test = test
-        self._name = name
-
-
-    @inlineCallbacks
-    def calendarHome(self, request):
-        if self._name in self.invalid_names:
-            returnValue(None)
-        a, _ignore_seg = yield self._test.calendarCollection.locateChild(request, [&quot;__uids__&quot;])
-        b, _ignore_seg = yield a.locateChild(request, [self._name])
-        if b is None:
-            # XXX all tests except test_noWikiAccess currently rely on the
-            # fake thing here.
-            returnValue(FakeHome())
-        returnValue(b)
-
-
-    def calendarHomeURLs(self):
-        return (self.homepath,)
-
-
-    def principalURL(self):
-        return self.path
-
-
-    def principalUID(self):
-        return self.record.guid
-
-
-    def displayName(self):
-        return self.displayname
-
-
-
</del><span class="cx"> class SharingTests(StoreTestCase):
</span><span class="cx"> 
</span><span class="cx">     def configure(self):
</span><span class="lines">@@ -140,39 +66,6 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def setUp(self):
</span><span class="cx">         yield super(SharingTests, self).setUp()
</span><del>-
-        # FIXME: not sure what these were for:
-
-        #     def patched(c):
-        #         &quot;&quot;&quot;
-        #         The decorated method is patched on L{CalDAVResource} for the
-        #         duration of the test.
-        #         &quot;&quot;&quot;
-        #         self.patch(CalDAVResource, c.__name__, c)
-        #         return c
-
-        #     @patched
-        #     def principalForCalendarUserAddress(resourceSelf, cuaddr):
-        #         if &quot;bogus&quot; in cuaddr:
-        #             return None
-        #         else:
-        #             return FakePrincipal(cuaddr, self)
-
-        #     @patched
-        #     def validUserIDForShare(resourceSelf, userid, request):
-        #         &quot;&quot;&quot;
-        #         Temporary replacement for L{CalDAVResource.validUserIDForShare}
-        #         that marks any principal without 'bogus' in its name.
-        #         &quot;&quot;&quot;
-        #         result = principalForCalendarUserAddress(resourceSelf, userid)
-        #         if result is None:
-        #             return result
-        #         return result.principalURL()
-
-        #     @patched
-        #     def principalForUID(resourceSelf, principalUID):
-        #         return FakePrincipal(&quot;urn:uuid:&quot; + principalUID, self)
-
</del><span class="cx">         self.resource = yield self._getResource()
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -216,9 +109,9 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def _doPOSTSharerAccept(self, body, resultcode=responsecode.OK):
-        authRecord = yield self.directory.recordWithUID(u&quot;user02&quot;)
-        request = SimpleStoreRequest(self, &quot;POST&quot;, &quot;/calendars/__uids__/user02/&quot;, content=body, authRecord=authRecord)
</del><ins>+    def _doPOSTSharerAccept(self, body, resultcode=responsecode.OK, sharer=&quot;user02&quot;):
+        authRecord = yield self.directory.recordWithUID(unicode(sharer))
+        request = SimpleStoreRequest(self, &quot;POST&quot;, &quot;/calendars/__uids__/{}/&quot;.format(sharer), content=body, authRecord=authRecord)
</ins><span class="cx">         request.headers.setHeader(&quot;content-type&quot;, MimeType(&quot;text&quot;, &quot;xml&quot;))
</span><span class="cx">         response = yield self.send(request)
</span><span class="cx">         response = IResponse(response)
</span><span class="lines">@@ -248,12 +141,22 @@
</span><span class="cx">         return None
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _getUIDElementValues(self, xml):
+
+        results = {}
+        for user in xml.children:
+            href = str(user.childOfType(davxml.HRef))
+            uid = str(user.childOfType(customxml.UID))
+            results[href] = uid
+        return results
+
+
</ins><span class="cx">     def _clearUIDElementValue(self, xml):
</span><span class="cx"> 
</span><span class="cx">         for user in xml.children:
</span><del>-            for element in user.children:
-                if type(element) == customxml.UID:
-                    element.children[0].data = &quot;&quot;
</del><ins>+            uid = user.childOfType(customxml.UID)
+            if uid is not None:
+                uid.children[0].data = &quot;&quot;
</ins><span class="cx">         return xml
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -713,25 +616,24 @@
</span><span class="cx">             )
</span><span class="cx">         ))
</span><span class="cx"> 
</span><del>-        self.resource.validUserIDForShare = lambda userid, request: None
-        self.resource.principalForCalendarUserAddress = lambda cuaddr: None
-        self.resource.principalForUID = lambda principalUID: None
</del><ins>+        record = yield self.userRecordWithShortName(&quot;user02&quot;)
+        yield self.changeRecord(record, self.directory.fieldName.hasCalendars, False)
</ins><span class="cx"> 
</span><span class="cx">         propInvite = (yield self.resource.readProperty(customxml.Invite, None))
</span><span class="cx">         self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
</span><span class="cx">             customxml.InviteUser(
</span><span class="cx">                 customxml.UID.fromString(&quot;&quot;),
</span><span class="cx">                 davxml.HRef.fromString(&quot;urn:x-uid:user02&quot;),
</span><del>-                customxml.CommonName.fromString(&quot;user02&quot;),
</del><ins>+                customxml.CommonName.fromString(&quot;User 02&quot;),
</ins><span class="cx">                 customxml.InviteAccess(customxml.ReadWriteAccess()),
</span><del>-                customxml.InviteStatusNoResponse(),
</del><ins>+                customxml.InviteStatusInvalid(),
</ins><span class="cx">             )
</span><span class="cx">         ))
</span><span class="cx"> 
</span><span class="cx">         yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
</span><span class="cx">             &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
</span><span class="cx">                 &lt;CS:remove&gt;
</span><del>-                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
</del><ins>+                    &lt;D:href&gt;urn:x-uid:user02&lt;/D:href&gt;
</ins><span class="cx">                 &lt;/CS:remove&gt;
</span><span class="cx">             &lt;/CS:share&gt;
</span><span class="cx">             &quot;&quot;&quot;)
</span><span class="lines">@@ -832,7 +734,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_POSTDowngradeWithDisabledInvitee(self):
</del><ins>+    def test_POSTDowngradeWithMissingInvitee(self):
</ins><span class="cx"> 
</span><span class="cx">         yield self.resource.upgradeToShare()
</span><span class="cx"> 
</span><span class="lines">@@ -857,12 +759,14 @@
</span><span class="cx">             ),
</span><span class="cx">         ))
</span><span class="cx"> 
</span><del>-        self.patch(FakePrincipal, &quot;invalid_names&quot;, set((&quot;user02&quot;,)))
</del><ins>+        yield self.directory.removeRecords(((yield self.userUIDFromShortName(&quot;user02&quot;)),))
+        self.assertTrue((yield self.userUIDFromShortName(&quot;user02&quot;)) is None)
+
</ins><span class="cx">         yield self.resource.downgradeFromShare(norequest())
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_POSTRemoveWithDisabledInvitee(self):
</del><ins>+    def test_POSTRemoveWithMissingInvitee(self):
</ins><span class="cx"> 
</span><span class="cx">         yield self.resource.upgradeToShare()
</span><span class="cx"> 
</span><span class="lines">@@ -887,12 +791,13 @@
</span><span class="cx">             ),
</span><span class="cx">         ))
</span><span class="cx"> 
</span><del>-        self.patch(FakePrincipal, &quot;invalid_names&quot;, set((&quot;user02&quot;,)))
</del><ins>+        yield self.directory.removeRecords(((yield self.userUIDFromShortName(&quot;user02&quot;)),))
+        self.assertTrue((yield self.userUIDFromShortName(&quot;user02&quot;)) is None)
</ins><span class="cx"> 
</span><span class="cx">         yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
</span><span class="cx">             &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
</span><span class="cx">                 &lt;CS:remove&gt;
</span><del>-                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
</del><ins>+                    &lt;D:href&gt;urn:x-uid:user02&lt;/D:href&gt;
</ins><span class="cx">                 &lt;/CS:remove&gt;
</span><span class="cx">             &lt;/CS:share&gt;
</span><span class="cx">             &quot;&quot;&quot;)
</span><span class="lines">@@ -948,10 +853,286 @@
</span><span class="cx">         )
</span><span class="cx">         href = self._getHRefElementValue(result) + &quot;/&quot;
</span><span class="cx"> 
</span><del>-        self.patch(FakePrincipal, &quot;invalid_names&quot;, set((&quot;user01&quot;,)))
</del><ins>+        record = yield self.userRecordWithShortName(&quot;user01&quot;)
+        yield self.changeRecord(record, self.directory.fieldName.hasCalendars, False)
</ins><span class="cx"> 
</span><span class="cx">         resource = (yield self._getResourceSharer(href))
</span><span class="cx">         yield resource.removeShareeResource(SimpleStoreRequest(self, &quot;DELETE&quot;, href))
</span><span class="cx"> 
</span><span class="cx">         resource = (yield self._getResourceSharer(href))
</span><span class="cx">         self.assertFalse(resource.exists())
</span><ins>+
+
+    @inlineCallbacks
+    def test_POSTShareeRemoveWithMissingSharer(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
+            &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
+                &lt;CS:set&gt;
+                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
+                    &lt;CS:summary&gt;My Shared Calendar&lt;/CS:summary&gt;
+                    &lt;CS:read-write/&gt;
+                &lt;/CS:set&gt;
+            &lt;/CS:share&gt;
+            &quot;&quot;&quot;)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uid = self._getUIDElementValue(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:x-uid:user02&quot;),
+                customxml.CommonName.fromString(&quot;User 02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        result = (yield self._doPOSTSharerAccept(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;
+            &lt;invite-reply xmlns='http://calendarserver.org/ns/'&gt;
+              &lt;href xmlns='DAV:'&gt;mailto:user01@example.com&lt;/href&gt;
+              &lt;invite-accepted/&gt;
+              &lt;hosturl&gt;
+                &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
+              &lt;/hosturl&gt;
+              &lt;in-reply-to&gt;%s&lt;/in-reply-to&gt;
+              &lt;summary&gt;The Shared Calendar&lt;/summary&gt;
+              &lt;common-name&gt;User 02&lt;/common-name&gt;
+              &lt;first-name&gt;user&lt;/first-name&gt;
+              &lt;last-name&gt;02&lt;/last-name&gt;
+            &lt;/invite-reply&gt;
+            &quot;&quot;&quot; % (uid,))
+        )
+        href = self._getHRefElementValue(result) + &quot;/&quot;
+
+        yield self.directory.removeRecords(((yield self.userUIDFromShortName(&quot;user01&quot;)),))
+        self.assertTrue((yield self.userUIDFromShortName(&quot;user01&quot;)) is None)
+
+        resource = (yield self._getResourceSharer(href))
+        yield resource.removeShareeResource(SimpleStoreRequest(self, &quot;DELETE&quot;, href))
+
+        resource = (yield self._getResourceSharer(href))
+        self.assertFalse(resource.exists())
+
+
+    @inlineCallbacks
+    def test_shareeInviteWithDisabledSharer(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
+            &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
+                &lt;CS:set&gt;
+                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
+                    &lt;CS:summary&gt;My Shared Calendar&lt;/CS:summary&gt;
+                    &lt;CS:read-write/&gt;
+                &lt;/CS:set&gt;
+            &lt;/CS:share&gt;
+            &quot;&quot;&quot;)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uid = self._getUIDElementValue(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:x-uid:user02&quot;),
+                customxml.CommonName.fromString(&quot;User 02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        result = (yield self._doPOSTSharerAccept(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;
+            &lt;invite-reply xmlns='http://calendarserver.org/ns/'&gt;
+              &lt;href xmlns='DAV:'&gt;mailto:user01@example.com&lt;/href&gt;
+              &lt;invite-accepted/&gt;
+              &lt;hosturl&gt;
+                &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
+              &lt;/hosturl&gt;
+              &lt;in-reply-to&gt;%s&lt;/in-reply-to&gt;
+              &lt;summary&gt;The Shared Calendar&lt;/summary&gt;
+              &lt;common-name&gt;User 02&lt;/common-name&gt;
+              &lt;first-name&gt;user&lt;/first-name&gt;
+              &lt;last-name&gt;02&lt;/last-name&gt;
+            &lt;/invite-reply&gt;
+            &quot;&quot;&quot; % (uid,))
+        )
+        href = self._getHRefElementValue(result) + &quot;/&quot;
+
+        record = yield self.userRecordWithShortName(&quot;user01&quot;)
+        yield self.changeRecord(record, self.directory.fieldName.hasCalendars, False)
+
+        resource = (yield self._getResourceSharer(href))
+        propInvite = yield resource.inviteProperty(None)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.Organizer(
+                davxml.HRef.fromString(&quot;/principals/__uids__/user01/&quot;),
+                customxml.CommonName.fromString(&quot;User 01&quot;),
+            ),
+            customxml.InviteUser(
+                davxml.HRef.fromString(&quot;urn:x-uid:user02&quot;),
+                customxml.CommonName.fromString(&quot;User 02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusAccepted(),
+            ),
+        ))
+
+
+    @inlineCallbacks
+    def test_shareeInviteWithMissingSharer(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
+            &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
+                &lt;CS:set&gt;
+                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
+                    &lt;CS:summary&gt;My Shared Calendar&lt;/CS:summary&gt;
+                    &lt;CS:read-write/&gt;
+                &lt;/CS:set&gt;
+            &lt;/CS:share&gt;
+            &quot;&quot;&quot;)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uid = self._getUIDElementValue(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:x-uid:user02&quot;),
+                customxml.CommonName.fromString(&quot;User 02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        result = (yield self._doPOSTSharerAccept(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;
+            &lt;invite-reply xmlns='http://calendarserver.org/ns/'&gt;
+              &lt;href xmlns='DAV:'&gt;mailto:user01@example.com&lt;/href&gt;
+              &lt;invite-accepted/&gt;
+              &lt;hosturl&gt;
+                &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
+              &lt;/hosturl&gt;
+              &lt;in-reply-to&gt;%s&lt;/in-reply-to&gt;
+              &lt;summary&gt;The Shared Calendar&lt;/summary&gt;
+              &lt;common-name&gt;User 02&lt;/common-name&gt;
+              &lt;first-name&gt;user&lt;/first-name&gt;
+              &lt;last-name&gt;02&lt;/last-name&gt;
+            &lt;/invite-reply&gt;
+            &quot;&quot;&quot; % (uid,))
+        )
+        href = self._getHRefElementValue(result) + &quot;/&quot;
+
+        yield self.directory.removeRecords(((yield self.userUIDFromShortName(&quot;user01&quot;)),))
+        self.assertTrue((yield self.userUIDFromShortName(&quot;user01&quot;)) is None)
+
+        resource = (yield self._getResourceSharer(href))
+        propInvite = yield resource.inviteProperty(None)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.Organizer(
+                davxml.HRef.fromString(&quot;invalid&quot;),
+                customxml.CommonName.fromString(&quot;Invalid&quot;),
+            ),
+            customxml.InviteUser(
+                davxml.HRef.fromString(&quot;urn:x-uid:user02&quot;),
+                customxml.CommonName.fromString(&quot;User 02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusAccepted(),
+            ),
+        ))
+
+
+    @inlineCallbacks
+    def test_hideInvalidSharers(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST(&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
+            &lt;CS:share xmlns:D=&quot;DAV:&quot; xmlns:CS=&quot;http://calendarserver.org/ns/&quot;&gt;
+                &lt;CS:set&gt;
+                    &lt;D:href&gt;mailto:user02@example.com&lt;/D:href&gt;
+                    &lt;CS:summary&gt;My Shared Calendar&lt;/CS:summary&gt;
+                    &lt;CS:read-write/&gt;
+                &lt;/CS:set&gt;
+                &lt;CS:set&gt;
+                    &lt;D:href&gt;mailto:user03@example.com&lt;/D:href&gt;
+                    &lt;CS:summary&gt;My Shared Calendar&lt;/CS:summary&gt;
+                    &lt;CS:read-write/&gt;
+                &lt;/CS:set&gt;
+            &lt;/CS:share&gt;
+            &quot;&quot;&quot;)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uids = self._getUIDElementValues(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:x-uid:user02&quot;),
+                customxml.CommonName.fromString(&quot;User 02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:x-uid:user03&quot;),
+                customxml.CommonName.fromString(&quot;User 03&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        yield self._doPOSTSharerAccept(&quot;&quot;&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;
+            &lt;invite-reply xmlns='http://calendarserver.org/ns/'&gt;
+              &lt;href xmlns='DAV:'&gt;mailto:user01@example.com&lt;/href&gt;
+              &lt;invite-accepted/&gt;
+              &lt;hosturl&gt;
+                &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
+              &lt;/hosturl&gt;
+              &lt;in-reply-to&gt;%s&lt;/in-reply-to&gt;
+              &lt;summary&gt;The Shared Calendar&lt;/summary&gt;
+              &lt;common-name&gt;User 02&lt;/common-name&gt;
+              &lt;first-name&gt;user&lt;/first-name&gt;
+              &lt;last-name&gt;02&lt;/last-name&gt;
+            &lt;/invite-reply&gt;
+            &quot;&quot;&quot; % (uids[&quot;urn:x-uid:user02&quot;],))
+
+        yield self._doPOSTSharerAccept(
+            &quot;&quot;&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;
+                &lt;invite-reply xmlns='http://calendarserver.org/ns/'&gt;
+                  &lt;href xmlns='DAV:'&gt;mailto:user01@example.com&lt;/href&gt;
+                  &lt;invite-accepted/&gt;
+                  &lt;hosturl&gt;
+                    &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
+                  &lt;/hosturl&gt;
+                  &lt;in-reply-to&gt;%s&lt;/in-reply-to&gt;
+                  &lt;summary&gt;The Shared Calendar&lt;/summary&gt;
+                  &lt;common-name&gt;User 03&lt;/common-name&gt;
+                  &lt;first-name&gt;user&lt;/first-name&gt;
+                  &lt;last-name&gt;03&lt;/last-name&gt;
+                &lt;/invite-reply&gt;
+            &quot;&quot;&quot; % (uids[&quot;urn:x-uid:user03&quot;],),
+            sharer=&quot;user03&quot;
+        )
+
+        record = yield self.directory.recordWithUID((yield self.userUIDFromShortName(&quot;user02&quot;)))
+        yield self.changeRecord(record, self.directory.fieldName.hasCalendars, False)
+
+        resource = yield self._getResource()
+        propInvite = yield resource.inviteProperty(None)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:x-uid:user02&quot;),
+                customxml.CommonName.fromString(&quot;User 02&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusInvalid(),
+            ),
+            customxml.InviteUser(
+                customxml.UID.fromString(&quot;&quot;),
+                davxml.HRef.fromString(&quot;urn:x-uid:user03&quot;),
+                customxml.CommonName.fromString(&quot;User 03&quot;),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusAccepted(),
+            ),
+        ))
</ins></span></pre></div>
<a id="CalendarServertrunktwistedcaldavtestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/test/util.py (13361 => 13362)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/test/util.py        2014-04-23 00:49:34 UTC (rev 13361)
+++ CalendarServer/trunk/twistedcaldav/test/util.py        2014-04-23 16:47:07 UTC (rev 13362)
</span><span class="lines">@@ -77,8 +77,6 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
-
</del><span class="cx"> class SimpleStoreRequest(SimpleRequest):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     A SimpleRequest that automatically grabs the proper transaction for a test.
</span><span class="lines">@@ -190,7 +188,6 @@
</span><span class="cx">         proxies.setContent(proxiesFile.getContent())
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     def createHierarchy(self, structure, root=None):
</span><span class="cx">         if root is None:
</span><span class="cx">             root = os.path.abspath(self.mktemp())
</span><span class="lines">@@ -407,7 +404,6 @@
</span><span class="cx">         config.UsePackageTimezones = True
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     def setUp(self):
</span><span class="cx">         super(TestCase, self).setUp()
</span><span class="cx"> 
</span><span class="lines">@@ -430,7 +426,6 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> class norequest(object):
</span><span class="cx">     def addResponseFilter(self, filter):
</span><span class="cx">         &quot;stub; ignore me&quot;
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoretestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/test/util.py (13361 => 13362)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/test/util.py        2014-04-23 00:49:34 UTC (rev 13361)
+++ CalendarServer/trunk/txdav/common/datastore/test/util.py        2014-04-23 16:47:07 UTC (rev 13362)
</span><span class="lines">@@ -38,10 +38,9 @@
</span><span class="cx"> 
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from twext.python.filepath import CachingFilePath
</span><del>-from twistedcaldav.ical import Component as VComponent, Component
</del><span class="cx"> from twext.enterprise.adbapi2 import ConnectionPool
</span><span class="cx"> from twext.enterprise.ienterprise import AlreadyFinishedError
</span><del>-from txweb2.dav.resource import TwistedGETContentMD5
</del><ins>+from twext.who.directory import DirectoryRecord
</ins><span class="cx"> 
</span><span class="cx"> from twisted.application.service import Service
</span><span class="cx"> from twisted.internet import reactor
</span><span class="lines">@@ -52,6 +51,7 @@
</span><span class="cx"> 
</span><span class="cx"> from twistedcaldav import ical
</span><span class="cx"> from twistedcaldav.config import config
</span><ins>+from twistedcaldav.ical import Component as VComponent, Component
</ins><span class="cx"> from twistedcaldav.stdconfig import DEFAULT_CONFIG
</span><span class="cx"> from twistedcaldav.vcard import Component as ABComponent
</span><span class="cx"> 
</span><span class="lines">@@ -63,6 +63,8 @@
</span><span class="cx"> from txdav.common.datastore.sql_tables import schema
</span><span class="cx"> from txdav.common.icommondatastore import NoSuchHomeChildError
</span><span class="cx"> 
</span><ins>+from txweb2.dav.resource import TwistedGETContentMD5
+
</ins><span class="cx"> from zope.interface.exceptions import BrokenMethodImplementation, \
</span><span class="cx">     DoesNotImplement
</span><span class="cx"> from zope.interface.verify import verifyObject
</span><span class="lines">@@ -823,7 +825,27 @@
</span><span class="cx">                     .addressbookObjectWithName(name)))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def userRecordWithShortName(self, shortname):
+        record = yield self.directory.recordWithShortName(self.directory.recordType.user, shortname)
+        returnValue(record)
</ins><span class="cx"> 
</span><ins>+
+    @inlineCallbacks
+    def userUIDFromShortName(self, shortname):
+        record = yield self.directory.recordWithShortName(self.directory.recordType.user, shortname)
+        returnValue(record.uid if record is not None else None)
+
+
+    @inlineCallbacks
+    def changeRecord(self, record, fieldname, value):
+        fields = record.fields.copy()
+        fields[fieldname] = value
+        updatedRecord = DirectoryRecord(self.directory, fields)
+        yield self.directory.updateRecords((updatedRecord,))
+
+
+
</ins><span class="cx"> class StubNotifierFactory(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     For testing push notifications without an XMPP server.
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhodirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/directory.py (13361 => 13362)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/directory.py        2014-04-23 00:49:34 UTC (rev 13361)
+++ CalendarServer/trunk/txdav/who/directory.py        2014-04-23 16:47:07 UTC (rev 13362)
</span><span class="lines">@@ -377,9 +377,6 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def enabledAsOrganizer(self):
</span><del>-        # FIXME:
-        from twistedcaldav.config import config
-
</del><span class="cx">         if self.recordType == self.service.recordType.user:
</span><span class="cx">             return True
</span><span class="cx">         elif self.recordType == self.service.recordType.group:
</span><span class="lines">@@ -396,9 +393,6 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         URL of the server hosting this record. Return None if hosted on this server.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        # FIXME:
-        from twistedcaldav.config import config
-
</del><span class="cx">         if config.Servers.Enabled and getattr(self, &quot;serviceNodeUID&quot;, None):
</span><span class="cx">             return Servers.getServerURIById(self.serviceNodeUID)
</span><span class="cx">         else:
</span><span class="lines">@@ -409,9 +403,6 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Server hosting this record. Return None if hosted on this server.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        # FIXME:
-        from twistedcaldav.config import config
-
</del><span class="cx">         if config.Servers.Enabled and getattr(self, &quot;serviceNodeUID&quot;, None):
</span><span class="cx">             return Servers.getServerById(self.serviceNodeUID)
</span><span class="cx">         else:
</span><span class="lines">@@ -428,17 +419,11 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def calendarsEnabled(self):
</span><del>-        # FIXME:
-        from twistedcaldav.config import config
-
</del><span class="cx">         return config.EnableCalDAV and self.hasCalendars
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def canAutoSchedule(self, organizer=None):
</span><del>-        # FIXME:
-        from twistedcaldav.config import config
-
</del><span class="cx">         if config.Scheduling.Options.AutoSchedule.Enabled:
</span><span class="cx">             if (
</span><span class="cx">                 config.Scheduling.Options.AutoSchedule.Always or
</span></span></pre>
</div>
</div>

</body>
</html>