[CalendarServer-changes] [11524] CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix

source_changes at macosforge.org source_changes at macosforge.org
Wed Jul 17 11:42:24 PDT 2013


Revision: 11524
          http://trac.calendarserver.org//changeset/11524
Author:   cdaboo at apple.com
Date:     2013-07-17 11:42:24 -0700 (Wed, 17 Jul 2013)
Log Message:
-----------
Remove O(N) dependency on number of sharees when looking up the CS:invite property. Also re-factored some sharing api for clarity.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/sharing.py
    CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/storebridge.py
    CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/test/test_sharing.py
    CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/test/test_wrapping.py
    CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/datastore/test/common.py
    CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/datastore/test/test_file.py
    CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/icalendarstore.py
    CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/common/datastore/file.py
    CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/common/datastore/sql.py

Modified: CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/sharing.py	2013-07-17 18:36:27 UTC (rev 11523)
+++ CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/sharing.py	2013-07-17 18:42:24 UTC (rev 11524)
@@ -29,10 +29,13 @@
 from twext.web2.dav.http import ErrorResponse, MultiStatusResponse
 from twext.web2.dav.resource import TwistedACLInheritable
 from twext.web2.dav.util import allDataFromStream, joinURL
+
+from txdav.common.datastore.sql import SharingInvitation
 from txdav.common.datastore.sql_tables import _BIND_MODE_OWN, \
     _BIND_MODE_READ, _BIND_MODE_WRITE, _BIND_STATUS_INVITED, \
     _BIND_MODE_DIRECT, _BIND_STATUS_ACCEPTED, _BIND_STATUS_DECLINED, \
     _BIND_STATUS_INVALID
+
 from txdav.xml import element
 
 from twisted.internet.defer import succeed, inlineCallbacks, DeferredList, \
@@ -83,15 +86,14 @@
                     customxml.UID.fromString(invitation.uid()) if includeUID else None,
                     element.HRef.fromString(userid),
                     customxml.CommonName.fromString(cn),
-                    customxml.InviteAccess(invitationAccessMapToXML[invitation.access()]()),
-                    invitationStatusMapToXML[invitation.state()](),
+                    customxml.InviteAccess(invitationBindModeToXMLMap[invitation.mode()]()),
+                    invitationBindStatusToXMLMap[invitation.status()](),
                 )
 
             # See if this property is on the shared calendar
             isShared = yield self.isShared(request)
             if isShared:
-                yield self.validateInvites(request)
-                invitations = yield self._allInvitations()
+                invitations = yield self.validateInvites(request)
                 returnValue(customxml.Invite(
                     *[invitePropertyElement(invitation) for invitation in invitations]
                 ))
@@ -99,8 +101,7 @@
             # See if it is on the sharee calendar
             if self.isShareeCollection():
                 original = (yield request.locateResource(self._share.url()))
-                yield original.validateInvites(request)
-                invitations = yield original._allInvitations()
+                invitations = yield original.validateInvites(request)
 
                 ownerPrincipal = (yield original.ownerPrincipal(request))
                 owner = ownerPrincipal.principalURL()
@@ -163,8 +164,8 @@
             ))
 
         # Only certain states are sharer controlled
-        if invitation.state() in ("NEEDS-ACTION", "ACCEPTED", "DECLINED",):
-            yield self._updateInvitation(invitation, state=state, summary=summary)
+        if invitation.status() in (_BIND_STATUS_INVITED, _BIND_STATUS_ACCEPTED, _BIND_STATUS_DECLINED,):
+            yield self._updateInvitation(invitation, status=state, summary=summary)
 
 
     @inlineCallbacks
@@ -349,7 +350,7 @@
         else:
             # Invited shares use access mode from the invite
             # Get the access for self
-            returnValue(Invitation(self._newStoreObject).access())
+            returnValue(invitationAccessFromBindModeMap.get(self._newStoreObject.shareMode()))
 
 
     @inlineCallbacks
@@ -495,11 +496,11 @@
         #assert request
         invitations = yield self._allInvitations()
         for invitation in invitations:
-            if invitation.state() != "INVALID":
+            if invitation.status() != _BIND_STATUS_INVALID:
                 if not (yield self.validUserIDForShare("urn:uuid:" + invitation.shareeUID(), request)):
-                    yield self._updateInvitation(invitation, state="INVALID")
+                    yield self._updateInvitation(invitation, status=_BIND_STATUS_INVALID)
 
-        returnValue(len(invitations))
+        returnValue(invitations)
 
 
     def inviteUserToShare(self, userid, cn, ace, summary, request):
@@ -558,7 +559,7 @@
 
 
     @inlineCallbacks
-    def _createInvitation(self, shareeUID, access, summary,):
+    def _createInvitation(self, shareeUID, mode, summary,):
         '''
         Create a new homeChild and wrap it in an Invitation
         '''
@@ -568,54 +569,47 @@
             shareeHome = yield self._newStoreObject._txn.addressbookHomeWithUID(shareeUID, create=True)
 
         sharedName = yield self._newStoreObject.shareWith(shareeHome,
-                                                    mode=invitationAccessToBindModeMap[access],
+                                                    mode=mode,
                                                     status=_BIND_STATUS_INVITED,
                                                     message=summary)
 
         shareeHomeChild = yield shareeHome.invitedChildWithName(sharedName)
-        invitation = Invitation(shareeHomeChild)
+        invitation = SharingInvitation.fromCommonHomeChild(shareeHomeChild)
         returnValue(invitation)
 
 
     @inlineCallbacks
-    def _updateInvitation(self, invitation, access=None, state=None, summary=None):
-        mode = None if access is None else invitationAccessToBindModeMap[access]
-        status = None if state is None else invitationStateToBindStatusMap[state]
+    def _updateInvitation(self, invitation, mode=None, status=None, summary=None):
+        yield self._newStoreObject.updateShareUIDName(invitation.shareeUID(), invitation.resourceName(), mode=mode, status=status, message=summary)
+        if mode is not None:
+            invitation.setMode(mode)
+        if status is not None:
+            invitation.setStatus(status)
+        if summary is not None:
+            invitation.setSummary(summary)
 
-        yield self._newStoreObject.updateShare(invitation._shareeHomeChild, mode=mode, status=status, message=summary)
-        assert not access or access == invitation.access(), "access=%s != invitation.access()=%s" % (access, invitation.access())
-        assert not state or state == invitation.state(), "state=%s != invitation.state()=%s" % (state, invitation.state())
-        assert not summary or summary == invitation.summary(), "summary=%s != invitation.summary()=%s" % (summary, invitation.summary())
 
-
     @inlineCallbacks
-    def _allInvitations(self, includeAccepted=True):
+    def _allInvitations(self):
         """
-        Get list of all invitations to this object
-
-        For legacy reasons, all invitations are all invited + shared (accepted, not direct).
-        Combine these two into a single sorted list so code is similar to that for legacy invite db
+        Get list of all invitations (non-direct) to this object.
         """
-        invitedHomeChildren = yield self._newStoreObject.asInvited()
-        if includeAccepted:
-            acceptedHomeChildren = yield self._newStoreObject.asShared()
-            # remove direct shares (it might be OK not to remove these, that would be different from legacy code)
-            indirectAccceptedHomeChildren = [homeChild for homeChild in acceptedHomeChildren
-                                             if homeChild.shareMode() != _BIND_MODE_DIRECT]
-            invitedHomeChildren += indirectAccceptedHomeChildren
+        invitations = yield self._newStoreObject.sharingInvites()
 
-        invitations = [Invitation(homeChild) for homeChild in invitedHomeChildren]
+        # remove direct shares as those are not "real" invitations
+        invitations = filter(lambda x: x.mode() != _BIND_MODE_DIRECT, invitations)
+
         invitations.sort(key=lambda invitation: invitation.shareeUID())
 
         returnValue(invitations)
 
 
     @inlineCallbacks
-    def _invitationForShareeUID(self, shareeUID, includeAccepted=True):
+    def _invitationForShareeUID(self, shareeUID):
         """
         Get an invitation for this sharee principal UID
         """
-        invitations = yield self._allInvitations(includeAccepted=includeAccepted)
+        invitations = yield self._allInvitations()
         for invitation in invitations:
             if invitation.shareeUID() == shareeUID:
                 returnValue(invitation)
@@ -623,11 +617,11 @@
 
 
     @inlineCallbacks
-    def _invitationForUID(self, uid, includeAccepted=True):
+    def _invitationForUID(self, uid):
         """
         Get an invitation for an invitations uid
         """
-        invitations = yield self._allInvitations(includeAccepted=includeAccepted)
+        invitations = yield self._allInvitations()
         for invitation in invitations:
             if invitation.uid() == uid:
                 returnValue(invitation)
@@ -647,11 +641,11 @@
         # Look for existing invite and update its fields or create new one
         invitation = yield self._invitationForShareeUID(shareeUID)
         if invitation:
-            yield self._updateInvitation(invitation, access=invitationAccessMapFromXML[type(ace)], summary=summary)
+            yield self._updateInvitation(invitation, mode=invitationBindModeFromXMLMap[type(ace)], summary=summary)
         else:
             invitation = yield self._createInvitation(
                                 shareeUID=shareeUID,
-                                access=invitationAccessMapFromXML[type(ace)],
+                                mode=invitationBindModeFromXMLMap[type(ace)],
                                 summary=summary)
         # Send invite notification
         yield self.sendInviteNotification(invitation, request)
@@ -691,13 +685,13 @@
             displayName = (yield shareeHomeResource.removeShareByUID(request, invitation.uid()))
             # If current user state is accepted then we send an invite with the new state, otherwise
             # we cancel any existing invites for the user
