[CalendarServer-changes] [13651] CalendarServer/branches/release/CalendarServer-5.3-dev

source_changes at macosforge.org source_changes at macosforge.org
Wed Jun 18 11:06:15 PDT 2014


Revision: 13651
          http://trac.calendarserver.org//changeset/13651
Author:   cdaboo at apple.com
Date:     2014-06-18 11:06:15 -0700 (Wed, 18 Jun 2014)
Log Message:
-----------
Hide missing or invalid proxyFors. Fix some sharing issues related to missing or invalid users.

Modified Paths:
--------------
    CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py
    CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml
    CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml
    CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py
    CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py
    CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py
    CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py

Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py	2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py	2014-06-18 18:06:15 UTC (rev 13651)
@@ -923,7 +923,7 @@
 
 
     @inlineCallbacks
-    def proxyFor(self, read_write, resolve_memberships=True):
+    def proxyFor(self, read_write, resolve_memberships=True, ignoreDisabled=True):
 
         proxyFors = set()
 
@@ -960,7 +960,7 @@
 
         uids = set()
         for principal in tuple(proxyFors):
-            if principal.principalUID() in uids:
+            if principal.principalUID() in uids or (ignoreDisabled and not principal.record.enabledForCalendaring):
                 proxyFors.remove(principal)
             else:
                 uids.add(principal.principalUID())

Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml	2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml	2014-06-18 18:06:15 UTC (rev 13651)
@@ -139,7 +139,7 @@
       <member type="users">delegateviagroup</member>
     </members>
   </group>
-  <user repeat="2">
+  <user repeat="3">
     <uid>user%02d</uid>
     <guid>user%02d</guid>
     <password>%02duser</password>

Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml	2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml	2014-06-18 18:06:15 UTC (rev 13651)
@@ -57,7 +57,7 @@
     <enable-calendar>false</enable-calendar>
     <enable-addressbook>false</enable-addressbook>
   </record>
-  <record repeat="2">
+  <record repeat="3">
     <uid>user%02d</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>

Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2014-06-18 18:06:15 UTC (rev 13651)
@@ -504,3 +504,49 @@
             DirectoryService.recordType_users, "delegator", "calendar-proxy-write",
             ("Occasional Delegate", "Delegate Via Group", "Delegate Group"),
         )
+
+
+    @inlineCallbacks
+    def test_hideDisabledProxies(self):
+        """
+        Make sure users that are missing or not enabled for calendaring are removed
+        from the proxyFor list.
+        """
+
+        # Check proxies empty right now
+        principal01 = self._getPrincipalByShortName(self.directoryService.recordType_users, "user01")
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 0)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        principal02 = self._getPrincipalByShortName(self.directoryService.recordType_users, "user02")
+        self.assertTrue(len((yield principal02.proxyFor(False))) == 0)
+        self.assertTrue(len((yield principal02.proxyFor(True))) == 0)
+
+        principal03 = self._getPrincipalByShortName(self.directoryService.recordType_users, "user03")
+        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 self._addProxy(principal02, "calendar-proxy-read", principal01)
+        yield self._addProxy(principal03, "calendar-proxy-read", principal01)
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 2)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        # Now disable user02
+        principal02.record.enabledForCalendaring = False
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 1)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        # Now enable user02
+        principal02.record.enabledForCalendaring = True
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 2)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+        # Now remove user02
+        self.directoryService._removeFromIndex(principal02.record)
+
+        self.assertTrue(len((yield principal01.proxyFor(False))) == 1)
+        self.assertTrue(len((yield principal01.proxyFor(True))) == 0)

Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py	2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py	2014-06-18 18:06:15 UTC (rev 13651)
@@ -103,12 +103,16 @@
                     invitations = yield original.validateInvites(request)
 
                     ownerPrincipal = (yield original.ownerPrincipal(request))
-                    # FIXME:  use urn:uuid in all cases
-                    if self.isCalendarCollection():
-                        owner = ownerPrincipal.principalURL()
+                    if ownerPrincipal is None:
+                        owner = "invalid"
+                        ownerCN = "Invalid"
                     else:
