[CalendarServer-changes] [11759]

source_changes at macosforge.org source_changes at macosforge.org
Fri Sep 27 21:04:39 PDT 2013


Revision: 11759
          http://trac.calendarserver.org//changeset/11759
Author:   gaya at apple.com
Date:     2013-09-27 21:04:39 -0700 (Fri, 27 Sep 2013)
Log Message:
-----------
checkpoint - shared group revisions tests work, but other tests are broken

Modified Paths:
--------------
    CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CardDAV/sharing/sync/group-one/4.xml
    CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/sharing-sync.xml
    CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py

Added Paths:
-----------
    CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CardDAV/sharing/sync/group-one/5.xml

Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CardDAV/sharing/sync/group-one/4.xml
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CardDAV/sharing/sync/group-one/4.xml	2013-09-25 22:35:50 UTC (rev 11758)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CardDAV/sharing/sync/group-one/4.xml	2013-09-28 04:04:39 UTC (rev 11759)
@@ -1,20 +1,8 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<notification xmlns='http://calendarserver.org/ns/'>
-  <dtstamp></dtstamp>
-  <invite-notification shared-type='group'>
-    <uid></uid>
-    <href xmlns='DAV:'>$cuaddrurn2:</href>
-    <invite-noresponse/>
-    <access>
-      <read-write/>
-    </access>
-    <hosturl>
-      <href xmlns='DAV:'>$addressbookpath1:/3.vcf</href>
-    </hosturl>
-    <organizer>
-      <href xmlns='DAV:'>$cuaddrurn1:</href>
-      <common-name>User 01</common-name>
-    </organizer>
-    <summary>My Shared Calendar</summary>
-  </invite-notification>
-</notification>
+<?xml version="1.0" encoding="utf-8" ?>
+<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+    <CS:set>
+        <D:href>$cuaddrurn2:</D:href>
+        <CS:summary>Shared Group</CS:summary>
+        <CS:read-write/>
+    </CS:set>
+</CS:share>

Added: CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CardDAV/sharing/sync/group-one/5.xml
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CardDAV/sharing/sync/group-one/5.xml	                        (rev 0)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CardDAV/sharing/sync/group-one/5.xml	2013-09-28 04:04:39 UTC (rev 11759)
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<invite-reply xmlns='http://calendarserver.org/ns/'>
+  <href xmlns='DAV:'>$cuaddrurn2:</href>
+  <invite-accepted/>
+  <hosturl>
+    <href xmlns='DAV:'>$addressbookpath1:/3.vcf</href>
+  </hosturl>
+  <in-reply-to>$inviteuid:</in-reply-to>
+  <summary>Shared Group</summary>
+  <common-name>$username2:</common-name>
+  <first-name>$firstname2:</first-name>
+  <last-name>$lastname2:</last-name>
+</invite-reply>

Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/sharing-sync.xml
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/sharing-sync.xml	2013-09-25 22:35:50 UTC (rev 11758)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/sharing-sync.xml	2013-09-28 04:04:39 UTC (rev 11759)
@@ -38,8 +38,8 @@
 	</start>
 	
 	<!-- test suite below is similar to test suite in ../CalDAV/sharing-sync.xml -->
-	<test-suite name='Read-write addressbook' ignore='no'>
-		<test name='1' ignore='no'>
+	<test-suite name='shared addressbook' ignore='no'>
+		<test name='1a' ignore='no'>
 			<description>Initial sync tokens</description>
 			<request print-response='no'>
 				<method>REPORT</method>
@@ -56,6 +56,8 @@
 					<variable>$synctoken1:</variable>
 				</grabelement>
 			</request>
+		</test>
+		<test name='1b' ignore='no'>
 			<request print-response='no'>
 				<method>REPORT</method>
 				<ruri>$addressbookhome1:/</ruri>
@@ -71,6 +73,8 @@
 					<variable>$synctoken2:</variable>
 				</grabelement>
 			</request>
+		</test>
+		<test name='1c' ignore='no'>
 			<request print-response='no'>
 				<method>REPORT</method>
 				<ruri>$addressbookpath1:/</ruri>