-            if invitation and invitation.state() != "ACCEPTED":
+            if invitation and invitation.status() != _BIND_STATUS_ACCEPTED:
                 yield self.removeInviteNotification(invitation, request)
             elif invitation:
                 yield self.sendInviteNotification(invitation, request, displayName=displayName, notificationState="DELETED")
 
-        # Direct shares for  with valid sharee principal will already be deleted
-        yield self._newStoreObject.unshareWith(invitation._shareeHomeChild.viewerHome())
+        # Direct shares for with valid sharee principal will already be deleted
+        yield self._newStoreObject.unshareWithUID(invitation.shareeUID())
 
         returnValue(True)
 
@@ -736,7 +730,7 @@
 
         # Generate invite XML
         userid = "urn:uuid:" + invitation.shareeUID()
-        state = notificationState if notificationState else invitation.state()
+        state = notificationState if notificationState else invitation.status()
         summary = invitation.summary() if displayName is None else displayName
 
         typeAttr = {'shared-type': self.sharedResourceType()}
@@ -746,8 +740,8 @@
             customxml.InviteNotification(
                 customxml.UID.fromString(invitation.uid()),
                 element.HRef.fromString(userid),
-                invitationStatusMapToXML[state](),
-                customxml.InviteAccess(invitationAccessMapToXML[invitation.access()]()),
+                invitationBindStatusToXMLMap[state](),
+                customxml.InviteAccess(invitationBindModeToXMLMap[invitation.mode()]()),
                 customxml.HostURL(
                     element.HRef.fromString(hosturl),
                 ),
@@ -893,7 +887,8 @@
             ok_code = responsecode.FAILED_DEPENDENCY
 
         # Do a final validation of the entire set of invites
-        numRecords = (yield self.validateInvites(request))
+        invites = (yield self.validateInvites(request))
+        numRecords = len(invites)
 
         # Set the sharing state on the collection
         shared = (yield self.isShared(request))
@@ -981,28 +976,21 @@
         ("text", "xml") : xmlRequestHandler,
     }
 
