[CalendarServer-changes] [11544] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Mon Jul 22 12:56:16 PDT 2013
Revision: 11544
http://trac.calendarserver.org//changeset/11544
Author: cdaboo at apple.com
Date: 2013-07-22 12:56:16 -0700 (Mon, 22 Jul 2013)
Log Message:
-----------
Fix scaling of asShared() by using a new method that is O(1) wrt number of sharees. Also, handle the case of shares where the
sharer's directory record is partially or fully disabled. Another side-effect: propstore caching is now turned off if SQL
statement caching is disabled.
Modified Paths:
--------------
CalendarServer/trunk/twistedcaldav/sharing.py
CalendarServer/trunk/twistedcaldav/storebridge.py
CalendarServer/trunk/twistedcaldav/test/test_sharing.py
CalendarServer/trunk/twistedcaldav/test/test_wrapping.py
CalendarServer/trunk/txdav/base/propertystore/sql.py
CalendarServer/trunk/txdav/base/propertystore/test/test_sql.py
CalendarServer/trunk/txdav/caldav/datastore/test/common.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py
CalendarServer/trunk/txdav/caldav/icalendarstore.py
CalendarServer/trunk/txdav/carddav/datastore/sql.py
CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py
CalendarServer/trunk/txdav/common/datastore/file.py
CalendarServer/trunk/txdav/common/datastore/sql.py
Modified: CalendarServer/trunk/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/sharing.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/twistedcaldav/sharing.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -29,6 +29,8 @@
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, \
@@ -83,14 +85,13 @@
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
if self.isShared():
- yield self.validateInvites(request)
- invitations = yield self._allInvitations()
+ invitations = yield self.validateInvites(request)
returnValue(customxml.Invite(
*[invitePropertyElement(invitation) for invitation in invitations]
))
@@ -98,20 +99,20 @@
# See if it is on the sharee calendar
if self.isShareeResource():
original = (yield request.locateResource(self._share.url()))
- yield original.validateInvites(request)
- invitations = yield original._allInvitations()
+ if original is not None:
+ invitations = yield original.validateInvites(request)
- ownerPrincipal = (yield original.ownerPrincipal(request))
- owner = ownerPrincipal.principalURL()
- ownerCN = ownerPrincipal.displayName()
+ ownerPrincipal = (yield original.ownerPrincipal(request))
+ owner = ownerPrincipal.principalURL()
+ ownerCN = ownerPrincipal.displayName()
- returnValue(customxml.Invite(
- customxml.Organizer(
- element.HRef.fromString(owner),
- customxml.CommonName.fromString(ownerCN),
- ),
- *[invitePropertyElement(invitation, includeUID=False) for invitation in invitations]
- ))
+ returnValue(customxml.Invite(
+ customxml.Organizer(
+ element.HRef.fromString(owner),
+ customxml.CommonName.fromString(ownerCN),
+ ),
+ *[invitePropertyElement(invitation, includeUID=False) for invitation in invitations]
+ ))
returnValue(None)
@@ -157,8 +158,8 @@
))
# Only certain states are owner 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
@@ -328,7 +329,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
@@ -475,11 +476,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):
@@ -539,7 +540,7 @@
@inlineCallbacks
- def _createInvitation(self, shareeUID, access, summary,):
+ def _createInvitation(self, shareeUID, mode, summary,):
"""
Create a new homeChild and wrap it in an Invitation
"""
@@ -549,45 +550,41 @@
shareeHome = yield self._newStoreObject._txn.addressbookHomeWithUID(shareeUID, create=True)
shareUID = yield self._newStoreObject.shareWith(shareeHome,
- mode=invitationAccessToBindModeMap[access],
+ mode=mode,
status=_BIND_STATUS_INVITED,
message=summary)
shareeStoreObject = yield shareeHome.invitedObjectWithShareUID(shareUID)
- invitation = Invitation(shareeStoreObject)
+ invitation = SharingInvitation.fromCommonHomeChild(shareeStoreObject)
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]
- yield self._newStoreObject.updateShare(invitation._shareeStoreObject, mode=mode, status=status, message=summary)
+ def _updateInvitation(self, invitation, mode=None, status=None, summary=None):
+ yield self._newStoreObject.updateShareFromSharingInvitation(invitation, 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)
@inlineCallbacks
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.
"""
if not self.exists():
returnValue([])
- #TODO: Cache
- if True: # not hasattr(self, "_invitations"):
+ invitations = yield self._newStoreObject.sharingInvites()
- acceptedHomeChildren = yield self._newStoreObject.asShared()
- # remove direct shares (it might be OK not to remove these, but that would be different from legacy code)
- indirectAccceptedHomeChildren = [homeChild for homeChild in acceptedHomeChildren
- if homeChild.shareMode() != _BIND_MODE_DIRECT]
- invitedHomeChildren = (yield self._newStoreObject.asInvited()) + indirectAccceptedHomeChildren
+ # remove direct shares as those are not "real" invitations
+ invitations = filter(lambda x: x.mode() != _BIND_MODE_DIRECT, invitations)
- self._invitations = sorted([Invitation(homeChild) for homeChild in invitedHomeChildren],
- key=lambda invitation: invitation.shareeUID())
+ invitations.sort(key=lambda invitation: invitation.shareeUID())
- returnValue(self._invitations)
+ returnValue(invitations)
@inlineCallbacks
@@ -627,11 +624,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)
@@ -664,7 +661,7 @@
# Remove any shared calendar or address book
sharee = self.principalForUID(invitation.shareeUID())
if sharee:
- previousInvitationState = invitation.state()
+ previousInvitationStatus = invitation.status()
if self.isCalendarCollection():
shareeHomeResource = yield sharee.calendarHome(request)
displayName = yield shareeHomeResource.removeShareByUID(request, invitation.uid())
@@ -674,13 +671,13 @@
displayName = None
# 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 previousInvitationState != "ACCEPTED":
+ if previousInvitationStatus != _BIND_STATUS_ACCEPTED:
yield self.removeInviteNotification(invitation, request)
else:
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._shareeStoreObject.viewerHome())
+ yield self._newStoreObject.unshareWithUID(invitation.shareeUID())
returnValue(True)
@@ -719,7 +716,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()}
@@ -729,8 +726,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),
),
@@ -877,7 +874,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 = self.isShared()
@@ -974,28 +972,21 @@
}
-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,
@@ -1004,35 +995,6 @@
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, shareeStoreObject):
- self._shareeStoreObject = shareeStoreObject
-
-
- def uid(self):
- return self._shareeStoreObject.shareUID()
-
-
- def shareeUID(self):
- return self._shareeStoreObject.viewerHome().uid()
-
-
- def access(self):
- return invitationAccessFromBindModeMap.get(self._shareeStoreObject.shareMode())
-
-
- def state(self):
- return invitationStateFromBindStatusMap.get(self._shareeStoreObject.shareStatus())
-
-
- def summary(self):
- return self._shareeStoreObject.shareMessage()
-
-
-
class SharedHomeMixin(LinkFollowerMixIn):
"""
A mix-in for calendar/addressbook homes that defines the operations for
@@ -1074,33 +1036,42 @@
if not storeObject or storeObject.owned():
returnValue(None)
- # get the shared object's URL
+ # Get the shared object's URL - we may need to fake this if the sharer principal is missing or disabled
+ url = None
owner = self.principalForUID(storeObject.ownerHome().uid())
+ from twistedcaldav.directory.principal import DirectoryCalendarPrincipalResource
+ if isinstance(owner, DirectoryCalendarPrincipalResource):
- if not request:
- # FIXEME: Fake up a request that can be used to get the owner home resource
- class _FakeRequest(object):
- pass
- fakeRequest = _FakeRequest()
- setattr(fakeRequest, TRANSACTION_KEY, self._newStoreHome._txn)
- request = fakeRequest
+ if not request:
+ # FIXEME: Fake up a request that can be used to get the owner home resource
+ class _FakeRequest(object):
+ pass
+ fakeRequest = _FakeRequest()
+ setattr(fakeRequest, TRANSACTION_KEY, self._newStoreHome._txn)
+ request = fakeRequest
- if self._newStoreHome._homeType == ECALENDARTYPE:
- ownerHomeCollection = yield owner.calendarHome(request)
- elif self._newStoreHome._homeType == EADDRESSBOOKTYPE:
- ownerHomeCollection = yield owner.addressBookHome(request)
+ if self._newStoreHome._homeType == ECALENDARTYPE:
+ ownerHomeCollection = yield owner.calendarHome(request)
+ elif self._newStoreHome._homeType == EADDRESSBOOKTYPE:
+ ownerHomeCollection = yield owner.addressBookHome(request)
+ if ownerHomeCollection is not None:
+ url = ownerHomeCollection.url()
+
+ if url is None:
+ url = "/calendars/__uids__/%s/" % (storeObject.ownerHome().uid(),)
+
ownerHomeChild = yield storeObject.ownerHome().childWithID(storeObject._resourceID)
if ownerHomeChild:
assert ownerHomeChild != storeObject
- url = joinURL(ownerHomeCollection.url(), ownerHomeChild.name())
+ url = joinURL(url, ownerHomeChild.name())
share = Share(shareeStoreObject=storeObject, ownerStoreObject=ownerHomeChild, url=url)
else:
for ownerHomeChild in (yield storeObject.ownerHome().children()):
if ownerHomeChild.owned():
sharedGroup = yield ownerHomeChild.objectResourceWithID(storeObject._resourceID)
if sharedGroup:
- url = joinURL(ownerHomeCollection.url(), ownerHomeChild.name(), sharedGroup.name())
+ url = joinURL(url, ownerHomeChild.name(), sharedGroup.name())
share = Share(shareeStoreObject=storeObject, ownerStoreObject=sharedGroup, url=url)
break
@@ -1133,8 +1104,7 @@
oldShare = yield self._shareForUID(inviteUID, request)
# Send the invite reply then add the link
- yield self._changeShare(request, "ACCEPTED", hostUrl, inviteUID,
- displayname)
+ yield self._changeShare(request, _BIND_STATUS_ACCEPTED, hostUrl, inviteUID, displayname)
if oldShare:
share = oldShare
else:
@@ -1145,8 +1115,7 @@
ownerStoreObject=sharedResource._newStoreObject,
url=hostUrl)
- response = yield self._acceptShare(request, not oldShare, share,
- displayname)
+ response = yield self._acceptShare(request, not oldShare, share, displayname)
returnValue(response)
@@ -1172,8 +1141,7 @@
ownerStoreObject=sharedCollection._newStoreObject,
url=hostUrl)
- response = yield self._acceptShare(request, not oldShare, share,
- displayname)
+ response = yield self._acceptShare(request, not oldShare, share, displayname)
returnValue(response)
@@ -1309,12 +1277,12 @@
# 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, processed=True)
returnValue(Response(code=responsecode.NO_CONTENT))
@inlineCallbacks
- def _changeShare(self, request, state, hostUrl, replytoUID, displayname=None):
+ def _changeShare(self, request, state, hostUrl, replytoUID, displayname=None, processed=False):
"""
Accept or decline an invite to a shared collection.
"""
@@ -1323,6 +1291,10 @@
ownerPrincipalUID = ownerPrincipal.principalUID()
sharedResource = (yield request.locateResource(hostUrl))
if sharedResource is None:
+ # FIXME: have to return here rather than raise to allow removal of a share for a sharer
+ # whose principal is no longer valid yet still exists in the store. Really we need to get rid of
+ # locateResource calls and just do everything via store objects.
+ returnValue(None)
# Original shared collection is gone - nothing we can do except ignore it
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
@@ -1331,7 +1303,8 @@
))
# Change the record
- yield sharedResource.changeUserInviteState(request, replytoUID, ownerPrincipalUID, state, displayname)
+ if not processed:
+ yield sharedResource.changeUserInviteState(request, replytoUID, ownerPrincipalUID, state, displayname)
yield self.sendReply(request, ownerPrincipal, sharedResource, state, hostUrl, replytoUID, displayname)
@@ -1341,6 +1314,12 @@
# Locate notifications collection for owner
owner = (yield sharedResource.ownerPrincipal(request))
+ if owner is None:
+ # FIXME: have to return here rather than raise to allow removal of a share for a sharer
+ # whose principal is no longer valid yet still exists in the store. Really we need to get rid of
+ # locateResource calls and just do everything via store objects.
+ returnValue(None)
+
notificationResource = (yield request.locateResource(owner.notificationURL()))
notifications = notificationResource._newStoreNotifications
@@ -1364,7 +1343,7 @@
*(
(
element.HRef.fromString(cua),
- invitationStatusMapToXML[state](),
+ invitationBindStatusToXMLMap[state](),
customxml.HostURL(
element.HRef.fromString(hostUrl),
),
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -62,7 +62,7 @@
from txdav.carddav.iaddressbookstore import GroupWithUnsharedAddressNotAllowedError, \
GroupForSharedAddressBookDeleteNotAllowedError, SharedGroupDeleteNotAllowedError
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, \
TooManyObjectResourcesError, ObjectResourceTooBigError, \
InvalidObjectResourceError, ObjectResourceNameNotAllowedError, \
@@ -1628,23 +1628,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(
@@ -1923,7 +1928,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.
@@ -1939,10 +1944,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
@@ -1958,9 +1963,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")
@@ -1969,19 +1974,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/trunk/twistedcaldav/test/test_sharing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_sharing.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/twistedcaldav/test/test_sharing.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -24,6 +24,7 @@
from twistedcaldav import customxml
from twistedcaldav import sharing
from twistedcaldav.config import config
+from twistedcaldav.directory.principal import DirectoryCalendarPrincipalResource
from twistedcaldav.resource import CalDAVResource
from twistedcaldav.sharing import WikiDirectoryService
from twistedcaldav.test.test_cache import StubResponseCacheResource
@@ -71,7 +72,7 @@
-class FakePrincipal(object):
+class FakePrincipal(DirectoryCalendarPrincipalResource):
def __init__(self, cuaddr, test):
if cuaddr.startswith("mailto:"):
Modified: CalendarServer/trunk/twistedcaldav/test/test_wrapping.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_wrapping.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/twistedcaldav/test/test_wrapping.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -380,7 +380,7 @@
if not hasattr(self._sqlCalendarStore, "_dropbox_ok"):
self._sqlCalendarStore._dropbox_ok = False
self.patch(self._sqlCalendarStore, "_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/trunk/txdav/base/propertystore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/base/propertystore/sql.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/txdav/base/propertystore/sql.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -86,15 +86,15 @@
def _cache_user_props(uid):
# First check whether uid already has a valid cached entry
- valid_cached_users = yield self._cacher.get(str(self._resourceID))
- if valid_cached_users is None:
- valid_cached_users = set()
+ rows = None
+ if self._cacher is not None:
+ valid_cached_users = yield self._cacher.get(str(self._resourceID))
+ if valid_cached_users is None:
+ valid_cached_users = set()
- # Fetch cached user data if valid and present
- if uid in valid_cached_users:
- rows = yield self._cacher.get(self._cacheToken(uid))
- else:
- rows = None
+ # Fetch cached user data if valid and present
+ if uid in valid_cached_users:
+ rows = yield self._cacher.get(self._cacheToken(uid))
# If no cached data, fetch from SQL DB and cache
if rows is None:
@@ -103,11 +103,12 @@
resourceID=self._resourceID,
viewerID=uid,
)
- yield self._cacher.set(self._cacheToken(uid), rows if rows is not None else ())
+ if self._cacher is not None:
+ yield self._cacher.set(self._cacheToken(uid), rows if rows is not None else ())
- # Mark this uid as valid
- valid_cached_users.add(uid)
- yield self._cacher.set(str(self._resourceID), valid_cached_users)
+ # Mark this uid as valid
+ valid_cached_users.add(uid)
+ yield self._cacher.set(str(self._resourceID), valid_cached_users)
for name, value in rows:
self._cached[(name, uid)] = value
@@ -129,6 +130,8 @@
super(PropertyStore, self).__init__(defaultuser, shareUser)
self._txn = txn
self._resourceID = resourceID
+ if not self._txn.store().queryCachingEnabled():
+ self._cacher = None
self._cached = {}
if not created:
yield self._refresh(txn)
@@ -305,7 +308,8 @@
yield self._insertQuery.on(
txn, resourceID=self._resourceID, value=value_str,
name=key_str, uid=uid)
- self._cacher.delete(self._cacheToken(uid))
+ if self._cacher is not None:
+ self._cacher.delete(self._cacheToken(uid))
# Call the registered notification callback - we need to do this as a preCommit since it involves
# a bunch of deferred operations, but this propstore api is not deferred. preCommit will execute
@@ -337,7 +341,8 @@
resourceID=self._resourceID,
name=key_str, uid=uid
)
- self._cacher.delete(self._cacheToken(uid))
+ if self._cacher is not None:
+ self._cacher.delete(self._cacheToken(uid))
# Call the registered notification callback - we need to do this as a preCommit since it involves
# a bunch of deferred operations, but this propstore api is not deferred. preCommit will execute
@@ -368,7 +373,8 @@
yield self._deleteResourceQuery.on(self._txn, resourceID=self._resourceID)
# Invalidate entire set of cached per-user data for this resource
- self._cacher.delete(str(self._resourceID))
+ if self._cacher is not None:
+ self._cacher.delete(str(self._resourceID))
@inlineCallbacks
@@ -392,5 +398,6 @@
# Invalidate entire set of cached per-user data for this resource and reload
self._cached = {}
- self._cacher.delete(str(self._resourceID))
+ if self._cacher is not None:
+ self._cacher.delete(str(self._resourceID))
yield self._refresh(self._txn)
Modified: CalendarServer/trunk/txdav/base/propertystore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/base/propertystore/test/test_sql.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/txdav/base/propertystore/test/test_sql.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -269,5 +269,38 @@
self.assertEqual(len(store1_user1._cached), 0)
self.assertFalse("SQL.props:10/user01" in store1_user1._cacher._memcacheProtocol._cache)
+
+ @inlineCallbacks
+ def test_cacher_off(self):
+ """
+ Test that properties can still be read and written when the cacher is disabled.
+ """
+
+ self.patch(self.store, "queryCacher", None)
+
+ # Existing store - add a normal property
+ self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
+ store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
+ self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
+
+ pname1 = propertyName("dummy1")
+ pvalue1 = propertyValue("*")
+
+ yield store1_user1.__setitem__(pname1, pvalue1)
+ self.assertEqual(store1_user1[pname1], pvalue1)
+
+ self.assertEqual(len(store1_user1._cached), 1)
+ self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
+
+ yield self._txn.commit()
+ self._txn = self.store.newTransaction()
+
+ # Existing store - check a normal property
+ self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
+ store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
+ self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
+ self.assertEqual(store1_user1[pname1], pvalue1)
+
+
if PropertyStore is None:
PropertyStoreTest.skip = importErrorMessage
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/common.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/common.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -988,9 +988,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
@@ -1007,7 +1007,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)
@@ -1027,7 +1027,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)
@@ -1047,7 +1047,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)
@@ -1062,24 +1062,23 @@
@inlineCallbacks
- def test_asShared(self):
+ def test_sharingInvites(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()
- # It's not shared yet; make sure asShared doesn't include owner version.
+ sharedBefore = yield cal.sharingInvites()
+ # It's not shared yet; make sure sharingInvites 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/trunk/txdav/caldav/datastore/test/test_file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -467,7 +467,7 @@
test_shareAgainChangesMode = test_shareWith
test_unshareWith = test_shareWith
test_unshareWithInDifferentTransaction = test_shareWith
- test_asShared = test_shareWith
+ test_sharingInvites = test_shareWith
test_unshareSharerSide = test_shareWith
test_unshareShareeSide = test_shareWith
test_sharedNotifierID = test_shareWith
Modified: CalendarServer/trunk/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/icalendarstore.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/txdav/caldav/icalendarstore.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -385,15 +385,12 @@
Low-level query to gather names for calendarObjectsSinceToken.
"""
- def asShared(): #@NoSelf
+ def sharingInvites(): #@NoSelf
"""
- Get a view of this L{ICalendar} as present in everyone's calendar home
- except for its owner's.
+ Retrieve the list of all L{SharingInvitation} for this L{CommonHomeChild}, irrespective of mode.
- @return: a L{Deferred} which fires with a list of L{ICalendar}s, each
- L{ICalendar} as seen by its respective sharee. This means that its
- C{shareMode} will be something other than L{_BIND_MODE_OWN}, and its
- L{ICalendar.viewerCalendarHome} will return the home of the sharee.
+ @return: L{SharingInvitation} objects
+ @rtype: a L{Deferred} which fires with a L{list} of L{SharingInvitation}s.
"""
# FIXME: This module should define it's own constants and this
Modified: CalendarServer/trunk/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/sql.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/txdav/carddav/datastore/sql.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -48,7 +48,7 @@
IAddressBookObject, GroupForSharedAddressBookDeleteNotAllowedError, \
GroupWithUnsharedAddressNotAllowedError, SharedGroupDeleteNotAllowedError
from txdav.common.datastore.sql import CommonHome, CommonHomeChild, \
- CommonObjectResource, EADDRESSBOOKTYPE, SharingMixIn
+ CommonObjectResource, EADDRESSBOOKTYPE, SharingMixIn, SharingInvitation
from txdav.common.datastore.sql_legacy import PostgresLegacyABIndexEmulator
from txdav.common.datastore.sql_tables import _ABO_KIND_PERSON, \
_ABO_KIND_GROUP, _ABO_KIND_RESOURCE, _ABO_KIND_LOCATION, schema, \
@@ -101,7 +101,7 @@
@classproperty
- def _resourceIDAndHomeResourceIDFromOwnerQuery(cls): #@NoSelf
+ def _resourceIDAndHomeResourceIDFromOwnerQuery(cls): #@NoSelf
home = cls._homeSchema
return Select([home.RESOURCE_ID, home.ADDRESSBOOK_PROPERTY_STORE_ID],
From=home, Where=home.OWNER_UID == Parameter("ownerUID"))
@@ -264,7 +264,7 @@
@classproperty
- def _syncTokenQuery(cls): #@NoSelf
+ def _syncTokenQuery(cls): #@NoSelf
"""
DAL Select statement to find the sync token.
"""
@@ -308,7 +308,7 @@
@classproperty
- def _changesQuery(cls): #@NoSelf
+ def _changesQuery(cls): #@NoSelf
rev = cls._revisionsSchema
return Select(
[rev.COLLECTION_NAME,
@@ -370,12 +370,14 @@
returnValue(bool(sharedRows))
+
@inlineCallbacks
def _initIsShared(self):
isShared = yield self._isSharedOrInvited()
self.setShared(isShared)
+
class AddressBook(CommonHomeChild, AddressBookSharingMixIn):
"""
SQL-based implementation of L{IAddressBook}.
@@ -430,10 +432,21 @@
addressbookObjectsSinceToken = CommonHomeChild.objectResourcesSinceToken
- def shareeAddressBookName(self):
- return self._home.shareeAddressBookName()
+ def shareeName(self):
+ """
+ The sharee's name for a shared address book is the sharer's home ownerUID.
+ """
+ return self.ownerHome().shareeAddressBookName()
+ def bindNameIsResourceName(self):
+ """
+ For shared address books the resource name of an accepted share is not the same as the name
+ in the bind table.
+ """
+ return False
+
+
@inlineCallbacks
def _loadPropertyStore(self, props=None):
if props is None:
@@ -568,7 +581,7 @@
Where=obj.ADDRESSBOOK_HOME_RESOURCE_ID == Parameter("addressbookResourceID"),)
- def _fullySharedAddressBookGroupRow(self): #@NoSelf
+ def _fullySharedAddressBookGroupRow(self): #@NoSelf
return [
self._resourceID, # obj.ADDRESSBOOK_HOME_RESOURCE_ID,
self._resourceID, # obj.RESOURCE_ID,
@@ -593,7 +606,7 @@
@inlineCallbacks
def _fullySharedAddressBookGroupComponent(self):
- n = self.ownerHome().shareeAddressBookName()
+ n = self.shareeName()
fn = n
uid = self.name()
@@ -661,7 +674,7 @@
)
# get ownerHomeIDs
for dataRow in dataRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount] #@UnusedVariable
ownerHome = yield home.ownerHomeWithChildID(resourceID)
ownerHomeToDataRowMap[ownerHome] = dataRow
@@ -670,7 +683,7 @@
home._txn, homeID=home._resourceID
)
for groupBindRow in groupBindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
ownerAddressBookID = yield AddressBookObject.ownerAddressBookIDFromGroupID(home._txn, resourceID)
ownerHome = yield home.ownerHomeWithChildID(ownerAddressBookID)
if ownerHome not in ownerHomeToDataRowMap:
@@ -693,7 +706,7 @@
# Create the actual objects merging in properties
for ownerHome, dataRow in ownerHomeToDataRowMap.iteritems():
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount] #@UnusedVariable
additionalBind = dataRow[cls.bindColumnCount:cls.bindColumnCount + len(cls.additionalBindColumns())]
metadata = dataRow[cls.bindColumnCount + len(cls.additionalBindColumns()):]
@@ -785,7 +798,7 @@
if not rows:
returnValue(None)
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage, ownerAddressBookID, cachedBindStatus = rows[0] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage, ownerAddressBookID, cachedBindStatus = rows[0] #@UnusedVariable
# if wrong status, exit here. Item is in queryCache
if (cachedBindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
@@ -795,7 +808,7 @@
ownerAddressBook = ownerHome.addressbook()
child = cls(
home=home,
- name=ownerAddressBook.shareeAddressBookName(), resourceID=ownerAddressBookID,
+ name=ownerAddressBook.shareeName(), resourceID=ownerAddressBookID,
mode=bindMode, status=bindStatus,
revision=bindRevision,
message=bindMessage, ownerHome=ownerHome,
@@ -821,7 +834,7 @@
"""
bindRows = yield cls._bindForNameAndHomeID.on(home._txn, name=name, homeID=home._resourceID)
if bindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = bindRows[0] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = bindRows[0] #@UnusedVariable
if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
returnValue(None)
@@ -837,7 +850,7 @@
home._txn, name=name, homeID=home._resourceID
)
if groupBindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRows[0] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRows[0] #@UnusedVariable
if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
returnValue(None)
@@ -876,7 +889,7 @@
home._txn, resourceID=resourceID, homeID=home._resourceID
)
if bindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = bindRows[0] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = bindRows[0] #@UnusedVariable
if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
returnValue(None)
@@ -890,7 +903,7 @@
home._txn, homeID=home._resourceID, addressbookID=resourceID
)
if groupBindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRows[0] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRows[0] #@UnusedVariable
if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
returnValue(None)
@@ -929,7 +942,7 @@
home._txn, homeID=home._resourceID
)
for row in rows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:cls.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:cls.bindColumnCount] #@UnusedVariable
ownerHome = yield home._txn.homeWithResourceID(home._homeType, resourceID, create=True)
names |= set([ownerHome.shareeAddressBookName()])
@@ -937,7 +950,7 @@
home._txn, homeID=home._resourceID
)
for groupRow in groupRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
ownerAddressBookID = yield AddressBookObject.ownerAddressBookIDFromGroupID(home._txn, resourceID)
ownerHome = yield home._txn.homeWithResourceID(home._homeType, ownerAddressBookID, create=True)
names |= set([ownerHome.shareeAddressBookName()])
@@ -1008,7 +1021,7 @@
readWriteGroupIDs = []
readOnlyGroupIDs = []
for groupBindRow in groupBindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
if bindMode == _BIND_MODE_WRITE:
readWriteGroupIDs.append(resourceID)
else:
@@ -1056,7 +1069,7 @@
readWriteGroupIDs = []
readOnlyGroupIDs = []
for groupBindRow in groupBindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
if bindMode == _BIND_MODE_WRITE:
readWriteGroupIDs.append(resourceID)
else:
@@ -1177,60 +1190,6 @@
@inlineCallbacks
- def asShared(self):
- """
- Retrieve all the versions of this L{AddressBook} as it is shared to
- everyone.
-
- @see: L{IAddressBookHome.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.
- """
- result = []
- if self.owned():
- # get all accepted shared binds
- bindRows = yield self._sharedBindForResourceID.on(
- self._txn, resourceID=self._resourceID, homeID=self._home._resourceID
- )
- for bindRow in bindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = bindRow[:self.bindColumnCount] #@UnusedVariable
- home = yield self._txn.homeWithResourceID(self._home._homeType, homeID, create=True)
- new = yield home.childWithName(self.shareeAddressBookName())
- result.append(new)
-
- returnValue(result)
-
-
- @inlineCallbacks
- def asInvited(self):
- """
- Retrieve all the versions of this L{CommonHomeChild} as it is invited to
- everyone.
-
- @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.
- """
- result = []
- if self.owned():
- # get all accepted shared binds
- bindRows = yield self._unacceptedBindForResourceID.on(
- self._txn, resourceID=self._resourceID
- )
- for bindRow in bindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = bindRow[:self.bindColumnCount] #@UnusedVariable
- home = yield self._txn.homeWithResourceID(self._home._homeType, homeID, create=True)
- new = yield self.objectWithName(home, self.shareeAddressBookName(), accepted=False)
- result.append(new)
-
- returnValue(result)
-
-
- @inlineCallbacks
def shareWith(self, shareeHome, mode, status=None, message=None):
"""
call super and set isShared = True
@@ -1253,7 +1212,7 @@
@return: a L{Deferred} which will fire with the previous shareUID
"""
- sharedAddressBook = yield shareeHome.addressbookWithName(self.shareeAddressBookName())
+ sharedAddressBook = yield shareeHome.addressbookWithName(self.shareeName())
if sharedAddressBook:
acceptedBindCount = 1 if sharedAddressBook.fullyShared() else 0
@@ -1279,7 +1238,7 @@
deletedBindName = deletedBindNameRows[0][0]
queryCacher = self._txn._queryCacher
if queryCacher:
- cacheKey = queryCacher.keyForObjectWithName(shareeHome._resourceID, self.shareeAddressBookName())
+ cacheKey = queryCacher.keyForObjectWithName(shareeHome._resourceID, self.shareeName())
queryCacher.invalidateAfterCommit(self._txn, cacheKey)
else:
deletedBindName = None
@@ -1293,6 +1252,7 @@
implements(IAddressBookObject)
+ _homeSchema = schema.ADDRESSBOOK_HOME
_objectSchema = schema.ADDRESSBOOK_OBJECT
_bindSchema = schema.SHARED_GROUP_BIND
@@ -1301,7 +1261,7 @@
#_homeChildMetaDataSchema = schema.ADDRESSBOOK_OBJECT
- def __init__(self, addressbook, name, uid, resourceID=None, options=None): #@UnusedVariable
+ def __init__(self, addressbook, name, uid, resourceID=None, options=None): #@UnusedVariable
self._kind = None
self._ownerAddressBookResourceID = None
@@ -1436,7 +1396,7 @@
@classproperty
- def _allColumnsWithResourceID(cls): #@NoSelf
+ def _allColumnsWithResourceID(cls): #@NoSelf
obj = cls._objectSchema
return Select(
cls._allColumns, From=obj,
@@ -1521,7 +1481,7 @@
if groupBindRows:
groupBindRow = groupBindRows[0]
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
self._bindMode = bindMode
self._bindStatus = bindStatus
self._bindMessage = bindMessage
@@ -1537,7 +1497,7 @@
@classproperty
- def _allColumns(cls): #@NoSelf
+ def _allColumns(cls): #@NoSelf
"""
Full set of columns in the object table that need to be loaded to
initialize the object resource state.
@@ -1651,7 +1611,7 @@
self.validAddressDataCheck(component, inserting)
- def validAddressDataCheck(self, component, inserting): #@UnusedVariable
+ def validAddressDataCheck(self, component, inserting): #@UnusedVariable
"""
Check that the calendar data is valid iCalendar.
@return: tuple: (True/False if the calendar data is valid,
@@ -1714,7 +1674,9 @@
if self.owned():
# update revision table of the sharee group address book
if self._kind == _ABO_KIND_GROUP: # optimization
- for shareeAddressBook in (yield self.asShared()):
+ invites = yield self.sharingInvites()
+ for invite in invites:
+ shareeAddressBook = (yield self._txn.homeWithResourceID(self.addressbook()._home._homeType, invite.shareeHomeID()))
yield self._changeAddressBookRevision(shareeAddressBook, inserting)
# one is enough because all have the same resourceID
break
@@ -1757,7 +1719,7 @@
@classproperty
- def _insertABObject(cls): #@NoSelf
+ def _insertABObject(cls): #@NoSelf
"""
DAL statement to create an addressbook object with all default values.
"""
@@ -1777,7 +1739,7 @@
@inlineCallbacks
- def updateDatabase(self, component, expand_until=None, reCreate=False, #@UnusedVariable
+ def updateDatabase(self, component, expand_until=None, reCreate=False, #@UnusedVariable
inserting=False):
"""
Update the database tables for the new data being written.
@@ -2099,7 +2061,7 @@
# same as CommonHomeChild._childrenAndMetadataForHomeID() w/o metadata join
@classproperty
- def _childrenAndMetadataForHomeID(cls): #@NoSelf
+ def _childrenAndMetadataForHomeID(cls): #@NoSelf
bind = cls._bindSchema
child = cls._objectSchema
columns = cls.bindColumns() + cls.additionalBindColumns() + cls.metadataColumns()
@@ -2115,68 +2077,8 @@
return self.addressbook().notifyChanged()
- @inlineCallbacks
- def asShared(self):
- """
- Retrieve all the versions of this L{AddressBookObject} as it is shared to
- everyone.
-
- @see: L{IAddressBookHome.asShared}
-
- @return: L{AddressBookObject} objects that represent this
- L{AddressBookObject} as a child of different L{AddressBooks}s
- in different L{CommonHome}s
- @rtype: a L{Deferred} which fires with a L{list} of L{AddressBookObject}s.
- """
- result = []
- if self.owned():
- # get all accepted shared binds
- groupBindRows = yield self._sharedBindForResourceID.on(
- self._txn, resourceID=self._resourceID, homeID=self._home._resourceID
- )
- for groupBindRow in groupBindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:self.bindColumnCount] #@UnusedVariable
- home = yield self._txn.homeWithResourceID(self._home._homeType, homeID, create=True)
- addressbook = yield home.childWithName(self._home.shareeAddressBookName())
- new = yield addressbook.objectResourceWithID(resourceID)
- result.append(new)
-
- returnValue(result)
-
-
- @inlineCallbacks
- def asInvited(self):
- """
- Retrieve all the versions of this L{AddressBookObject} as it is shared to
- everyone.
-
- @see: L{ICalendarHome.asShared}
-
- @return: L{AddressBookObject} objects that represent this
- L{AddressBookObject} as a child of different L{AddressBooks}s
- in different L{CommonHome}s
- @rtype: a L{Deferred} which fires with a L{list} of L{AddressBookObject}s.
- """
- result = []
- if self.owned():
- # get all accepted shared binds
- groupBindRows = yield self._unacceptedBindForResourceID.on(
- self._txn, resourceID=self._resourceID
- )
- for groupBindRow in groupBindRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:self.bindColumnCount] #@UnusedVariable
- home = yield self._txn.homeWithResourceID(self._home._homeType, homeID, create=True)
- addressbook = yield home.childWithName(self._home.shareeAddressBookName())
- if not addressbook:
- addressbook = yield AddressBook.objectWithName(home, self._home.shareeAddressBookName(), accepted=False)
- new = yield AddressBookObject.objectWithID(addressbook, resourceID) # avoids object cache
- result.append(new)
-
- returnValue(result)
-
-
@classproperty
- def _addressbookIDForResourceID(cls): #@NoSelf
+ def _addressbookIDForResourceID(cls): #@NoSelf
obj = cls._objectSchema
return Select([obj.PARENT_RESOURCE_ID],
From=obj,
@@ -2192,6 +2094,40 @@
@inlineCallbacks
+ def sharingInvites(self):
+ """
+ Retrieve the list of all L{SharingInvitation} for this L{CommonHomeChild}, irrespective of mode.
+
+ @return: L{SharingInvitation} objects
+ @rtype: a L{Deferred} which fires with a L{list} of L{SharingInvitation}s.
+ """
+ if not self.owned():
+ returnValue([])
+
+ # get all accepted binds
+ acceptedRows = yield self._sharedInvitationBindForResourceID.on(
+ self._txn, resourceID=self._resourceID, homeID=self.addressbook()._home._resourceID
+ )
+
+ result = []
+ for homeUID, homeRID, resourceID, resourceName, bindMode, bindStatus, bindMessage in acceptedRows: #@UnusedVariable
+ invite = SharingInvitation(
+ resourceName,
+ self.addressbook()._home.name(),
+ self.addressbook()._home._resourceID,
+ homeUID,
+ homeRID,
+ resourceID,
+ self.addressbook().shareeName(),
+ bindMode,
+ bindStatus,
+ bindMessage,
+ )
+ result.append(invite)
+ returnValue(result)
+
+
+ @inlineCallbacks
def unshare(self):
"""
Unshares a group, regardless of which "direction" it was shared.
@@ -2199,8 +2135,10 @@
if self._kind == _ABO_KIND_GROUP:
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(self.addressbook()._home._homeType, invite.shareeHomeID()))
+ (yield self.unshareWith(shareeHome))
else:
# This collection is shared to me
ownerAddressBook = self.addressbook().ownerHome().addressbook()
@@ -2222,7 +2160,7 @@
@return: a L{Deferred} which will fire with the previously-used name.
"""
- sharedAddressBook = yield shareeHome.addressbookWithName(self.addressbook().shareeAddressBookName())
+ sharedAddressBook = yield shareeHome.addressbookWithName(self.addressbook().shareeName())
if sharedAddressBook:
@@ -2235,7 +2173,7 @@
if acceptedBindCount == 1:
yield sharedAddressBook._deletedSyncToken(sharedRemoval=True)
- shareeHome._children.pop(self.addressbook().shareeAddressBookName(), None)
+ shareeHome._children.pop(self.addressbook().shareeName(), None)
shareeHome._children.pop(self.addressbook()._resourceID, None)
# Must send notification to ensure cache invalidation occurs
@@ -2250,7 +2188,7 @@
deletedBindName = deletedBindNameRows[0][0]
queryCacher = self._txn._queryCacher
if queryCacher:
- cacheKey = queryCacher.keyForObjectWithName(shareeHome._resourceID, self.addressbook().shareeAddressBookName())
+ cacheKey = queryCacher.keyForObjectWithName(shareeHome._resourceID, self.addressbook().shareeName())
queryCacher.invalidateAfterCommit(self._txn, cacheKey)
else:
deletedBindName = None
@@ -2303,7 +2241,7 @@
self._txn, resourceID=self._resourceID, homeID=shareeHome._resourceID
)
groupBindRow = groupBindRows[0]
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:self.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:self.bindColumnCount] #@UnusedVariable
if bindStatus == _BIND_STATUS_ACCEPTED:
group = yield shareeHome.objectWithShareUID(bindName)
else:
@@ -2438,7 +2376,7 @@
@classproperty
- def _acceptedBindForHomeIDAndAddressBookID(cls): #@NoSelf
+ def _acceptedBindForHomeIDAndAddressBookID(cls): #@NoSelf
bind = cls._bindSchema
abo = cls._objectSchema
return Select(
@@ -2452,7 +2390,7 @@
@classproperty
- def _unacceptedBindForHomeIDAndAddressBookID(cls): #@NoSelf
+ def _unacceptedBindForHomeIDAndAddressBookID(cls): #@NoSelf
bind = cls._bindSchema
abo = cls._objectSchema
return Select(
@@ -2466,7 +2404,7 @@
@classproperty
- def _bindForHomeIDAndAddressBookID(cls): #@NoSelf
+ def _bindForHomeIDAndAddressBookID(cls): #@NoSelf
bind = cls._bindSchema
abo = cls._objectSchema
return Select(
Modified: CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -466,7 +466,6 @@
can be retrieved with L{IAddressBookHome.addressbookWithName}.
"""
-
@testUnimplemented
def test_removeAddressBookWithName_exists(self):
"""
Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/txdav/common/datastore/file.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -1285,7 +1285,7 @@
@inlineCallbacks
- def asInvited(self):
+ def sharingInvites(self):
"""
Stub for interface-compliance tests.
"""
@@ -1293,16 +1293,7 @@
returnValue([])
- @inlineCallbacks
- def asShared(self):
- """
- Stub for interface-compliance tests.
- """
- yield None
- returnValue([])
-
-
class CommonObjectResource(FileMetaDataMixin, FancyEqMixin):
"""
@ivar _path: The path of the file on disk
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2013-07-22 19:25:12 UTC (rev 11543)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2013-07-22 19:56:16 UTC (rev 11544)
@@ -293,7 +293,17 @@
returnValue(self._dropbox_ok)
+ def queryCachingEnabled(self):
+ """
+ Indicate whether SQL statement query caching is enabled. Also controls whether propstore caching is done.
+ @return: C{True} if enabled, else C{False}
+ @rtype: C{bool}
+ """
+ return self.queryCacher is not None
+
+
+
class TransactionStatsCollector(object):
"""
Used to log each SQL query and statistics about that query during the course of a single transaction.
@@ -2620,6 +2630,125 @@
+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 properties/data when only this subset of information is needed.
+ """
+ def __init__(self, uid, owner_uid, owner_rid, sharee_uid, sharee_rid, resource_id, resource_name, mode, status, summary):
+ self._uid = uid
+ 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().uid(),
+ homeChild.ownerHome()._resourceID,
+ homeChild.viewerHome().uid(),
+ homeChild.viewerHome()._resourceID,
+ homeChild._resourceID,
+ homeChild.shareeName(),
+ homeChild.shareMode(),
+ homeChild.shareStatus(),
+ homeChild.shareMessage(),
+ )
+
+
+ def uid(self):
+ """
+ This maps to the resource name in the bind table, the "bind name". This is used as the "uid"
+ for invites, and is not necessarily the name of the resource as it appears in the collection.
+ """
+ return self._uid
+
+
+ def ownerUID(self):
+ """
+ The ownerUID of the sharer.
+ """
+ return self._owner_uid
+
+
+ def ownerHomeID(self):
+ """
+ The resourceID of the sharer's L{CommonHome}.
+ """
+ return self._owner_rid
+
+
+ def shareeUID(self):
+ """
+ The ownerUID of the sharee.
+ """
+ return self._sharee_uid
+
+
+ def shareeHomeID(self):
+ """
+ The resourceID of the sharee's L{CommonHome}.
+ """
+ return self._sharee_rid
+
+
+ def resourceID(self):
+ """
+ The resourceID of the shared object.
+ """
+ return self._resource_id
+
+
+ def resourceName(self):
+ """
+ This maps to the name of the shared resource in the collection it is bound into. It is not necessarily the
+ same as the "bind name" which is used as the "uid" for invites.
+ """
+ return self._resource_name
+
+
+ def mode(self):
+ """
+ The sharing mode: one of the _BIND_MODE_XXX values.
+ """
+ return self._mode
+
+
+ def setMode(self, mode):
+ self._mode = mode
+
+
+ def status(self):
+ """
+ The sharing status: one of the _BIND_STATUS_XXX values.
+ """
+ return self._status
+
+
+ def setStatus(self, status):
+ self._status = status
+
+
+ def summary(self):
+ """
+ Message associated with the invitation.
+ """
+ return self._summary
+
+
+ def setSummary(self, summary):
+ self._summary = summary
+
+
+
class SharingMixIn(object):
"""
Common class for CommonHomeChild and AddressBookObject
@@ -2682,13 +2811,32 @@
)
+ @classmethod
+ def _bindInviteFor(cls, condition): #@NoSelf
+ home = cls._homeSchema
+ bind = cls._bindSchema
+ 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
+ )
+
+
@classproperty
- def _sharedBindForResourceID(cls): #@NoSelf
+ def _sharedInvitationBindForResourceID(cls): #@NoSelf
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)
- )
+ return cls._bindInviteFor(
+ (bind.RESOURCE_ID == Parameter("resourceID")).And
+ (bind.BIND_MODE != _BIND_MODE_OWN)
+ )
@classproperty
@@ -2699,14 +2847,6 @@
@classproperty
- def _unacceptedBindForResourceID(cls): #@NoSelf
- bind = cls._bindSchema
- return cls._bindFor((bind.RESOURCE_ID == Parameter("resourceID"))
- .And(bind.BIND_STATUS != _BIND_STATUS_ACCEPTED)
- )
-
-
- @classproperty
def _bindForResourceIDAndHomeID(cls): #@NoSelf
"""
DAL query that looks up home bind rows by home child
@@ -2791,6 +2931,24 @@
@inlineCallbacks
+ def updateShareFromSharingInvitation(self, invitation, mode=None, status=None, message=None, name=None):
+ """
+ Like L{updateShare} except that the original invitation is provided. That is used
+ to find the actual sharee L{CommonHomeChild} which is then passed to L{updateShare}.
+ """
+
+ # Look up the shared child - might be accepted or not. If accepted use the resource name
+ # to look it up, else use the invitation uid (bind name)
+ shareeHome = yield self._txn.homeWithUID(self._home._homeType, invitation.shareeUID())
+ shareeView = yield shareeHome.childWithName(invitation.resourceName())
+ if shareeView is None:
+ shareeView = yield shareeHome.invitedObjectWithShareUID(invitation.uid())
+
+ 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
@@ -2870,6 +3028,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
@@ -2918,8 +3088,10 @@
"""
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(self._home._homeType, invite.shareeHomeID()))
+ (yield self.unshareWith(shareeHome))
else:
# This collection is shared to me
ownerHomeChild = yield self.ownerHome().childWithID(self._resourceID)
@@ -2927,65 +3099,40 @@
@inlineCallbacks
- def asShared(self):
+ def sharingInvites(self):
"""
- Retrieve all the versions of this L{CommonHomeChild} as it is shared to
- everyone.
+ Retrieve the list of all L{SharingInvitation}'s for this L{CommonHomeChild}, irrespective of mode.
- @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.
+ @return: L{SharingInvitation} objects
+ @rtype: a L{Deferred} which fires with a L{list} of L{SharingInvitation}s.
"""
if not self.owned():
returnValue([])
# get all accepted binds
- acceptedRows = yield self._sharedBindForResourceID.on(
- self._txn, resourceID=self._resourceID,
+ acceptedRows = yield self._sharedInvitationBindForResourceID.on(
+ self._txn, resourceID=self._resourceID, homeID=self._home._resourceID
)
result = []
- for row in acceptedRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:self.bindColumnCount] #@UnusedVariable
- home = yield self._txn.homeWithResourceID(self._home._homeType, homeID)
- new = yield home.objectWithShareUID(bindName)
- result.append(new)
-
+ for homeUID, homeRID, resourceID, resourceName, bindMode, bindStatus, bindMessage in acceptedRows: #@UnusedVariable
+ invite = SharingInvitation(
+ resourceName,
+ self._home.name(),
+ self._home._resourceID,
+ homeUID,
+ homeRID,
+ resourceID,
+ resourceName if self.bindNameIsResourceName() else self.shareeName(),
+ bindMode,
+ bindStatus,
+ bindMessage,
+ )
+ result.append(invite)
returnValue(result)
@inlineCallbacks
- def asInvited(self):
- """
- Retrieve all the versions of this L{CommonHomeChild} as it is invited to
- everyone.
-
- @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.
- """
- if not self.owned():
- returnValue([])
-
- rows = yield self._unacceptedBindForResourceID.on(
- self._txn, resourceID=self._resourceID,
- )
-
- result = []
- for row in rows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:self.bindColumnCount] #@UnusedVariable
- home = yield self._txn.homeWithResourceID(self._home._homeType, homeID)
- new = yield home.invitedObjectWithShareUID(bindName)
- result.append(new)
-
- returnValue(result)
-
-
- @inlineCallbacks
def _initBindRevision(self):
self._bindRevision = self._syncTokenRevision
@@ -3050,6 +3197,20 @@
yield self.notifyPropertyChanged()
+ def shareeName(self):
+ """
+ The sharee's name for a shared L{CommonHomeChild} is the name of the resource by default.
+ """
+ return self.name()
+
+
+ def bindNameIsResourceName(self):
+ """
+ By default, the shared resource name of an accepted share is the same as the name in the bind table.
+ """
+ return True
+
+
def shareStatus(self):
"""
@see: L{ICalendar.shareStatus}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130722/0d04c352/attachment-0001.html>
More information about the calendarserver-changes
mailing list