[prev in list] [next in list] [prev in thread] [next in thread]
List: calendarserver-changes
Subject: [CalendarServer-changes] [11645] CalendarServer/branches/users/gaya/sharedgroupfixes
From: source_changes () macosforge ! org
Date: 2013-08-26 22:58:11
Message-ID: 20130826230000.6885F140F10 () svn ! calendarserver ! org
[Download RAW message or body]
[Attachment #2 (multipart/alternative)]
Revision: 11645
http://trac.calendarserver.org//changeset/11645
Author: gaya@apple.com
Date: 2013-08-26 16:00:00 -0700 (Mon, 26 Aug 2013)
Log Message:
-----------
Add revision info to group schema
Modified Paths:
--------------
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/test_sql.py
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py 2013-08-26 \
17:42:59 UTC (rev 11644)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py 2013-08-26 \
23:00:00 UTC (rev 11645) @@ -2607,7 +2607,7 @@
# Content-type check
content_type = request.headers.getHeader("content-type")
if content_type is not None and (content_type.mediaType, \
content_type.mediaSubtype) != ("text", "calendar"):
- log.error("MIME type %s not allowed in calendar collection" % \
(content_type,)) + log.error("MIME type {content_type} not allowed in \
calendar collection", content_type=content_type) raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(caldav_namespace, "supported-calendar-data"),
@@ -3245,7 +3245,7 @@
# Content-type check
content_type = request.headers.getHeader("content-type")
if content_type is not None and (content_type.mediaType, \
content_type.mediaSubtype) != ("text", "vcard"):
- log.error("MIME type %s not allowed in vcard collection" % \
(content_type,)) + log.error("MIME type {content_type} not allowed in \
vcard collection", content_type=content_type) raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(carddav_namespace, "supported-address-data"),
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py 2013-08-26 \
17:42:59 UTC (rev 11644)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py 2013-08-26 \
23:00:00 UTC (rev 11645) @@ -493,9 +493,26 @@
@inlineCallbacks
+ def removedObjectResource(self, child):
+ """
+ just like CommonHomeChild.removedObjectResource() but does not call \
self._deleteRevision(child.name()) + """
+ self._objects.pop(child.name(), None)
+ self._objects.pop(child.uid(), None)
+ if self._objectNames and child.name() in self._objectNames:
+ self._objectNames.remove(child.name())
+ #yield self._deleteRevision(child.name())
+ yield self.notifyChanged()
+
+
+ @inlineCallbacks
def remove(self):
if self._resourceID == self._home._resourceID:
+
+ # Note that revision table is NOT queried for removes
+ yield self._updateRevision(self.name())
+
# Allow remove, as a way to reset the address book to an empty state
for abo in (yield self.objectResources()):
yield abo.remove()
@@ -503,9 +520,6 @@
yield self.unshare() # storebridge should already have done this
- # Note that revision table is NOT queried for removes
- yield self._updateRevision(self.name())
-
yield self.properties()._removeResource()
yield self._loadPropertyStore()
@@ -966,7 +980,8 @@
"""
aboMembers = schema.ABO_MEMBERS
return Select([aboMembers.MEMBER_ID], From=aboMembers,
- Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", \
len(groupIDs))), + \
Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs))) + \
.And(aboMembers.REMOVED == False), )
@@ -1302,15 +1317,6 @@
return self._resourceID == self.addressbook()._resourceID
- @classmethod
- def _deleteMembersWithMemberIDAndGroupIDsQuery(cls, memberID, groupIDs):
- aboMembers = schema.ABO_MEMBERS
- return Delete(
- aboMembers,
- Where=(aboMembers.MEMBER_ID == memberID).And(
- aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs)))))
-
-
@inlineCallbacks
def remove(self):
@@ -1327,29 +1333,61 @@
if readWriteGroupIDs:
readWriteObjectIDs = yield \
self.addressbook().expandGroupIDs(self._txn, readWriteGroupIDs)
- # can't delete item in shared group, even if user has addressbook unbind
+ # can't delete item in read-only shared group, even if user has \
addressbook unbind if self._resourceID not in readWriteObjectIDs:
raise HTTPError(FORBIDDEN)
- # convert delete in sharee shared group address book to remove of \
memberships
- # that make this object visible to the sharee
- if readWriteObjectIDs:
- yield \
self._deleteMembersWithMemberIDAndGroupIDsQuery(self._resourceID, \
readWriteObjectIDs).on(
- self._txn, groupIDs=readWriteObjectIDs
- )
+ # 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]
+ for groupID in groupIDs:
+ yield self._updateMember.on(self._txn,
+ groupID=groupID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=self._resourceID,
+ resourceName=self.name(),
+ revision=self.addressbook()._syncTokenRevision,
+ removed=True
+ )
+ else:
+ # get sync token for delete now
+ yield self.addressbook()._deleteRevision(self.name())
+
aboMembers = schema.ABO_MEMBERS
- aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
-
- groupIDRows = yield Delete(
- aboMembers,
- Where=aboMembers.MEMBER_ID == self._resourceID,
- Return=aboMembers.GROUP_ID
+ 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]
+ if self.owned() or self.addressbook().fullyShared():
+ # remove memberships
+ for groupID in groupIDs:
+ yield self._updateMember.on(self._txn,
+ groupID=groupID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=self._resourceID,
+ resourceName=self.name(),
+ revision=self.addressbook()._syncTokenRevision,
+ removed=True
+ )
+
# add to foreign member table row by UID (aboForeignMembers on address \
books) memberAddress = "urn:uuid:" + self._uid
- for groupID in set([groupIDRow[0] for groupIDRow in groupIDRows]) - \
set([self._ownerAddressBookResourceID]): + aboForeignMembers = \
schema.ABO_FOREIGN_MEMBERS + for groupID in set(groupIDs) - \
set([self._ownerAddressBookResourceID]): yield Insert(
{aboForeignMembers.GROUP_ID: groupID,
aboForeignMembers.ADDRESSBOOK_ID: self._ownerAddressBookResourceID,
@@ -1588,16 +1626,6 @@
returnValue(rows)
- @inlineCallbacks
- def _changeAddressBookRevision(self, addressbook, inserting=False):
- if inserting:
- yield addressbook._insertRevision(self._name)
- else:
- yield addressbook._updateRevision(self._name)
-
- yield addressbook.notifyChanged()
-
-
# Stuff from put_addressbook_common
def fullValidation(self, component, inserting):
"""
@@ -1608,7 +1636,14 @@
# Valid data sizes
if config.MaxResourceSize:
- vcardsize = len(str(component))
+ if self._componentResourceKindToKind(component) == _ABO_KIND_GROUP:
+ thinGroup = deepcopy(component)
+ thinGroup.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
+ thinGroup.removeProperties("X-ADDRESSBOOKSERVER-KIND")
+ thinGroup.removeProperties("UID")
+ vcardsize = len(str(thinGroup))
+ else:
+ vcardsize = len(str(component))
if vcardsize > config.MaxResourceSize:
raise ObjectResourceTooBigError()
@@ -1687,30 +1722,22 @@
self._componentChanged = False
- # Handle all validation operations here.
- self.fullValidation(component, inserting)
+ if self._options.get("coaddedUIDs") is None:
+ # Handle all validation operations here.
+ self.fullValidation(component, inserting)
- # UID lock - this will remain active until the end of the current txn
- if not inserting or self._options.get("coaddedUIDs") is None:
+ # UID lock - this will remain active until the end of the current txn
yield self._lockUID(component, inserting)
+ if inserting:
+ yield self.addressbook()._insertRevision(self._name)
+ else:
+ yield self.addressbook()._updateRevision(self._name)
+
+ yield self.addressbook().notifyChanged()
+
yield self.updateDatabase(component, inserting=inserting)
- yield self._changeAddressBookRevision(self._addressbook, inserting)
- if self.owned():
- # update revision table of the sharee group address book
- if self._kind == _ABO_KIND_GROUP: # optimization
- invites = yield self.sharingInvites()
- for invite in invites:
- shareeHome = (yield \
self._txn.homeWithResourceID(self.addressbook()._home._homeType, \
invite.shareeHomeID()))
- yield self._changeAddressBookRevision(shareeHome.addressbook(), \
inserting)
- # one is enough because all have the same resourceID
- break
- else:
- if self.addressbook()._resourceID != self._ownerAddressBookResourceID:
- # update revisions table of shared group's containing address book
- yield \
self._changeAddressBookRevision(self.ownerHome().addressbook(), \
inserting)
-
returnValue(self._componentChanged)
@@ -1726,15 +1753,6 @@
@classmethod
- def _deleteMembersWithGroupIDAndMemberIDsQuery(cls, groupID, memberIDs):
- aboMembers = schema.ABO_MEMBERS
- return Delete(
- aboMembers,
- Where=(aboMembers.GROUP_ID == groupID).And(
- aboMembers.MEMBER_ID.In(Parameter("memberIDs", \
len(memberIDs)))))
-
-
- @classmethod
def _deleteForeignMembersWithGroupIDAndMembeAddrsQuery(cls, groupID, \
memberAddrs): aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
return Delete(
@@ -1763,6 +1781,38 @@
abo.MODIFIED))
+ @classproperty
+ def _insertMember(cls): #@NoSelf
+ """
+ DAL statement insert a group member
+ """
+ aboMembers = schema.ABO_MEMBERS
+ return Insert(
+ {aboMembers.GROUP_ID: Parameter("groupID"),
+ aboMembers.ADDRESSBOOK_ID: Parameter("addressbookID"),
+ aboMembers.MEMBER_ID: Parameter("memberID"),
+ aboMembers.RESOURCE_NAME: Parameter("resourceName"),
+ aboMembers.REVISION: Parameter("revision"),
+ aboMembers.REMOVED: Parameter("removed"), }
+ )
+
+
+ @classproperty
+ def _updateMember(cls): #@NoSelf
+ """
+ DAL statement update a group member
+ """
+ aboMembers = schema.ABO_MEMBERS
+ return Update(
+ {aboMembers.RESOURCE_NAME: Parameter("resourceName"),
+ aboMembers.REVISION: Parameter("revision"),
+ aboMembers.REMOVED: Parameter("removed"), },
+ Where=(aboMembers.GROUP_ID == Parameter("groupID"))
+ .And(aboMembers.ADDRESSBOOK_ID == Parameter("addressbookID"))
+ .And(aboMembers.MEMBER_ID == Parameter("memberID")),
+ )
+
+
@inlineCallbacks
def updateDatabase(self, component, expand_until=None, reCreate=False, \
#@UnusedVariable inserting=False):
@@ -1829,11 +1879,11 @@
componentText = str(component)
# remove unneeded fields to get stored _objectText
- thinComponent = deepcopy(component)
- thinComponent.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
- thinComponent.removeProperties("X-ADDRESSBOOKSERVER-KIND")
- thinComponent.removeProperties("UID")
- self._objectText = str(thinComponent)
+ thinGroup = deepcopy(component)
+ thinGroup.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
+ thinGroup.removeProperties("X-ADDRESSBOOKSERVER-KIND")
+ thinGroup.removeProperties("UID")
+ self._objectText = str(thinGroup)
else:
componentText = str(component)
self._objectText = componentText
@@ -1867,12 +1917,12 @@
# delete foreign members table row for this object
groupIDRows = yield Delete(
aboForeignMembers,
- # should this be scoped to the owner address book?
Where=aboForeignMembers.MEMBER_ADDRESS == "urn:uuid:" + self._uid,
Return=aboForeignMembers.GROUP_ID
).on(self._txn)
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"
@@ -1880,12 +1930,27 @@
# add to member table rows
for groupID in groupIDs:
- yield Insert(
- {aboMembers.GROUP_ID: groupID,
- aboMembers.ADDRESSBOOK_ID: self._ownerAddressBookResourceID,
- aboMembers.MEMBER_ID: self._resourceID, }
- ).on(self._txn)
-
+ @inlineCallbacks
+ def doInsert(subt):
+ yield self._insertMember.on(subt,
+ groupID=groupID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=self._resourceID,
+ resourceName="",
+ revision=self.addressbook()._syncTokenRevision,
+ removed=False
+ )
+ try:
+ yield self._txn.subtransaction(doInsert)
+ except AllRetriesFailed:
+ yield self._updateMember.on(self._txn,
+ groupID=groupID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=self._resourceID,
+ resourceName="",
+ revision=self.addressbook()._syncTokenRevision,
+ removed=False
+ )
else:
self._modified = (yield Update(
{abo.VCARD_TEXT: self._objectText,
@@ -1901,26 +1966,55 @@
memberIDs.append(self._resourceID)
# get current members
- currentMemberRows = yield Select([aboMembers.MEMBER_ID],
+ currentMemberRows = yield Select([aboMembers.MEMBER_ID, \
aboMembers.REMOVED], From=aboMembers,
Where=aboMembers.GROUP_ID == self._resourceID,).on(self._txn)
- currentMemberIDs = [currentMemberRow[0] for currentMemberRow in \
currentMemberRows] + currentMemberIDs = [currentMemberRow[0] for \
currentMemberRow in currentMemberRows if not currentMemberRow[1]] + \
removedMemberIDs = [currentMemberRow[0] for currentMemberRow in currentMemberRows if \
currentMemberRow[1]]
- memberIDsToDelete = set(currentMemberIDs) - set(memberIDs)
- memberIDsToAdd = set(memberIDs) - set(currentMemberIDs)
+ memberIDsToRemove = set(currentMemberIDs) - set(memberIDs)
+ memberIDsToAdd = set(memberIDs) - set(currentMemberIDs) - \
set(removedMemberIDs) + memberIDsToReadd = set(memberIDs) & \
set(removedMemberIDs)
- if memberIDsToDelete:
- yield \
self._deleteMembersWithGroupIDAndMemberIDsQuery(self._resourceID, \
memberIDsToDelete).on(
- self._txn, memberIDs=memberIDsToDelete
- )
-
for memberIDToAdd in memberIDsToAdd:
- yield Insert(
- {aboMembers.GROUP_ID: self._resourceID,
- aboMembers.ADDRESSBOOK_ID: self._ownerAddressBookResourceID,
- aboMembers.MEMBER_ID: memberIDToAdd, }
- ).on(self._txn)
+ yield self._insertMember.on(self._txn,
+ groupID=self._resourceID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=memberIDToAdd,
+ resourceName="",
+ revision=self.addressbook()._syncTokenRevision,
+ removed=False
+ )
+ for memberIDToUpdate in memberIDsToReadd:
+ yield self._updateMember.on(self._txn,
+ groupID=self._resourceID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=memberIDToUpdate,
+ resourceName="",
+ revision=self.addressbook()._syncTokenRevision,
+ removed=False
+ )
+
+ # get member names
+ abo = schema.ADDRESSBOOK_OBJECT
+ memberIDNameRows = (
+ yield self._columnsWithResourceIDsQuery(
+ [abo.RESOURCE_ID, abo.RESOURCE_NAME],
+ memberIDsToRemove
+ ).on(self._txn, resourceIDs=memberIDsToRemove)
+ ) if memberIDsToRemove else []
+
+ for memberIDToRemove, memberNameToRemove in memberIDNameRows:
+ yield self._updateMember.on(self._txn,
+ groupID=self._resourceID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=memberIDToRemove,
+ resourceName=memberNameToRemove,
+ revision=self.addressbook()._syncTokenRevision,
+ removed=True
+ )
+
# don't bother with aboForeignMembers on address books
if self._resourceID != self._ownerAddressBookResourceID:
@@ -1990,7 +2084,8 @@
memberRows = yield Select(
[aboMembers.MEMBER_ID],
From=aboMembers,
- Where=aboMembers.GROUP_ID == self._resourceID,
+ Where=(aboMembers.GROUP_ID == self._resourceID)
+ .And(aboMembers.REMOVED == False),
).on(self._txn)
memberIDs = [memberRow[0] for memberRow in memberRows]
Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/test_sql.py 2013-08-26 \
17:42:59 UTC (rev 11644)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/test_sql.py 2013-08-26 \
23:00:00 UTC (rev 11645) @@ -576,7 +576,7 @@
aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
aboMembers = schema.ABO_MEMBERS
- memberRows = yield Select([aboMembers.GROUP_ID, aboMembers.MEMBER_ID], \
From=aboMembers,).on(txn) + memberRows = yield Select([aboMembers.GROUP_ID, \
aboMembers.MEMBER_ID], From=aboMembers, Where=aboMembers.REMOVED == False).on(txn) \
self.assertEqual(memberRows, [])
foreignMemberRows = yield Select([aboForeignMembers.GROUP_ID, \
aboForeignMembers.MEMBER_ADDRESS], From=aboForeignMembers).on(txn) @@ -597,7 +597,7 \
@@ )
subgroupObject = yield adbk.createAddressBookObjectWithName("sg.vcf", \
subgroup)
- memberRows = yield Select([aboMembers.GROUP_ID, aboMembers.MEMBER_ID], \
From=aboMembers,).on(txn) + memberRows = yield Select([aboMembers.GROUP_ID, \
aboMembers.MEMBER_ID], From=aboMembers, Where=aboMembers.REMOVED == False).on(txn) \
self.assertEqual(sorted(memberRows), sorted([
[groupObject._resourceID, \
subgroupObject._resourceID],
[subgroupObject._resourceID, \
personObject._resourceID], @@ -607,7 +607,7 @@
self.assertEqual(foreignMemberRows, [])
yield subgroupObject.remove()
- memberRows = yield Select([aboMembers.GROUP_ID, aboMembers.MEMBER_ID], \
From=aboMembers,).on(txn) + memberRows = yield Select([aboMembers.GROUP_ID, \
aboMembers.MEMBER_ID], From=aboMembers, Where=aboMembers.REMOVED == False).on(txn) \
self.assertEqual(memberRows, [])
foreignMemberRows = yield Select([aboForeignMembers.GROUP_ID, \
aboForeignMembers.MEMBER_ADDRESS], From=aboForeignMembers,
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-08-26 \
17:42:59 UTC (rev 11644)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql 2013-08-26 \
23:00:00 UTC (rev 11645) @@ -448,6 +448,12 @@
insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
---------------------------------
-- Address Book Object Members --
---------------------------------
@@ -455,7 +461,10 @@
create table ABO_MEMBERS (
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_ID integer not null references \
ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID + MEMBER_ID \
integer not null, --references ADDRESSBOOK_OBJECT, -- member AddressBook \
Object's RESOURCE_ID + RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ REMOVED boolean default false not null,
primary key (GROUP_ID, MEMBER_ID) -- implicit index
);
@@ -507,9 +516,7 @@
-- Revisions --
---------------
-create sequence REVISION_SEQ;
-
-------------------------------
-- Calendar Object Revisions --
-------------------------------
[Attachment #5 (text/html)]
<!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>[11645] CalendarServer/branches/users/gaya/sharedgroupfixes</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/11645">11645</a></dd> <dt>Author</dt> \
<dd>gaya@apple.com</dd> <dt>Date</dt> <dd>2013-08-26 16:00:00 -0700 (Mon, 26 Aug \
2013)</dd> </dl>
<h3>Log Message</h3>
<pre>Add revision info to group schema</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavstorebridgep \
y">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcarddavdatastoresql \
py">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcarddavdatastoretes \
ttest_sqlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/test_sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_ \
schemacurrentsql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavstorebridgepy"></a>
<div class="modfile"><h4>Modified: \
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py \
(11644 => 11645)</h4> <pre class="diff"><span>
<span class="info">--- \
CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py 2013-08-26 \
17:42:59 UTC (rev 11644)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py 2013-08-26 \
23:00:00 UTC (rev 11645) </span><span class="lines">@@ -2607,7 +2607,7 @@
</span><span class="cx"> # Content-type check
</span><span class="cx"> content_type = \
request.headers.getHeader("content-type") </span><span class="cx"> \
if content_type is not None and (content_type.mediaType, content_type.mediaSubtype) \
!= ("text", "calendar"): </span><del>- \
log.error("MIME type %s not allowed in calendar collection" % \
(content_type,)) </del><ins>+ log.error("MIME type {content_type} not \
allowed in calendar collection", content_type=content_type) </ins><span \
class="cx"> raise HTTPError(ErrorResponse( </span><span class="cx"> \
responsecode.FORBIDDEN, </span><span class="cx"> (caldav_namespace, \
"supported-calendar-data"), </span><span class="lines">@@ -3245,7 +3245,7 \
@@ </span><span class="cx"> # Content-type check
</span><span class="cx"> content_type = \
request.headers.getHeader("content-type") </span><span class="cx"> \
if content_type is not None and (content_type.mediaType, content_type.mediaSubtype) \
!= ("text", "vcard"): </span><del>- \
log.error("MIME type %s not allowed in vcard collection" % (content_type,)) \
</del><ins>+ log.error("MIME type {content_type} not allowed in vcard \
collection", content_type=content_type) </ins><span class="cx"> \
raise HTTPError(ErrorResponse( </span><span class="cx"> \
responsecode.FORBIDDEN, </span><span class="cx"> (carddav_namespace, \
"supported-address-data"), </span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcarddavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: \
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py \
(11644 => 11645)</h4> <pre class="diff"><span>
<span class="info">--- \
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py 2013-08-26 \
17:42:59 UTC (rev 11644)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py 2013-08-26 \
23:00:00 UTC (rev 11645) </span><span class="lines">@@ -493,9 +493,26 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><ins>+ def removedObjectResource(self, child):
+ """
+ just like CommonHomeChild.removedObjectResource() but does not call \
self._deleteRevision(child.name()) + """
+ self._objects.pop(child.name(), None)
+ self._objects.pop(child.uid(), None)
+ if self._objectNames and child.name() in self._objectNames:
+ self._objectNames.remove(child.name())
+ #yield self._deleteRevision(child.name())
+ yield self.notifyChanged()
+
+
+ @inlineCallbacks
</ins><span class="cx"> def remove(self):
</span><span class="cx">
</span><span class="cx"> if self._resourceID == self._home._resourceID:
</span><ins>+
+ # Note that revision table is NOT queried for removes
+ yield self._updateRevision(self.name())
+
</ins><span class="cx"> # Allow remove, as a way to reset the address \
book to an empty state </span><span class="cx"> for abo in (yield \
self.objectResources()): </span><span class="cx"> yield abo.remove()
</span><span class="lines">@@ -503,9 +520,6 @@
</span><span class="cx">
</span><span class="cx"> yield self.unshare() # storebridge should \
already have done this </span><span class="cx">
</span><del>- # Note that revision table is NOT queried for removes
- yield self._updateRevision(self.name())
-
</del><span class="cx"> yield self.properties()._removeResource()
</span><span class="cx"> yield self._loadPropertyStore()
</span><span class="cx">
</span><span class="lines">@@ -966,7 +980,8 @@
</span><span class="cx"> """
</span><span class="cx"> aboMembers = schema.ABO_MEMBERS
</span><span class="cx"> return Select([aboMembers.MEMBER_ID], \
From=aboMembers, </span><del>- \
Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs))), \
</del><ins>+ \
Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs))) + \
.And(aboMembers.REMOVED == False), </ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1302,15 +1317,6 @@
</span><span class="cx"> return self._resourceID == \
self.addressbook()._resourceID </span><span class="cx">
</span><span class="cx">
</span><del>- @classmethod
- def _deleteMembersWithMemberIDAndGroupIDsQuery(cls, memberID, groupIDs):
- aboMembers = schema.ABO_MEMBERS
- return Delete(
- aboMembers,
- Where=(aboMembers.MEMBER_ID == memberID).And(
- aboMembers.GROUP_ID.In(Parameter("groupIDs", \
len(groupIDs)))))
-
-
</del><span class="cx"> @inlineCallbacks
</span><span class="cx"> def remove(self):
</span><span class="cx">
</span><span class="lines">@@ -1327,29 +1333,61 @@
</span><span class="cx"> if readWriteGroupIDs:
</span><span class="cx"> readWriteObjectIDs = yield \
self.addressbook().expandGroupIDs(self._txn, readWriteGroupIDs) </span><span \
class="cx"> </span><del>- # can't delete item in shared group, even if \
user has addressbook unbind </del><ins>+ # can't delete item in read-only \
shared group, even if user has addressbook unbind </ins><span class="cx"> \
if self._resourceID not in readWriteObjectIDs: </span><span class="cx"> \
raise HTTPError(FORBIDDEN) </span><span class="cx">
</span><del>- # convert delete in sharee shared group address book to \
remove of memberships
- # that make this object visible to the sharee
- if readWriteObjectIDs:
- yield \
self._deleteMembersWithMemberIDAndGroupIDsQuery(self._resourceID, \
readWriteObjectIDs).on(
- self._txn, groupIDs=readWriteObjectIDs
- )
</del><ins>+ # get sync token for delete now
+ yield self.addressbook()._deleteRevision(self.name())
</ins><span class="cx">
</span><ins>+ # 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]
+ for groupID in groupIDs:
+ yield self._updateMember.on(self._txn,
+ groupID=groupID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=self._resourceID,
+ resourceName=self.name(),
+ revision=self.addressbook()._syncTokenRevision,
+ removed=True
+ )
+ else:
+ # get sync token for delete now
+ yield self.addressbook()._deleteRevision(self.name())
+
</ins><span class="cx"> aboMembers = schema.ABO_MEMBERS
</span><del>- aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
-
- groupIDRows = yield Delete(
- aboMembers,
- Where=aboMembers.MEMBER_ID == self._resourceID,
- Return=aboMembers.GROUP_ID
</del><ins>+ groupIDRows = yield Select(
+ [aboMembers.GROUP_ID],
+ From=aboMembers,
+ Where=(aboMembers.MEMBER_ID == self._resourceID)
+ .And(aboMembers.REMOVED == False),
</ins><span class="cx"> ).on(self._txn)
</span><ins>+ groupIDs = [groupIDRow[0] for groupIDRow in groupIDRows]
</ins><span class="cx">
</span><ins>+ if self.owned() or self.addressbook().fullyShared():
+ # remove memberships
+ for groupID in groupIDs:
+ yield self._updateMember.on(self._txn,
+ groupID=groupID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=self._resourceID,
+ resourceName=self.name(),
+ revision=self.addressbook()._syncTokenRevision,
+ removed=True
+ )
+
</ins><span class="cx"> # add to foreign member table row by UID \
(aboForeignMembers on address books) </span><span class="cx"> memberAddress = \
"urn:uuid:" + self._uid </span><del>- for groupID in \
set([groupIDRow[0] for groupIDRow in groupIDRows]) - \
set([self._ownerAddressBookResourceID]): </del><ins>+ aboForeignMembers = \
schema.ABO_FOREIGN_MEMBERS + for groupID in set(groupIDs) - \
set([self._ownerAddressBookResourceID]): </ins><span class="cx"> yield \
Insert( </span><span class="cx"> {aboForeignMembers.GROUP_ID: \
groupID, </span><span class="cx"> aboForeignMembers.ADDRESSBOOK_ID: \
self._ownerAddressBookResourceID, </span><span class="lines">@@ -1588,16 +1626,6 @@
</span><span class="cx"> returnValue(rows)
</span><span class="cx">
</span><span class="cx">
</span><del>- @inlineCallbacks
- def _changeAddressBookRevision(self, addressbook, inserting=False):
- if inserting:
- yield addressbook._insertRevision(self._name)
- else:
- yield addressbook._updateRevision(self._name)
-
- yield addressbook.notifyChanged()
-
-
</del><span class="cx"> # Stuff from put_addressbook_common
</span><span class="cx"> def fullValidation(self, component, inserting):
</span><span class="cx"> """
</span><span class="lines">@@ -1608,7 +1636,14 @@
</span><span class="cx">
</span><span class="cx"> # Valid data sizes
</span><span class="cx"> if config.MaxResourceSize:
</span><del>- vcardsize = len(str(component))
</del><ins>+ if self._componentResourceKindToKind(component) == \
_ABO_KIND_GROUP: + thinGroup = deepcopy(component)
+ thinGroup.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
+ thinGroup.removeProperties("X-ADDRESSBOOKSERVER-KIND")
+ thinGroup.removeProperties("UID")
+ vcardsize = len(str(thinGroup))
+ else:
+ vcardsize = len(str(component))
</ins><span class="cx"> if vcardsize > config.MaxResourceSize:
</span><span class="cx"> raise ObjectResourceTooBigError()
</span><span class="cx">
</span><span class="lines">@@ -1687,30 +1722,22 @@
</span><span class="cx">
</span><span class="cx"> self._componentChanged = False
</span><span class="cx">
</span><del>- # Handle all validation operations here.
- self.fullValidation(component, inserting)
</del><ins>+ if self._options.get("coaddedUIDs") is None:
+ # Handle all validation operations here.
+ self.fullValidation(component, inserting)
</ins><span class="cx">
</span><del>- # UID lock - this will remain active until the end of the \
current txn
- if not inserting or self._options.get("coaddedUIDs") is None:
</del><ins>+ # UID lock - this will remain active until the end of the \
current txn </ins><span class="cx"> yield self._lockUID(component, \
inserting) </span><span class="cx">
</span><ins>+ if inserting:
+ yield self.addressbook()._insertRevision(self._name)
+ else:
+ yield self.addressbook()._updateRevision(self._name)
+
+ yield self.addressbook().notifyChanged()
+
</ins><span class="cx"> yield self.updateDatabase(component, \
inserting=inserting) </span><del>- yield \
self._changeAddressBookRevision(self._addressbook, inserting) </del><span class="cx"> \
</span><del>- if self.owned():
- # update revision table of the sharee group address book
- if self._kind == _ABO_KIND_GROUP: # optimization
- invites = yield self.sharingInvites()
- for invite in invites:
- shareeHome = (yield \
self._txn.homeWithResourceID(self.addressbook()._home._homeType, \
invite.shareeHomeID()))
- yield self._changeAddressBookRevision(shareeHome.addressbook(), \
inserting)
- # one is enough because all have the same resourceID
- break
- else:
- if self.addressbook()._resourceID != self._ownerAddressBookResourceID:
- # update revisions table of shared group's containing address book
- yield \
self._changeAddressBookRevision(self.ownerHome().addressbook(), \
inserting)
-
</del><span class="cx"> returnValue(self._componentChanged)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1726,15 +1753,6 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><del>- def _deleteMembersWithGroupIDAndMemberIDsQuery(cls, groupID, \
memberIDs):
- aboMembers = schema.ABO_MEMBERS
- return Delete(
- aboMembers,
- Where=(aboMembers.GROUP_ID == groupID).And(
- aboMembers.MEMBER_ID.In(Parameter("memberIDs", \
len(memberIDs)))))
-
-
- @classmethod
</del><span class="cx"> def \
_deleteForeignMembersWithGroupIDAndMembeAddrsQuery(cls, groupID, memberAddrs): \
</span><span class="cx"> aboForeignMembers = schema.ABO_FOREIGN_MEMBERS \
</span><span class="cx"> return Delete( </span><span class="lines">@@ -1763,6 \
+1781,38 @@ </span><span class="cx"> abo.MODIFIED))
</span><span class="cx">
</span><span class="cx">
</span><ins>+ @classproperty
+ def _insertMember(cls): #@NoSelf
+ """
+ DAL statement insert a group member
+ """
+ aboMembers = schema.ABO_MEMBERS
+ return Insert(
+ {aboMembers.GROUP_ID: Parameter("groupID"),
+ aboMembers.ADDRESSBOOK_ID: Parameter("addressbookID"),
+ aboMembers.MEMBER_ID: Parameter("memberID"),
+ aboMembers.RESOURCE_NAME: Parameter("resourceName"),
+ aboMembers.REVISION: Parameter("revision"),
+ aboMembers.REMOVED: Parameter("removed"), }
+ )
+
+
+ @classproperty
+ def _updateMember(cls): #@NoSelf
+ """
+ DAL statement update a group member
+ """
+ aboMembers = schema.ABO_MEMBERS
+ return Update(
+ {aboMembers.RESOURCE_NAME: Parameter("resourceName"),
+ aboMembers.REVISION: Parameter("revision"),
+ aboMembers.REMOVED: Parameter("removed"), },
+ Where=(aboMembers.GROUP_ID == Parameter("groupID"))
+ .And(aboMembers.ADDRESSBOOK_ID == \
Parameter("addressbookID")) + .And(aboMembers.MEMBER_ID == \
Parameter("memberID")), + )
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def updateDatabase(self, component, expand_until=None, \
reCreate=False, #@UnusedVariable </span><span class="cx"> \
inserting=False): </span><span class="lines">@@ -1829,11 +1879,11 @@
</span><span class="cx"> componentText = str(component)
</span><span class="cx">
</span><span class="cx"> # remove unneeded fields to get stored \
_objectText </span><del>- thinComponent = deepcopy(component)
- thinComponent.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
- thinComponent.removeProperties("X-ADDRESSBOOKSERVER-KIND")
- thinComponent.removeProperties("UID")
- self._objectText = str(thinComponent)
</del><ins>+ thinGroup = deepcopy(component)
+ thinGroup.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
+ thinGroup.removeProperties("X-ADDRESSBOOKSERVER-KIND")
+ thinGroup.removeProperties("UID")
+ self._objectText = str(thinGroup)
</ins><span class="cx"> else:
</span><span class="cx"> componentText = str(component)
</span><span class="cx"> self._objectText = componentText
</span><span class="lines">@@ -1867,12 +1917,12 @@
</span><span class="cx"> # delete foreign members table row for this \
object </span><span class="cx"> groupIDRows = yield Delete(
</span><span class="cx"> aboForeignMembers,
</span><del>- # should this be scoped to the owner address book?
</del><span class="cx"> Where=aboForeignMembers.MEMBER_ADDRESS == \
"urn:uuid:" + self._uid, </span><span class="cx"> \
Return=aboForeignMembers.GROUP_ID </span><span class="cx"> \
).on(self._txn) </span><span class="cx"> groupIDs = set([groupIDRow[0] \
for groupIDRow in groupIDRows]) </span><span class="cx">
</span><ins>+ # add vCard to writable groups
</ins><span class="cx"> if not self.owned() and not \
self.addressbook().fullyShared(): </span><span class="cx"> \
readWriteGroupIDs = yield self.addressbook().readWriteGroupIDs() </span><span \
class="cx"> assert readWriteGroupIDs, "no access" \
</span><span class="lines">@@ -1880,12 +1930,27 @@ </span><span class="cx">
</span><span class="cx"> # add to member table rows
</span><span class="cx"> for groupID in groupIDs:
</span><del>- yield Insert(
- {aboMembers.GROUP_ID: groupID,
- aboMembers.ADDRESSBOOK_ID: self._ownerAddressBookResourceID,
- aboMembers.MEMBER_ID: self._resourceID, }
- ).on(self._txn)
-
</del><ins>+ @inlineCallbacks
+ def doInsert(subt):
+ yield self._insertMember.on(subt,
+ groupID=groupID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=self._resourceID,
+ resourceName="",
+ revision=self.addressbook()._syncTokenRevision,
+ removed=False
+ )
+ try:
+ yield self._txn.subtransaction(doInsert)
+ except AllRetriesFailed:
+ yield self._updateMember.on(self._txn,
+ groupID=groupID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=self._resourceID,
+ resourceName="",
+ revision=self.addressbook()._syncTokenRevision,
+ removed=False
+ )
</ins><span class="cx"> else:
</span><span class="cx"> self._modified = (yield Update(
</span><span class="cx"> {abo.VCARD_TEXT: self._objectText,
</span><span class="lines">@@ -1901,26 +1966,55 @@
</span><span class="cx"> memberIDs.append(self._resourceID)
</span><span class="cx">
</span><span class="cx"> # get current members
</span><del>- currentMemberRows = yield Select([aboMembers.MEMBER_ID],
</del><ins>+ currentMemberRows = yield Select([aboMembers.MEMBER_ID, \
aboMembers.REMOVED], </ins><span class="cx"> From=aboMembers,
</span><span class="cx"> Where=aboMembers.GROUP_ID == \
self._resourceID,).on(self._txn) </span><del>- currentMemberIDs = \
[currentMemberRow[0] for currentMemberRow in currentMemberRows] </del><ins>+ \
currentMemberIDs = [currentMemberRow[0] for currentMemberRow in currentMemberRows if \
not currentMemberRow[1]] + removedMemberIDs = [currentMemberRow[0] for \
currentMemberRow in currentMemberRows if currentMemberRow[1]] </ins><span class="cx"> \
</span><del>- memberIDsToDelete = set(currentMemberIDs) - set(memberIDs)
- memberIDsToAdd = set(memberIDs) - set(currentMemberIDs)
</del><ins>+ memberIDsToRemove = set(currentMemberIDs) - set(memberIDs)
+ memberIDsToAdd = set(memberIDs) - set(currentMemberIDs) - \
set(removedMemberIDs) + memberIDsToReadd = set(memberIDs) & \
set(removedMemberIDs) </ins><span class="cx">
</span><del>- if memberIDsToDelete:
- yield \
self._deleteMembersWithGroupIDAndMemberIDsQuery(self._resourceID, \
memberIDsToDelete).on(
- self._txn, memberIDs=memberIDsToDelete
- )
-
</del><span class="cx"> for memberIDToAdd in memberIDsToAdd:
</span><del>- yield Insert(
- {aboMembers.GROUP_ID: self._resourceID,
- aboMembers.ADDRESSBOOK_ID: self._ownerAddressBookResourceID,
- aboMembers.MEMBER_ID: memberIDToAdd, }
- ).on(self._txn)
</del><ins>+ yield self._insertMember.on(self._txn,
+ groupID=self._resourceID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=memberIDToAdd,
+ resourceName="",
+ revision=self.addressbook()._syncTokenRevision,
+ removed=False
+ )
</ins><span class="cx">
</span><ins>+ for memberIDToUpdate in memberIDsToReadd:
+ yield self._updateMember.on(self._txn,
+ groupID=self._resourceID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=memberIDToUpdate,
+ resourceName="",
+ revision=self.addressbook()._syncTokenRevision,
+ removed=False
+ )
+
+ # get member names
+ abo = schema.ADDRESSBOOK_OBJECT
+ memberIDNameRows = (
+ yield self._columnsWithResourceIDsQuery(
+ [abo.RESOURCE_ID, abo.RESOURCE_NAME],
+ memberIDsToRemove
+ ).on(self._txn, resourceIDs=memberIDsToRemove)
+ ) if memberIDsToRemove else []
+
+ for memberIDToRemove, memberNameToRemove in memberIDNameRows:
+ yield self._updateMember.on(self._txn,
+ groupID=self._resourceID,
+ addressbookID=self._ownerAddressBookResourceID,
+ memberID=memberIDToRemove,
+ resourceName=memberNameToRemove,
+ revision=self.addressbook()._syncTokenRevision,
+ removed=True
+ )
+
</ins><span class="cx"> # don't bother with aboForeignMembers on address \
books </span><span class="cx"> if self._resourceID != \
self._ownerAddressBookResourceID: </span><span class="cx">
</span><span class="lines">@@ -1990,7 +2084,8 @@
</span><span class="cx"> memberRows = yield Select(
</span><span class="cx"> [aboMembers.MEMBER_ID],
</span><span class="cx"> From=aboMembers,
</span><del>- Where=aboMembers.GROUP_ID == self._resourceID,
</del><ins>+ Where=(aboMembers.GROUP_ID == self._resourceID)
+ .And(aboMembers.REMOVED == False),
</ins><span class="cx"> ).on(self._txn)
</span><span class="cx"> memberIDs = [memberRow[0] for memberRow \
in memberRows] </span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcarddavdatastoretesttest_sqlpy"></a>
<div class="modfile"><h4>Modified: \
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/test_sql.py \
(11644 => 11645)</h4> <pre class="diff"><span>
<span class="info">--- \
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/test_sql.py 2013-08-26 \
17:42:59 UTC (rev 11644)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/test_sql.py 2013-08-26 \
23:00:00 UTC (rev 11645) </span><span class="lines">@@ -576,7 +576,7 @@
</span><span class="cx">
</span><span class="cx"> aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
</span><span class="cx"> aboMembers = schema.ABO_MEMBERS
</span><del>- memberRows = yield Select([aboMembers.GROUP_ID, \
aboMembers.MEMBER_ID], From=aboMembers,).on(txn) </del><ins>+ memberRows = \
yield Select([aboMembers.GROUP_ID, aboMembers.MEMBER_ID], From=aboMembers, \
Where=aboMembers.REMOVED == False).on(txn) </ins><span class="cx"> \
self.assertEqual(memberRows, []) </span><span class="cx">
</span><span class="cx"> foreignMemberRows = yield \
Select([aboForeignMembers.GROUP_ID, aboForeignMembers.MEMBER_ADDRESS], \
From=aboForeignMembers).on(txn) </span><span class="lines">@@ -597,7 +597,7 @@
</span><span class="cx"> )
</span><span class="cx"> subgroupObject = yield \
adbk.createAddressBookObjectWithName("sg.vcf", subgroup) </span><span \
class="cx"> </span><del>- memberRows = yield Select([aboMembers.GROUP_ID, \
aboMembers.MEMBER_ID], From=aboMembers,).on(txn) </del><ins>+ memberRows = \
yield Select([aboMembers.GROUP_ID, aboMembers.MEMBER_ID], From=aboMembers, \
Where=aboMembers.REMOVED == False).on(txn) </ins><span class="cx"> \
self.assertEqual(sorted(memberRows), sorted([ </span><span class="cx"> \
[groupObject._resourceID, subgroupObject._resourceID], </span><span class="cx"> \
[subgroupObject._resourceID, personObject._resourceID], </span><span class="lines">@@ \
-607,7 +607,7 @@ </span><span class="cx"> self.assertEqual(foreignMemberRows, \
[]) </span><span class="cx">
</span><span class="cx"> yield subgroupObject.remove()
</span><del>- memberRows = yield Select([aboMembers.GROUP_ID, \
aboMembers.MEMBER_ID], From=aboMembers,).on(txn) </del><ins>+ memberRows = \
yield Select([aboMembers.GROUP_ID, aboMembers.MEMBER_ID], From=aboMembers, \
Where=aboMembers.REMOVED == False).on(txn) </ins><span class="cx"> \
self.assertEqual(memberRows, []) </span><span class="cx">
</span><span class="cx"> foreignMemberRows = yield \
Select([aboForeignMembers.GROUP_ID, aboForeignMembers.MEMBER_ADDRESS], \
From=aboForeignMembers, </span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemacurrentsql"></a>
<div class="modfile"><h4>Modified: \
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql \
(11644 => 11645)</h4> <pre class="diff"><span>
<span class="info">--- \
CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql 2013-08-26 \
17:42:59 UTC (rev 11644)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql 2013-08-26 \
23:00:00 UTC (rev 11645) </span><span class="lines">@@ -448,6 +448,12 @@
</span><span class="cx"> insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
</span><span class="cx">
</span><span class="cx">
</span><ins>+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
</ins><span class="cx"> ---------------------------------
</span><span class="cx"> -- Address Book Object Members --
</span><span class="cx"> ---------------------------------
</span><span class="lines">@@ -455,7 +461,10 @@
</span><span class="cx"> create table ABO_MEMBERS (
</span><span class="cx"> GROUP_ID integer not null references \
ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') \
RESOURCE_ID </span><span class="cx"> ADDRESSBOOK_ID integer not null \
references ADDRESSBOOK_HOME on delete cascade, </span><del>- MEMBER_ID \
integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook \
Object's RESOURCE_ID </del><ins>+ MEMBER_ID integer not null, \
--references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID + \
RESOURCE_NAME varchar(255), + REVISION integer default \
nextval('REVISION_SEQ') not null, + REMOVED boolean default \
false not null, </ins><span class="cx">
</span><span class="cx"> primary key (GROUP_ID, MEMBER_ID) -- implicit index
</span><span class="cx"> );
</span><span class="lines">@@ -507,9 +516,7 @@
</span><span class="cx"> -- Revisions --
</span><span class="cx"> ---------------
</span><span class="cx">
</span><del>-create sequence REVISION_SEQ;
</del><span class="cx">
</span><del>-
</del><span class="cx"> -------------------------------
</span><span class="cx"> -- Calendar Object Revisions --
</span><span class="cx"> -------------------------------
</span></span></pre>
</div>
</div>
</body>
</html>
_______________________________________________
calendarserver-changes mailing list
calendarserver-changes@lists.macosforge.org
https://lists.macosforge.org/mailman/listinfo/calendarserver-changes
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic