[CalendarServer-changes] [11679] CalendarServer/branches/users/gaya/sharedgroupfixes/txdav
source_changes at macosforge.org
source_changes at macosforge.org
Thu Sep 12 14:24:21 PDT 2013
Revision: 11679
http://trac.calendarserver.org//changeset/11679
Author: gaya at apple.com
Date: 2013-09-12 14:24:21 -0700 (Thu, 12 Sep 2013)
Log Message:
-----------
checkpoint
Modified Paths:
--------------
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py 2013-09-12 21:17:25 UTC (rev 11678)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py 2013-09-12 21:24:21 UTC (rev 11679)
@@ -453,7 +453,88 @@
return False
+ @classproperty
+ def _deleteBumpTokenQuery(cls): #@NoSelf
+ rev = cls._revisionsSchema
+ return Update({rev.REVISION: schema.REVISION_SEQ,
+ rev.DELETED: True},
+ Where=(rev.RESOURCE_ID == Parameter("resourceID")).And(
+ rev.RESOURCE_NAME == Parameter("name")).And(
+ rev.OBJECT_RESOURCE_ID == Parameter("id")),
+ Return=rev.REVISION)
+
+
+ @classproperty
+ def _unshareBumpTokenQuery(cls): #@NoSelf
+ rev = cls._revisionsSchema
+ return Update({rev.REVISION: schema.REVISION_SEQ,
+ rev.OBJECT_RESOURCE_ID: Parameter("id"),
+ rev.UNSHARED: True,
+ },
+ Where=(rev.RESOURCE_ID == Parameter("resourceID")).And(
+ rev.RESOURCE_NAME == Parameter("name")),
+ Return=rev.REVISION)
+
+
@inlineCallbacks
+ def _changeRevision(self, action, name, id=0):
+
+ # Need to handle the case where for some reason the revision entry is
+ # actually missing. For a "delete" we don't care, for an "update" we
+ # will turn it into an "insert".
+ if action == "delete":
+ rows = (
+ yield self._deleteBumpTokenQuery.on(
+ self._txn, resourceID=self._resourceID, name=name, id=id))
+ if rows:
+ self._syncTokenRevision = rows[0][0]
+ elif action == "unshare":
+ rows = (
+ yield self._unshareBumpTokenQuery.on(
+ self._txn, resourceID=self._resourceID, name=name, id=id))
+ if rows:
+ self._syncTokenRevision = rows[0][0]
+ elif action == "update":
+ rows = (
+ yield self._updateBumpTokenQuery.on(
+ self._txn, resourceID=self._resourceID, name=name))
+ if rows:
+ self._syncTokenRevision = rows[0][0]
+ else:
+ action = "insert"
+
+ if action == "insert":
+ # Note that an "insert" may happen for a resource that previously
+ # existed and then was deleted. In that case an entry in the
+ # REVISIONS table still exists so we have to detect that and do db
+ # INSERT or UPDATE as appropriate
+
+ found = bool((
+ yield self._insertFindPreviouslyNamedQuery.on(
+ self._txn, resourceID=self._resourceID, name=name)))
+ if found:
+ self._syncTokenRevision = (
+ yield self._updatePreviouslyNamedQuery.on(
+ self._txn, resourceID=self._resourceID, name=name)
+ )[0][0]
+ else:
+ self._syncTokenRevision = (
+ yield self._completelyNewRevisionQuery.on(
+ self._txn, homeID=self.ownerHome()._resourceID,
+ resourceID=self._resourceID, name=name)
+ )[0][0]
+ self._maybeNotify()
+
+
+ def _deleteRevision(self, name, id):
+ return self._changeRevision("delete", name, id)
+
+
+ def _unshareRevision(self, name, id):
+ return self._changeRevision("unshare", name, id)
+
+
+ @inlineCallbacks
def resourceNamesSinceRevision(self, revision):
"""
Return the changed and deleted resources since a particular revision. This implementation takes
@@ -495,6 +576,11 @@
record a revision for the sharee home and sharee collection name with the "deleted" flag set. That way
the shared collection can be reported as removed.
+ For shared groups. Find the items that have be added and removed since revision in the aboMembers
+ tables. Then add in changes from the revision table.
+
+ TODO: Cover the case where the sharing changes. Thinking now that I need a unshared group tombstone.
+
@param revision: the sync revision to compare to
@type revision: C{str}
@param depth: depth for determine what changed
@@ -507,147 +593,92 @@
if self.fullyShared():
returnValue((yield super(AddressBook, self).sharedChildResourceNamesSinceRevision(revision, depth)))
+ path = self.name()
changed = set()
deleted = set()
groupBindRows = yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
)
- acceptedGroupIDs = [groupBindRow[2] for groupBindRow in groupBindRows]
+ acceptedGroupIDs = set([groupBindRow[2] for groupBindRow in groupBindRows])
minRevision = min([groupBindRow[5] for groupBindRow in groupBindRows])
sharerevision = 0 if revision < minRevision else revision
- print("sharedChildResourceNamesSinceRevision:%s minRevision:%s, sharerevision:%s" % (self, minRevision, sharerevision))
+ print("sharedChildResourceNamesSinceRevision:%s acceptedGroupIDs:%s, minRevision:%s, sharerevision:%s" % (self, acceptedGroupIDs, minRevision, sharerevision))
# get revision table changes
rev = self._revisionsSchema
results = [(
name,
+ id,
wasdeleted,
- ) for name, wasdeleted in (
- yield Select([rev.RESOURCE_NAME, rev.DELETED],
+ wasunshared,
+ ) for name, id, wasdeleted, wasunshared in (
+ yield Select([rev.RESOURCE_NAME, rev.OBJECT_RESOURCE_ID, rev.DELETED, rev.UNSHARED],
From=rev,
Where=(rev.REVISION > sharerevision).And(
rev.RESOURCE_ID == self._resourceID)).on(self._txn)
) if name
]
print("sharedChildResourceNamesSinceRevision:%s results:%s" % (self, results))
+ allowedObjectIDs = set((yield self.expandGroupIDs(self._txn, acceptedGroupIDs)))
+ print("sharedChildResourceNamesSinceRevision:%s allowedObjectIDs=%s," % (self, allowedObjectIDs,))
- # get members table changes
- aboMembers = schema.ABO_MEMBERS
- memberRows = yield Select(
- [aboMembers.GROUP_ID, aboMembers.MEMBER_ID, aboMembers.REMOVED],
- From=aboMembers,
- Where=(aboMembers.REVISION > sharerevision).And(
- aboMembers.ADDRESSBOOK_ID == self._resourceID)
- ).on(self._txn)
- groupIDToChangedMemberIDMap = {}
- for groupID, memberID, removed in memberRows:
- if groupID not in groupIDToChangedMemberIDMap:
- groupIDToChangedMemberIDMap[groupID] = set()
- groupIDToChangedMemberIDMap[groupID].add(memberID)
- print("sharedChildResourceNamesSinceRevision:%s memberRows:%s groupIDToChangedMemberIDMap=%s" % (self, memberRows, groupIDToChangedMemberIDMap,))
+ # get unshared groups from revisions
+ unsharedGroupIDs = set([id for name, id, wasdeleted, wasunshared in results if wasunshared])
+ print("sharedChildResourceNamesSinceRevision:%s allowedObjectIDs=%s," % (self, unsharedGroupIDs,))
- allowedObjectIDs = None
- @inlineCallbacks
- def allowedObjectIDs(acceptedGroupIDs):
- if not hasattr(self, "__allowedObjectIDs"):
- self.__allowedObjectIDs = yield self.expandGroupIDs(self._txn, acceptedGroupIDs)
- returnValue(self.__allowedObjectIDs)
+ oldAllowedObjectIDs = set((yield self.expandGroupIDs(self._txn, acceptedGroupIDs | unsharedGroupIDs, revision)))
+ print("sharedChildResourceNamesSinceRevision:%s oldAllowedObjectIDs=%s," % (self, oldAllowedObjectIDs,))
- @inlineCallbacks
- def getMissingNames(ids, idToNameMap):
- idsForMissingNames = list(set(ids) - set(idToNameMap.keys()))
- if idsForMissingNames:
- abo = schema.ADDRESSBOOK_OBJECT
- memberIDNameRows = (
- yield AddressBookObject._columnsWithResourceIDsQuery(
- [abo.RESOURCE_ID, abo.RESOURCE_NAME],
- idsForMissingNames
- ).on(self._txn, resourceIDs=idsForMissingNames)
- )
- idToNameMap = dict(dict(idToNameMap), **dict(memberIDNameRows))
+ addedObjectIds = allowedObjectIDs - oldAllowedObjectIDs
+ removedObjectIds = oldAllowedObjectIDs - allowedObjectIDs
+ print("sharedChildResourceNamesSinceRevision:%s addedObjectIds=%s," % (self, addedObjectIds,))
+ print("sharedChildResourceNamesSinceRevision:%s removedObjectIds=%s," % (self, removedObjectIds,))
- returnValue(idToNameMap)
+ idToNameMap = dict([(name, id) for name, id, wasdeleted, wasunshared in results if id != 0])
+ missingNameIds = (allowedObjectIDs | oldAllowedObjectIDs) - set(idToNameMap.keys())
+ if missingNameIds:
+ abo = schema.ADDRESSBOOK_OBJECT
+ memberIDNameRows = (
+ yield AddressBookObject._columnsWithResourceIDsQuery(
+ [abo.RESOURCE_ID, abo.RESOURCE_NAME],
+ missingNameIds
+ ).on(self._txn, resourceIDs=missingNameIds)
+ )
+ idToNameMap = dict(dict(idToNameMap), **dict(memberIDNameRows))
- allIDs = set()
- for groupID, memberID, removed in memberRows:
- allIDs.add(groupID)
- allIDs.add(memberID)
-
- idToNameMap = yield getMissingNames(allIDs, {})
print("sharedChildResourceNamesSinceRevision:%s idToNameMap=%s" % (self, idToNameMap,))
+ #nameToIDMap = dict([(v, k) for k, v in idToNameMap.iteritems()])
- path = self.name()
- removedMemberIDs = set()
- changedMemberIDs = set()
+ # for changes, get object names all at once here
+ if sharerevision:
+ if depth == "1":
+ if removedObjectIds:
+ changed.add("%s/" % (path,))
+ else:
+ for removedObjectId in removedObjectIds:
+ deleted.add("%s/%s" % (path, idToNameMap[removedObjectId],))
- # TODO: use for query
- allowedObjectIDs = yield self.expandGroupIDs(self._txn, acceptedGroupIDs)
- print("sharedChildResourceNamesSinceRevision:%s acceptedGroupIDs:%s allowedObjectIDs=%s," % (self, acceptedGroupIDs, allowedObjectIDs,))
-
- for groupID, memberID, removed in memberRows:
-
- if memberID not in allowedObjectIDs or groupID not in allowedObjectIDs:
- print("sharedChildResourceNamesSinceRevision:%s SKIP groupID:%s memberID=%s, wasdeleted=%s" % (self, groupID, memberID, wasdeleted,))
- continue
- print("sharedChildResourceNamesSinceRevision:%s groupID:%s memberID=%s, wasdeleted=%s" % (self, groupID, memberID, wasdeleted,))
-
- if removed:
- if sharerevision:
- if depth == "1":
- changed.add("%s/" % (path,))
- else:
- removedMemberIDs.add(memberID)
-
- # Always report collection as changed
- changed.add("%s/" % (path,))
-
- # Resource changed - for depth "infinity" report resource as changed
if depth != "1":
- changedMemberIDs.add(memberID)
- changedMemberIDs.add(groupID)
+ for addedObjectID in addedObjectIds:
+ changed.add("%s/%s" % (path, idToNameMap[addedObjectID],))
- print("sharedChildResourceNamesSinceRevision:%s changed:%s deleted=%s, removedMemberIDs=%s, changedMemberIDs=%s" % (self, changed, deleted, removedMemberIDs, changedMemberIDs))
+ for name, id, wasdeleted, wasunshared in results:
+ if name in idToNameMap.values():
+ # Always report collection as changed
+ changed.add("%s/" % (path,))
- '''
- nameToIDMap = dict([(v, k) for k, v in idToNameMap.iteritems()])
+ # Resource changed - for depth "infinity" report resource as changed
+ if depth != "1":
+ item = "%s/%s" % (path, name,)
+ if item not in deleted:
+ changed.add("%s/%s" % (path, name,))
- for name, wasdeleted in results:
+ returnValue((changed, deleted))
- id = nameToIDMap[name]
- if id not in allowedObjectIDs:
- print("sharedChildResourceNamesSinceRevision:%s SKIP name:%s id=%s, wasdeleted=%s" % (self, name, id, wasdeleted,))
- continue
- print("sharedChildResourceNamesSinceRevision:%s name:%s id=%s, wasdeleted=%s" % (self, name, id, wasdeleted,))
- if wasdeleted:
- if sharerevision:
- if depth == "1":
- changed.add("%s/" % (path,))
- else:
- deleted.add("%s/%s" % (path, name,))
- # Always report collection as changed
- changed.add("%s/" % (path,))
- # Resource changed - for depth "infinity" report resource as changed
- if depth != "1":
- changed.add("%s/%s" % (path, name,))
-
- print("sharedChildResourceNamesSinceRevision:%s changed:%s deleted=%s, removedMemberIDs=%s, changedMemberIDs=%s" % (self, changed, deleted, removedMemberIDs, changedMemberIDs))
- '''
-
- # for changes, get object names all at once here
- idToNameMap = yield getMissingNames(removedMemberIDs | changedMemberIDs, idToNameMap)
- for removedMemberID in removedMemberIDs:
- deleted.add("%s/%s" % (path, idToNameMap[removedMemberID],))
-
- for changedMemberID in changedMemberIDs:
- changed.add("%s/%s" % (path, idToNameMap[changedMemberID],))
-
- returnValue((changed, deleted))
-
-
@inlineCallbacks
def _loadPropertyStore(self, props=None):
if props is None:
@@ -693,7 +724,7 @@
@inlineCallbacks
def removedObjectResource(self, child):
"""
- just like CommonHomeChild.removedObjectResource() but does not call self._deleteRevision(child.name())
+ just like CommonHomeChild.removedObjectResource() but does not call self._deleteRevision()
"""
self._objects.pop(child.name(), None)
self._objects.pop(child.uid(), None)
@@ -1177,15 +1208,28 @@
DAL query to load all object resource names for a home child.
"""
aboMembers = schema.ABO_MEMBERS
- return Select([aboMembers.MEMBER_ID], From=aboMembers,
+ return Select([aboMembers.MEMBER_ID, aboMembers.REMOVED, Max(aboMembers.REVISION)], From=aboMembers,
+ Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs))),
+ GroupBy=(aboMembers.MEMBER_ID, aboMembers.REMOVED)
+ )
+
+
+ @classmethod
+ def _memberIDsWithGroupIDsAndRevisionQuery(cls, groupIDs):
+ """
+ DAL query to load all object resource names for a home child.
+ """
+ aboMembers = schema.ABO_MEMBERS
+ return Select([aboMembers.MEMBER_ID, aboMembers.REMOVED, Max(aboMembers.REVISION)], From=aboMembers,
Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs)))
- .And(aboMembers.REMOVED == False),
- )
+ .And(aboMembers.REVISION <= Parameter("revision")),
+ GroupBy=(aboMembers.MEMBER_ID, aboMembers.REMOVED)
+ )
@classmethod
@inlineCallbacks
- def expandGroupIDs(cls, txn, groupIDs, includeGroupIDs=True):
+ def expandGroupIDs(cls, txn, groupIDs, atRevision=0, includeGroupIDs=True):
"""
Get all AddressBookObject resource IDs contains in the given shared groups with the given groupIDs
"""
@@ -1193,10 +1237,15 @@
examinedIDs = set()
remainingIDs = set(groupIDs)
while remainingIDs:
- memberRows = yield cls._memberIDsWithGroupIDsQuery(remainingIDs).on(
- txn, groupIDs=remainingIDs
- )
- objectIDs |= set(memberRow[0] for memberRow in memberRows)
+ if atRevision == 0:
+ memberRows = yield cls._memberIDsWithGroupIDsQuery(remainingIDs).on(
+ txn, groupIDs=remainingIDs
+ )
+ else:
+ memberRows = yield cls._memberIDsWithGroupIDsAndRevisionQuery(remainingIDs).on(
+ txn, groupIDs=remainingIDs, revision=atRevision
+ )
+ objectIDs |= set(memberRow[0] for memberRow in memberRows if not memberRow[1]) # not removed
examinedIDs |= remainingIDs
remainingIDs = objectIDs - examinedIDs
@@ -1433,19 +1482,19 @@
@return: a L{Deferred} which will fire with the previous shareUID
"""
- sharedAddressBook = yield shareeHome.addressbookWithName(self.shareeName())
- if sharedAddressBook:
+ shareeAddressBook = yield shareeHome.addressbookWithName(self.shareeName())
+ if shareeAddressBook:
- acceptedBindCount = 1 if sharedAddressBook.fullyShared() else 0
+ acceptedBindCount = 1 if shareeAddressBook.fullyShared() else 0
acceptedBindCount += len((yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
- self._txn, homeID=shareeHome._resourceID, addressbookID=sharedAddressBook._resourceID
+ self._txn, homeID=shareeHome._resourceID, addressbookID=shareeAddressBook._resourceID
)))
if acceptedBindCount == 1:
- yield sharedAddressBook._deletedSyncToken(sharedRemoval=True)
+ yield shareeAddressBook._deletedSyncToken(sharedRemoval=True)
shareeHome._children.pop(self.shareeName(), None)
- shareeHome._children.pop(sharedAddressBook._resourceID, None)
- elif not sharedAddressBook.fullyShared():
- # FIXME: remove objects for this group only using self.removeObjectResource
+ shareeHome._children.pop(shareeAddressBook._resourceID, None)
+ elif not shareeAddressBook.fullyShared():
+ # FIXME: remove objects for this group only using self.removeObjectResource()
self._objectNames = None
# Must send notification to ensure cache invalidation occurs
@@ -1540,55 +1589,24 @@
if self._resourceID not in readWriteObjectIDs:
raise HTTPError(FORBIDDEN)
- '''
- # get sync token for delete now
- yield self.addressbook()._deleteRevision(self.name())
-
- # remove group memberships that make this UID visible
- abo = schema.ADDRESSBOOK_OBJECT
- groupIDRows = (
- yield Select([abo.RESOURCE_ID],
- From=abo,
- Where=(abo.KIND == _ABO_KIND_GROUP)
- .And(abo.RESOURCE_ID.In(Parameter("readWriteObjectIDs", len(readWriteObjectIDs)))),
- ).on(self._txn, readWriteObjectIDs=readWriteObjectIDs)
- )
- groupIDs = [groupIDRow[0] for groupIDRow in groupIDRows]
- if groupIDs:
- print("remove:%s _removeMemberIDFromGroupIDsQuery:groupIDs=%s memberID=%s" % (self, groupIDs, self._resourceID,))
- yield self._removeMemberIDFromGroupIDsQuery(groupIDs).on(self._txn,
- groupIDs=groupIDs,
- addressbookID=self._ownerAddressBookResourceID,
- memberID=self._resourceID,
- revision=self._syncTokenRevision,
- )
-
- else:
- # get sync token for delete now
- yield self.addressbook()._deleteRevision(self.name())
- '''
-
# get sync token for delete now
- yield self.addressbook()._deleteRevision(self.name())
+ yield self.addressbook()._deleteRevision(self.name(), self._resourceID)
aboMembers = schema.ABO_MEMBERS
- groupIDRevisions = yield Select(
- [aboMembers.GROUP_ID, aboMembers.REVISION],
+ groupIDRows = yield Select(
+ [aboMembers.GROUP_ID],
From=aboMembers,
Where=(aboMembers.MEMBER_ID == self._resourceID)
.And(aboMembers.REMOVED == False),
).on(self._txn)
+ groupIDs = [groupIDRow[0] for groupIDRow in groupIDRows]
- groupIDsToRemoveFrom = (
- [groupIDRow[0] for groupIDRow in groupIDRevisions]
- if self.owned() or self.addressbook().fullyShared()
- else readWriteObjectIDs
- )
+ groupIDsToRemoveFrom = groupIDs if self.owned() or self.addressbook().fullyShared() else readWriteObjectIDs
if groupIDsToRemoveFrom:
# remove memberships
- print("remove:%s _removeMemberIDFromGroupIDsQuery:revision=%s groupIDs=%s memberID=%s" % (self, self._ownerAddressBookResourceID, groupIDsToRemoveFrom, self._resourceID,))
- yield self._removeMemberIDFromGroupIDsQuery(groupIDsToRemoveFrom).on(self._txn,
+ print("remove:%s _removeMemberIDFromGroupsQuery:revision=%s groupIDs=%s memberID=%s" % (self, self._syncTokenRevision, groupIDsToRemoveFrom, self._resourceID,))
+ yield self._removeMemberIDFromGroupsQuery(groupIDsToRemoveFrom).on(self._txn,
groupIDs=groupIDsToRemoveFrom,
addressbookID=self._ownerAddressBookResourceID,
memberID=self._resourceID,
@@ -1598,15 +1616,14 @@
# add to foreign member table row by UID (aboForeignMembers on address books)
memberAddress = "urn:uuid:" + self._uid
aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
- if groupIDRevisions:
- print("updateDatabase:%s _insertForeignMemberAddrQuery:revision=%s foreignMemberAddrToAdd=%s" % (self, self._syncTokenRevision, groupIDRevisions,))
+ if groupIDs:
+ print("updateDatabase:%s foreignMemberAddrToAdd=%s" % (self, groupIDs,))
- for groupID, revision in groupIDRevisions:#set(groupIDs) - set([self._ownerAddressBookResourceID]):
+ for groupID in groupIDs:#set(groupIDs) - set([self._ownerAddressBookResourceID]):
yield Insert(
{aboForeignMembers.GROUP_ID: groupID,
aboForeignMembers.ADDRESSBOOK_ID: self._ownerAddressBookResourceID,
- aboForeignMembers.MEMBER_ADDRESS: memberAddress,
- aboForeignMembers.REVISION: revision, }
+ aboForeignMembers.MEMBER_ADDRESS: memberAddress, }
).on(self._txn)
if self.kind() == _ABO_KIND_GROUP:
@@ -1621,8 +1638,8 @@
memberIDsToRemove = [memberIDRow[0] for memberIDRow in memberIDRows]
if memberIDsToRemove:
- print("remove:%s _removeMemberIDsFromGroupIDQuery:revision=%s groupID=%s memberIDsToRemove=%s" % (self, self._ownerAddressBookResourceID, self._resourceID, memberIDsToRemove,))
- yield self._removeMemberIDsFromGroupIDQuery(memberIDsToRemove).on(
+ print("remove:%s _removeMemberIDsFromGroupQuery:revision=%s groupID=%s memberIDsToRemove=%s" % (self, self._ownerAddressBookResourceID, self._resourceID, memberIDsToRemove,))
+ yield self._removeMemberIDsFromGroupQuery(memberIDsToRemove).on(
self._txn,
groupID=self._resourceID,
addressbookID=self._ownerAddressBookResourceID,
@@ -2033,23 +2050,8 @@
@classmethod
- def _addRemovedMemberIDsToGroupIDQuery(cls, memberIDs): #@NoSelf
+ def _removeMemberIDsFromGroupQuery(cls, memberIDs): #@NoSelf
"""
- DAL statement to mark a member table rows as not removed
- """
- aboMembers = schema.ABO_MEMBERS
- return Update(
- {aboMembers.REVISION: Parameter("revision"),
- aboMembers.REMOVED: False, },
- Where=(aboMembers.GROUP_ID == Parameter("groupID"))
- .And(aboMembers.ADDRESSBOOK_ID == Parameter("addressbookID"))
- .And(aboMembers.MEMBER_ID.In(Parameter("memberIDs", len(memberIDs)))),
- )
-
-
- @classmethod
- def _removeMemberIDsFromGroupIDQuery(cls, memberIDs): #@NoSelf
- """
DAL statement to mark a member table row to as removed
"""
aboMembers = schema.ABO_MEMBERS
@@ -2063,7 +2065,7 @@
@classmethod
- def _removeMemberIDFromGroupIDsQuery(cls, groupIDs): #@NoSelf
+ def _removeMemberIDFromGroupsQuery(cls, groupIDs): #@NoSelf
"""
DAL statement update a group member
"""
@@ -2163,7 +2165,6 @@
abo = schema.ADDRESSBOOK_OBJECT
aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
- aboMembers = schema.ABO_MEMBERS
if inserting:
self._resourceID, self._created, self._modified = (
@@ -2182,26 +2183,24 @@
groupIDRows = yield Delete(
aboForeignMembers,
Where=aboForeignMembers.MEMBER_ADDRESS == "urn:uuid:" + self._uid,
- Return=[aboForeignMembers.GROUP_ID, aboForeignMembers.REVISION]
+ Return=aboForeignMembers.GROUP_ID
).on(self._txn)
- groupIDToRevisionMap = dict(groupIDRows)
+ groupIDs = set([groupIDRow[0] for groupIDRow in groupIDRows])
- # add vCard to writable groups
if not self.owned() and not self.addressbook().fullyShared():
readWriteGroupIDs = yield self.addressbook().readWriteGroupIDs()
assert readWriteGroupIDs, "no access"
- for id in readWriteGroupIDs:
- groupIDToRevisionMap[id] = self._syncTokenRevision
+ groupIDs |= set(readWriteGroupIDs)
# add to member table rows
- if groupIDToRevisionMap:
- print("updateDatabase:%s _insertMemberIDQuery:revision=%s groupIDToRevisionMap=%s memberIDToAdd=%s" % (self, self._syncTokenRevision, groupIDToRevisionMap, self._resourceID,))
- for groupID, revision in groupIDToRevisionMap.iteritems():
+ if groupIDs:
+ print("updateDatabase:%s _insertMemberIDQuery:revision=%s groupIDToRevisionMap=%s memberIDToAdd=%s" % (self, self._syncTokenRevision, groupIDs, self._resourceID,))
+ for groupID in groupIDs:
yield self._insertMemberIDQuery.on(self._txn,
groupID=groupID,
addressbookID=self._ownerAddressBookResourceID,
memberID=self._resourceID,
- revision=revision,
+ revision=self._syncTokenRevision,
)
else:
@@ -2219,15 +2218,13 @@
memberIDs.append(self._resourceID)
# get current members
- currentMemberRows = yield Select([aboMembers.MEMBER_ID, aboMembers.REMOVED],
- From=aboMembers,
- Where=aboMembers.GROUP_ID == self._resourceID,).on(self._txn)
+ currentMemberRows = yield AddressBook._memberIDsWithGroupIDsQuery([self._resourceID]).on(
+ self._txn, groupIDs=[self._resourceID]
+ )
currentMemberIDs = [currentMemberRow[0] for currentMemberRow in currentMemberRows if not currentMemberRow[1]]
- removedMemberIDs = [currentMemberRow[0] for currentMemberRow in currentMemberRows if currentMemberRow[1]]
memberIDsToRemove = set(currentMemberIDs) - set(memberIDs)
- memberIDsToAdd = set(memberIDs) - set(currentMemberIDs) - set(removedMemberIDs)
- memberIDsToReadd = set(memberIDs) & set(removedMemberIDs)
+ memberIDsToAdd = set(memberIDs) - set(currentMemberIDs)
if memberIDsToAdd:
print("updateDatabase:%s _insertMemberIDQuery:revision=%s groupID=%s memberIDsToAdd=%s" % (self, self._syncTokenRevision, self._resourceID, memberIDsToAdd,))
@@ -2237,20 +2234,11 @@
addressbookID=self._ownerAddressBookResourceID,
memberID=memberIDToAdd,
revision=self._syncTokenRevision,
- )
+ )
- if memberIDsToReadd:
- print("updateDatabase:%s _addRemovedMemberIDsToGroupIDQuery:revision=%s groupID=%s memberIDsToRemove=%s" % (self, self._syncTokenRevision, self._resourceID, memberIDsToReadd,))
- yield self._addRemovedMemberIDsToGroupIDQuery(memberIDsToReadd).on(self._txn,
- groupID=self._resourceID,
- addressbookID=self._ownerAddressBookResourceID,
- memberIDs=memberIDsToReadd,
- revision=self._syncTokenRevision,
- )
-
if memberIDsToRemove:
- print("updateDatabase:%s _removeMemberIDsFromGroupIDQuery:revision=%s groupID=%s memberIDsToRemove=%s" % (self, self._syncTokenRevision, self._resourceID, memberIDsToRemove,))
- yield self._removeMemberIDsFromGroupIDQuery(memberIDsToRemove).on(
+ print("updateDatabase:%s _removeMemberIDsFromGroupQuery:revision=%s groupID=%s memberIDsToRemove=%s" % (self, self._syncTokenRevision, self._resourceID, memberIDsToRemove,))
+ yield self._removeMemberIDsFromGroupQuery(memberIDsToRemove).on(
self._txn,
groupID=self._resourceID,
addressbookID=self._ownerAddressBookResourceID,
@@ -2282,8 +2270,7 @@
yield Insert(
{aboForeignMembers.GROUP_ID: self._resourceID,
aboForeignMembers.ADDRESSBOOK_ID: self._ownerAddressBookResourceID,
- aboForeignMembers.MEMBER_ADDRESS: foreignMemberAddrToAdd,
- aboForeignMembers.REVISION: self._syncTokenRevision, }
+ aboForeignMembers.MEMBER_ADDRESS: foreignMemberAddrToAdd, }
).on(self._txn)
@@ -2326,14 +2313,10 @@
# generate "X-ADDRESSBOOKSERVER-MEMBER" properties
# first get member resource ids
- aboMembers = schema.ABO_MEMBERS
- memberRows = yield Select(
- [aboMembers.MEMBER_ID],
- From=aboMembers,
- Where=(aboMembers.GROUP_ID == self._resourceID)
- .And(aboMembers.REMOVED == False),
- ).on(self._txn)
- memberIDs = [memberRow[0] for memberRow in memberRows]
+ memberRows = yield AddressBook._memberIDsWithGroupIDsQuery([self._resourceID]).on(
+ self._txn, groupIDs=[self._resourceID]
+ )
+ memberIDs = [memberRow[0] for memberRow in memberRows if not memberRow[1]]
# then get member UIDs
abo = schema.ADDRESSBOOK_OBJECT
@@ -2522,21 +2505,23 @@
@return: a L{Deferred} which will fire with the previously-used name.
"""
- sharedAddressBook = yield shareeHome.addressbookWithName(self.addressbook().shareeName())
+ shareeAddressBook = yield shareeHome.addressbookWithName(self.addressbook().shareeName())
- if sharedAddressBook:
+ if shareeAddressBook:
- acceptedBindCount = 1 if sharedAddressBook.fullyShared() else 0
+ acceptedBindCount = 1 if shareeAddressBook.fullyShared() else 0
acceptedBindCount += len((
yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
- self._txn, homeID=shareeHome._resourceID, addressbookID=sharedAddressBook._resourceID
+ self._txn, homeID=shareeHome._resourceID, addressbookID=shareeAddressBook._resourceID
)
))
if acceptedBindCount == 1:
- yield sharedAddressBook._deletedSyncToken(sharedRemoval=True)
+ yield shareeAddressBook._deletedSyncToken(sharedRemoval=True)
shareeHome._children.pop(self.addressbook().shareeName(), None)
shareeHome._children.pop(self.addressbook()._resourceID, None)
+ else:
+ yield shareeAddressBook._unshareRevision(self.name(), self._resourceID)
# Must send notification to ensure cache invalidation occurs
yield self.notifyChanged()
@@ -2635,8 +2620,9 @@
@inlineCallbacks
- def syncToken(self):
- returnValue((yield self.addressbook().syncToken()))
+ def _initBindRevision(self):
+ yield self.addressbook().syncToken() # init self.addressbook()._syncTokenRevision
+ yield super(AddressBookObject, self)._initBindRevision()
@inlineCallbacks
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py 2013-09-12 21:17:25 UTC (rev 11678)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py 2013-09-12 21:24:21 UTC (rev 11679)
@@ -3101,7 +3101,6 @@
@inlineCallbacks
def _initBindRevision(self):
- yield self.syncToken()
self._bindRevision = self._syncTokenRevision
bind = self._bindSchema
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql 2013-09-12 21:17:25 UTC (rev 11678)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql 2013-09-12 21:24:21 UTC (rev 11679)
@@ -465,7 +465,7 @@
REVISION integer default nextval('REVISION_SEQ') not null,
REMOVED boolean default false not null,
- primary key (GROUP_ID, MEMBER_ID) -- implicit index
+ primary key (GROUP_ID, MEMBER_ID, REVISION) -- implicit index
);
create index ABO_MEMBERS_ADDRESSBOOK_ID on
@@ -481,7 +481,6 @@
GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
- REVISION integer default nextval('REVISION_SEQ') not null,
primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
);
@@ -550,9 +549,11 @@
ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME,
ADDRESSBOOK_NAME varchar(255) default null,
+ OBJECT_RESOURCE_ID integer default 0,
RESOURCE_NAME varchar(255),
REVISION integer default nextval('REVISION_SEQ') not null,
- DELETED boolean not null
+ DELETED boolean not null,
+ UNSHARED boolean not null default false
);
create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130912/e212f9cc/attachment-0001.html>
More information about the calendarserver-changes
mailing list