[CalendarServer-changes] [11963] CalendarServer/branches/users/cdaboo/sharing-in-the-store
source_changes at macosforge.org
source_changes at macosforge.org
Wed Mar 12 11:16:42 PDT 2014
Revision: 11963
http://trac.calendarserver.org//changeset/11963
Author: cdaboo at apple.com
Date: 2013-11-18 13:59:19 -0800 (Mon, 18 Nov 2013)
Log Message:
-----------
Checkpoint changes: more api changes for carddav. Re-working group sharing support is next.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/resource.py
CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/storebridge.py
CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/carddav/datastore/sql.py
CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql.py
CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_schema/current.sql
CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_tables.py
Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/resource.py 2013-11-18 19:24:16 UTC (rev 11962)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/resource.py 2013-11-18 21:59:19 UTC (rev 11963)
@@ -859,8 +859,12 @@
Return the DAV:owner property value (MUST be a DAV:href or None).
"""
- if self.isShareeResource():
- parent = (yield self.locateParent(request, self._share_url))
+ if hasattr(self, "_newStoreObject"):
+ if not hasattr(self._newStoreObject, "ownerHome"):
+ home = self._newStoreObject.parentCollection().ownerHome()
+ else:
+ home = self._newStoreObject.ownerHome()
+ returnValue(element.HRef(self.principalForUID(home.uid()).principalURL()))
else:
parent = (yield self.locateParent(request, request.urlForResource(self)))
if parent and isinstance(parent, CalDAVResource):
@@ -875,8 +879,12 @@
"""
Return the DAV:owner property value (MUST be a DAV:href or None).
"""
- if self.isShareeResource():
- parent = (yield self.locateParent(request, self._share_url))
+ if hasattr(self, "_newStoreObject"):
+ if not hasattr(self._newStoreObject, "ownerHome"):
+ home = self._newStoreObject.parentCollection().ownerHome()
+ else:
+ home = self._newStoreObject.ownerHome()
+ returnValue(self.principalForUID(home.uid()))
else:
parent = (yield self.locateParent(request, request.urlForResource(self)))
if parent and isinstance(parent, CalDAVResource):
Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/storebridge.py 2013-11-18 19:24:16 UTC (rev 11962)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/storebridge.py 2013-11-18 21:59:19 UTC (rev 11963)
@@ -3110,8 +3110,8 @@
call super and provision group share
"""
abObjectResource = yield super(AddressBookCollectionResource, self).makeChild(name)
- if abObjectResource.exists() and abObjectResource._newStoreObject.shareUID() is not None:
- abObjectResource = yield self.parentResource().provisionShare(abObjectResource)
+ #if abObjectResource.exists() and abObjectResource._newStoreObject.shareUID() is not None:
+ # abObjectResource = yield self.parentResource().provisionShare(abObjectResource)
returnValue(abObjectResource)
Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/carddav/datastore/sql.py 2013-11-18 19:24:16 UTC (rev 11962)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/carddav/datastore/sql.py 2013-11-18 21:59:19 UTC (rev 11963)
@@ -55,7 +55,7 @@
from txdav.common.datastore.sql_tables import _ABO_KIND_PERSON, \
_ABO_KIND_GROUP, _ABO_KIND_RESOURCE, _ABO_KIND_LOCATION, schema, \
_BIND_MODE_OWN, _BIND_MODE_WRITE, _BIND_STATUS_ACCEPTED, \
- _BIND_STATUS_DECLINED, _BIND_STATUS_INVITED
+ _BIND_STATUS_DECLINED, _BIND_STATUS_INVITED, _BIND_MODE_READ
from txdav.common.icommondatastore import InternalDataStoreError, \
InvalidUIDError, UIDExistsError, ObjectResourceTooBigError, \
InvalidObjectResourceError, InvalidComponentForStoreError, \
@@ -101,13 +101,36 @@
removeAddressBookWithName = CommonHome.removeChildWithName
- @classproperty
- 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"))
+ @classmethod
+ def homeColumns(cls):
+ """
+ Return a list of column names to retrieve when doing an ownerUID->home lookup.
+ """
+ # Common behavior is to have created and modified
+ return (
+ cls._homeSchema.RESOURCE_ID,
+ cls._homeSchema.OWNER_UID,
+ cls._homeSchema.ADDRESSBOOK_PROPERTY_STORE_ID,
+ )
+
+
+ @classmethod
+ def homeAttributes(cls):
+ """
+ Return a list of attributes names to map L{homeColumns} to.
+ """
+
+ # Common behavior is to have created and modified
+
+ return (
+ "_resourceID",
+ "_ownerUID",
+ "_addressbookPropertyStoreID",
+ )
+
+
@inlineCallbacks
def initFromStore(self, no_cache=False):
"""
@@ -115,48 +138,22 @@
extra meta-data from the DB to avoid having to do DB queries for those
individually later.
"""
- result = yield self._cacher.get(self._ownerUID)
- if result is None:
- result = yield self._resourceIDAndHomeResourceIDFromOwnerQuery.on(
- self._txn, ownerUID=self._ownerUID)
- if result and not no_cache:
- yield self._cacher.set(self._ownerUID, result)
- if result:
- self._resourceID, self._addressbookPropertyStoreID = result[0]
+ result = yield super(AddressBookHome, self).initFromStore(no_cache)
+ if result is not None:
- queryCacher = self._txn._queryCacher
- if queryCacher:
- # Get cached copy
- cacheKey = queryCacher.keyForHomeMetaData(self._resourceID)
- data = yield queryCacher.get(cacheKey)
- else:
- data = None
- if data is None:
- # Don't have a cached copy
- data = (yield self._metaDataQuery.on(
- self._txn, resourceID=self._resourceID))[0]
- if queryCacher:
- # Cache the data
- yield queryCacher.setAfterCommit(self._txn, cacheKey, data)
-
- # self._created, self._modified = data
- yield self._loadPropertyStore()
-
# created owned address book
addressbook = AddressBook(
home=self,
- name="addressbook", resourceID=self._resourceID,
- mode=_BIND_MODE_OWN, status=_BIND_STATUS_ACCEPTED,
+ name="addressbook",
+ resourceID=self._resourceID,
+ mode=_BIND_MODE_OWN,
+ status=_BIND_STATUS_ACCEPTED,
)
- self._created, self._modified = data
yield addressbook._loadPropertyStore()
- yield addressbook._initIsShared()
self._addressbook = addressbook
- returnValue(self)
- else:
- returnValue(None)
+ returnValue(result)
@inlineCallbacks
@@ -204,6 +201,7 @@
"""
super(AddressBookHome, self).removeUnacceptedShares()
+ # Remove group binds too
bind = AddressBookObject._bindSchema
kwds = {"homeResourceID" : self._resourceID}
yield Delete(
@@ -318,30 +316,173 @@
Sharing code shared between AddressBook and AddressBookObject
"""
- @inlineCallbacks
- def _isSharedOrInvited(self):
+ def sharedResourceType(self):
"""
- return True if this L{AddressBook} is shared or invited
+ The sharing resource type
"""
- sharedRows = []
- if self.owned():
- bind = self._bindSchema
- sharedRows = yield self._bindFor(
- (bind.RESOURCE_ID == Parameter("resourceID"))).on(
- self._txn, resourceID=self._resourceID,
- )
+ return "addressbook"
- returnValue(bool(sharedRows))
+ def newShareName(self):
+ """
+ For shared address books the resource name of a share is the ownerUID of the owner's resource.
+ """
+ return self.ownerHome().uid()
- @inlineCallbacks
- def _initIsShared(self):
- isShared = yield self._isSharedOrInvited()
- self.setShared(isShared)
+# @inlineCallbacks
+# def updateShare(self, shareeView, mode=None, status=None, summary=None):
+# """
+# Update share mode, status, and message for a home child shared with
+# this (owned) L{CommonHomeChild}.
+#
+# @param shareeView: The sharee home child that shares this.
+# @type shareeView: L{CommonHomeChild}
+#
+# @param mode: The sharing mode; L{_BIND_MODE_READ} or
+# L{_BIND_MODE_WRITE} or None to not update
+# @type mode: L{str}
+#
+# @param status: The sharing status; L{_BIND_STATUS_INVITED} or
+# L{_BIND_STATUS_ACCEPTED} or L{_BIND_STATUS_DECLINED} or
+# L{_BIND_STATUS_INVALID} or None to not update
+# @type status: L{str}
+#
+# @param summary: The proposed message to go along with the share, which
+# will be used as the default display name, or None to not update
+# @type summary: L{str}
+#
+# @return: the name of the shared item in the sharee's home.
+# @rtype: a L{Deferred} which fires with a L{str}
+# """
+# # TODO: raise a nice exception if shareeView is not, in fact, a shared
+# # version of this same L{CommonHomeChild}
+#
+# # remove None parameters, and substitute None for empty string
+# bind = self._bindSchema
+# columnMap = dict([(k, v if v != "" else None)
+# for k, v in {bind.BIND_MODE:mode,
+# bind.BIND_STATUS:status,
+# bind.MESSAGE:summary}.iteritems() if v is not None])
+#
+# if len(columnMap):
+#
+# # count accepted
+# if status is not None:
+# previouslyAcceptedBindCount = 1 if not shareeView.indirect() else 0
+# previouslyAcceptedBindCount += len((yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
+# self._txn, homeID=shareeView.viewerHome()._resourceID, addressbookID=shareeView._resourceID
+# )))
+#
+# yield self._updateBindColumnsQuery(columnMap).on(
+# self._txn,
+# resourceID=self._resourceID, homeID=shareeView.viewerHome()._resourceID
+# )
+#
+# # update affected attributes
+# if mode is not None:
+# shareeView._bindMode = columnMap[bind.BIND_MODE]
+#
+# if status is not None:
+# shareeView._bindStatus = columnMap[bind.BIND_STATUS]
+# if shareeView._bindStatus == _BIND_STATUS_ACCEPTED:
+# if 0 == previouslyAcceptedBindCount:
+# yield shareeView._initSyncToken()
+# yield shareeView._initBindRevision()
+# shareeView.viewerHome()._children[shareeView._name] = shareeView
+# shareeView.viewerHome()._children[shareeView._resourceID] = shareeView
+# elif shareeView._bindStatus == _BIND_STATUS_DECLINED:
+# if 1 == previouslyAcceptedBindCount:
+# yield shareeView._deletedSyncToken(sharedRemoval=True)
+# shareeView.viewerHome()._children.pop(shareeView._name, None)
+# shareeView.viewerHome()._children.pop(shareeView._resourceID, None)
+#
+# if summary is not None:
+# shareeView._bindMessage = columnMap[bind.MESSAGE]
+#
+# queryCacher = self._txn._queryCacher
+# if queryCacher:
+# cacheKey = queryCacher.keyForObjectWithName(shareeView._home._resourceID, shareeView._name)
+# yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
+# cacheKey = queryCacher.keyForObjectWithResourceID(shareeView._home._resourceID, shareeView._resourceID)
+# yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
+#
+# # Must send notification to ensure cache invalidation occurs
+# yield self.notifyPropertyChanged()
-class AddressBook(CommonHomeChild, AddressBookSharingMixIn):
+# @inlineCallbacks
+# def unshareWith(self, shareeHome):
+# """
+# Remove the shared version of this (owned) L{CommonHomeChild} from the
+# referenced L{CommonHome}.
+#
+# @see: L{CommonHomeChild.shareWith}
+#
+# @param shareeHome: The home with which this L{CommonHomeChild} was
+# previously shared.
+#
+# @return: a L{Deferred} which will fire with the previous shareUID
+# """
+# sharedAddressBook = yield shareeHome.addressbookWithName(self.name())
+# if sharedAddressBook:
+#
+# acceptedBindCount = 1 if not sharedAddressBook.indirect() else 0
+# acceptedBindCount += len((yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
+# self._txn, homeID=shareeHome._resourceID, addressbookID=sharedAddressBook._resourceID
+# )))
+# if acceptedBindCount == 1:
+# yield sharedAddressBook._deletedSyncToken(sharedRemoval=True)
+# shareeHome._children.pop(self.name(), None)
+# shareeHome._children.pop(sharedAddressBook._resourceID, None)
+# elif sharedAddressBook.indirect():
+# # FIXME: remove objects for this group only using self.removeObjectResource
+# self._objectNames = None
+#
+# # Must send notification to ensure cache invalidation occurs
+# yield self.notifyPropertyChanged()
+#
+# # delete binds including invites
+# deletedBindNameRows = yield self._deleteBindForResourceIDAndHomeID.on(self._txn, resourceID=self._resourceID,
+# homeID=shareeHome._resourceID
+# )
+# if deletedBindNameRows:
+# deletedBindName = deletedBindNameRows[0][0]
+# queryCacher = self._txn._queryCacher
+# if queryCacher:
+# cacheKey = queryCacher.keyForObjectWithName(shareeHome._resourceID, self.name())
+# queryCacher.invalidateAfterCommit(self._txn, cacheKey)
+# else:
+# deletedBindName = None
+#
+# self._initIsShared()
+# returnValue(deletedBindName)
+
+
+# @inlineCallbacks
+# def _isSharedOrInvited(self):
+# """
+# return True if this L{AddressBook} is shared or invited
+# """
+# sharedRows = []
+# if self.owned():
+# bind = self._bindSchema
+# sharedRows = yield self._bindFor(
+# (bind.RESOURCE_ID == Parameter("resourceID"))).on(
+# self._txn, resourceID=self._resourceID,
+# )
+#
+# returnValue(bool(sharedRows))
+#
+#
+# @inlineCallbacks
+# def _initIsShared(self):
+# isShared = yield self._isSharedOrInvited()
+# self.setShared(isShared)
+
+
+
+class AddressBook(AddressBookSharingMixIn, CommonHomeChild):
"""
SQL-based implementation of L{IAddressBook}.
"""
@@ -356,7 +497,7 @@
_objectSchema = schema.ADDRESSBOOK_OBJECT
- def __init__(self, home, name, resourceID, mode, status, revision=0, message=None, ownerHome=None):
+ def __init__(self, home, name, resourceID, mode, status, revision=0, message=None, ownerHome=None, ownerName=None):
ownerName = ownerHome.addressbook().name() if ownerHome else None
super(AddressBook, self).__init__(home, name, resourceID, mode, status, revision=revision, message=message, ownerHome=ownerHome, ownerName=ownerName)
self._index = PostgresLegacyABIndexEmulator(self)
@@ -394,13 +535,6 @@
addressbookObjectsSinceToken = CommonHomeChild.objectResourcesSinceToken
- def newShareName(self):
- """
- For shared address books the resource name of a share is the ownerUID of the owner's resource.
- """
- return self.ownerHome().uid()
-
-
@inlineCallbacks
def _loadPropertyStore(self, props=None):
if props is None:
@@ -426,13 +560,6 @@
)
- def sharedResourceType(self):
- """
- The sharing resource type
- """
- return "addressbook"
-
-
def contentType(self):
"""
The content type of addressbook objects is text/vcard.
@@ -491,20 +618,22 @@
@inlineCallbacks
def listObjectResources(self):
+ # Check for non-group shared
+ if self.owned() or not self.indirect():
+ result = yield super(AddressBook, self).listObjectResources()
+ returnValue(result)
+
+ # Group shared
if self._objectNames is None:
- if self.owned() or self.fullyShared():
- rows = yield self._objectResourceNamesQuery.on(
- self._txn, resourceID=self._resourceID)
- else:
- acceptedGroupIDs = yield self.acceptedGroupIDs()
- allowedObjectIDs = yield self.expandGroupIDs(self._txn, acceptedGroupIDs)
- rows = (yield self._objectResourceNamesWithResourceIDsQuery(allowedObjectIDs).on(
- self._txn, resourceIDs=allowedObjectIDs
- ))
+ acceptedGroupIDs = yield self.acceptedGroupIDs()
+ allowedObjectIDs = yield self.expandGroupIDs(self._txn, acceptedGroupIDs)
+ rows = (yield self._objectResourceNamesWithResourceIDsQuery(allowedObjectIDs).on(
+ self._txn, resourceIDs=allowedObjectIDs
+ ))
objectNames = [row[0] for row in rows]
# account for fully-shared address book group
- if self.fullyShared():
+ if not self.indirect():
if not self._groupForSharedAddressBookName() in objectNames:
objectNames.append(self._groupForSharedAddressBookName())
self._objectNames = sorted(objectNames)
@@ -514,18 +643,18 @@
@inlineCallbacks
def countObjectResources(self):
+ # Check for non-group shared
+ if self.owned() or not self.indirect():
+ result = yield super(AddressBook, self).countObjectResources()
+ returnValue(result)
+
+ # Group shared
if self._objectNames is None:
- if self.owned() or self.fullyShared():
- rows = yield self._objectCountQuery.on(
- self._txn, resourceID=self._resourceID
- )
- count = rows[0][0]
- else:
- acceptedGroupIDs = yield self.acceptedGroupIDs()
- count = len((yield self.expandGroupIDs(self._txn, acceptedGroupIDs)))
+ acceptedGroupIDs = yield self.acceptedGroupIDs()
+ count = len((yield self.expandGroupIDs(self._txn, acceptedGroupIDs)))
# account for fully-shared address book group
- if self.fullyShared():
+ if not self.indirect():
count += 1
returnValue(count)
@@ -618,6 +747,48 @@
@classmethod
@inlineCallbacks
+ def listObjects(cls, home):
+ """
+ Retrieve the names of the children with invitations in the given home. Make sure
+ to include the default owner address book.
+
+ @return: an iterable of C{str}s.
+ """
+ names = yield super(AddressBook, cls).listObjects(home)
+ names.insert(0, home.addressbook().name())
+ returnValue(names)
+
+# @classmethod
+# @inlineCallbacks
+# def listObjects(cls, home):
+# """
+# Retrieve the names of the children with invitations in the given home.
+#
+# @return: an iterable of C{str}s.
+# """
+# names = set([home.addressbook().name()])
+#
+# rows = yield cls._acceptedBindForHomeID.on(
+# home._txn, homeID=home._resourceID
+# )
+# for row in rows:
+# bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = row[:cls.bindColumnCount] #@UnusedVariable
+# ownerHome = yield home._txn.homeWithResourceID(home._homeType, resourceID, create=True)
+# names |= set([ownerHome.uid()])
+#
+# groupRows = yield AddressBookObject._acceptedBindForHomeID.on(
+# home._txn, homeID=home._resourceID
+# )
+# for groupRow in groupRows:
+# bindMode, homeID, resourceID, name, 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.uid()])
+# returnValue(tuple(names))
+
+
+ @classmethod
+ @inlineCallbacks
def loadAllObjects(cls, home):
"""
Load all L{CommonHomeChild} instances which are children of a given
@@ -626,78 +797,94 @@
operations to keep this constant wrt the number of children. This is an
optimization for Depth:1 operations on the home.
"""
- results = [home.addressbook()]
- ownerHomeToDataRowMap = {}
- # Load from the main table first
- dataRows = yield cls._childrenAndMetadataForHomeID.on(
- home._txn, homeID=home._resourceID
- )
- # get ownerHomeIDs
- for dataRow in dataRows:
- bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount] #@UnusedVariable
- ownerHome = yield home.ownerHomeWithChildID(resourceID)
- ownerHomeToDataRowMap[ownerHome] = dataRow
+ results = yield super(AddressBook, cls).loadAllObjects(home)
+ results.insert(0, home.addressbook())
+ returnValue(results)
- # now get group rows:
- groupBindRows = yield AddressBookObject._childrenAndMetadataForHomeID.on(
- home._txn, homeID=home._resourceID
- )
- for groupBindRow in groupBindRows:
- bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
- ownerAddressBookID = yield AddressBookObject.ownerAddressBookIDFromGroupID(home._txn, resourceID)
- ownerHome = yield home.ownerHomeWithChildID(ownerAddressBookID)
- if ownerHome not in ownerHomeToDataRowMap:
- groupBindRow[0] = _BIND_MODE_WRITE
- groupBindRow[3] = None # name
- groupBindRow[4] = None # bindStatus
- groupBindRow[6] = None # bindMessage
- ownerHomeToDataRowMap[ownerHome] = groupBindRow
- if ownerHomeToDataRowMap:
- # Get property stores for all these child resources (if any found)
- addressbookPropertyStoreIDs = [ownerHome._addressbookPropertyStoreID for ownerHome in ownerHomeToDataRowMap]
- propertyStores = yield PropertyStore.forMultipleResourcesWithResourceIDs(
- home.uid(), home._txn, addressbookPropertyStoreIDs
- )
+# @classmethod
+# @inlineCallbacks
+# def loadAllObjects(cls, home):
+# """
+# Load all L{CommonHomeChild} instances which are children of a given
+# L{CommonHome} and return a L{Deferred} firing a list of them. This must
+# create the child classes and initialize them using "batched" SQL
+# operations to keep this constant wrt the number of children. This is an
+# optimization for Depth:1 operations on the home.
+# """
+# results = [home.addressbook()]
+# ownerHomeToDataRowMap = {}
+#
+# # Load from the main table first
+# dataRows = yield cls._childrenAndMetadataForHomeID.on(
+# home._txn, homeID=home._resourceID
+# )
+# # get ownerHomeIDs
+# for dataRow in dataRows:
+# bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount] #@UnusedVariable
+# ownerHome = yield home.ownerHomeWithChildID(resourceID)
+# ownerHomeToDataRowMap[ownerHome] = dataRow
+#
+# # now get group rows:
+# groupBindRows = yield AddressBookObject._childrenAndMetadataForHomeID.on(
+# home._txn, homeID=home._resourceID
+# )
+# for groupBindRow in groupBindRows:
+# bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
+# ownerAddressBookID = yield AddressBookObject.ownerAddressBookIDFromGroupID(home._txn, resourceID)
+# ownerHome = yield home.ownerHomeWithChildID(ownerAddressBookID)
+# if ownerHome not in ownerHomeToDataRowMap:
+# groupBindRow[0] = _BIND_MODE_WRITE
+# groupBindRow[3] = None # name
+# groupBindRow[4] = None # bindStatus
+# groupBindRow[6] = None # bindMessage
+# ownerHomeToDataRowMap[ownerHome] = groupBindRow
+#
+# if ownerHomeToDataRowMap:
+# # Get property stores for all these child resources (if any found)
+# addressbookPropertyStoreIDs = [ownerHome._addressbookPropertyStoreID for ownerHome in ownerHomeToDataRowMap]
+# propertyStores = yield PropertyStore.forMultipleResourcesWithResourceIDs(
+# home.uid(), home._txn, addressbookPropertyStoreIDs
+# )
+#
+# addressbookResourceIDs = [ownerHome.addressbook()._resourceID for ownerHome in ownerHomeToDataRowMap]
+# revisions = yield cls._revisionsForResourceIDs(addressbookResourceIDs).on(home._txn, resourceIDs=addressbookResourceIDs)
+# revisions = dict(revisions)
+#
+# # Create the actual objects merging in properties
+# for ownerHome, dataRow in ownerHomeToDataRowMap.iteritems():
+# bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount] #@UnusedVariable
+# additionalBind = dataRow[cls.bindColumnCount:cls.bindColumnCount + len(cls.additionalBindColumns())]
+# metadata = dataRow[cls.bindColumnCount + len(cls.additionalBindColumns()):]
+#
+# child = cls(
+# home=home,
+# name=name,
+# resourceID=ownerHome._resourceID,
+# mode=bindMode,
+# status=bindStatus,
+# revision=bindRevision,
+# message=bindMessage,
+# ownerHome=ownerHome,
+# )
+#
+# for attr, value in zip(cls.additionalBindAttributes(), additionalBind):
+# setattr(child, attr, value)
+# for attr, value in zip(cls.metadataAttributes(), metadata):
+# setattr(child, attr, value)
+# child._syncTokenRevision = revisions[child._resourceID]
+# propstore = propertyStores.get(ownerHome._addressbookPropertyStoreID, None)
+# # We have to re-adjust the property store object to account for possible shared
+# # collections as previously we loaded them all as if they were owned
+# if propstore:
+# propstore._setDefaultUserUID(ownerHome.uid())
+# yield child._loadPropertyStore(propstore)
+# results.append(child)
+#
+# returnValue(results)
- addressbookResourceIDs = [ownerHome.addressbook()._resourceID for ownerHome in ownerHomeToDataRowMap]
- revisions = yield cls._revisionsForResourceIDs(addressbookResourceIDs).on(home._txn, resourceIDs=addressbookResourceIDs)
- revisions = dict(revisions)
- # Create the actual objects merging in properties
- for ownerHome, dataRow in ownerHomeToDataRowMap.iteritems():
- bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount] #@UnusedVariable
- additionalBind = dataRow[cls.bindColumnCount:cls.bindColumnCount + len(cls.additionalBindColumns())]
- metadata = dataRow[cls.bindColumnCount + len(cls.additionalBindColumns()):]
-
- child = cls(
- home=home,
- name=name,
- resourceID=ownerHome._resourceID,
- mode=bindMode,
- status=bindStatus,
- revision=bindRevision,
- message=bindMessage,
- ownerHome=ownerHome,
- )
-
- for attr, value in zip(cls.additionalBindAttributes(), additionalBind):
- setattr(child, attr, value)
- for attr, value in zip(cls.metadataAttributes(), metadata):
- setattr(child, attr, value)
- child._syncTokenRevision = revisions[child._resourceID]
- propstore = propertyStores.get(ownerHome._addressbookPropertyStoreID, None)
- # We have to re-adjust the property store object to account for possible shared
- # collections as previously we loaded them all as if they were owned
- if propstore:
- propstore._setDefaultUserUID(ownerHome.uid())
- yield child._loadPropertyStore(propstore)
- results.append(child)
-
- returnValue(results)
-
-
@classmethod
@inlineCallbacks
def objectWithName(cls, home, name, accepted=True):
@@ -709,78 +896,99 @@
@param name: a string; the name of the L{CommonHomeChild} to retrieve.
- @return: an L{CommonHomeChild} or C{None} if no such child
- exists.
+ @return: an L{CommonHomeChild} or C{None} if no such child exists.
"""
- if accepted and name == home.addressbook().name():
+
+ if name == home.addressbook().name():
returnValue(home.addressbook())
- # shared address books only from this point on
- rows = None
- queryCacher = home._txn._queryCacher
- ownerHome = None
+ result = yield super(AddressBook, cls).objectWithName(home, name, accepted)
+ returnValue(result)
- if queryCacher:
- # Retrieve data from cache
- cacheKey = queryCacher.keyForObjectWithName(home._resourceID, name)
- rows = yield queryCacher.get(cacheKey)
- if not rows:
- # name must be a home uid
- ownerHome = yield home._txn.addressbookHomeWithUID(name)
- if ownerHome:
- # see if address book resource id in bind table
- ownerAddressBook = ownerHome.addressbook()
- bindRows = yield cls._bindForResourceIDAndHomeID.on(
- home._txn, resourceID=ownerAddressBook._resourceID, homeID=home._resourceID
- )
- if bindRows:
- bindRows[0].insert(cls.bindColumnCount, ownerAddressBook._resourceID)
- bindRows[0].insert(cls.bindColumnCount + 1, bindRows[0][4]) # cachedStatus = bindStatus
- rows = bindRows
- else:
- groupBindRows = yield AddressBookObject._bindForHomeIDAndAddressBookID.on(
- home._txn, homeID=home._resourceID, addressbookID=ownerAddressBook._resourceID
- )
- for groupBindRow in groupBindRows:
- groupBindRow.insert(AddressBookObject.bindColumnCount, ownerAddressBook._resourceID)
- groupBindRow.insert(AddressBookObject.bindColumnCount + 1, groupBindRow[4])
- groupBindRow[0] = _BIND_MODE_WRITE
- groupBindRow[3] = ownerHome.uid() # bindName
- groupBindRow[4] = None # bindStatus
- groupBindRow[6] = None # bindMessage
- rows = [groupBindRow]
- break
+# @classmethod
+# @inlineCallbacks
+# def objectWithName(cls, home, name, accepted=True):
+# """
+# Retrieve the child with the given C{name} contained in the given
+# C{home}.
+#
+# @param home: a L{CommonHome}.
+#
+# @param name: a string; the name of the L{CommonHomeChild} to retrieve.
+#
+# @return: an L{CommonHomeChild} or C{None} if no such child
+# exists.
+# """
+# if accepted and name == home.addressbook().name():
+# returnValue(home.addressbook())
+# # shared address books only from this point on
+#
+# rows = None
+# queryCacher = home._txn._queryCacher
+# ownerHome = None
+#
+# if queryCacher:
+# # Retrieve data from cache
+# cacheKey = queryCacher.keyForObjectWithName(home._resourceID, name)
+# rows = yield queryCacher.get(cacheKey)
+#
+# if not rows:
+# # name must be a home uid
+# ownerHome = yield home._txn.addressbookHomeWithUID(name)
+# if ownerHome:
+# # see if address book resource id in bind table
+# ownerAddressBook = ownerHome.addressbook()
+# bindRows = yield cls._bindForResourceIDAndHomeID.on(
+# home._txn, resourceID=ownerAddressBook._resourceID, homeID=home._resourceID
+# )
+# if bindRows:
+# bindRows[0].insert(cls.bindColumnCount, ownerAddressBook._resourceID)
+# bindRows[0].insert(cls.bindColumnCount + 1, bindRows[0][4]) # cachedStatus = bindStatus
+# rows = bindRows
+# else:
+# groupBindRows = yield AddressBookObject._bindForHomeIDAndAddressBookID.on(
+# home._txn, homeID=home._resourceID, addressbookID=ownerAddressBook._resourceID
+# )
+# for groupBindRow in groupBindRows:
+# groupBindRow.insert(AddressBookObject.bindColumnCount, ownerAddressBook._resourceID)
+# groupBindRow.insert(AddressBookObject.bindColumnCount + 1, groupBindRow[4])
+# groupBindRow[0] = _BIND_MODE_WRITE
+# groupBindRow[3] = ownerHome.uid() # bindName
+# groupBindRow[4] = None # bindStatus
+# groupBindRow[6] = None # bindMessage
+# rows = [groupBindRow]
+# break
+#
+# if rows and queryCacher:
+# # Cache the result
+# queryCacher.setAfterCommit(home._txn, cacheKey, rows)
+#
+# if not rows:
+# returnValue(None)
+#
+# row = rows[0]
+# bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage, ownerAddressBookID, cachedBindStatus = row[:cls.bindColumnCount + 2] #@UnusedVariable
+#
+# # if wrong status, exit here. Item is in queryCache
+# if accepted is not None and (cachedBindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
+# returnValue(None)
+#
+# ownerHome = yield home.ownerHomeWithChildID(ownerAddressBookID)
+# child = cls(
+# home=home,
+# name=name,
+# resourceID=ownerAddressBookID,
+# mode=bindMode,
+# status=bindStatus,
+# revision=bindRevision,
+# message=bindMessage,
+# ownerHome=ownerHome,
+# )
+# yield child.initFromStore()
+# returnValue(child)
- if rows and queryCacher:
- # Cache the result
- queryCacher.setAfterCommit(home._txn, cacheKey, rows)
- if not rows:
- returnValue(None)
-
- row = rows[0]
- bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage, ownerAddressBookID, cachedBindStatus = row[:cls.bindColumnCount + 2] #@UnusedVariable
-
- # if wrong status, exit here. Item is in queryCache
- if accepted is not None and (cachedBindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
- returnValue(None)
-
- ownerHome = yield home.ownerHomeWithChildID(ownerAddressBookID)
- child = cls(
- home=home,
- name=name,
- resourceID=ownerAddressBookID,
- mode=bindMode,
- status=bindStatus,
- revision=bindRevision,
- message=bindMessage,
- ownerHome=ownerHome,
- )
- yield child.initFromStore()
- returnValue(child)
-
-
@classmethod
@inlineCallbacks
def objectWithID(cls, home, resourceID, accepted=True):
@@ -796,91 +1004,79 @@
if home._resourceID == resourceID:
returnValue(home.addressbook())
- bindRows = yield cls._bindForResourceIDAndHomeID.on(
- home._txn, resourceID=resourceID, homeID=home._resourceID
- )
- if bindRows:
- bindRow = bindRows[0]
- bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = bindRow[:cls.bindColumnCount] #@UnusedVariable
+ result = yield super(AddressBook, cls).objectWithID(home, resourceID, accepted)
+ returnValue(result)
- if accepted is not None and (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
- returnValue(None)
- ownerHome = yield home.ownerHomeWithChildID(resourceID)
- if bindStatus == _BIND_STATUS_ACCEPTED:
- returnValue((yield home.childWithName(ownerHome.uid())))
- else:
- returnValue((yield cls.objectWithName(home, ownerHome.uid(), accepted=False)))
+# @classmethod
+# @inlineCallbacks
+# def objectWithID(cls, home, resourceID, accepted=True):
+# """
+# Retrieve the child with the given C{resourceID} contained in the given
+# C{home}.
+#
+# @param home: a L{CommonHome}.
+# @param resourceID: a string.
+# @return: an L{CommonHomeChild} or C{None} if no such child
+# exists.
+# """
+# if home._resourceID == resourceID:
+# returnValue(home.addressbook())
+#
+# bindRows = yield cls._bindForResourceIDAndHomeID.on(
+# home._txn, resourceID=resourceID, homeID=home._resourceID
+# )
+# if bindRows:
+# bindRow = bindRows[0]
+# bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = bindRow[:cls.bindColumnCount] #@UnusedVariable
+#
+# if accepted is not None and (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
+# returnValue(None)
+#
+# ownerHome = yield home.ownerHomeWithChildID(resourceID)
+# if bindStatus == _BIND_STATUS_ACCEPTED:
+# returnValue((yield home.childWithName(ownerHome.uid())))
+# else:
+# returnValue((yield cls.objectWithName(home, ownerHome.uid(), accepted=False)))
+#
+# groupBindRows = yield AddressBookObject._bindForHomeIDAndAddressBookID.on(
+# home._txn, homeID=home._resourceID, addressbookID=resourceID
+# )
+# if groupBindRows:
+# groupBindRow = groupBindRows[0]
+# bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
+#
+# if accepted is not None and (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
+# returnValue(None)
+#
+# ownerAddressBookID = yield AddressBookObject.ownerAddressBookIDFromGroupID(home._txn, resourceID)
+# ownerHome = yield home.ownerHomeWithChildID(ownerAddressBookID)
+# if bindStatus == _BIND_STATUS_ACCEPTED:
+# returnValue((yield home.childWithName(ownerHome.uid())))
+# else:
+# returnValue((yield cls.objectWithName(home, ownerHome.uid(), accepted=False)))
+#
+# returnValue(None)
- groupBindRows = yield AddressBookObject._bindForHomeIDAndAddressBookID.on(
- home._txn, homeID=home._resourceID, addressbookID=resourceID
- )
- if groupBindRows:
- groupBindRow = groupBindRows[0]
- bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = groupBindRow[:AddressBookObject.bindColumnCount] #@UnusedVariable
- if accepted is not None and (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
- returnValue(None)
-
- ownerAddressBookID = yield AddressBookObject.ownerAddressBookIDFromGroupID(home._txn, resourceID)
- ownerHome = yield home.ownerHomeWithChildID(ownerAddressBookID)
- if bindStatus == _BIND_STATUS_ACCEPTED:
- returnValue((yield home.childWithName(ownerHome.uid())))
- else:
- returnValue((yield cls.objectWithName(home, ownerHome.uid(), accepted=False)))
-
- returnValue(None)
-
-
- def fullyShared(self):
- return not self.owned() and self._bindStatus == _BIND_STATUS_ACCEPTED
-
-
@classmethod
- @inlineCallbacks
- def listObjects(cls, home):
- """
- Retrieve the names of the children with invitations in the given home.
-
- @return: an iterable of C{str}s.
- """
- names = set([home.addressbook().name()])
-
- rows = yield cls._acceptedBindForHomeID.on(
- home._txn, homeID=home._resourceID
- )
- for row in rows:
- bindMode, homeID, resourceID, name, bindStatus, bindRevision, bindMessage = row[:cls.bindColumnCount] #@UnusedVariable
- ownerHome = yield home._txn.homeWithResourceID(home._homeType, resourceID, create=True)
- names |= set([ownerHome.uid()])
-
- groupRows = yield AddressBookObject._acceptedBindForHomeID.on(
- home._txn, homeID=home._resourceID
- )
- for groupRow in groupRows:
- bindMode, homeID, resourceID, name, 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.uid()])
- returnValue(tuple(names))
-
-
- @classmethod
def _memberIDsWithGroupIDsQuery(cls, groupIDs):
"""
DAL query to load all object resource names for a home child.
"""
aboMembers = schema.ABO_MEMBERS
- return Select([aboMembers.MEMBER_ID], From=aboMembers,
- Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs))),
- )
+ return Select(
+ [aboMembers.MEMBER_ID],
+ From=aboMembers,
+ Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs))),
+ )
@classmethod
@inlineCallbacks
def expandGroupIDs(cls, txn, groupIDs, includeGroupIDs=True):
"""
- Get all AddressBookObject resource IDs contains in the given shared groups with the given groupIDs
+ Get all AddressBookObject resource IDs contained in the given shared groups with the given groupIDs
"""
objectIDs = set(groupIDs) if includeGroupIDs else set()
examinedIDs = set()
@@ -898,33 +1094,44 @@
@inlineCallbacks
def unacceptedGroupIDs(self):
+ """
+ Return the list of shared groups that have not yet been accepted.
+ """
if self.owned():
returnValue([])
else:
groupBindRows = yield AddressBookObject._unacceptedBindForHomeIDAndAddressBookID.on(
- self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
+ self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
)
returnValue([groupBindRow[2] for groupBindRow in groupBindRows])
@inlineCallbacks
def acceptedGroupIDs(self):
+ """
+ Return the list of accepted shared groups.
+ """
if self.owned():
returnValue([])
else:
groupBindRows = yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
- self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
+ self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
)
returnValue([groupBindRow[2] for groupBindRow in groupBindRows])
@inlineCallbacks
def accessControlGroupIDs(self):
+ """
+ For each accepted shared group, determine what its access mode is and return the sets of read-only
+ and read-write groups. Handle the case where a read-only group is actually nested in a read-write
+ group by putting the read-only one into the read-write list.
+ """
if self.owned():
returnValue(([], []))
else:
groupBindRows = yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
- self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
+ self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
)
readWriteGroupIDs = []
readOnlyGroupIDs = []
@@ -960,19 +1167,32 @@
# FIXME: Unused: Use for caching access
@inlineCallbacks
def accessControlObjectIDs(self):
+ """
+ For each object resource in this collection, determine what its access mode is and return the sets of read-only
+ and read-write objects. Handle the case where a read-only group is actually nested in a read-write
+ group by putting the read-only one into the read-write list.
+ """
+
readOnlyIDs = set()
readWriteIDs = set()
- if self.owned() or self.fullyShared():
- rows = yield self._allColumnsWithParent(self)
- ids = set([row[1] for row in rows])
- if self.fullyShared():
- ids |= set([self._resourceID, ])
- if self.owned() or self._bindMode == _BIND_MODE_WRITE:
- returnValue(tuple(readOnlyIDs), tuple(readWriteIDs))
+
+ # All objects in the collection
+ rows = yield self._allColumnsWithParent(self)
+ ids = set([row[1] for row in rows])
+
+ # Everything is read-write
+ if self.owned() or self._bindMode == _BIND_MODE_WRITE:
+ returnValue(tuple(readOnlyIDs), tuple(ids))
+
+ # Fully shared but mode is read-only
+ if self._bindMode == _BIND_MODE_READ:
+ ids |= set([self._resourceID, ])
readOnlyIDs = set(ids)
+ # Look for shared groups and for those that tare read-write, transfer their object ids
+ # to the read-write set
groupBindRows = yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
- self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
+ self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
)
readWriteGroupIDs = []
readOnlyGroupIDs = []
@@ -1010,142 +1230,37 @@
returnValue((readOnlyIDs + readWriteIDs))
- @inlineCallbacks
- def updateShare(self, shareeView, mode=None, status=None, summary=None):
- """
- Update share mode, status, and message for a home child shared with
- this (owned) L{CommonHomeChild}.
- @param shareeView: The sharee home child that shares this.
- @type shareeView: L{CommonHomeChild}
+class AddressBookObjectSharingMixIn(SharingMixIn):
+ """
+ Sharing code for AddressBookObject
+ """
- @param mode: The sharing mode; L{_BIND_MODE_READ} or
- L{_BIND_MODE_WRITE} or None to not update
- @type mode: L{str}
+# @inlineCallbacks
+# def _isSharedOrInvited(self):
+# """
+# return True if this L{AddressBook} is shared or invited
+# """
+# sharedRows = []
+# if self.owned():
+# bind = self._bindSchema
+# sharedRows = yield self._bindFor(
+# (bind.RESOURCE_ID == Parameter("resourceID"))).on(
+# self._txn, resourceID=self._resourceID,
+# )
+#
+# returnValue(bool(sharedRows))
+#
+#
+# @inlineCallbacks
+# def _initIsShared(self):
+# isShared = yield self._isSharedOrInvited()
+# self.setShared(isShared)
- @param status: The sharing status; L{_BIND_STATUS_INVITED} or
- L{_BIND_STATUS_ACCEPTED} or L{_BIND_STATUS_DECLINED} or
- L{_BIND_STATUS_INVALID} or None to not update
- @type status: L{str}
- @param summary: The proposed message to go along with the share, which
- will be used as the default display name, or None to not update
- @type summary: L{str}
- @return: the name of the shared item in the sharee's home.
- @rtype: a L{Deferred} which fires with a L{str}
- """
- # TODO: raise a nice exception if shareeView is not, in fact, a shared
- # version of this same L{CommonHomeChild}
+class AddressBookObject(CommonObjectResource, AddressBookObjectSharingMixIn):
- # remove None parameters, and substitute None for empty string
- bind = self._bindSchema
- columnMap = dict([(k, v if v != "" else None)
- for k, v in {bind.BIND_MODE:mode,
- bind.BIND_STATUS:status,
- bind.MESSAGE:summary}.iteritems() if v is not None])
-
- if len(columnMap):
-
- # count accepted
- if status is not None:
- previouslyAcceptedBindCount = 1 if shareeView.fullyShared() else 0
- previouslyAcceptedBindCount += len((yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
- self._txn, homeID=shareeView.viewerHome()._resourceID, addressbookID=shareeView._resourceID
- )))
-
- bindNameRows = yield self._updateBindColumnsQuery(columnMap).on(
- self._txn,
- resourceID=self._resourceID, homeID=shareeView.viewerHome()._resourceID
- )
-
- # update affected attributes
- if mode is not None:
- shareeView._bindMode = columnMap[bind.BIND_MODE]
-
- if status is not None:
- shareeView._bindStatus = columnMap[bind.BIND_STATUS]
- if shareeView._bindStatus == _BIND_STATUS_ACCEPTED:
- if 0 == previouslyAcceptedBindCount:
- yield shareeView._initSyncToken()
- yield shareeView._initBindRevision()
- shareeView.viewerHome()._children[shareeView._name] = shareeView
- shareeView.viewerHome()._children[shareeView._resourceID] = shareeView
- elif shareeView._bindStatus == _BIND_STATUS_DECLINED:
- if 1 == previouslyAcceptedBindCount:
- yield shareeView._deletedSyncToken(sharedRemoval=True)
- shareeView.viewerHome()._children.pop(shareeView._name, None)
- shareeView.viewerHome()._children.pop(shareeView._resourceID, None)
-
- if summary is not None:
- shareeView._bindMessage = columnMap[bind.MESSAGE]
-
- queryCacher = self._txn._queryCacher
- if queryCacher:
- cacheKey = queryCacher.keyForObjectWithName(shareeView._home._resourceID, shareeView._name)
- yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
- cacheKey = queryCacher.keyForObjectWithResourceID(shareeView._home._resourceID, shareeView._resourceID)
- yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
-
- shareeView._name = bindNameRows[0][0]
-
- # Must send notification to ensure cache invalidation occurs
- yield self.notifyPropertyChanged()
-
- returnValue(shareeView._name)
-
-
- @inlineCallbacks
- def unshareWith(self, shareeHome):
- """
- Remove the shared version of this (owned) L{CommonHomeChild} from the
- referenced L{CommonHome}.
-
- @see: L{CommonHomeChild.shareWith}
-
- @param shareeHome: The home with which this L{CommonHomeChild} was
- previously shared.
-
- @return: a L{Deferred} which will fire with the previous shareUID
- """
- sharedAddressBook = yield shareeHome.addressbookWithName(self.name())
- if sharedAddressBook:
-
- acceptedBindCount = 1 if sharedAddressBook.fullyShared() else 0
- acceptedBindCount += len((yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
- self._txn, homeID=shareeHome._resourceID, addressbookID=sharedAddressBook._resourceID
- )))
- if acceptedBindCount == 1:
- yield sharedAddressBook._deletedSyncToken(sharedRemoval=True)
- shareeHome._children.pop(self.name(), None)
- shareeHome._children.pop(sharedAddressBook._resourceID, None)
- elif not sharedAddressBook.fullyShared():
- # FIXME: remove objects for this group only using self.removeObjectResource
- self._objectNames = None
-
- # Must send notification to ensure cache invalidation occurs
- yield self.notifyPropertyChanged()
-
- # delete binds including invites
- deletedBindNameRows = yield self._deleteBindForResourceIDAndHomeID.on(self._txn, resourceID=self._resourceID,
- homeID=shareeHome._resourceID
- )
- if deletedBindNameRows:
- deletedBindName = deletedBindNameRows[0][0]
- queryCacher = self._txn._queryCacher
- if queryCacher:
- cacheKey = queryCacher.keyForObjectWithName(shareeHome._resourceID, self.name())
- queryCacher.invalidateAfterCommit(self._txn, cacheKey)
- else:
- deletedBindName = None
-
- self._initIsShared()
- returnValue(deletedBindName)
-
-
-
-class AddressBookObject(CommonObjectResource, AddressBookSharingMixIn):
-
implements(IAddressBookObject)
_homeSchema = schema.ADDRESSBOOK_HOME
@@ -1213,7 +1328,7 @@
if self.isGroupForSharedAddressBook() or self.shareUID():
raise HTTPError(FORBIDDEN)
- if not self.owned() and not self.addressbook().fullyShared():
+ if not self.owned() and self.addressbook().indirect():
readWriteObjectIDs = []
readWriteGroupIDs = yield self.addressbook().readWriteGroupIDs()
if readWriteGroupIDs:
@@ -1264,7 +1379,7 @@
returnValue(False)
# if fully shared and rw, must be RW since sharing group read-only has no affect
- if self.addressbook().fullyShared() and self.addressbook().shareMode() == _BIND_MODE_WRITE:
+ if not self.addressbook().indirect() and self.addressbook().shareMode() == _BIND_MODE_WRITE:
returnValue(True)
#otherwise, must be in a read-write group
@@ -1315,34 +1430,9 @@
@return: L{self} if object exists in the DB, else C{None}
"""
- rows = None
- if self.owned() or self.addressbook().fullyShared(): # owned or fully shared
- if self._name:
- rows = yield self._allColumnsWithParentAndName.on(
- self._txn, name=self._name,
- parentID=self._parentCollection._resourceID
- )
- elif self._uid:
- rows = yield self._allColumnsWithParentAndUID.on(
- self._txn, uid=self._uid,
- parentID=self._parentCollection._resourceID
- )
- elif self._resourceID:
- rows = yield self._allColumnsWithParentAndID.on(
- self._txn, resourceID=self._resourceID,
- parentID=self._parentCollection._resourceID
- )
-
- if not rows and self.addressbook().fullyShared(): # perhaps add special group
- if self._name:
- if self._name == self.addressbook()._groupForSharedAddressBookName():
- rows = [self.addressbook()._groupForSharedAddressBookRow()]
- elif self._uid:
- if self._uid == (yield self.addressbook()._groupForSharedAddressBookUID()):
- rows = [self.addressbook()._groupForSharedAddressBookRow()]
- elif self._resourceID:
- if self.isGroupForSharedAddressBook():
- rows = [self.addressbook()._groupForSharedAddressBookRow()]
+ abo = None
+ if self.owned() or not self.addressbook().indirect(): # owned or fully shared
+ abo = yield super(AddressBookObject, self).initFromStore()
else:
acceptedGroupIDs = yield self.addressbook().acceptedGroupIDs()
allowedObjectIDs = yield self.addressbook().expandGroupIDs(self._txn, acceptedGroupIDs)
@@ -1366,10 +1456,12 @@
rows = (yield self._allColumnsWithResourceID.on(
self._txn, resourceID=self._resourceID,
))
+ if rows:
+ self._initFromRow(tuple(rows[0]))
+ yield self._loadPropertyStore()
+ abo = self
- if rows:
- self._initFromRow(tuple(rows[0]))
-
+ if abo is not None:
if self._kind == _ABO_KIND_GROUP:
groupBindRows = yield AddressBookObject._bindForResourceIDAndHomeID.on(
@@ -1384,10 +1476,8 @@
self._bindMessage = bindMessage
self._bindName = bindName
- yield self._initIsShared()
+ #yield self._initIsShared()
- yield self._loadPropertyStore()
-
returnValue(self)
else:
returnValue(None)
@@ -1442,10 +1532,8 @@
@classmethod
@inlineCallbacks
def _allColumnsWithParent(cls, addressbook):
- if addressbook.owned() or addressbook.fullyShared():
+ if addressbook.owned() or not addressbook.indirect():
rows = yield super(AddressBookObject, cls)._allColumnsWithParent(addressbook)
- if addressbook.fullyShared():
- rows.append(addressbook._groupForSharedAddressBookRow())
else:
acceptedGroupIDs = yield addressbook.acceptedGroupIDs()
allowedObjectIDs = yield addressbook.expandGroupIDs(addressbook._txn, acceptedGroupIDs)
@@ -1467,10 +1555,8 @@
@inlineCallbacks
def _allColumnsWithParentAndNames(cls, addressbook, names):
- if addressbook.owned() or addressbook.fullyShared():
+ if addressbook.owned() or not addressbook.indirect():
rows = yield super(AddressBookObject, cls)._allColumnsWithParentAndNames(addressbook, names)
- if addressbook.fullyShared() and addressbook._groupForSharedAddressBookName() in names:
- rows.append(addressbook._groupForSharedAddressBookRow())
else:
acceptedGroupIDs = yield addressbook.acceptedGroupIDs()
allowedObjectIDs = yield addressbook.expandGroupIDs(addressbook._txn, acceptedGroupIDs)
@@ -1563,7 +1649,7 @@
raise InvalidUIDError("Cannot change the UID in an existing resource.")
else:
# for partially shared addressbooks, cannot use name that already exists in owner
- if not self.owned() and not self.addressbook().fullyShared():
+ if not self.owned() and self.addressbook().indirect():
nameElsewhere = (yield self.ownerHome().addressbook().addressbookObjectWithName(self.name()))
if nameElsewhere is not None:
raise ObjectResourceNameAlreadyExistsError(self.name() + ' in use by owning addressbook.')
@@ -1698,7 +1784,7 @@
foundUIDs.append(self._uid) # circular self reference is OK
missingUIDs = set(memberUIDs) - set(foundUIDs)
- if not self.owned() and not self.addressbook().fullyShared():
+ if not self.owned() and self.addressbook().indirect():
# in partially shared addressbook, all members UIDs must be inside the shared groups
# except during bulk operations, when other UIDs added are OK
coaddedUIDs = set() if self._options.get("coaddedUIDs") is None else self._options["coaddedUIDs"]
@@ -1765,7 +1851,7 @@
).on(self._txn)
groupIDs = set([groupIDRow[0] for groupIDRow in groupIDRows])
- if not self.owned() and not self.addressbook().fullyShared():
+ if not self.owned() and self.addressbook().indirect():
readWriteGroupIDs = yield self.addressbook().readWriteGroupIDs()
assert readWriteGroupIDs, "no access"
groupIDs |= set(readWriteGroupIDs)
@@ -2077,7 +2163,7 @@
if sharedAddressBook:
- acceptedBindCount = 1 if sharedAddressBook.fullyShared() else 0
+ acceptedBindCount = 1 if not sharedAddressBook.indirect() else 0
acceptedBindCount += len((
yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
self._txn, homeID=shareeHome._resourceID, addressbookID=sharedAddressBook._resourceID
@@ -2106,7 +2192,7 @@
else:
deletedBindName = None
- yield self._initIsShared()
+ #yield self._initIsShared()
returnValue(deletedBindName)
@@ -2234,7 +2320,7 @@
# count accepted
if status is not None:
- previouslyAcceptedBindCount = 1 if self.addressbook().fullyShared() else 0
+ previouslyAcceptedBindCount = 1 if not self.addressbook().indirect() else 0
previouslyAcceptedBindCount += len((
yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
self._txn, homeID=shareeView.viewerHome()._resourceID, addressbookID=self.addressbook()._resourceID
Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql.py 2013-11-18 19:24:16 UTC (rev 11962)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql.py 2013-11-18 21:59:19 UTC (rev 11963)
@@ -64,7 +64,8 @@
from txdav.common.datastore.common import HomeChildBase
from txdav.common.datastore.sql_tables import _BIND_MODE_OWN, \
_BIND_STATUS_ACCEPTED, _BIND_STATUS_DECLINED, _BIND_STATUS_INVALID, \
- _BIND_STATUS_INVITED, _BIND_MODE_DIRECT, _BIND_STATUS_DELETED
+ _BIND_STATUS_INVITED, _BIND_MODE_DIRECT, _BIND_STATUS_DELETED, \
+ _BIND_MODE_INDIRECT
from txdav.common.datastore.sql_tables import schema, splitSQLString
from txdav.common.icommondatastore import ConcurrentModification
from txdav.common.icommondatastore import HomeChildNameNotAllowedError, \
@@ -1516,10 +1517,13 @@
@classproperty
- def _resourceIDFromOwnerQuery(cls): #@NoSelf
+ def _homeColumnsFromOwnerQuery(cls): #@NoSelf
home = cls._homeSchema
- return Select([home.RESOURCE_ID],
- From=home, Where=home.OWNER_UID == Parameter("ownerUID"))
+ return Select(
+ cls.homeColumns(),
+ From=home,
+ Where=home.OWNER_UID == Parameter("ownerUID")
+ )
@classproperty
@@ -1539,6 +1543,34 @@
@classmethod
+ def homeColumns(cls):
+ """
+ Return a list of column names to retrieve when doing an ownerUID->home lookup.
+ """
+
+ # Common behavior is to have created and modified
+
+ return (
+ cls._homeSchema.RESOURCE_ID,
+ cls._homeSchema.OWNER_UID,
+ )
+
+
+ @classmethod
+ def homeAttributes(cls):
+ """
+ Return a list of attributes names to map L{homeColumns} to.
+ """
+
+ # Common behavior is to have created and modified
+
+ return (
+ "_resourceID",
+ "_ownerUID",
+ )
+
+
+ @classmethod
def metadataColumns(cls):
"""
Return a list of column name for retrieval of metadata. This allows
@@ -1579,13 +1611,14 @@
"""
result = yield self._cacher.get(self._ownerUID)
if result is None:
- result = yield self._resourceIDFromOwnerQuery.on(
+ result = yield self._homeColumnsFromOwnerQuery.on(
self._txn, ownerUID=self._ownerUID)
if result and not no_cache:
yield self._cacher.set(self._ownerUID, result)
if result:
- self._resourceID = result[0][0]
+ for attr, value in zip(self.homeAttributes(), result[0]):
+ setattr(self, attr, value)
queryCacher = self._txn._queryCacher
if queryCacher:
@@ -2929,6 +2962,26 @@
@inlineCallbacks
+ def indirectShareWithUser(self, shareeUID):
+ """
+ Create a indirect share with the specified user. An indirect share is one created as a
+ side-effect of some other object being shared.
+
+ NB no invitations are used with indirect sharing.
+
+ @param shareeUID: UID of the sharee
+ @type shareeUID: C{str}
+ """
+
+ # Ignore if it already exists
+ shareeView = yield self.shareeView(shareeUID)
+ if shareeView is None:
+ shareeView = yield self.createShare(shareeUID=shareeUID, mode=_BIND_MODE_INDIRECT)
+ yield shareeView.newShare()
+ returnValue(shareeView)
+
+
+ @inlineCallbacks
def uninviteUserFromShare(self, shareeUID):
"""
Remove a user from a share. Make sure a notification is sent as well.
@@ -2942,10 +2995,11 @@
if shareeView is not 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 shareeView.shareStatus() != _BIND_STATUS_ACCEPTED:
- yield self._removeInviteNotification(shareeView)
- else:
- yield self._sendInviteNotification(shareeView, notificationState=_BIND_STATUS_DELETED)
+ if shareeView.useInvite():
+ if shareeView.shareStatus() != _BIND_STATUS_ACCEPTED:
+ yield self._removeInviteNotification(shareeView)
+ else:
+ yield self._sendInviteNotification(shareeView, notificationState=_BIND_STATUS_DELETED)
# Remove the bind
yield self.removeShare(shareeView)
@@ -2957,7 +3011,7 @@
This share is being accepted.
"""
- if self.shareStatus() != _BIND_STATUS_ACCEPTED:
+ if self.useInvite() and self.shareStatus() != _BIND_STATUS_ACCEPTED:
ownerView = yield self.ownerView()
yield ownerView.updateShare(self, status=_BIND_STATUS_ACCEPTED)
yield self.newShare(displayname=summary)
@@ -2970,7 +3024,7 @@
This share is being declined.
"""
- if self.shareStatus() != _BIND_STATUS_DECLINED:
+ if self.useInvite() and self.shareStatus() != _BIND_STATUS_DECLINED:
ownerView = yield self.ownerView()
yield ownerView.updateShare(self, status=_BIND_STATUS_DECLINED)
yield self._sendReplyNotification(ownerView)
@@ -2983,10 +3037,10 @@
"""
ownerView = yield self.ownerView()
- if self.direct():
+ if self.useInvite():
+ yield self.declineShare()
+ else:
yield ownerView.removeShare(self)
- else:
- yield self.declineShare()
def newShare(self, displayname=None):
@@ -3004,8 +3058,8 @@
"""
invitations = yield self.sharingInvites()
- # remove direct shares as those are not "real" invitations
- invitations = filter(lambda x: x.mode != _BIND_MODE_DIRECT, invitations)
+ # remove direct/indirect shares as those are not "real" invitations
+ invitations = filter(lambda x: x.mode not in (_BIND_MODE_DIRECT, _BIND_MODE_INDIRECT), invitations)
invitations.sort(key=lambda invitation: invitation.shareeUID)
returnValue(invitations)
@@ -3191,7 +3245,7 @@
yield self.shareWith(
shareeHome,
mode=mode,
- status=_BIND_STATUS_INVITED if mode != _BIND_MODE_DIRECT else _BIND_STATUS_ACCEPTED,
+ status=_BIND_STATUS_INVITED if mode not in (_BIND_MODE_DIRECT, _BIND_MODE_INDIRECT) else _BIND_STATUS_ACCEPTED,
summary=summary,
)
shareeView = yield self.shareeView(shareeUID)
@@ -3228,10 +3282,11 @@
#remove None parameters, and substitute None for empty string
bind = self._bindSchema
- columnMap = dict([(k, v if v != "" else None)
- for k, v in {bind.BIND_MODE:mode,
- bind.BIND_STATUS:status,
- bind.MESSAGE:summary}.iteritems() if v is not None])
+ columnMap = dict([(k, v if v != "" else None) for k, v in {
+ bind.BIND_MODE:mode,
+ bind.BIND_STATUS:status,
+ bind.MESSAGE:summary
+ }.iteritems() if v is not None])
if len(columnMap):
@@ -3240,7 +3295,7 @@
resourceID=self._resourceID, homeID=shareeView._home._resourceID
)
- #update affected attributes
+ # Update affected attributes
if mode is not None:
shareeView._bindMode = columnMap[bind.BIND_MODE]
@@ -3359,7 +3414,8 @@
bind = self._bindSchema
yield self._updateBindColumnsQuery(
- {bind.BIND_REVISION : Parameter("revision"), }).on(
+ {bind.BIND_REVISION : Parameter("revision"), }
+ ).on(
self._txn,
revision=self._bindRevision,
resourceID=self._resourceID,
@@ -3442,6 +3498,24 @@
return self._bindMode == _BIND_MODE_DIRECT
+ def indirect(self):
+ """
+ Is this an "indirect" share?
+
+ @return: a boolean indicating whether it's indirect.
+ """
+ return self._bindMode == _BIND_MODE_INDIRECT
+
+
+ def useInvite(self):
+ """
+ Does this type of share use invitations?
+
+ @return: a boolean indicating whether invitations are used.
+ """
+ return self._bindMode not in (_BIND_MODE_DIRECT, _BIND_MODE_INDIRECT)
+
+
def shareUID(self):
"""
@see: L{ICalendar.shareUID}
@@ -3670,7 +3744,7 @@
"""
# FIXME: tests don't cover this as directly as they should.
rows = yield cls._acceptedBindForHomeID.on(
- home._txn, homeID=home._resourceID
+ home._txn, homeID=home._resourceID
)
names = [row[3] for row in rows]
returnValue(names)
Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_schema/current.sql 2013-11-18 19:24:16 UTC (rev 11962)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_schema/current.sql 2013-11-18 21:59:19 UTC (rev 11963)
@@ -169,6 +169,7 @@
insert into CALENDAR_BIND_MODE values (1, 'read' );
insert into CALENDAR_BIND_MODE values (2, 'write');
insert into CALENDAR_BIND_MODE values (3, 'direct');
+insert into CALENDAR_BIND_MODE values (4, 'indirect');
-- Enumeration of statuses
Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_tables.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_tables.py 2013-11-18 19:24:16 UTC (rev 11962)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_tables.py 2013-11-18 21:59:19 UTC (rev 11963)
@@ -174,6 +174,7 @@
_BIND_MODE_READ = _bindMode('read')
_BIND_MODE_WRITE = _bindMode('write')
_BIND_MODE_DIRECT = _bindMode('direct')
+_BIND_MODE_INDIRECT = _bindMode('indirect')
_addressBookObjectKind = _schemaConstants(
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140312/5f3f1da6/attachment.html>
More information about the calendarserver-changes
mailing list