<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[11963] CalendarServer/branches/users/cdaboo/sharing-in-the-store</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/11963">11963</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2013-11-18 13:59:19 -0800 (Mon, 18 Nov 2013)</dd>
</dl>
<h3>Log Message</h3>
<pre>Checkpoint changes: more api changes for carddav. Re-working group sharing support is next.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavresourcepy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavstorebridgepy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/storebridge.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretxdavcarddavdatastoresqlpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/carddav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretxdavcommondatastoresqlpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretxdavcommondatastoresql_schemacurrentsql">CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_schema/current.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretxdavcommondatastoresql_tablespy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_tables.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/resource.py (11962 => 11963)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -859,8 +859,12 @@
</span><span class="cx"> Return the DAV:owner property value (MUST be a DAV:href or None).
</span><span class="cx"> """
</span><span class="cx">
</span><del>- if self.isShareeResource():
- parent = (yield self.locateParent(request, self._share_url))
</del><ins>+ 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()))
</ins><span class="cx"> else:
</span><span class="cx"> parent = (yield self.locateParent(request, request.urlForResource(self)))
</span><span class="cx"> if parent and isinstance(parent, CalDAVResource):
</span><span class="lines">@@ -875,8 +879,12 @@
</span><span class="cx"> """
</span><span class="cx"> Return the DAV:owner property value (MUST be a DAV:href or None).
</span><span class="cx"> """
</span><del>- if self.isShareeResource():
- parent = (yield self.locateParent(request, self._share_url))
</del><ins>+ 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()))
</ins><span class="cx"> else:
</span><span class="cx"> parent = (yield self.locateParent(request, request.urlForResource(self)))
</span><span class="cx"> if parent and isinstance(parent, CalDAVResource):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavstorebridgepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/storebridge.py (11962 => 11963)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -3110,8 +3110,8 @@
</span><span class="cx"> call super and provision group share
</span><span class="cx"> """
</span><span class="cx"> abObjectResource = yield super(AddressBookCollectionResource, self).makeChild(name)
</span><del>- if abObjectResource.exists() and abObjectResource._newStoreObject.shareUID() is not None:
- abObjectResource = yield self.parentResource().provisionShare(abObjectResource)
</del><ins>+ #if abObjectResource.exists() and abObjectResource._newStoreObject.shareUID() is not None:
+ # abObjectResource = yield self.parentResource().provisionShare(abObjectResource)
</ins><span class="cx"> returnValue(abObjectResource)
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretxdavcarddavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/carddav/datastore/sql.py (11962 => 11963)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -55,7 +55,7 @@
</span><span class="cx"> from txdav.common.datastore.sql_tables import _ABO_KIND_PERSON, \
</span><span class="cx"> _ABO_KIND_GROUP, _ABO_KIND_RESOURCE, _ABO_KIND_LOCATION, schema, \
</span><span class="cx"> _BIND_MODE_OWN, _BIND_MODE_WRITE, _BIND_STATUS_ACCEPTED, \
</span><del>- _BIND_STATUS_DECLINED, _BIND_STATUS_INVITED
</del><ins>+ _BIND_STATUS_DECLINED, _BIND_STATUS_INVITED, _BIND_MODE_READ
</ins><span class="cx"> from txdav.common.icommondatastore import InternalDataStoreError, \
</span><span class="cx"> InvalidUIDError, UIDExistsError, ObjectResourceTooBigError, \
</span><span class="cx"> InvalidObjectResourceError, InvalidComponentForStoreError, \
</span><span class="lines">@@ -101,13 +101,36 @@
</span><span class="cx"> removeAddressBookWithName = CommonHome.removeChildWithName
</span><span class="cx">
</span><span class="cx">
</span><del>- @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"))
</del><ins>+ @classmethod
+ def homeColumns(cls):
+ """
+ Return a list of column names to retrieve when doing an ownerUID->home lookup.
+ """
</ins><span class="cx">
</span><ins>+ # Common behavior is to have created and modified
</ins><span class="cx">
</span><ins>+ 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",
+ )
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def initFromStore(self, no_cache=False):
</span><span class="cx"> """
</span><span class="lines">@@ -115,48 +138,22 @@
</span><span class="cx"> extra meta-data from the DB to avoid having to do DB queries for those
</span><span class="cx"> individually later.
</span><span class="cx"> """
</span><del>- 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)
</del><span class="cx">
</span><del>- if result:
- self._resourceID, self._addressbookPropertyStoreID = result[0]
</del><ins>+ result = yield super(AddressBookHome, self).initFromStore(no_cache)
+ if result is not None:
</ins><span class="cx">
</span><del>- 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()
-
</del><span class="cx"> # created owned address book
</span><span class="cx"> addressbook = AddressBook(
</span><span class="cx"> home=self,
</span><del>- name="addressbook", resourceID=self._resourceID,
- mode=_BIND_MODE_OWN, status=_BIND_STATUS_ACCEPTED,
</del><ins>+ name="addressbook",
+ resourceID=self._resourceID,
+ mode=_BIND_MODE_OWN,
+ status=_BIND_STATUS_ACCEPTED,
</ins><span class="cx"> )
</span><del>- self._created, self._modified = data
</del><span class="cx"> yield addressbook._loadPropertyStore()
</span><del>- yield addressbook._initIsShared()
</del><span class="cx"> self._addressbook = addressbook
</span><span class="cx">
</span><del>- returnValue(self)
- else:
- returnValue(None)
</del><ins>+ returnValue(result)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -204,6 +201,7 @@
</span><span class="cx"> """
</span><span class="cx"> super(AddressBookHome, self).removeUnacceptedShares()
</span><span class="cx">
</span><ins>+ # Remove group binds too
</ins><span class="cx"> bind = AddressBookObject._bindSchema
</span><span class="cx"> kwds = {"homeResourceID" : self._resourceID}
</span><span class="cx"> yield Delete(
</span><span class="lines">@@ -318,30 +316,173 @@
</span><span class="cx"> Sharing code shared between AddressBook and AddressBookObject
</span><span class="cx"> """
</span><span class="cx">
</span><del>- @inlineCallbacks
- def _isSharedOrInvited(self):
</del><ins>+ def sharedResourceType(self):
</ins><span class="cx"> """
</span><del>- return True if this L{AddressBook} is shared or invited
</del><ins>+ The sharing resource type
</ins><span class="cx"> """
</span><del>- sharedRows = []
- if self.owned():
- bind = self._bindSchema
- sharedRows = yield self._bindFor(
- (bind.RESOURCE_ID == Parameter("resourceID"))).on(
- self._txn, resourceID=self._resourceID,
- )
</del><ins>+ return "addressbook"
</ins><span class="cx">
</span><del>- returnValue(bool(sharedRows))
</del><span class="cx">
</span><ins>+ 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()
</ins><span class="cx">
</span><del>- @inlineCallbacks
- def _initIsShared(self):
- isShared = yield self._isSharedOrInvited()
- self.setShared(isShared)
</del><span class="cx">
</span><ins>+# @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()
</ins><span class="cx">
</span><span class="cx">
</span><del>-class AddressBook(CommonHomeChild, AddressBookSharingMixIn):
</del><ins>+# @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):
</ins><span class="cx"> """
</span><span class="cx"> SQL-based implementation of L{IAddressBook}.
</span><span class="cx"> """
</span><span class="lines">@@ -356,7 +497,7 @@
</span><span class="cx"> _objectSchema = schema.ADDRESSBOOK_OBJECT
</span><span class="cx">
</span><span class="cx">
</span><del>- def __init__(self, home, name, resourceID, mode, status, revision=0, message=None, ownerHome=None):
</del><ins>+ def __init__(self, home, name, resourceID, mode, status, revision=0, message=None, ownerHome=None, ownerName=None):
</ins><span class="cx"> ownerName = ownerHome.addressbook().name() if ownerHome else None
</span><span class="cx"> super(AddressBook, self).__init__(home, name, resourceID, mode, status, revision=revision, message=message, ownerHome=ownerHome, ownerName=ownerName)
</span><span class="cx"> self._index = PostgresLegacyABIndexEmulator(self)
</span><span class="lines">@@ -394,13 +535,6 @@
</span><span class="cx"> addressbookObjectsSinceToken = CommonHomeChild.objectResourcesSinceToken
</span><span class="cx">
</span><span class="cx">
</span><del>- 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()
-
-
</del><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _loadPropertyStore(self, props=None):
</span><span class="cx"> if props is None:
</span><span class="lines">@@ -426,13 +560,6 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><del>- def sharedResourceType(self):
- """
- The sharing resource type
- """
- return "addressbook"
-
-
</del><span class="cx"> def contentType(self):
</span><span class="cx"> """
</span><span class="cx"> The content type of addressbook objects is text/vcard.
</span><span class="lines">@@ -491,20 +618,22 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def listObjectResources(self):
</span><ins>+ # Check for non-group shared
+ if self.owned() or not self.indirect():
+ result = yield super(AddressBook, self).listObjectResources()
+ returnValue(result)
+
+ # Group shared
</ins><span class="cx"> if self._objectNames is None:
</span><del>- 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
- ))
</del><ins>+ acceptedGroupIDs = yield self.acceptedGroupIDs()
+ allowedObjectIDs = yield self.expandGroupIDs(self._txn, acceptedGroupIDs)
+ rows = (yield self._objectResourceNamesWithResourceIDsQuery(allowedObjectIDs).on(
+ self._txn, resourceIDs=allowedObjectIDs
+ ))
</ins><span class="cx"> objectNames = [row[0] for row in rows]
</span><span class="cx">
</span><span class="cx"> # account for fully-shared address book group
</span><del>- if self.fullyShared():
</del><ins>+ if not self.indirect():
</ins><span class="cx"> if not self._groupForSharedAddressBookName() in objectNames:
</span><span class="cx"> objectNames.append(self._groupForSharedAddressBookName())
</span><span class="cx"> self._objectNames = sorted(objectNames)
</span><span class="lines">@@ -514,18 +643,18 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def countObjectResources(self):
</span><ins>+ # Check for non-group shared
+ if self.owned() or not self.indirect():
+ result = yield super(AddressBook, self).countObjectResources()
+ returnValue(result)
+
+ # Group shared
</ins><span class="cx"> if self._objectNames is None:
</span><del>- 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)))
</del><ins>+ acceptedGroupIDs = yield self.acceptedGroupIDs()
+ count = len((yield self.expandGroupIDs(self._txn, acceptedGroupIDs)))
</ins><span class="cx">
</span><span class="cx"> # account for fully-shared address book group
</span><del>- if self.fullyShared():
</del><ins>+ if not self.indirect():
</ins><span class="cx"> count += 1
</span><span class="cx"> returnValue(count)
</span><span class="cx">
</span><span class="lines">@@ -618,6 +747,48 @@
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><ins>+ 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
</ins><span class="cx"> def loadAllObjects(cls, home):
</span><span class="cx"> """
</span><span class="cx"> Load all L{CommonHomeChild} instances which are children of a given
</span><span class="lines">@@ -626,78 +797,94 @@
</span><span class="cx"> operations to keep this constant wrt the number of children. This is an
</span><span class="cx"> optimization for Depth:1 operations on the home.
</span><span class="cx"> """
</span><del>- results = [home.addressbook()]
- ownerHomeToDataRowMap = {}
</del><span class="cx">
</span><del>- # 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
</del><ins>+ results = yield super(AddressBook, cls).loadAllObjects(home)
+ results.insert(0, home.addressbook())
+ returnValue(results)
</ins><span class="cx">
</span><del>- # 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
</del><span class="cx">
</span><del>- 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
- )
</del><ins>+# @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)
</ins><span class="cx">
</span><del>- addressbookResourceIDs = [ownerHome.addressbook()._resourceID for ownerHome in ownerHomeToDataRowMap]
- revisions = yield cls._revisionsForResourceIDs(addressbookResourceIDs).on(home._txn, resourceIDs=addressbookResourceIDs)
- revisions = dict(revisions)
</del><span class="cx">
</span><del>- # 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)
-
-
</del><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def objectWithName(cls, home, name, accepted=True):
</span><span class="lines">@@ -709,78 +896,99 @@
</span><span class="cx">
</span><span class="cx"> @param name: a string; the name of the L{CommonHomeChild} to retrieve.
</span><span class="cx">
</span><del>- @return: an L{CommonHomeChild} or C{None} if no such child
- exists.
</del><ins>+ @return: an L{CommonHomeChild} or C{None} if no such child exists.
</ins><span class="cx"> """
</span><del>- if accepted and name == home.addressbook().name():
</del><ins>+
+ if name == home.addressbook().name():
</ins><span class="cx"> returnValue(home.addressbook())
</span><del>- # shared address books only from this point on
</del><span class="cx">
</span><del>- rows = None
- queryCacher = home._txn._queryCacher
- ownerHome = None
</del><ins>+ result = yield super(AddressBook, cls).objectWithName(home, name, accepted)
+ returnValue(result)
</ins><span class="cx">
</span><del>- if queryCacher:
- # Retrieve data from cache
- cacheKey = queryCacher.keyForObjectWithName(home._resourceID, name)
- rows = yield queryCacher.get(cacheKey)
</del><span class="cx">
</span><del>- 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
</del><ins>+# @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)
</ins><span class="cx">
</span><del>- if rows and queryCacher:
- # Cache the result
- queryCacher.setAfterCommit(home._txn, cacheKey, rows)
</del><span class="cx">
</span><del>- 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)
-
-
</del><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def objectWithID(cls, home, resourceID, accepted=True):
</span><span class="lines">@@ -796,91 +1004,79 @@
</span><span class="cx"> if home._resourceID == resourceID:
</span><span class="cx"> returnValue(home.addressbook())
</span><span class="cx">
</span><del>- 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
</del><ins>+ result = yield super(AddressBook, cls).objectWithID(home, resourceID, accepted)
+ returnValue(result)
</ins><span class="cx">
</span><del>- if accepted is not None and (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
- returnValue(None)
</del><span class="cx">
</span><del>- 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)))
</del><ins>+# @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)
</ins><span class="cx">
</span><del>- 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
</del><span class="cx">
</span><del>- 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
-
-
</del><span class="cx"> @classmethod
</span><del>- @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
</del><span class="cx"> def _memberIDsWithGroupIDsQuery(cls, groupIDs):
</span><span class="cx"> """
</span><span class="cx"> DAL query to load all object resource names for a home child.
</span><span class="cx"> """
</span><span class="cx"> aboMembers = schema.ABO_MEMBERS
</span><del>- return Select([aboMembers.MEMBER_ID], From=aboMembers,
- Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs))),
- )
</del><ins>+ return Select(
+ [aboMembers.MEMBER_ID],
+ From=aboMembers,
+ Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs))),
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def expandGroupIDs(cls, txn, groupIDs, includeGroupIDs=True):
</span><span class="cx"> """
</span><del>- Get all AddressBookObject resource IDs contains in the given shared groups with the given groupIDs
</del><ins>+ Get all AddressBookObject resource IDs contained in the given shared groups with the given groupIDs
</ins><span class="cx"> """
</span><span class="cx"> objectIDs = set(groupIDs) if includeGroupIDs else set()
</span><span class="cx"> examinedIDs = set()
</span><span class="lines">@@ -898,33 +1094,44 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def unacceptedGroupIDs(self):
</span><ins>+ """
+ Return the list of shared groups that have not yet been accepted.
+ """
</ins><span class="cx"> if self.owned():
</span><span class="cx"> returnValue([])
</span><span class="cx"> else:
</span><span class="cx"> groupBindRows = yield AddressBookObject._unacceptedBindForHomeIDAndAddressBookID.on(
</span><del>- self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
</del><ins>+ self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
</ins><span class="cx"> )
</span><span class="cx"> returnValue([groupBindRow[2] for groupBindRow in groupBindRows])
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def acceptedGroupIDs(self):
</span><ins>+ """
+ Return the list of accepted shared groups.
+ """
</ins><span class="cx"> if self.owned():
</span><span class="cx"> returnValue([])
</span><span class="cx"> else:
</span><span class="cx"> groupBindRows = yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
</span><del>- self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
</del><ins>+ self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
</ins><span class="cx"> )
</span><span class="cx"> returnValue([groupBindRow[2] for groupBindRow in groupBindRows])
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def accessControlGroupIDs(self):
</span><ins>+ """
+ 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.
+ """
</ins><span class="cx"> if self.owned():
</span><span class="cx"> returnValue(([], []))
</span><span class="cx"> else:
</span><span class="cx"> groupBindRows = yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
</span><del>- self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
</del><ins>+ self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
</ins><span class="cx"> )
</span><span class="cx"> readWriteGroupIDs = []
</span><span class="cx"> readOnlyGroupIDs = []
</span><span class="lines">@@ -960,19 +1167,32 @@
</span><span class="cx"> # FIXME: Unused: Use for caching access
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def accessControlObjectIDs(self):
</span><ins>+ """
+ 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.
+ """
+
</ins><span class="cx"> readOnlyIDs = set()
</span><span class="cx"> readWriteIDs = set()
</span><del>- 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))
</del><ins>+
+ # 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, ])
</ins><span class="cx"> readOnlyIDs = set(ids)
</span><span class="cx">
</span><ins>+ # Look for shared groups and for those that tare read-write, transfer their object ids
+ # to the read-write set
</ins><span class="cx"> groupBindRows = yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
</span><del>- self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
</del><ins>+ self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
</ins><span class="cx"> )
</span><span class="cx"> readWriteGroupIDs = []
</span><span class="cx"> readOnlyGroupIDs = []
</span><span class="lines">@@ -1010,142 +1230,37 @@
</span><span class="cx"> returnValue((readOnlyIDs + readWriteIDs))
</span><span class="cx">
</span><span class="cx">
</span><del>- @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}.
</del><span class="cx">
</span><del>- @param shareeView: The sharee home child that shares this.
- @type shareeView: L{CommonHomeChild}
</del><ins>+class AddressBookObjectSharingMixIn(SharingMixIn):
+ """
+ Sharing code for AddressBookObject
+ """
</ins><span class="cx">
</span><del>- @param mode: The sharing mode; L{_BIND_MODE_READ} or
- L{_BIND_MODE_WRITE} or None to not update
- @type mode: L{str}
</del><ins>+# @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)
</ins><span class="cx">
</span><del>- @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}
</del><span class="cx">
</span><del>- @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}
</del><span class="cx">
</span><del>- @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}
</del><ins>+class AddressBookObject(CommonObjectResource, AddressBookObjectSharingMixIn):
</ins><span class="cx">
</span><del>- # 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):
-
</del><span class="cx"> implements(IAddressBookObject)
</span><span class="cx">
</span><span class="cx"> _homeSchema = schema.ADDRESSBOOK_HOME
</span><span class="lines">@@ -1213,7 +1328,7 @@
</span><span class="cx"> if self.isGroupForSharedAddressBook() or self.shareUID():
</span><span class="cx"> raise HTTPError(FORBIDDEN)
</span><span class="cx">
</span><del>- if not self.owned() and not self.addressbook().fullyShared():
</del><ins>+ if not self.owned() and self.addressbook().indirect():
</ins><span class="cx"> readWriteObjectIDs = []
</span><span class="cx"> readWriteGroupIDs = yield self.addressbook().readWriteGroupIDs()
</span><span class="cx"> if readWriteGroupIDs:
</span><span class="lines">@@ -1264,7 +1379,7 @@
</span><span class="cx"> returnValue(False)
</span><span class="cx">
</span><span class="cx"> # if fully shared and rw, must be RW since sharing group read-only has no affect
</span><del>- if self.addressbook().fullyShared() and self.addressbook().shareMode() == _BIND_MODE_WRITE:
</del><ins>+ if not self.addressbook().indirect() and self.addressbook().shareMode() == _BIND_MODE_WRITE:
</ins><span class="cx"> returnValue(True)
</span><span class="cx">
</span><span class="cx"> #otherwise, must be in a read-write group
</span><span class="lines">@@ -1315,34 +1430,9 @@
</span><span class="cx">
</span><span class="cx"> @return: L{self} if object exists in the DB, else C{None}
</span><span class="cx"> """
</span><del>- 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()]
</del><ins>+ abo = None
+ if self.owned() or not self.addressbook().indirect(): # owned or fully shared
+ abo = yield super(AddressBookObject, self).initFromStore()
</ins><span class="cx"> else:
</span><span class="cx"> acceptedGroupIDs = yield self.addressbook().acceptedGroupIDs()
</span><span class="cx"> allowedObjectIDs = yield self.addressbook().expandGroupIDs(self._txn, acceptedGroupIDs)
</span><span class="lines">@@ -1366,10 +1456,12 @@
</span><span class="cx"> rows = (yield self._allColumnsWithResourceID.on(
</span><span class="cx"> self._txn, resourceID=self._resourceID,
</span><span class="cx"> ))
</span><ins>+ if rows:
+ self._initFromRow(tuple(rows[0]))
+ yield self._loadPropertyStore()
+ abo = self
</ins><span class="cx">
</span><del>- if rows:
- self._initFromRow(tuple(rows[0]))
-
</del><ins>+ if abo is not None:
</ins><span class="cx"> if self._kind == _ABO_KIND_GROUP:
</span><span class="cx">
</span><span class="cx"> groupBindRows = yield AddressBookObject._bindForResourceIDAndHomeID.on(
</span><span class="lines">@@ -1384,10 +1476,8 @@
</span><span class="cx"> self._bindMessage = bindMessage
</span><span class="cx"> self._bindName = bindName
</span><span class="cx">
</span><del>- yield self._initIsShared()
</del><ins>+ #yield self._initIsShared()
</ins><span class="cx">
</span><del>- yield self._loadPropertyStore()
-
</del><span class="cx"> returnValue(self)
</span><span class="cx"> else:
</span><span class="cx"> returnValue(None)
</span><span class="lines">@@ -1442,10 +1532,8 @@
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _allColumnsWithParent(cls, addressbook):
</span><del>- if addressbook.owned() or addressbook.fullyShared():
</del><ins>+ if addressbook.owned() or not addressbook.indirect():
</ins><span class="cx"> rows = yield super(AddressBookObject, cls)._allColumnsWithParent(addressbook)
</span><del>- if addressbook.fullyShared():
- rows.append(addressbook._groupForSharedAddressBookRow())
</del><span class="cx"> else:
</span><span class="cx"> acceptedGroupIDs = yield addressbook.acceptedGroupIDs()
</span><span class="cx"> allowedObjectIDs = yield addressbook.expandGroupIDs(addressbook._txn, acceptedGroupIDs)
</span><span class="lines">@@ -1467,10 +1555,8 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _allColumnsWithParentAndNames(cls, addressbook, names):
</span><span class="cx">
</span><del>- if addressbook.owned() or addressbook.fullyShared():
</del><ins>+ if addressbook.owned() or not addressbook.indirect():
</ins><span class="cx"> rows = yield super(AddressBookObject, cls)._allColumnsWithParentAndNames(addressbook, names)
</span><del>- if addressbook.fullyShared() and addressbook._groupForSharedAddressBookName() in names:
- rows.append(addressbook._groupForSharedAddressBookRow())
</del><span class="cx"> else:
</span><span class="cx"> acceptedGroupIDs = yield addressbook.acceptedGroupIDs()
</span><span class="cx"> allowedObjectIDs = yield addressbook.expandGroupIDs(addressbook._txn, acceptedGroupIDs)
</span><span class="lines">@@ -1563,7 +1649,7 @@
</span><span class="cx"> raise InvalidUIDError("Cannot change the UID in an existing resource.")
</span><span class="cx"> else:
</span><span class="cx"> # for partially shared addressbooks, cannot use name that already exists in owner
</span><del>- if not self.owned() and not self.addressbook().fullyShared():
</del><ins>+ if not self.owned() and self.addressbook().indirect():
</ins><span class="cx"> nameElsewhere = (yield self.ownerHome().addressbook().addressbookObjectWithName(self.name()))
</span><span class="cx"> if nameElsewhere is not None:
</span><span class="cx"> raise ObjectResourceNameAlreadyExistsError(self.name() + ' in use by owning addressbook.')
</span><span class="lines">@@ -1698,7 +1784,7 @@
</span><span class="cx"> foundUIDs.append(self._uid) # circular self reference is OK
</span><span class="cx"> missingUIDs = set(memberUIDs) - set(foundUIDs)
</span><span class="cx">
</span><del>- if not self.owned() and not self.addressbook().fullyShared():
</del><ins>+ if not self.owned() and self.addressbook().indirect():
</ins><span class="cx"> # in partially shared addressbook, all members UIDs must be inside the shared groups
</span><span class="cx"> # except during bulk operations, when other UIDs added are OK
</span><span class="cx"> coaddedUIDs = set() if self._options.get("coaddedUIDs") is None else self._options["coaddedUIDs"]
</span><span class="lines">@@ -1765,7 +1851,7 @@
</span><span class="cx"> ).on(self._txn)
</span><span class="cx"> groupIDs = set([groupIDRow[0] for groupIDRow in groupIDRows])
</span><span class="cx">
</span><del>- if not self.owned() and not self.addressbook().fullyShared():
</del><ins>+ if not self.owned() and self.addressbook().indirect():
</ins><span class="cx"> readWriteGroupIDs = yield self.addressbook().readWriteGroupIDs()
</span><span class="cx"> assert readWriteGroupIDs, "no access"
</span><span class="cx"> groupIDs |= set(readWriteGroupIDs)
</span><span class="lines">@@ -2077,7 +2163,7 @@
</span><span class="cx">
</span><span class="cx"> if sharedAddressBook:
</span><span class="cx">
</span><del>- acceptedBindCount = 1 if sharedAddressBook.fullyShared() else 0
</del><ins>+ acceptedBindCount = 1 if not sharedAddressBook.indirect() else 0
</ins><span class="cx"> acceptedBindCount += len((
</span><span class="cx"> yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
</span><span class="cx"> self._txn, homeID=shareeHome._resourceID, addressbookID=sharedAddressBook._resourceID
</span><span class="lines">@@ -2106,7 +2192,7 @@
</span><span class="cx"> else:
</span><span class="cx"> deletedBindName = None
</span><span class="cx">
</span><del>- yield self._initIsShared()
</del><ins>+ #yield self._initIsShared()
</ins><span class="cx"> returnValue(deletedBindName)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -2234,7 +2320,7 @@
</span><span class="cx">
</span><span class="cx"> # count accepted
</span><span class="cx"> if status is not None:
</span><del>- previouslyAcceptedBindCount = 1 if self.addressbook().fullyShared() else 0
</del><ins>+ previouslyAcceptedBindCount = 1 if not self.addressbook().indirect() else 0
</ins><span class="cx"> previouslyAcceptedBindCount += len((
</span><span class="cx"> yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
</span><span class="cx"> self._txn, homeID=shareeView.viewerHome()._resourceID, addressbookID=self.addressbook()._resourceID
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql.py (11962 => 11963)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -64,7 +64,8 @@
</span><span class="cx"> from txdav.common.datastore.common import HomeChildBase
</span><span class="cx"> from txdav.common.datastore.sql_tables import _BIND_MODE_OWN, \
</span><span class="cx"> _BIND_STATUS_ACCEPTED, _BIND_STATUS_DECLINED, _BIND_STATUS_INVALID, \
</span><del>- _BIND_STATUS_INVITED, _BIND_MODE_DIRECT, _BIND_STATUS_DELETED
</del><ins>+ _BIND_STATUS_INVITED, _BIND_MODE_DIRECT, _BIND_STATUS_DELETED, \
+ _BIND_MODE_INDIRECT
</ins><span class="cx"> from txdav.common.datastore.sql_tables import schema, splitSQLString
</span><span class="cx"> from txdav.common.icommondatastore import ConcurrentModification
</span><span class="cx"> from txdav.common.icommondatastore import HomeChildNameNotAllowedError, \
</span><span class="lines">@@ -1516,10 +1517,13 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classproperty
</span><del>- def _resourceIDFromOwnerQuery(cls): #@NoSelf
</del><ins>+ def _homeColumnsFromOwnerQuery(cls): #@NoSelf
</ins><span class="cx"> home = cls._homeSchema
</span><del>- return Select([home.RESOURCE_ID],
- From=home, Where=home.OWNER_UID == Parameter("ownerUID"))
</del><ins>+ return Select(
+ cls.homeColumns(),
+ From=home,
+ Where=home.OWNER_UID == Parameter("ownerUID")
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classproperty
</span><span class="lines">@@ -1539,6 +1543,34 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><ins>+ 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
</ins><span class="cx"> def metadataColumns(cls):
</span><span class="cx"> """
</span><span class="cx"> Return a list of column name for retrieval of metadata. This allows
</span><span class="lines">@@ -1579,13 +1611,14 @@
</span><span class="cx"> """
</span><span class="cx"> result = yield self._cacher.get(self._ownerUID)
</span><span class="cx"> if result is None:
</span><del>- result = yield self._resourceIDFromOwnerQuery.on(
</del><ins>+ result = yield self._homeColumnsFromOwnerQuery.on(
</ins><span class="cx"> self._txn, ownerUID=self._ownerUID)
</span><span class="cx"> if result and not no_cache:
</span><span class="cx"> yield self._cacher.set(self._ownerUID, result)
</span><span class="cx">
</span><span class="cx"> if result:
</span><del>- self._resourceID = result[0][0]
</del><ins>+ for attr, value in zip(self.homeAttributes(), result[0]):
+ setattr(self, attr, value)
</ins><span class="cx">
</span><span class="cx"> queryCacher = self._txn._queryCacher
</span><span class="cx"> if queryCacher:
</span><span class="lines">@@ -2929,6 +2962,26 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><ins>+ 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
</ins><span class="cx"> def uninviteUserFromShare(self, shareeUID):
</span><span class="cx"> """
</span><span class="cx"> Remove a user from a share. Make sure a notification is sent as well.
</span><span class="lines">@@ -2942,10 +2995,11 @@
</span><span class="cx"> if shareeView is not None:
</span><span class="cx"> # If current user state is accepted then we send an invite with the new state, otherwise
</span><span class="cx"> # we cancel any existing invites for the user
</span><del>- if shareeView.shareStatus() != _BIND_STATUS_ACCEPTED:
- yield self._removeInviteNotification(shareeView)
- else:
- yield self._sendInviteNotification(shareeView, notificationState=_BIND_STATUS_DELETED)
</del><ins>+ if shareeView.useInvite():
+ if shareeView.shareStatus() != _BIND_STATUS_ACCEPTED:
+ yield self._removeInviteNotification(shareeView)
+ else:
+ yield self._sendInviteNotification(shareeView, notificationState=_BIND_STATUS_DELETED)
</ins><span class="cx">
</span><span class="cx"> # Remove the bind
</span><span class="cx"> yield self.removeShare(shareeView)
</span><span class="lines">@@ -2957,7 +3011,7 @@
</span><span class="cx"> This share is being accepted.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- if self.shareStatus() != _BIND_STATUS_ACCEPTED:
</del><ins>+ if self.useInvite() and self.shareStatus() != _BIND_STATUS_ACCEPTED:
</ins><span class="cx"> ownerView = yield self.ownerView()
</span><span class="cx"> yield ownerView.updateShare(self, status=_BIND_STATUS_ACCEPTED)
</span><span class="cx"> yield self.newShare(displayname=summary)
</span><span class="lines">@@ -2970,7 +3024,7 @@
</span><span class="cx"> This share is being declined.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- if self.shareStatus() != _BIND_STATUS_DECLINED:
</del><ins>+ if self.useInvite() and self.shareStatus() != _BIND_STATUS_DECLINED:
</ins><span class="cx"> ownerView = yield self.ownerView()
</span><span class="cx"> yield ownerView.updateShare(self, status=_BIND_STATUS_DECLINED)
</span><span class="cx"> yield self._sendReplyNotification(ownerView)
</span><span class="lines">@@ -2983,10 +3037,10 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> ownerView = yield self.ownerView()
</span><del>- if self.direct():
</del><ins>+ if self.useInvite():
+ yield self.declineShare()
+ else:
</ins><span class="cx"> yield ownerView.removeShare(self)
</span><del>- else:
- yield self.declineShare()
</del><span class="cx">
</span><span class="cx">
</span><span class="cx"> def newShare(self, displayname=None):
</span><span class="lines">@@ -3004,8 +3058,8 @@
</span><span class="cx"> """
</span><span class="cx"> invitations = yield self.sharingInvites()
</span><span class="cx">
</span><del>- # remove direct shares as those are not "real" invitations
- invitations = filter(lambda x: x.mode != _BIND_MODE_DIRECT, invitations)
</del><ins>+ # 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)
</ins><span class="cx"> invitations.sort(key=lambda invitation: invitation.shareeUID)
</span><span class="cx"> returnValue(invitations)
</span><span class="cx">
</span><span class="lines">@@ -3191,7 +3245,7 @@
</span><span class="cx"> yield self.shareWith(
</span><span class="cx"> shareeHome,
</span><span class="cx"> mode=mode,
</span><del>- status=_BIND_STATUS_INVITED if mode != _BIND_MODE_DIRECT else _BIND_STATUS_ACCEPTED,
</del><ins>+ status=_BIND_STATUS_INVITED if mode not in (_BIND_MODE_DIRECT, _BIND_MODE_INDIRECT) else _BIND_STATUS_ACCEPTED,
</ins><span class="cx"> summary=summary,
</span><span class="cx"> )
</span><span class="cx"> shareeView = yield self.shareeView(shareeUID)
</span><span class="lines">@@ -3228,10 +3282,11 @@
</span><span class="cx">
</span><span class="cx"> #remove None parameters, and substitute None for empty string
</span><span class="cx"> bind = self._bindSchema
</span><del>- 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])
</del><ins>+ 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])
</ins><span class="cx">
</span><span class="cx"> if len(columnMap):
</span><span class="cx">
</span><span class="lines">@@ -3240,7 +3295,7 @@
</span><span class="cx"> resourceID=self._resourceID, homeID=shareeView._home._resourceID
</span><span class="cx"> )
</span><span class="cx">
</span><del>- #update affected attributes
</del><ins>+ # Update affected attributes
</ins><span class="cx"> if mode is not None:
</span><span class="cx"> shareeView._bindMode = columnMap[bind.BIND_MODE]
</span><span class="cx">
</span><span class="lines">@@ -3359,7 +3414,8 @@
</span><span class="cx">
</span><span class="cx"> bind = self._bindSchema
</span><span class="cx"> yield self._updateBindColumnsQuery(
</span><del>- {bind.BIND_REVISION : Parameter("revision"), }).on(
</del><ins>+ {bind.BIND_REVISION : Parameter("revision"), }
+ ).on(
</ins><span class="cx"> self._txn,
</span><span class="cx"> revision=self._bindRevision,
</span><span class="cx"> resourceID=self._resourceID,
</span><span class="lines">@@ -3442,6 +3498,24 @@
</span><span class="cx"> return self._bindMode == _BIND_MODE_DIRECT
</span><span class="cx">
</span><span class="cx">
</span><ins>+ 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)
+
+
</ins><span class="cx"> def shareUID(self):
</span><span class="cx"> """
</span><span class="cx"> @see: L{ICalendar.shareUID}
</span><span class="lines">@@ -3670,7 +3744,7 @@
</span><span class="cx"> """
</span><span class="cx"> # FIXME: tests don't cover this as directly as they should.
</span><span class="cx"> rows = yield cls._acceptedBindForHomeID.on(
</span><del>- home._txn, homeID=home._resourceID
</del><ins>+ home._txn, homeID=home._resourceID
</ins><span class="cx"> )
</span><span class="cx"> names = [row[3] for row in rows]
</span><span class="cx"> returnValue(names)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretxdavcommondatastoresql_schemacurrentsql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_schema/current.sql (11962 => 11963)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -169,6 +169,7 @@
</span><span class="cx"> insert into CALENDAR_BIND_MODE values (1, 'read' );
</span><span class="cx"> insert into CALENDAR_BIND_MODE values (2, 'write');
</span><span class="cx"> insert into CALENDAR_BIND_MODE values (3, 'direct');
</span><ins>+insert into CALENDAR_BIND_MODE values (4, 'indirect');
</ins><span class="cx">
</span><span class="cx"> -- Enumeration of statuses
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretxdavcommondatastoresql_tablespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/common/datastore/sql_tables.py (11962 => 11963)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -174,6 +174,7 @@
</span><span class="cx"> _BIND_MODE_READ = _bindMode('read')
</span><span class="cx"> _BIND_MODE_WRITE = _bindMode('write')
</span><span class="cx"> _BIND_MODE_DIRECT = _bindMode('direct')
</span><ins>+_BIND_MODE_INDIRECT = _bindMode('indirect')
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> _addressBookObjectKind = _schemaConstants(
</span></span></pre>
</div>
</div>
</body>
</html>