-                        owner = "urn:uuid:" + ownerPrincipal.principalUID()
-                    ownerCN = ownerPrincipal.displayName()
+                        # FIXME:  use urn:uuid in all cases
+                        if self.isCalendarCollection():
+                            owner = ownerPrincipal.principalURL()
+                        else:
+                            owner = "urn:uuid:" + ownerPrincipal.principalUID()
+                        ownerCN = ownerPrincipal.displayName()
 
                     returnValue(customxml.Invite(
                         customxml.Organizer(
@@ -315,7 +319,7 @@
             access control mechanism has dictate the home should no longer have
             any access at all.
         """
-        if self._share.direct():
+        if self._share is not None and self._share.direct():
             ownerUID = self._share.ownerUID()
             owner = self.principalForUID(ownerUID)
             if owner.record.recordType == WikiDirectoryService.recordType_wikis:
@@ -482,14 +486,17 @@
         """
         # assert request
         invitations = yield self._allInvitations()
+        adjusted_invitations = []
         for invitation in invitations:
             if invitation.status() != _BIND_STATUS_INVALID:
                 if not (yield self.validUserIDForShare("urn:uuid:" + invitation.shareeUID(), request)):
                     # FIXME: temporarily disable this to deal with flaky directory
                     #yield self._updateInvitation(invitation, status=_BIND_STATUS_INVALID)
                     self.log.error("Invalid sharee detected: {uid}", uid=invitation.shareeUID())
+                    invitation = invitation._replace(status=_BIND_STATUS_INVALID)
+            adjusted_invitations.append(invitation)
 
-        returnValue(invitations)
+        returnValue(adjusted_invitations)
 
 
     def inviteUserToShare(self, userid, cn, ace, summary, request):
@@ -648,15 +655,19 @@
 
     @inlineCallbacks
     def uninviteSingleUserFromShare(self, userid, aces, request): #@UnusedVariable
-        # Cancel invites - we'll just use whatever userid we are given
 
+        # 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).
         sharee = self.principalForCalendarUserAddress(userid)
-        if not sharee:
+        if sharee is not None:
+            uid = sharee.principalUID()
+        elif userid.startswith("urn:uuid:"):
+            uid = userid[9:]
+        else:
             returnValue(False)
 
-        shareeUID = sharee.principalUID()
-
-        invitation = yield self._invitationForShareeUID(shareeUID)
+        invitation = yield self._invitationForShareeUID(uid)
         if invitation:
             result = (yield self.uninviteFromShare(invitation, request))
         else:
@@ -1026,8 +1037,8 @@
         L{SharedHomeMixin} is a I{sharee}'s view of a shared calendar object,
         associate it with a L{Share}.
         """
-        share = yield self._shareForStoreObject(child._newStoreObject, request)
-        if share:
+        if child._newStoreObject is not None and not child._newStoreObject.owned():
+            share = yield self._shareForStoreObject(child._newStoreObject, request)
             child.setShare(share)
             access = yield child._checkAccessControl()
             if access is None:

Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py	2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py	2014-06-18 18:06:15 UTC (rev 13651)
@@ -51,6 +51,12 @@
 
 
 
+class FakeRequest(object):
+    def locateResource(self, url):
+        return succeed(None)
+
+
+
 class FakeHome(object):
     def removeShareByUID(self, request, uid):
         pass
@@ -171,8 +177,8 @@
             that marks any principal without 'bogus' in its name.
             """
             result = principalForCalendarUserAddress(resourceSelf, userid)
-            if result is None:
-                return result
+            if result is None or userid[9:] in result.invalid_names:
+                return None
             return result.principalURL()
 
         @patched
@@ -221,8 +227,8 @@
 
 
     @inlineCallbacks
-    def _doPOSTSharerAccept(self, body, resultcode=responsecode.OK):
-        request = SimpleStoreRequest(self, "POST", "/calendars/__uids__/user02/", content=body, authid="user02")
+    def _doPOSTSharerAccept(self, body, resultcode=responsecode.OK, sharer="user02"):
+        request = SimpleStoreRequest(self, "POST", "/calendars/__uids__/{}/".format(sharer), content=body, authid=sharer)
         request.headers.setHeader("content-type", MimeType("text", "xml"))
         response = yield self.send(request)
         response = IResponse(response)
@@ -252,12 +258,22 @@
         return None
 
 
+    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
+
+
     def _clearUIDElementValue(self, xml):
 
         for user in xml.children:
-            for element in user.children:
-                if type(element) == customxml.UID:
-                    element.children[0].data = ""
+            uid = user.childOfType(customxml.UID)
+            if uid is not None:
+                uid.children[0].data = ""
         return xml
 
 
@@ -717,25 +733,23 @@
             )
         ))
 
-        self.resource.validUserIDForShare = lambda userid, request: None
-        self.resource.principalForCalendarUserAddress = lambda cuaddr: None
-        self.resource.principalForUID = lambda principalUID: None
+        self.patch(FakePrincipal, "invalid_names", set(("user02",)))
 
         propInvite = (yield self.resource.readProperty(customxml.Invite, None))
         self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
             customxml.InviteUser(
                 customxml.UID.fromString(""),
                 davxml.HRef.fromString("urn:uuid:user02"),
-                customxml.CommonName.fromString("user02"),
+                customxml.CommonName.fromString("USER02"),
                 customxml.InviteAccess(customxml.ReadWriteAccess()),
-                customxml.InviteStatusNoResponse(),
+                customxml.InviteStatusInvalid(),
             )
         ))
 
         yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
             <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
                 <CS:remove>