-invitationAccessMapToXML = {
-    "read-only"           : customxml.ReadAccess,
-    "read-write"          : customxml.ReadWriteAccess,
+invitationBindStatusToXMLMap = {
+    _BIND_STATUS_INVITED      : customxml.InviteStatusNoResponse,
+    _BIND_STATUS_ACCEPTED     : customxml.InviteStatusAccepted,
+    _BIND_STATUS_DECLINED     : customxml.InviteStatusDeclined,
+    _BIND_STATUS_INVALID      : customxml.InviteStatusInvalid,
+    "DELETED"                 : customxml.InviteStatusDeleted,
 }
-invitationAccessMapFromXML = dict([(v, k) for k, v in invitationAccessMapToXML.iteritems()])
+invitationBindStatusFromXMLMap = dict((v, k) for k, v in invitationBindStatusToXMLMap.iteritems())
 
-invitationStatusMapToXML = {
-    "NEEDS-ACTION" : customxml.InviteStatusNoResponse,
-    "ACCEPTED"     : customxml.InviteStatusAccepted,
-    "DECLINED"     : customxml.InviteStatusDeclined,
-    "DELETED"      : customxml.InviteStatusDeleted,
-    "INVALID"      : customxml.InviteStatusInvalid,
+invitationBindModeToXMLMap = {
+    _BIND_MODE_READ           : customxml.ReadAccess,
+    _BIND_MODE_WRITE          : customxml.ReadWriteAccess,
 }
-invitationStatusMapFromXML = dict([(v, k) for k, v in invitationStatusMapToXML.iteritems()])
+invitationBindModeFromXMLMap = dict((v, k) for k, v in invitationBindModeToXMLMap.iteritems())
 
-invitationStateToBindStatusMap = {
-    "NEEDS-ACTION": _BIND_STATUS_INVITED,
-    "ACCEPTED": _BIND_STATUS_ACCEPTED,
-    "DECLINED": _BIND_STATUS_DECLINED,
-    "INVALID": _BIND_STATUS_INVALID,
-}
-invitationStateFromBindStatusMap = dict((v, k) for k, v in invitationStateToBindStatusMap.iteritems())
 invitationAccessToBindModeMap = {
     "own": _BIND_MODE_OWN,
     "read-only": _BIND_MODE_READ,
@@ -1010,35 +998,8 @@
     }
 invitationAccessFromBindModeMap = dict((v, k) for k, v in invitationAccessToBindModeMap.iteritems())
 
-class Invitation(object):
-    """
-        Invitation is a read-only wrapper for CommonHomeChild, that uses terms similar LegacyInvite sharing.py code base.
-    """
-    def __init__(self, shareeHomeChild):
-        self._shareeHomeChild = shareeHomeChild
 
 
-    def uid(self):
-        return self._shareeHomeChild.shareUID()
-
-
-    def shareeUID(self):
-        return self._shareeHomeChild.viewerHome().uid()
-
-
-    def access(self):
-        return invitationAccessFromBindModeMap.get(self._shareeHomeChild.shareMode())
-
-
-    def state(self):
-        return invitationStateFromBindStatusMap.get(self._shareeHomeChild.shareStatus())
-
-
-    def summary(self):
-        return self._shareeHomeChild.shareMessage()
-
-
-
 class SharedHomeMixin(LinkFollowerMixIn):
     """
     A mix-in for calendar/addressbook homes that defines the operations for
@@ -1105,8 +1066,7 @@
         if sharerHomeCollection is None:
             returnValue(None)
         url = joinURL(sharerHomeCollection.url(), sharerHomeChild.name())
-        share = Share(shareeHomeChild=child, sharerHomeChild=sharerHomeChild,
-                      url=url)
+        share = Share(shareeHomeChild=child, sharerHomeChild=sharerHomeChild, url=url)
 
         returnValue(share)
 
@@ -1138,7 +1098,7 @@
         oldShare = yield self._shareForUID(inviteUID, request)
 
         # Send the invite reply then add the link
-        yield self._changeShare(request, "ACCEPTED", hostUrl, inviteUID,
+        yield self._changeShare(request, _BIND_STATUS_ACCEPTED, hostUrl, inviteUID,
                                 displayname)
         if oldShare:
             share = oldShare
@@ -1314,7 +1274,7 @@
         # Remove it if it is in the DB
         yield self.removeShareByUID(request, inviteUID)
 
-        yield self._changeShare(request, "DECLINED", hostUrl, inviteUID)
+        yield self._changeShare(request, _BIND_STATUS_DECLINED, hostUrl, inviteUID)
 
         returnValue(Response(code=responsecode.NO_CONTENT))
 
@@ -1371,7 +1331,7 @@
                 *(
                     (
                         element.HRef.fromString(cua),
-                        invitationStatusMapToXML[state](),
+                        invitationBindStatusToXMLMap[state](),
                         customxml.HostURL(
                             element.HRef.fromString(hostUrl),
                         ),

Modified: CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/storebridge.py	2013-07-17 18:36:27 UTC (rev 11523)
+++ CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/storebridge.py	2013-07-17 18:42:24 UTC (rev 11524)
@@ -62,7 +62,7 @@
     AttachmentStoreValidManagedID, AttachmentRemoveFailed, \
     AttachmentDropboxNotAllowed
 from txdav.common.datastore.sql_tables import _BIND_MODE_READ, _BIND_MODE_WRITE, \
-    _BIND_MODE_DIRECT
+    _BIND_MODE_DIRECT, _BIND_STATUS_ACCEPTED
 from txdav.common.icommondatastore import NoSuchObjectResourceError
 from txdav.idav import PropertyChangeNotAllowedError
 from txdav.xml import element as davxml
@@ -1584,23 +1584,28 @@
     def sharedDropboxACEs(self):
 
         aces = ()
-        calendars = yield self._newStoreCalendarObject._parentCollection.asShared()
-        for calendar in calendars:
 
+        invites = yield self._newStoreCalendarObject._parentCollection.sharingInvites()
+        for invite in invites:
+
+            # Only want accepted invites
+            if invite.status() != _BIND_STATUS_ACCEPTED:
+                continue
+
             userprivs = [
             ]
-            if calendar.shareMode() in (_BIND_MODE_READ, _BIND_MODE_WRITE,):
+            if invite.mode() in (_BIND_MODE_READ, _BIND_MODE_WRITE,):
                 userprivs.append(davxml.Privilege(davxml.Read()))
                 userprivs.append(davxml.Privilege(davxml.ReadACL()))
                 userprivs.append(davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()))
-            if calendar.shareMode() in (_BIND_MODE_READ,):
+            if invite.mode() in (_BIND_MODE_READ,):
                 userprivs.append(davxml.Privilege(davxml.WriteProperties()))
-            if calendar.shareMode() in (_BIND_MODE_WRITE,):
+            if invite.mode() in (_BIND_MODE_WRITE,):
                 userprivs.append(davxml.Privilege(davxml.Write()))
             proxyprivs = list(userprivs)
             proxyprivs.remove(davxml.Privilege(davxml.ReadACL()))
 
-            principal = self.principalForUID(calendar._home.uid())
+            principal = self.principalForUID(invite.shareeUID())
             aces += (
                 # Inheritable specific access for the resource's associated principal.
                 davxml.ACE(
@@ -1879,7 +1884,7 @@
 
 
     @inlineCallbacks
-    def _sharedAccessControl(self, calendar, shareMode):
+    def _sharedAccessControl(self, invite):
         """
         Check the shared access mode of this resource, potentially consulting
         an external access method if necessary.
@@ -1895,10 +1900,10 @@
             access control mechanism has dictate the home should no longer have
             any access at all.
         """
-        if shareMode in (_BIND_MODE_DIRECT,):
-            ownerUID = calendar.ownerHome().uid()
+        if invite.mode() in (_BIND_MODE_DIRECT,):
+            ownerUID = invite.ownerUID()
             owner = self.principalForUID(ownerUID)
-            shareeUID = calendar.viewerHome().uid()
+            shareeUID = invite.shareeUID()
             if owner.record.recordType == WikiDirectoryService.recordType_wikis:
                 # Access level comes from what the wiki has granted to the
                 # sharee
@@ -1914,9 +1919,9 @@
                     returnValue(None)
             else:
                 returnValue("original")
-        elif shareMode in (_BIND_MODE_READ,):
+        elif invite.mode() in (_BIND_MODE_READ,):
             returnValue("read-only")
-        elif shareMode in (_BIND_MODE_WRITE,):
+        elif invite.mode() in (_BIND_MODE_WRITE,):
             returnValue("read-write")
         returnValue("original")
 
@@ -1925,19 +1930,23 @@
     def sharedDropboxACEs(self):
 
         aces = ()
-        calendars = yield self._newStoreCalendarObject._parentCollection.asShared()
-        for calendar in calendars:
+        invites = yield self._newStoreCalendarObject._parentCollection.sharingInvites()
+        for invite in invites:
 
+            # Only want accepted invites
+            if invite.status() != _BIND_STATUS_ACCEPTED:
+                continue
+
             privileges = [
                 davxml.Privilege(davxml.Read()),
                 davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
             ]
             userprivs = []
-            access = (yield self._sharedAccessControl(calendar, calendar.shareMode()))
+            access = (yield self._sharedAccessControl(invite))
             if access in ("read-only", "read-write",):
                 userprivs.extend(privileges)
 
-            principal = self.principalForUID(calendar._home.uid())
+            principal = self.principalForUID(invite.shareeUID())
             aces += (
                 # Inheritable specific access for the resource's associated principal.
                 davxml.ACE(

Modified: CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/test/test_sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/test/test_sharing.py	2013-07-17 18:36:27 UTC (rev 11523)
+++ CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/test/test_sharing.py	2013-07-17 18:42:24 UTC (rev 11524)
@@ -34,6 +34,7 @@
 from txdav.common.datastore.test.util import buildStore, StubNotifierFactory
 from txdav.caldav.icalendarstore import BIND_DIRECT
 from twistedcaldav.test.test_cache import StubResponseCacheResource
+from twistedcaldav.directory.principal import DirectoryCalendarPrincipalResource
 
 
 sharedOwnerType = davxml.ResourceType.sharedownercalendar #@UndefinedVariable
@@ -71,7 +72,7 @@
 
 
 
-class FakePrincipal(object):
+class FakePrincipal(DirectoryCalendarPrincipalResource):
 
     def __init__(self, cuaddr, test):
         if cuaddr.startswith("mailto:"):
@@ -91,9 +92,9 @@
 
     @inlineCallbacks
     def calendarHome(self, request):
-        a, seg = yield self._test.homeProvisioner.locateChild(request,
+        a, _ignore_seg = yield self._test.homeProvisioner.locateChild(request,
                                                               ["__uids__"])
-        b, seg = yield a.locateChild(request, [self._name])
+        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.
@@ -496,6 +497,7 @@
             ),
         ))
 
+
     @inlineCallbacks
     def test_POSTaddRemoveSameInvitee(self):
 
@@ -760,7 +762,3 @@
         access = "no-access"
         childNames = yield listChildrenViaPropfind()
         self.assertNotIn(sharedName, childNames)
-
-
-
-

Modified: CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/test/test_wrapping.py
===================================================================
--- CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/test/test_wrapping.py	2013-07-17 18:36:27 UTC (rev 11523)
+++ CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/twistedcaldav/test/test_wrapping.py	2013-07-17 18:42:24 UTC (rev 11524)
@@ -427,7 +427,7 @@
         if not hasattr(self.calendarCollection._newStore, "_dropbox_ok"):
             self.calendarCollection._newStore._dropbox_ok = False
         self.patch(self.calendarCollection._newStore, "_dropbox_ok", True)
-        self.patch(Calendar, "asShared", lambda self: [])
+        self.patch(Calendar, "sharingInvites", lambda self: [])
 
         yield self.populateOneObject("1.ics", test_event_text)
         calendarObject = yield self.getResource(

Modified: CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/datastore/test/common.py	2013-07-17 18:36:27 UTC (rev 11523)
+++ CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/datastore/test/common.py	2013-07-17 18:42:24 UTC (rev 11524)
@@ -1014,9 +1014,9 @@
         self.assertEqual(newName, self.sharedName)
         self.assertNotIdentical(otherCal, None)
 
-        invitedCals = yield cal.asShared()
+        invitedCals = yield cal.sharingInvites()
         self.assertEqual(len(invitedCals), 1)
-        self.assertEqual(invitedCals[0].shareMode(), _BIND_MODE_READ)
+        self.assertEqual(invitedCals[0].mode(), _BIND_MODE_READ)
 
 
     @inlineCallbacks
@@ -1033,7 +1033,7 @@
         newName = yield cal.unshareWith(other)
         otherCal = yield other.childWithName(newName)
         self.assertIdentical(otherCal, None)
-        invitedCals = yield cal.asShared()
+        invitedCals = yield cal.sharingInvites()
         self.assertEqual(len(invitedCals), 0)
 
 
@@ -1053,7 +1053,7 @@
         yield cal.unshare()
         otherCal = yield other.childWithName(self.sharedName)
         self.assertEqual(otherCal, None)
-        invitedCals = yield cal.asShared()
+        invitedCals = yield cal.sharingInvites()
         self.assertEqual(len(invitedCals), 0)
 
 
@@ -1073,7 +1073,7 @@
         yield otherCal.unshare()
         otherCal = yield other.childWithName(self.sharedName)
         self.assertEqual(otherCal, None)
-        invitedCals = yield cal.asShared()
+        invitedCals = yield cal.sharingInvites()
         self.assertEqual(len(invitedCals), 0)
 
 
@@ -1088,24 +1088,23 @@
 
 
     @inlineCallbacks
-    def test_asShared(self):
+    def test_sharedInvites(self):
         """
-        L{ICalendar.asShared} returns an iterable of all versions of a shared
+        L{ICalendar.sharingInvites} returns an iterable of all versions of a shared
         calendar.
         """
         cal = yield self.calendarUnderTest()
-        sharedBefore = yield cal.asShared()
+        sharedBefore = yield cal.sharingInvites()
         # It's not shared yet; make sure asShared doesn't include owner version.
         self.assertEqual(len(sharedBefore), 0)
         yield self.test_shareWith()
         # FIXME: don't know why this separate transaction is needed; remove it.
         yield self.commit()
         cal = yield self.calendarUnderTest()
-        sharedAfter = yield cal.asShared()
+        sharedAfter = yield cal.sharingInvites()
         self.assertEqual(len(sharedAfter), 1)
-        self.assertEqual(sharedAfter[0].shareMode(), _BIND_MODE_WRITE)
-        self.assertEqual(sharedAfter[0].viewerCalendarHome().uid(),
-                         OTHER_HOME_UID)
+        self.assertEqual(sharedAfter[0].mode(), _BIND_MODE_WRITE)
+        self.assertEqual(sharedAfter[0].shareeUID(), OTHER_HOME_UID)
 
 
     @inlineCallbacks

Modified: CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/datastore/test/test_file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/datastore/test/test_file.py	2013-07-17 18:36:27 UTC (rev 11523)
+++ CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/datastore/test/test_file.py	2013-07-17 18:42:24 UTC (rev 11524)
@@ -487,7 +487,7 @@
     test_shareAgainChangesMode = test_shareWith
     test_unshareWith = test_shareWith
     test_unshareWithInDifferentTransaction = test_shareWith
-    test_asShared = test_shareWith
+    test_sharedInvites = test_shareWith
     test_unshareSharerSide = test_shareWith
     test_unshareShareeSide = test_shareWith
     test_sharedNotifierID = test_shareWith

Modified: CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/icalendarstore.py	2013-07-17 18:36:27 UTC (rev 11523)
+++ CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/caldav/icalendarstore.py	2013-07-17 18:42:24 UTC (rev 11524)
@@ -479,7 +479,7 @@
         """
 
 
-    def asShared():
+    def sharingInvites():
         """
         Get a view of this L{ICalendar} as present in everyone's calendar home
         except for its owner's.

Modified: CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/common/datastore/file.py	2013-07-17 18:36:27 UTC (rev 11523)
+++ CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/common/datastore/file.py	2013-07-17 18:42:24 UTC (rev 11524)
@@ -132,6 +132,7 @@
         # FIXME: see '@ivar queuer' above.
         self.queuer = _StubQueuer()
 
+
     def callWithNewTransactions(self, callback):
         """
         Registers a method to be called whenever a new transaction is
@@ -1318,7 +1319,7 @@
 
 
     @inlineCallbacks
-    def asInvited(self):
+    def sharingInvites(self):
         """
         Stub for interface-compliance tests.
         """
@@ -1326,16 +1327,7 @@
         returnValue([])
 
 
-    @inlineCallbacks
-    def asShared(self):
-        """
-        Stub for interface-compliance tests.
-        """
-        yield None
-        returnValue([])
 
-
-
 class CommonObjectResource(FileMetaDataMixin, LoggingMixIn, FancyEqMixin):
     """
     @ivar _path: The path of the file on disk

Modified: CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/common/datastore/sql.py	2013-07-17 18:36:27 UTC (rev 11523)
+++ CalendarServer/branches/users/cdaboo/CalendarServer-5.0-podfix/txdav/common/datastore/sql.py	2013-07-17 18:42:24 UTC (rev 11524)
@@ -2429,6 +2429,94 @@
 
 
 
+class SharingInvitation(object):
+    """
+    SharingInvitation covers all the information needed to expose sharing invites to upper layers. Its primarily used to
+    minimize the need to load full calendar data when only this subset of information is needed.
+    """
+    def __init__(self, owner_uid, owner_rid, sharee_uid, sharee_rid, resource_id, resource_name, mode, status, summary):
+        self._owner_uid = owner_uid
+        self._owner_rid = owner_rid
+        self._sharee_uid = sharee_uid
+        self._sharee_rid = sharee_rid
+        self._resource_id = resource_id
+        self._resource_name = resource_name
+        self._mode = mode
+        self._status = status
+        self._summary = summary
+
+
+    @classmethod
+    def fromCommonHomeChild(cls, homeChild):
+        return cls(
+            homeChild.shareUID(),
+            homeChild.ownerHome()._resourceID,
+            homeChild.viewerHome().uid(),
+            homeChild.viewerHome()._resourceID,
+            homeChild._resourceID,
+            homeChild.name(),
+            homeChild.shareMode(),
+            homeChild.shareStatus(),
+            homeChild.shareMessage(),
+        )
+
+
+    def uid(self):
+        """
+        uid of an invitation is just the resource name of the shared collection (which is always a uuid).
+        """
+        return self._resource_name
+
+
+    def ownerUID(self):
+        return self._owner_uid
+
+
+    def ownerHomeID(self):
+        return self._owner_rid
+
+
+    def shareeUID(self):
+        return self._sharee_uid
+
+
+    def shareeHomeID(self):
+        return self._sharee_rid
+
+
+    def resourceID(self):
+        return self._resource_id
+
+
+    def resourceName(self):
+        return self._resource_name
+
+
+    def mode(self):
+        return self._mode
+
+
+    def setMode(self, mode):
+        self._mode = mode
+
+
+    def status(self):
+        return self._status
+
+
+    def setStatus(self, status):
+        self._status = status
+
+
+    def summary(self):
+        return self._summary
+
+
+    def setSummary(self, summary):
+        self._summary = summary
+
+
+
 class CommonHomeChild(LoggingMixIn, FancyEqMixin, Memoizable, _SharedSyncLogic, HomeChildBase):
     """
     Common ancestor class of AddressBooks and Calendars.
@@ -2674,6 +2762,23 @@
 
 
     @inlineCallbacks
+    def updateShareUIDName(self, shareeUID, resourceName, mode=None, status=None, message=None, name=None):
+        """
+        Like L{updateShare} except that the sharee UID and shared child resource name are provided. Those are
+        used to find the actual sharee L{CommonHomeChild} which is then passed to L{updateShare}.
+        """
+
+        # Look up thr shared child - might be accepted or unaccepted
+        shareeHome = yield self._txn.homeWithUID(self._home._homeType, shareeUID)
+        shareeView = yield shareeHome.childWithName(resourceName)
+        if shareeView is None:
+            shareeView = yield shareeHome.invitedChildWithName(resourceName)
+
+        result = yield self.updateShare(shareeView, mode, status, message, name)
+        returnValue(result)
+
+
+    @inlineCallbacks
     def updateShare(self, shareeView, mode=None, status=None, message=None, name=None):
         """
         Update share mode, status, and message for a home child shared with
@@ -2748,6 +2853,18 @@
 
 
     @inlineCallbacks
+    def unshareWithUID(self, shareeUID):
+        """
+        Like L{unshareWith} except this is passed a sharee UID which is then used to lookup the
+        L{CommonHome} for the sharee to pass to L{unshareWith}.
+        """
+
+        shareeHome = yield self._txn.homeWithUID(self._home._homeType, shareeUID)
+        result = yield self.unshareWith(shareeHome)
+        returnValue(result)
+
+
+    @inlineCallbacks
     def unshareWith(self, shareeHome):
         """
         Remove the shared version of this (owned) L{CommonHomeChild} from the
@@ -2838,13 +2955,14 @@
         """
         if self.owned():
             # This collection may be shared to others
-            for sharedToHome in [x.viewerHome() for x in (yield self.asShared())]:
-                (yield self.unshareWith(sharedToHome))
+            invites = yield self.sharingInvites()
+            for invite in invites:
+                shareeHome = (yield self._txn.homeWithResourceID(homeType, invite.shareeHomeID()))
+                (yield self.unshareWith(shareeHome))
         else:
             # This collection is shared to me
             sharerHomeID = (yield self.sharerHomeID())
-            sharerHome = (yield self._txn.homeWithResourceID(homeType,
-                sharerHomeID))
+            sharerHome = (yield self._txn.homeWithResourceID(homeType, sharerHomeID))
             sharerCollection = (yield sharerHome.childWithID(self._resourceID))
             (yield sharerCollection.unshareWith(self._home))
 
@@ -2864,92 +2982,63 @@
         )
 
 
-    @classproperty
-    def _sharedBindForResourceID(cls): #@NoSelf
+    @classmethod
+    def _bindInviteFor(cls, condition): #@NoSelf
+        home = cls._homeSchema
         bind = cls._bindSchema
-        return cls._bindFor((bind.RESOURCE_ID == Parameter("resourceID"))
-                            .And(bind.BIND_STATUS == _BIND_STATUS_ACCEPTED)
-                            .And(bind.BIND_MODE != _BIND_MODE_OWN)
-                            )
-
-
-    @inlineCallbacks
-    def asShared(self):
-        """
-        Retrieve all the versions of this L{CommonHomeChild} as it is shared to
-        everyone.
-
-        @see: L{ICalendarHome.asShared}
-
-        @return: L{CommonHomeChild} objects that represent this
-            L{CommonHomeChild} as a child of different L{CommonHome}s
-        @rtype: a L{Deferred} which fires with a L{list} of L{ICalendar}s.
-        """
-        if not self.owned():
-            returnValue([])
-
-        # get all accepted binds
-        acceptedRows = yield self._sharedBindForResourceID.on(
-            self._txn, resourceID=self._resourceID, homeID=self._home._resourceID
+        return Select(
+            [
+                home.OWNER_UID,
+                bind.HOME_RESOURCE_ID,
+                bind.RESOURCE_ID,
+                bind.RESOURCE_NAME,
+                bind.BIND_MODE,
+                bind.BIND_STATUS,
+                bind.MESSAGE,
+            ],
+            From=bind.join(home, on=(bind.HOME_RESOURCE_ID == home.RESOURCE_ID)),
+            Where=condition
         )
 
-        cls = self.__class__ # for ease of grepping...
-        result = []
-        for bindMode, homeID, resourceID, resourceName, bindStatus, bindMessage in acceptedRows: #@UnusedVariable
-            assert bindStatus == _BIND_STATUS_ACCEPTED
-            # TODO: this could all be issued in parallel; no need to serialize
-            # the loop.
-            new = cls(
-                home=(yield self._txn.homeWithResourceID(self._home._homeType, homeID)),
-                name=resourceName, resourceID=self._resourceID,
-                mode=bindMode, status=bindStatus,
-                message=bindMessage, ownerHome=self._home
-            )
-            yield new.initFromStore()
-            result.append(new)
-        returnValue(result)
 
-
     @classproperty
-    def _invitedBindForResourceID(cls): #@NoSelf
+    def _sharedInvitationBindForResourceID(cls): #@NoSelf
         bind = cls._bindSchema
-        return cls._bindFor((bind.RESOURCE_ID == Parameter("resourceID"))
-                            .And(bind.BIND_STATUS != _BIND_STATUS_ACCEPTED)
+        return cls._bindInviteFor((bind.RESOURCE_ID == Parameter("resourceID"))
+                            .And(bind.BIND_MODE != _BIND_MODE_OWN)
                             )
 
 
     @inlineCallbacks
-    def asInvited(self):
+    def sharingInvites(self):
         """
-        Retrieve all the versions of this L{CommonHomeChild} as it is invited to
-        everyone.
+        Retrieve the list of all L{SharingInvitation} for this L{CommonHomeChild}, irrespective of mode.
 
-        @see: L{ICalendarHome.asInvited}
-
-        @return: L{CommonHomeChild} objects that represent this
-            L{CommonHomeChild} as a child of different L{CommonHome}s
-        @rtype: a L{Deferred} which fires with a L{list} of L{ICalendar}s.
+        @return: L{SharingInvitation} objects
+        @rtype: a L{Deferred} which fires with a L{list} of L{SharingInvitation}s.
         """
         if not self.owned():
             returnValue([])
 
-        rows = yield self._invitedBindForResourceID.on(
-            self._txn, resourceID=self._resourceID, homeID=self._home._resourceID,
+        # get all accepted binds
+        acceptedRows = yield self._sharedInvitationBindForResourceID.on(
+            self._txn, resourceID=self._resourceID, homeID=self._home._resourceID
         )
-        cls = self.__class__ # for ease of grepping...
 
         result = []
-        for bindMode, homeID, resourceID, resourceName, bindStatus, bindMessage in rows: #@UnusedVariable
-            # TODO: this could all be issued in parallel; no need to serialize
-            # the loop.
-            new = cls(
-                home=(yield self._txn.homeWithResourceID(self._home._homeType, homeID)),
-                name=resourceName, resourceID=self._resourceID,
-                mode=bindMode, status=bindStatus,
-                message=bindMessage, ownerHome=self._home
+        for homeUID, homeRID, resourceID, resourceName, bindMode, bindStatus, bindMessage in acceptedRows: #@UnusedVariable
+            invite = SharingInvitation(
+                self._home.name(),
+                self._home._resourceID,
+                homeUID,
+                homeRID,
+                resourceID,
+                resourceName,
+                bindMode,
+                bindStatus,
+                bindMessage,
             )
-            yield new.initFromStore()
-            result.append(new)
+            result.append(invite)
         returnValue(result)
 
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130717/a4969187/attachment-0001.html>


More information about the calendarserver-changes mailing list