[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