-                    <D:href>mailto:user02 at example.com</D:href>
+                    <D:href>urn:uuid:user02</D:href>
                 </CS:remove>
             </CS:share>
             """)
@@ -828,7 +842,7 @@
 
 
     @inlineCallbacks
-    def test_POSTDowngradeWithDisabledInvitee(self):
+    def test_POSTDowngradeWithMissingInvitee(self):
 
         yield self.resource.upgradeToShare()
 
@@ -858,7 +872,7 @@
 
 
     @inlineCallbacks
-    def test_POSTRemoveWithDisabledInvitee(self):
+    def test_POSTRemoveWithMissingInvitee(self):
 
         yield self.resource.upgradeToShare()
 
@@ -888,7 +902,7 @@
         yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
             <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
                 <CS:remove>
-                    <D:href>mailto:user02 at example.com</D:href>
+                    <D:href>urn:uuid:user02</D:href>
                 </CS:remove>
             </CS:share>
             """)
@@ -951,3 +965,252 @@
 
         resource = (yield self._getResourceSharer(href))
         self.assertFalse(resource.exists())
+
+
+    @inlineCallbacks
+    def test_POSTShareeRemoveWithMissingSharer(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+            <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+                <CS:set>
+                    <D:href>mailto:user02 at example.com</D:href>
+                    <CS:summary>My Shared Calendar</CS:summary>
+                    <CS:read-write/>
+                </CS:set>
+            </CS:share>
+            """)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uid = self._getUIDElementValue(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(""),
+                davxml.HRef.fromString("urn:uuid:user02"),
+                customxml.CommonName.fromString("USER02"),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        result = (yield self._doPOSTSharerAccept("""<?xml version='1.0' encoding='UTF-8'?>
+            <invite-reply xmlns='http://calendarserver.org/ns/'>
+              <href xmlns='DAV:'>mailto:user01 at example.com</href>
+              <invite-accepted/>
+              <hosturl>
+                <href xmlns='DAV:'>/calendars/__uids__/user01/calendar/</href>
+              </hosturl>
+              <in-reply-to>%s</in-reply-to>
+              <summary>The Shared Calendar</summary>
+              <common-name>USER02</common-name>
+              <first-name>user</first-name>
+              <last-name>02</last-name>
+            </invite-reply>
+            """ % (uid,))
+        )
+        href = self._getHRefElementValue(result) + "/"
+
+        self.patch(FakePrincipal, "invalid_names", set(("user01",)))
+
+        resource = (yield self._getResourceSharer(href))
+        yield resource.removeShareeResource(SimpleStoreRequest(self, "DELETE", href))
+
+        resource = (yield self._getResourceSharer(href))
+        self.assertFalse(resource.exists())
+
+
+    @inlineCallbacks
+    def test_shareeInviteWithDisabledSharer(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+            <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+                <CS:set>
+                    <D:href>mailto:user02 at example.com</D:href>
+                    <CS:summary>My Shared Calendar</CS:summary>
+                    <CS:read-write/>
+                </CS:set>
+            </CS:share>
+            """)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uid = self._getUIDElementValue(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(""),
+                davxml.HRef.fromString("urn:uuid:user02"),
+                customxml.CommonName.fromString("USER02"),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        result = (yield self._doPOSTSharerAccept("""<?xml version='1.0' encoding='UTF-8'?>
+            <invite-reply xmlns='http://calendarserver.org/ns/'>
+              <href xmlns='DAV:'>mailto:user01 at example.com</href>
+              <invite-accepted/>
+              <hosturl>
+                <href xmlns='DAV:'>/calendars/__uids__/user01/calendar/</href>
+              </hosturl>
+              <in-reply-to>%s</in-reply-to>
+              <summary>The Shared Calendar</summary>
+              <common-name>USER02</common-name>
+              <first-name>user</first-name>
+              <last-name>02</last-name>
+            </invite-reply>
+            """ % (uid,))
+        )
+        href = self._getHRefElementValue(result) + "/"
+
+        self.patch(FakePrincipal, "invalid_names", set(("user01",)))
+
+        resource = (yield self._getResourceSharer(href))
+        propInvite = yield resource.inviteProperty(FakeRequest())
+        self.assertEquals(propInvite, None)
+
+
+    @inlineCallbacks
+    def test_shareeInviteWithMissingSharer(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+            <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+                <CS:set>
+                    <D:href>mailto:user02 at example.com</D:href>
+                    <CS:summary>My Shared Calendar</CS:summary>
+                    <CS:read-write/>
+                </CS:set>
+            </CS:share>
+            """)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uid = self._getUIDElementValue(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(""),
+                davxml.HRef.fromString("urn:uuid:user02"),
+                customxml.CommonName.fromString("USER02"),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        result = (yield self._doPOSTSharerAccept("""<?xml version='1.0' encoding='UTF-8'?>
+            <invite-reply xmlns='http://calendarserver.org/ns/'>
+              <href xmlns='DAV:'>mailto:user01 at example.com</href>
+              <invite-accepted/>
+              <hosturl>
+                <href xmlns='DAV:'>/calendars/__uids__/user01/calendar/</href>
+              </hosturl>
+              <in-reply-to>%s</in-reply-to>
+              <summary>The Shared Calendar</summary>
+              <common-name>USER02</common-name>
+              <first-name>user</first-name>
+              <last-name>02</last-name>
+            </invite-reply>
+            """ % (uid,))
+        )
+        href = self._getHRefElementValue(result) + "/"
+
+        self.patch(FakePrincipal, "invalid_names", set(("user01",)))
+
+        resource = (yield self._getResourceSharer(href))
+        propInvite = yield resource.inviteProperty(FakeRequest())
+        self.assertEquals(propInvite, None)
+
+
+    @inlineCallbacks
+    def test_hideInvalidSharers(self):
+
+        yield self.resource.upgradeToShare()
+
+        yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+            <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+                <CS:set>
+                    <D:href>mailto:user02 at example.com</D:href>
+                    <CS:summary>My Shared Calendar</CS:summary>
+                    <CS:read-write/>
+                </CS:set>
+                <CS:set>
+                    <D:href>mailto:user03 at example.com</D:href>
+                    <CS:summary>My Shared Calendar</CS:summary>
+                    <CS:read-write/>
+                </CS:set>
+            </CS:share>
+            """)
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        uids = self._getUIDElementValues(propInvite)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(""),
+                davxml.HRef.fromString("urn:uuid:user02"),
+                customxml.CommonName.fromString("USER02"),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+            customxml.InviteUser(
+                customxml.UID.fromString(""),
+                davxml.HRef.fromString("urn:uuid:user03"),
+                customxml.CommonName.fromString("USER03"),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            ),
+        ))
+
+        yield self._doPOSTSharerAccept("""<?xml version='1.0' encoding='UTF-8'?>
+            <invite-reply xmlns='http://calendarserver.org/ns/'>
+              <href xmlns='DAV:'>mailto:user01 at example.com</href>
+              <invite-accepted/>
+              <hosturl>
+                <href xmlns='DAV:'>/calendars/__uids__/user01/calendar/</href>
+              </hosturl>
+              <in-reply-to>%s</in-reply-to>
+              <summary>The Shared Calendar</summary>
+              <common-name>USER02</common-name>
+              <first-name>user</first-name>
+              <last-name>02</last-name>
+            </invite-reply>
+            """ % (uids["urn:uuid:user02"],))
+
+        yield self._doPOSTSharerAccept(
+            """<?xml version='1.0' encoding='UTF-8'?>
+                <invite-reply xmlns='http://calendarserver.org/ns/'>
+                  <href xmlns='DAV:'>mailto:user01 at example.com</href>
+                  <invite-accepted/>
+                  <hosturl>
+                    <href xmlns='DAV:'>/calendars/__uids__/user01/calendar/</href>
+                  </hosturl>
+                  <in-reply-to>%s</in-reply-to>
+                  <summary>The Shared Calendar</summary>
+                  <common-name>USER03</common-name>
+                  <first-name>user</first-name>
+                  <last-name>03</last-name>
+                </invite-reply>
+            """ % (uids["urn:uuid:user03"],),
+            sharer="user03"
+        )
+
+        self.patch(FakePrincipal, "invalid_names", set(("user02",)))
+
+        resource = yield self._getResource()
+        propInvite = yield resource.inviteProperty(None)
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(""),
+                davxml.HRef.fromString("urn:uuid:user02"),
+                customxml.CommonName.fromString("USER02"),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusInvalid(),
+            ),
+            customxml.InviteUser(
+                customxml.UID.fromString(""),
+                davxml.HRef.fromString("urn:uuid:user03"),
+                customxml.CommonName.fromString("USER03"),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusAccepted(),
+            ),
+        ))

Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py	2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py	2014-06-18 18:06:15 UTC (rev 13651)
@@ -2740,6 +2740,27 @@
         )
 
 
+    def _replace(self, status):
+        """
+        Create a copy of this invitation with the status changed to the specified value.
+
+        @param status: the new status value
+        @type status: L{int}
+        """
+        return SharingInvitation(
+            self._uid,
+            self._owner_uid,
+            self._owner_rid,
+            self._sharee_uid,
+            self._sharee_rid,
+            self._resource_id,
+            self._resource_name,
+            self._mode,
+            status,
+            self._summary,
+        )
+
+
     def uid(self):
         """
         This maps to the resource name in the bind table, the "bind name". This is used as the "uid"
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140618/fd8650a0/attachment-0001.html>


More information about the calendarserver-changes mailing list