@@ -86,6 +90,8 @@
 					<variable>$synctoken3:</variable>
 				</grabelement>
 			</request>
+		</test>
+		<test name='1d' ignore='no'>
 			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
 				<method>REPORT</method>
 				<ruri>$addressbookhome2:/</ruri>
@@ -101,6 +107,8 @@
 					<variable>$synctoken4:</variable>
 				</grabelement>
 			</request>
+		</test>
+		<test name='1e' ignore='no'>
 			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
 				<method>REPORT</method>
 				<ruri>$addressbookhome2:/</ruri>
@@ -269,18 +277,19 @@
 				</grabelement>
 			</request>
 		</test>
-		<test name='5e' ignore='yes'>
+		<test name='5e' ignore='no'>
 			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
 				<method>REPORT</method>
 				<ruri>$addressbookhome2:/</ruri>
 				<data substitutions='yes'>
 					<content-type>text/xml; charset=utf-8</content-type>
-					<filepath>Resource/Common/REPORT/sync-token5-level-infinite.xml</filepath>
+					<filepath>Resource/Common/REPORT/sync-init-level-infinite.xml</filepath>
 				</data>
 				<verify>
 					<callback>multistatusItems</callback>
 					<arg>
 						<name>okhrefs</name>
+						<value>$addressbook:/</value>
 						<value>$sharedaddressbook:/</value>
 					</arg>
 				</verify>
@@ -407,7 +416,7 @@
 				</grabelement>
 			</request>
 		</test>
-		<test name='7f' ignore='yes'>
+		<test name='7f' ignore='no'>
 			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
 				<method>REPORT</method>
 				<ruri>$addressbookhome2:/</ruri>
@@ -550,7 +559,7 @@
 				</grabelement>
 			</request>
 		</test>
-		<test name='9e' ignore='yes'>
+		<test name='9e' ignore='no'>
 			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
 				<method>REPORT</method>
 				<ruri>$addressbookhome2:/</ruri>
@@ -692,7 +701,7 @@
 				</grabelement>
 			</request>
 		</test>
-		<test name='11e' ignore='yes'>
+		<test name='11e' ignore='no'>
 			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
 				<method>REPORT</method>
 				<ruri>$addressbookhome2:/</ruri>
@@ -872,6 +881,850 @@
 			</request>
 		</test>
 	</test-suite>
+
+	<!-- test suite below is similar to test suite in ../CalDAV/sharing-sync.xml -->
+	<test-suite name='one shared group' ignore='no'>
+		<test name='0' ignore='no'>
+			<description>clean up old data</description>
+			<request user="$useradmin:" pswd="$pswdadmin:">
+				<method>DELETEALL</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<ruri>$addressbookhome2:/</ruri>
+				<ruri>$notificationpath1:/</ruri>
+				<ruri>$notificationpath2:/</ruri>
+			</request>
+			<description>create empty group</description>
+			<request>
+				<method>PUT</method>
+				<ruri>$addressbookpath1:/3.vcf</ruri>
+				<data>
+					<content-type>text/vcard; charset=utf-8</content-type>
+					<filepath>Resource/CardDAV/sharing/sync/group-one/3.vcf</filepath>
+				</data>
+			</request>
+		</test>
+		<test name='1a' ignore='no'>
+			<description>Initial sync tokens</description>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-init-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken1:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='1b' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-init-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken2:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='1c' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookpath1:/</ruri>
+				<data>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-init-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken3:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='1d' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-init-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken4:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='1e' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-init-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken5:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='2' ignore='no'>
+			<description>user1 POSTs invitation</description>
+			<request print-response="no">
+				<method>POST</method>
+				<ruri>$addressbookpath1:/3.vcf</ruri>
+				<data>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/CardDAV/sharing/sync/group-one/4.xml</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+		</test>
+		<test name='3'>
+			<description>Check user2 notification collection and get invite uid</description>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>WAITCOUNT 1</method>
+				<ruri>$notificationpath2:/</ruri>
+			</request>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>GETNEW</method>
+				<ruri>$notificationpath2:/</ruri>
+				<grabelement>
+					<name>{http://calendarserver.org/ns/}invite-notification/{http://calendarserver.org/ns/}uid</name>
+					<variable>$inviteuid:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='4'>
+			<description>user2 replies ACCEPTED and deletes notification</description>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>POST</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions="yes">
+					<content-type>application/xml; charset=utf-8</content-type>
+					<filepath>Resource/CardDAV/sharing/sync/group-one/5.xml</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+			<request>
+				<method>DELETE</method>
+				<ruri>$notificationpath2:/$inviteuid:</ruri>
+			</request>
+		</test>
+		<test name='5a' ignore='no'>
+			<description>Updated tokens</description>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token1-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken1:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='5b' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token2-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken2:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='5c' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookpath1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token3-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken3:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='5d' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token4-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$userguid1:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken4:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='5e' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-init-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+						<value>$userguid1:/</value>
+						<value>$userguid1:/3.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken5:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='5f' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/$userguid1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-init-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken6:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='6'>
+			<description>Sharee creates vcard</description>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>PUT</method>
+				<ruri>$addressbookhome2:/$userguid1:/1.vcf</ruri>
+				<data>
+					<content-type>text/vcard; charset=utf-8</content-type>
+					<filepath>Resource/CardDAV/sharing/sync/group-one/1.vcf</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+		</test>
+		<test name='7a' ignore='no'>
+			<description>Updated tokens</description>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token1-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken1:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='7b' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token2-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+						<value>$addressbook:/1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken2:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='7c' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookpath1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token3-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken3:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='7e' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token4-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$userguid1:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken4:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='7f' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token5-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$userguid1:/</value>
+						<value>$userguid1:/1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken5:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='7g' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/$userguid1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token6-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken6:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='8'>
+			<description>Sharer changes vcard</description>
+			<request print-response='no'>
+				<method>PUT</method>
+				<ruri>$addressbookpath1:/1.vcf</ruri>
+				<data>
+					<content-type>text/vcard; charset=utf-8</content-type>
+					<filepath>Resource/CardDAV/sharing/sync/group-one/1.vcf</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+		</test>
+		<test name='9a' ignore='no'>
+			<description>Updated tokens</description>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token1-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken1:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='9b' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token2-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+						<value>$addressbook:/1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken2:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='9c' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookpath1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token3-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken3:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='9d' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token4-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$userguid1:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken4:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='9e' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token5-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$userguid1:/</value>
+						<value>$userguid1:/1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken5:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='9f' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/$userguid1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token6-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken6:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='10'>
+			<description>Sharee deletes vcard</description>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>DELETE</method>
+				<ruri>$addressbookhome2:/$userguid1:/1.vcf</ruri>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+		</test>
+		<test name='11a' ignore='no'>
+			<description>Updated tokens</description>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token1-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken1:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='11b' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token2-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+					</arg>
+					<arg>
+						<name>badhrefs</name>
+						<value>$addressbook:/1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken2:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='11c' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookpath1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token3-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>badhrefs</name>
+						<value>1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken3:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='11d' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token4-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$userguid1:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken4:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='11e' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token5-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$userguid1:/</value>
+					</arg>
+					<arg>
+						<name>badhrefs</name>
+						<value>$userguid1:/1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken5:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='11f' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/$userguid1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token6-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>badhrefs</name>
+						<value>1.vcf</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken6:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='12'>
+			<description>Sharer unshares</description>
+			<request print-response='no'>
+				<method>POST</method>
+				<ruri>$addressbookpath1:/3.vcf</ruri>
+				<data>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/POST/sharingremove2.xml</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+				</verify>
+			</request>
+		</test>
+		<test name='13a' ignore='no'>
+			<description>Updated tokens</description>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token1-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken1:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='13b' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token2-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>okhrefs</name>
+						<value>$addressbook:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken2:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='13c' ignore='no'>
+			<request print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookpath1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token3-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken3:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='13e' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token4-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>badhrefs</name>
+						<value>$userguid1:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken4:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='13f' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token5-level-infinite.xml</filepath>
+				</data>
+				<verify>
+					<callback>multistatusItems</callback>
+					<arg>
+						<name>badhrefs</name>
+						<value>$userguid1:/</value>
+					</arg>
+				</verify>
+				<grabelement>
+					<name>/{DAV:}multistatus/{DAV:}sync-token</name>
+					<variable>$synctoken5:</variable>
+				</grabelement>
+			</request>
+		</test>
+		<test name='13g' ignore='no'>
+			<request user="$userid2:" pswd="$pswd2:" print-response='no'>
+				<method>REPORT</method>
+				<ruri>$addressbookhome2:/$userguid1:/</ruri>
+				<data substitutions='yes'>
+					<content-type>text/xml; charset=utf-8</content-type>
+					<filepath>Resource/Common/REPORT/sync-token6-level-1.xml</filepath>
+				</data>
+				<verify>
+					<callback>statusCode</callback>
+					<arg>
+						<name>status</name>
+						<value>404</value>
+					</arg>
+				</verify>
+			</request>
+		</test>
+	</test-suite>
 	
 	<!-- test suites below are similar to those in ../CalDAV/sync-report.xml but use shared address books of calendars in the home -->
 	<test-suite name='support-report-set/sync-token property' ignore='no'>
@@ -3810,30 +4663,6 @@
 		</test>
 	</test-suite>
 
-	<test-suite name='simple reports - empty inbox' ignore='yes'>
-		<test name='1' ignore='no'>
-			<description>initial query</description>
-			<request print-response='no'>
-				<method>REPORT</method>
-				<ruri>$inboxpath1:/</ruri>
-				<header>
-					<name>Depth</name>
-					<value>1</value>
-				</header>
-				<data>
-					<content-type>text/xml; charset=utf-8</content-type>
-					<filepath>Resource/CardDAV/sharing/sync/addressbook/22.xml</filepath>
-				</data>
-				<verify>
-					<callback>multistatusItems</callback>
-					<arg>
-						<name>okhrefs</name>
-					</arg>
-				</verify>
-			</request>
-		</test>
-	</test-suite>
-
 	<test-suite name='simple reports - valid token' ignore='no'>
 		<test name='1' ignore='no'>
 			<description>initial query</description>
@@ -4105,7 +4934,6 @@
 		</test>
 	</test-suite>
 
-
 	<end>
 		<request user="$useradmin:" pswd="$pswdadmin:">
 			<method>DELETEALL</method>

Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py	2013-09-25 22:35:50 UTC (rev 11758)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/sql.py	2013-09-28 04:04:39 UTC (rev 11759)
@@ -272,6 +272,8 @@
         """
         rev = cls._revisionsSchema
         bind = cls._bindSchema
+        abo = cls._objectSchema
+        groupBind = AddressBookObject._bindSchema
         return Select(
             [Max(rev.REVISION)],
             # active shared address books
@@ -284,6 +286,22 @@
                             [bind.RESOURCE_ID],
                             From=bind,
                             Where=bind.HOME_RESOURCE_ID == Parameter("resourceID"),
+                            SetExpression=Union(
+                                Select(
+                                    [abo.ADDRESSBOOK_HOME_RESOURCE_ID],
+                                    From=abo,
+                                    Where=(
+                                        abo.RESOURCE_ID.In(
+                                            Select(
+                                                [groupBind.GROUP_RESOURCE_ID],
+                                                From=groupBind,
+                                                Where=groupBind.ADDRESSBOOK_HOME_RESOURCE_ID == Parameter("resourceID"),
+                                            )
+                                        )
+                                    )
+                                ),
+                                optype=Union.OPTYPE_ALL,
+                            )
                         )
                     )
                 ),
@@ -294,7 +312,7 @@
                         From=rev,
                         Where=(rev.HOME_RESOURCE_ID == Parameter("resourceID")).And(rev.RESOURCE_ID == None),
                         SetExpression=Union(
-                            # owned address book: owned address book cannot be deleted: See AddressBook.remove()
+                            # owned address book
                             Select(
                                 [rev.REVISION],
                                 From=rev,
@@ -313,7 +331,7 @@
     def _changesQuery(cls): #@NoSelf
         rev = cls._revisionsSchema
         return Select(
-            [rev.COLLECTION_NAME,
+            [rev.ADDRESSBOOK_NAME,
              rev.RESOURCE_NAME,
              rev.DELETED],
             From=rev,
@@ -325,9 +343,11 @@
     @inlineCallbacks
     def doChangesQuery(self, revision):
 
-        rows = yield self._changesQuery.on(self._txn,
-                                         resourceID=self._resourceID,
-                                         revision=revision)
+        rows = yield self._changesQuery.on(
+            self._txn,
+            resourceID=self._resourceID,
+            revision=revision
+        )
 
         # If the collection name is None that means we have a change to the owner's default address book,
         # so substitute in the name of that. If collection name is not None, then we have a revision
@@ -620,7 +640,7 @@
         print("sharedChildResourceNamesSinceRevision:%s results:%s" % (self, results))
 
         # id's are added on deletion
-        idToNameMap = dict([(name, id) for name, id, wasdeleted in results if id != 0])
+        idToNameMap = dict([(id, name) for name, id, wasdeleted in results if id != 0])
 
         # now get other names of existing objects
         missingNameIds = (allowedObjectIDs | oldAllowedObjectIDs) - set(idToNameMap.keys())
@@ -871,17 +891,6 @@
         returnValue(component)
 
 
-    @inlineCallbacks
-    def bumpModified(self):
-        # TODO: The next line seems to work too.  Why?
-        # returnValue((yield self.ownerHome().bumpModified()))
-        #
-        if self._resourceID == self._home._resourceID:
-            returnValue((yield self._home.bumpModified()))
-        else:
-            returnValue((yield super(AddressBook, self).bumpModified()))
-
-
     @classmethod
     @inlineCallbacks
     def loadAllObjects(cls, home):
@@ -1183,45 +1192,67 @@
         returnValue(tuple(names))
 
 
-    @classproperty
-    def _memberIDsWithGroupIDQuery(cls): #@NoSelf
-        """
-        DAL query to find all member resource ids with the maximum revision for the given group ID
-        """
-        aboMembers = schema.ABO_MEMBERS
-        return Select([aboMembers.MEMBER_ID, aboMembers.REMOVED, Max(aboMembers.REVISION)], From=aboMembers,
-                      Where=aboMembers.GROUP_ID == Parameter("groupID"),
-                      GroupBy=(aboMembers.MEMBER_ID, aboMembers.REMOVED)
-                     )
-
-
     @classmethod
     def _memberIDsWithGroupIDsQuery(cls, groupIDs):
         """
-        DAL query to find all member resource ids with the maximum revision for the given group IDs
+        DAL query to find members and revisions
         """
         aboMembers = schema.ABO_MEMBERS
-        return Select([aboMembers.MEMBER_ID, aboMembers.REMOVED, Max(aboMembers.REVISION)], From=aboMembers,
+        return Select([aboMembers.MEMBER_ID, aboMembers.REMOVED, 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 find all member resource ids with the maximum given revision for the given group IDs
+        DAL query to find members and revisions
         """
         aboMembers = schema.ABO_MEMBERS
-        return Select([aboMembers.MEMBER_ID, aboMembers.REMOVED, Max(aboMembers.REVISION)], From=aboMembers,
+        return Select([aboMembers.MEMBER_ID, aboMembers.REMOVED, aboMembers.REVISION],
+                      From=aboMembers,
                       Where=aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs)))
                             .And(aboMembers.REVISION <= Parameter("revision")),
-                      GroupBy=(aboMembers.MEMBER_ID, aboMembers.REMOVED)
                      )
 
 
     @classmethod
+    def _currentMemberIDsFromMemberIDRemovedRevisionRows(cls, memberRows):
+        memberIDs = set()
+        objectIDToVersionToRemovedMap = {}
+        for id, removed, version in memberRows:
+            versionRemovedRow = objectIDToVersionToRemovedMap.get(id, [])
+            versionRemovedRow.append((version, removed,))
+            objectIDToVersionToRemovedMap[id] = versionRemovedRow
+
+        for id, versionRemovedRows in objectIDToVersionToRemovedMap.iteritems():
+            versionToRemovedMap = dict(versionRemovedRows)
+            if not versionToRemovedMap[max(versionToRemovedMap.keys())]:
+                memberIDs.add(id)
+
+        return memberIDs
+
+
+    @classmethod
     @inlineCallbacks
+    def memberIDsWithGroupIDs(cls, txn, groupIDs, atRevision=0):
+
+        if atRevision == 0:
+            memberRows = yield cls._memberIDsWithGroupIDsQuery(groupIDs).on(
+                txn, groupIDs=groupIDs
+            )
+        else:
+            memberRows = yield cls._memberIDsWithGroupIDsAndRevisionQuery(groupIDs).on(
+                txn, groupIDs=groupIDs, revision=atRevision
+            )
+
+        memberIDs = cls._currentMemberIDsFromMemberIDRemovedRevisionRows(memberRows)
+        returnValue(memberIDs)
+
+
+    @classmethod
+    @inlineCallbacks
     def expandGroupIDs(cls, txn, groupIDs, atRevision=0, includeGroupIDs=True):
         """
         Get all AddressBookObject resource IDs contains in the given shared groups with the given groupIDs
@@ -1230,16 +1261,10 @@
         examinedIDs = set()
         remainingIDs = set(groupIDs)
         while remainingIDs:
-            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
+            memberIDs = yield cls.memberIDsWithGroupIDs(txn, remainingIDs, atRevision)
+
+            objectIDs |= memberIDs
             examinedIDs |= remainingIDs
             remainingIDs = objectIDs - examinedIDs
 
@@ -1443,6 +1468,7 @@
 
             # Must send notification to ensure cache invalidation occurs
             yield self.notifyPropertyChanged()
+            yield shareeView.viewerHome().notifyChanged()
 
         returnValue(shareeView._name)
 
@@ -1493,6 +1519,7 @@
 
             # Must send notification to ensure cache invalidation occurs
             yield self.notifyPropertyChanged()
+            yield shareeHome.notifyChanged()
 
         # delete bind table rows for this share
         deletedBindNameRows = yield self._deleteBindForResourceIDAndHomeID.on(self._txn, resourceID=self._resourceID,
@@ -1587,28 +1614,40 @@
         # get sync token for delete now
         yield self.addressbook()._deleteRevision(self.name(), self._resourceID)
 
+        # get groups where this object was once a member and version info
         aboMembers = schema.ABO_MEMBERS
-        groupIDRows = yield Select([aboMembers.GROUP_ID, aboMembers.REMOVED, Max(aboMembers.REVISION)],
+        groupRows = yield Select([aboMembers.GROUP_ID, aboMembers.MEMBER_ID, aboMembers.REMOVED, aboMembers.REVISION],
             From=aboMembers,
-            Where=aboMembers.MEMBER_ID == self._resourceID,
-            GroupBy=(aboMembers.GROUP_ID, aboMembers.REMOVED)
+            Where=(aboMembers.MEMBER_ID == self._resourceID).And(aboMembers.REMOVED == False),
         ).on(self._txn)
 
-        groupIDs = set([groupIDRow[0] for groupIDRow in groupIDRows if not groupIDRow[1]])
+        # combine by groupID
+        groupIDToMemberRowMap = {}
+        for groupID, id, removed, version in groupRows:
+            memberRow = groupIDToMemberRowMap.get(groupID, [])
+            memberRow.append((id, version, removed,))
+            groupIDToMemberRowMap[groupID] = memberRow
 
+        # see if this object is in current version
+        groupIDs = set([
+            groupID for groupID, memberRows in groupIDToMemberRowMap.iteritems()
+                if self._resourceID in AddressBook._currentMemberIDsFromMemberIDRemovedRevisionRows(memberRows)
+        ])
+
         if not self.owned() and not self.addressbook().fullyShared():
             groupIDsToRemoveFrom = readWriteObjectIDs | groupIDs
             groupIDs -= readWriteObjectIDs
         else:
             groupIDsToRemoveFrom = groupIDs
 
-        if groupIDsToRemoveFrom:
-            # remove memberships
-            yield self._removeMemberIDFromGroupsQuery(groupIDsToRemoveFrom).on(self._txn,
-                groupIDs=groupIDsToRemoveFrom,
+        # add to member table rows marked removed
+        for groupIDToRemoveFrom in groupIDsToRemoveFrom:
+            yield self._insertMemberIDQuery.on(self._txn,
+                groupID=groupIDToRemoveFrom,
                 addressbookID=self._ownerAddressBookResourceID,
                 memberID=self._resourceID,
                 revision=self._syncTokenRevision,
+                removed=True,
             )
 
         # add to foreign member table row by UID (aboForeignMembers on address books)
@@ -1624,22 +1663,17 @@
         if self.kind() == _ABO_KIND_GROUP:
 
             # mark members as deleted
-            memberIDRows = yield Select(
-                [aboMembers.MEMBER_ID],
-                From=aboMembers,
-                Where=(aboMembers.GROUP_ID == self._resourceID)
-                    .And(aboMembers.REMOVED == False),
-            ).on(self._txn)
-            memberIDsToRemove = [memberIDRow[0] for memberIDRow in memberIDRows]
+            memberIDsToRemove = yield AddressBook.memberIDsWithGroupIDs(self._txn, [self._resourceID])
 
-            if memberIDsToRemove:
-                yield self._removeMemberIDsFromGroupQuery(memberIDsToRemove).on(
+            for memberIDToRemove in memberIDsToRemove:
+                yield self._insertMemberIDQuery.on(
                     self._txn,
                     groupID=self._resourceID,
                     addressbookID=self._ownerAddressBookResourceID,
-                    memberIDs=memberIDsToRemove,
+                    memberID=memberIDToRemove,
                     revision=self._syncTokenRevision,
-               )
+                    removed=True,
+                )
 
         yield super(AddressBookObject, self).remove()
         self._kind = None
@@ -2038,41 +2072,11 @@
              aboMembers.ADDRESSBOOK_ID: Parameter("addressbookID"),
              aboMembers.MEMBER_ID: Parameter("memberID"),
              aboMembers.REVISION: Parameter("revision"),
-             #aboMembers.REMOVED: False,
+             aboMembers.REMOVED: Parameter("removed"),
              }
         )
 
 
-    @classmethod
-    def _removeMemberIDsFromGroupQuery(cls, memberIDs): #@NoSelf
-        """
-        DAL statement to mark a member table row to as removed
-        """
-        aboMembers = schema.ABO_MEMBERS
-        return Update(
-            {aboMembers.REVISION: Parameter("revision"),
-             aboMembers.REMOVED: True, },
-            Where=(aboMembers.GROUP_ID == Parameter("groupID"))
-              .And(aboMembers.ADDRESSBOOK_ID == Parameter("addressbookID"))
-              .And(aboMembers.MEMBER_ID.In(Parameter("memberIDs", len(memberIDs)))),
-       )
-
-
-    @classmethod
-    def _removeMemberIDFromGroupsQuery(cls, groupIDs): #@NoSelf
-        """
-        DAL statement update a group member
-        """
-        aboMembers = schema.ABO_MEMBERS
-        return Update(
-            {aboMembers.REVISION: Parameter("revision"),
-             aboMembers.REMOVED: True, },
-            Where=(aboMembers.GROUP_ID.In(Parameter("groupIDs", len(groupIDs))))
-              .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):
@@ -2193,6 +2197,7 @@
                     addressbookID=self._ownerAddressBookResourceID,
                     memberID=self._resourceID,
                     revision=self._syncTokenRevision,
+                    removed=False,
                 )
 
         else:
@@ -2210,31 +2215,20 @@
                 memberIDs.append(self._resourceID)
 
             # get current members
-            currentMemberRows = yield AddressBook._memberIDsWithGroupIDsQuery([self._resourceID]).on(
-                    self._txn, groupIDs=[self._resourceID]
-            )
-            currentMemberIDs = [currentMemberRow[0] for currentMemberRow in currentMemberRows if not currentMemberRow[1]]
-
+            currentMemberIDs = yield AddressBook.memberIDsWithGroupIDs(self._txn, [self._resourceID])
             memberIDsToRemove = set(currentMemberIDs) - set(memberIDs)
             memberIDsToAdd = set(memberIDs) - set(currentMemberIDs)
 
-            for memberIDToAdd in memberIDsToAdd:
-                yield self._insertMemberIDQuery.on(self._txn,
+            for memberIDToAdd in memberIDsToAdd | memberIDsToRemove:
+                yield self._insertMemberIDQuery.on(
+                    self._txn,
                     groupID=self._resourceID,
                     addressbookID=self._ownerAddressBookResourceID,
                     memberID=memberIDToAdd,
                     revision=self._syncTokenRevision,
+                    removed=self._resourceID in memberIDsToRemove,
                 )
 
-            if memberIDsToRemove:
-                yield self._removeMemberIDsFromGroupQuery(memberIDsToRemove).on(
-                    self._txn,
-                    groupID=self._resourceID,
-                    addressbookID=self._ownerAddressBookResourceID,
-                    memberIDs=memberIDsToRemove,
-                    revision=self._syncTokenRevision,
-               )
-
             # get current foreign members
             currentForeignMemberRows = yield Select(
                 [aboForeignMembers.MEMBER_ADDRESS],
@@ -2297,10 +2291,7 @@
 
                     # generate "X-ADDRESSBOOKSERVER-MEMBER" properties
                     # first get member resource ids
-                    memberRows = yield AddressBook._memberIDsWithGroupIDsQuery([self._resourceID]).on(
-                            self._txn, groupIDs=[self._resourceID]
-                    )
-                    memberIDs = [memberRow[0] for memberRow in memberRows if not memberRow[1]]
+                    memberIDs = yield AddressBook.memberIDsWithGroupIDs(self._txn, [self._resourceID])
 
                     # then get member UIDs
                     abo = schema.ADDRESSBOOK_OBJECT
@@ -2402,10 +2393,6 @@
                            ).And(bind.BIND_STATUS == _BIND_STATUS_ACCEPTED))
 
 
-    def notifyChanged(self):
-        return self.addressbook().notifyChanged()
-
-
     @classproperty
     def _addressbookIDForResourceID(cls): #@NoSelf
         obj = cls._objectSchema
@@ -2506,7 +2493,8 @@
                 shareeHome._children.pop(self.addressbook()._resourceID, None)
 
             # Must send notification to ensure cache invalidation occurs
-            yield self.notifyChanged()
+            yield self.addressbook().notifyPropertyChanged()
+            yield shareeHome.notifyChanged()
 
         # delete bind including invites
         deletedBindNameRows = yield self._deleteBindForResourceIDAndHomeID.on(
@@ -2583,8 +2571,7 @@
             if status == _BIND_STATUS_ACCEPTED:
                 shareeView = yield shareeHome.objectWithShareUID(bindName)
                 if not shareeView.addressbook().fullyShared():
-                    currentAcceptedBindCount = 1 if shareeView.addressbook().fullyShared() else 0
-                    currentAcceptedBindCount += len((
+                    currentAcceptedBindCount = len((
                         yield AddressBookObject._acceptedBindForHomeIDAndAddressBookID.on(
                             self._txn, homeID=shareeView.viewerHome()._resourceID, addressbookID=shareeView.addressbook()._resourceID
                         )
@@ -2592,7 +2579,6 @@
                     if currentAcceptedBindCount == 1:
                         yield shareeView.addressbook()._initSyncToken()
 
-                    # start share here
                 yield shareeView._initBindRevision()
 
         queryCacher = self._txn._queryCacher
@@ -2601,7 +2587,9 @@
             queryCacher.invalidateAfterCommit(self._txn, cacheKey)
 
         # Must send notification to ensure cache invalidation occurs
-        yield self.notifyChanged()
+        yield self.addressbook().notifyPropertyChanged()
+        yield shareeHome.notifyChanged()
+
         self.setShared(True)
         returnValue(bindName)
 
@@ -2700,7 +2688,8 @@
             shareeView._name = bindNameRows[0][0]
 
             # Must send notification to ensure cache invalidation occurs
-            yield self.notifyChanged()
+            yield self.addressbook().notifyPropertyChanged()
+            yield shareeView.viewerHome().notifyChanged()
 
         returnValue(shareeView._name)
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130927/eb010856/attachment-0001.html>


More information about the calendarserver-changes mailing list