[CalendarServer-changes] [11193] CalendarServer/branches/users/gaya/sharedgroups-3

source_changes at macosforge.org source_changes at macosforge.org
Wed May 15 15:34:00 PDT 2013


Revision: 11193
          http://trac.calendarserver.org//changeset/11193
Author:   gaya at apple.com
Date:     2013-05-15 15:34:00 -0700 (Wed, 15 May 2013)
Log Message:
-----------
merge from 11192

Modified Paths:
--------------
    CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/customxml.py
    CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/extensions.py
    CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/method/report_sync_collection.py
    CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/resource.py
    CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/stdconfig.py
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/base/propertystore/sql.py
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/sql.py
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/common.py
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/test_attachments.py
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/test_sql.py
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/carddav/datastore/sql.py
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/file.py
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql.py
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/current.sql
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/old/oracle-dialect/v19.sql
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/old/postgres-dialect/v19.sql
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/upgrade/sql/upgrade.py
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py

Property Changed:
----------------
    CalendarServer/branches/users/gaya/sharedgroups-3/
    CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql.py


Property changes on: CalendarServer/branches/users/gaya/sharedgroups-3
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:11090-11095,11097-11111,11115-11174
   + /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:11090-11095,11097-11111,11115-11192

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/customxml.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/customxml.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -72,7 +72,11 @@
     "calendarserver-partstat-changes",
 )
 
+calendarserver_home_sync_compliance = (
+    "calendarserver-home-sync",
+)
 
+
 @registerElement
 class TwistedCalendarSupportedComponents (WebDAVTextElement):
     """

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/extensions.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/extensions.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -535,24 +535,6 @@
 
 
 
-def updateCacheTokenOnCallback(f):
-    def wrapper(self, *args, **kwargs):
-        if hasattr(self, "notifyChanged"):
-            def updateToken(response):
-                d = self.notifyChanged()
-                d.addCallback(lambda _: response)
-                return d
-
-            d = maybeDeferred(f, self, *args, **kwargs)
-            d.addCallback(updateToken)
-            return d
-        else:
-            return f(self, *args, **kwargs)
-
-    return wrapper
-
-
-
 class DAVResource (DirectoryPrincipalPropertySearchMixIn,
                    SuperDAVResource, LoggingMixIn,
                    DirectoryRenderingMixIn, StaticRenderMixin):
@@ -563,20 +545,6 @@
     that is currently in static.py but is actually applicable to any type of resource.
     """
 
-    @updateCacheTokenOnCallback
-    def http_PROPPATCH(self, request):
-        return super(DAVResource, self).http_PROPPATCH(request)
-
-
-    @updateCacheTokenOnCallback
-    def http_DELETE(self, request):
-        return super(DAVResource, self).http_DELETE(request)
-
-
-    @updateCacheTokenOnCallback
-    def http_ACL(self, request):
-        return super(DAVResource, self).http_ACL(request)
-
     http_REPORT = http_REPORT
 
     def davComplianceClasses(self):

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/method/report_sync_collection.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/method/report_sync_collection.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/method/report_sync_collection.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -46,7 +46,7 @@
     """
     Generate a sync-collection REPORT.
     """
-    
+
     # These resource support the report
     if not config.EnableSyncReport or element.Report(element.SyncCollection(),) not in self.supportedReports():
         log.err("sync-collection report is only allowed on calendar/inbox/addressbook/notification collection resources %s" % (self,))
@@ -55,24 +55,26 @@
             element.SupportedReport(),
             "Report not supported on this resource",
         ))
-   
+
     responses = []
 
     # Process Depth and sync-level for backwards compatibility
     # Use sync-level if present and ignore Depth, else use Depth
     if sync_collection.sync_level:
         depth = sync_collection.sync_level
+        if depth == "infinite":
+            depth = "infinity"
         descriptor = "DAV:sync-level"
     else:
         depth = request.headers.getHeader("depth", None)
         descriptor = "Depth header without DAV:sync-level"
-    
+
     if depth not in ("1", "infinity"):
         log.err("sync-collection report with invalid depth header: %s" % (depth,))
         raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Invalid %s value" % (descriptor,)))
-        
-    propertyreq = sync_collection.property.children if sync_collection.property else None 
-    
+
+    propertyreq = sync_collection.property.children if sync_collection.property else None
+
     @inlineCallbacks
     def _namedPropertiesForResource(request, props, resource, forbidden=False):
         """
@@ -87,7 +89,7 @@
             responsecode.FORBIDDEN : [],
             responsecode.NOT_FOUND : [],
         }
-        
+
         for property in props:
             if isinstance(property, element.WebDAVElement):
                 qname = property.qname()
@@ -106,13 +108,14 @@
                         f = Failure()
                         log.err("Error reading property %r for resource %s: %s" % (qname, request.uri, f.value))
                         status = statusForFailure(f, "getting property: %s" % (qname,))
-                        if status not in properties_by_status: properties_by_status[status] = []
+                        if status not in properties_by_status:
+                            properties_by_status[status] = []
                         properties_by_status[status].append(propertyName(qname))
                 else:
                     properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname))
-        
+
         returnValue(properties_by_status)
-    
+
     # Do some optimization of access control calculation by determining any inherited ACLs outside of
     # the child resource loop and supply those to the checkPrivileges on each child.
     filteredaces = (yield self.inheritedACEsforChildren(request))
@@ -173,11 +176,11 @@
     for name in removed:
         href = element.HRef.fromString(joinURL(request.uri, name))
         responses.append(element.StatusResponse(element.HRef.fromString(href), element.Status.fromResponseCode(responsecode.NOT_FOUND)))
-    
+
     for name in notallowed:
         href = element.HRef.fromString(joinURL(request.uri, name))
         responses.append(element.StatusResponse(element.HRef.fromString(href), element.Status.fromResponseCode(responsecode.NOT_ALLOWED)))
-    
+
     if not hasattr(request, "extendedLogItems"):
         request.extendedLogItems = {}
     request.extendedLogItems["responses"] = len(responses)

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/resource.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/resource.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -202,22 +202,7 @@
 
 calendarPrivilegeSet = _calendarPrivilegeSet()
 
-def updateCacheTokenOnCallback(f):
-    def fun(self, *args, **kwargs):
-        def _updateToken(response):
-            return self.notifyChanged().addCallback(lambda _: response)
 
-        d = maybeDeferred(f, self, *args, **kwargs)
-
-        if hasattr(self, 'notifyChanged'):
-            d.addCallback(_updateToken)
-
-        return d
-
-    return fun
-
-
-
 class CalDAVResource (
         CalDAVComplianceMixIn, SharedResourceMixin,
         DAVResourceWithChildrenMixin, DAVResource, LoggingMixIn
@@ -391,21 +376,6 @@
     # End transitional new-store interface
 
 
-    @updateCacheTokenOnCallback
-    def http_PROPPATCH(self, request):
-        return super(CalDAVResource, self).http_PROPPATCH(request)
-
-
-    @updateCacheTokenOnCallback
-    def http_DELETE(self, request):
-        return super(CalDAVResource, self).http_DELETE(request)
-
-
-    @updateCacheTokenOnCallback
-    def http_ACL(self, request):
-        return super(CalDAVResource, self).http_ACL(request)
-
-
     ##
     # WebDAV
     ##

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/stdconfig.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/twistedcaldav/stdconfig.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -1542,6 +1542,9 @@
     compliance += customxml.calendarserver_principal_property_search_compliance
     compliance += customxml.calendarserver_principal_search_compliance
 
+    # Home Depth:1 sync report will include WebDAV property changes on home child resources
+    compliance += customxml.calendarserver_home_sync_compliance
+
     configDict.CalDAVComplianceClasses = compliance
 
 

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/base/propertystore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/base/propertystore/sql.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/base/propertystore/sql.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -250,9 +250,12 @@
                     name=key_str, uid=uid)
             self._cacher.delete(str(self._resourceID))
 
-        # Call the registered notification callback
+        # Call the registered notification callback - we need to do this as a preCommit since it involves
+        # a bunch of deferred operations, but this propstore api is not deferred. preCommit will execute
+        # the deferreds properly, and it is fine to wait until everything else is done before sending the
+        # notifications.
         if hasattr(self, "_notifyCallback") and self._notifyCallback is not None:
-            self._notifyCallback()
+            self._txn.preCommit(self._notifyCallback)
 
         def justLogIt(f):
             f.trap(AllRetriesFailed)
@@ -278,6 +281,14 @@
                                  name=key_str, uid=uid
                                 )
             self._cacher.delete(str(self._resourceID))
+
+        # Call the registered notification callback - we need to do this as a preCommit since it involves
+        # a bunch of deferred operations, but this propstore api is not deferred. preCommit will execute
+        # the deferreds properly, and it is fine to wait until everything else is done before sending the
+        # notifications.
+        if hasattr(self, "_notifyCallback") and self._notifyCallback is not None:
+            self._txn.preCommit(self._notifyCallback)
+
         def justLogIt(f):
             f.trap(AllRetriesFailed)
             self.log_error("setting a property failed; probably nothing.")

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/sql.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/sql.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -624,11 +624,8 @@
         pick another from the calendar home.
         """
 
-        chm = self._homeMetaDataSchema
         componentType = "VTODO" if tasks else "VEVENT"
         test_name = "tasks" if tasks else "calendar"
-        attribute_to_test = "_default_tasks" if tasks else "_default_events"
-        column_to_set = chm.DEFAULT_TASKS if tasks else chm.DEFAULT_EVENTS
 
         defaultCalendar = (yield self.calendarWithName(test_name))
         if defaultCalendar is None or not defaultCalendar.owned():
@@ -657,13 +654,7 @@
                     # Failed to even create a default - bad news...
                     raise RuntimeError("No valid calendars to use as a default %s calendar." % (componentType,))
 
-        setattr(self, attribute_to_test, defaultCalendar._resourceID)
-        yield Update(
-            {column_to_set: defaultCalendar._resourceID},
-            Where=chm.RESOURCE_ID == self._resourceID,
-        ).on(self._txn)
-        yield self.invalidateQueryCache()
-        yield self.notifyChanged()
+        yield self.setDefaultCalendar(defaultCalendar, tasks)
 
         returnValue(defaultCalendar)
 
@@ -701,7 +692,12 @@
         yield self.invalidateQueryCache()
         yield self.notifyChanged()
 
+        # CalDAV stores the default calendar properties on the inbox so we also need to send a changed notification on that
+        inbox = (yield self.calendarWithName("inbox"))
+        if inbox is not None:
+            yield inbox.notifyChanged()
 
+
     @inlineCallbacks
     def defaultCalendar(self, componentType, create=True):
         """
@@ -761,14 +757,7 @@
                     yield default.setSupportedComponents(componentType.upper())
 
             # Update the metadata
-            chm = self._homeMetaDataSchema
-            column_to_set = chm.DEFAULT_TASKS if componentType == "VTODO" else chm.DEFAULT_EVENTS
-            setattr(self, attribute_to_test, default._resourceID)
-            yield Update(
-                {column_to_set: default._resourceID},
-                Where=chm.RESOURCE_ID == self._resourceID,
-            ).on(self._txn)
-            yield self.invalidateQueryCache()
+            yield self.setDefaultCalendar(default, componentType == "VTODO")
 
         returnValue(default)
 

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/common.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/common.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -1733,15 +1733,16 @@
         home = yield self.homeUnderTest()
 
         changed, deleted = yield home.resourceNamesSinceToken(
-            self.token2revision(st), "depth_is_ignored")
+            self.token2revision(st), "infinity")
 
-        self.assertEquals(set(changed), set(["calendar_1/new.ics",
+        self.assertEquals(set(changed), set(["calendar_1/",
+                                             "calendar_1/new.ics",
                                              "calendar_1/2.ics",
                                              "other-calendar/"]))
         self.assertEquals(set(deleted), set(["calendar_1/2.ics"]))
 
         changed, deleted = yield home.resourceNamesSinceToken(
-            self.token2revision(st2), "depth_is_ignored")
+            self.token2revision(st2), "infinity")
         self.assertEquals(changed, [])
         self.assertEquals(deleted, [])
 

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/test_attachments.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/test_attachments.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/test_attachments.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -14,7 +14,7 @@
 # limitations under the License.
 ##
 
-from calendarserver.tap.util import getRootResource, directoryFromConfig
+from calendarserver.tap.util import directoryFromConfig
 
 from pycalendar.datetime import PyCalendarDateTime
 from pycalendar.value import PyCalendarValue
@@ -1404,10 +1404,7 @@
         self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory, directoryFromConfig(config))
         yield self.populate()
 
-        self.rootResource = getRootResource(config, self._sqlCalendarStore)
-        self.directory = self._sqlCalendarStore.directoryService()
 
-
     @inlineCallbacks
     def populate(self):
         yield populateCalendarsFrom(self.requirements, self.storeUnderTest())

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/test_sql.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/caldav/datastore/test/test_sql.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -38,8 +38,9 @@
 from twistedcaldav.query import calendarqueryfilter
 
 from txdav.base.propertystore.base import PropertyName
+from txdav.caldav.datastore.sql import Calendar
 from txdav.caldav.datastore.test.common import CommonTests as CalendarCommonTests, \
-    test_event_text
+    test_event_text, OTHER_HOME_UID
 from txdav.caldav.datastore.test.test_file import setUpCalendarStore
 from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.caldav.datastore.util import _migrateCalendar, migrateHome
@@ -47,7 +48,7 @@
 from txdav.common.datastore.sql import ECALENDARTYPE, CommonObjectResource
 from txdav.common.datastore.sql_legacy import PostgresLegacyIndexEmulator
 from txdav.common.datastore.sql_tables import schema, _BIND_MODE_DIRECT, \
-    _BIND_STATUS_ACCEPTED
+    _BIND_STATUS_ACCEPTED, _BIND_MODE_WRITE, _BIND_STATUS_INVITED
 from txdav.common.datastore.test.util import populateCalendarsFrom
 from txdav.common.icommondatastore import NoSuchObjectResourceError
 from txdav.xml.rfc2518 import GETContentLanguage, ResourceType
@@ -1046,7 +1047,7 @@
         children = yield calendar1.listCalendarObjects()
         self.assertEqual(len(children), 3)
         new_sync_token1 = yield calendar1.syncToken()
-        self.assertEqual(new_sync_token1, original_sync_token1)
+        self.assertNotEqual(new_sync_token1, original_sync_token1)
         result = yield calendar1.getSupportedComponents()
         self.assertEquals(result, "VEVENT")
 
@@ -1692,3 +1693,87 @@
             self.assertEquals(alarm_result, None)
 
         yield self.commit()
+
+
+    @inlineCallbacks
+    def test_shareWithRevision(self):
+        """
+        Verify that bindRevision on calendars and shared calendars has the correct value.
+        """
+        cal = yield self.calendarUnderTest()
+        self.assertEqual(cal._bindRevision, 0)
+        other = yield self.homeUnderTest(name=OTHER_HOME_UID)
+        newCalName = yield cal.shareWith(other, _BIND_MODE_WRITE)
+        yield self.commit()
+
+        normalCal = yield self.calendarUnderTest()
+        self.assertEqual(normalCal._bindRevision, 0)
+        otherHome = yield self.homeUnderTest(name=OTHER_HOME_UID)
+        otherCal = yield otherHome.childWithName(newCalName)
+        self.assertNotEqual(otherCal._bindRevision, 0)
+
+
+    @inlineCallbacks
+    def test_updateShareRevision(self):
+        """
+        Verify that bindRevision on calendars and shared calendars has the correct value.
+        """
+        cal = yield self.calendarUnderTest()
+        self.assertEqual(cal._bindRevision, 0)
+        other = yield self.homeUnderTest(name=OTHER_HOME_UID)
+        newCalName = yield cal.shareWith(other, _BIND_MODE_WRITE, status=_BIND_STATUS_INVITED)
+        yield self.commit()
+
+        normalCal = yield self.calendarUnderTest()
+        self.assertEqual(normalCal._bindRevision, 0)
+        otherHome = yield self.homeUnderTest(name=OTHER_HOME_UID)
+        otherCal = yield otherHome.invitedObjectWithShareUID(newCalName)
+        self.assertEqual(otherCal._bindRevision, 0)
+        yield self.commit()
+
+        normalCal = yield self.calendarUnderTest()
+        otherHome = yield self.homeUnderTest(name=OTHER_HOME_UID)
+        otherCal = yield otherHome.invitedObjectWithShareUID(newCalName)
+        yield normalCal.updateShare(otherCal, status=_BIND_STATUS_ACCEPTED)
+        yield self.commit()
+
+        normalCal = yield self.calendarUnderTest()
+        self.assertEqual(normalCal._bindRevision, 0)
+        otherHome = yield self.homeUnderTest(name=OTHER_HOME_UID)
+        otherCal = yield otherHome.childWithName(newCalName)
+        self.assertNotEqual(otherCal._bindRevision, 0)
+
+
+    @inlineCallbacks
+    def test_sharedRevisions(self):
+        """
+        Verify that resourceNamesSinceRevision returns all resources after initial bind and sync.
+        """
+        cal = yield self.calendarUnderTest()
+        self.assertEqual(cal._bindRevision, 0)
+        other = yield self.homeUnderTest(name=OTHER_HOME_UID)
+        newCalName = yield cal.shareWith(other, _BIND_MODE_WRITE)
+        yield self.commit()
+
+        normalCal = yield self.calendarUnderTest()
+        self.assertEqual(normalCal._bindRevision, 0)
+        otherHome = yield self.homeUnderTest(name=OTHER_HOME_UID)
+        otherCal = yield otherHome.childWithName(newCalName)
+        self.assertNotEqual(otherCal._bindRevision, 0)
+
+        changed, deleted = yield otherCal.resourceNamesSinceRevision(otherCal._bindRevision - 1)
+        self.assertNotEqual(len(changed), 0)
+        self.assertEqual(len(deleted), 0)
+
+        changed, deleted = yield otherCal.resourceNamesSinceRevision(otherCal._bindRevision)
+        self.assertEqual(len(changed), 0)
+        self.assertEqual(len(deleted), 0)
+
+        for depth in ("1", "infinity",):
+            changed, deleted = yield otherHome.resourceNamesSinceRevision(otherCal._bindRevision - 1, depth)
+            self.assertNotEqual(len(changed), 0)
+            self.assertEqual(len(deleted), 0)
+
+            changed, deleted = yield otherHome.resourceNamesSinceRevision(otherCal._bindRevision, depth)
+            self.assertEqual(len(changed), 0)
+            self.assertEqual(len(deleted), 0)

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/carddav/datastore/sql.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/carddav/datastore/sql.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -195,6 +195,7 @@
     def createdHome(self):
         # initialize synctoken
         yield self.addressbook()._initSyncToken()
+        yield self.addressbook()._initBindRevision()
 
 
     @inlineCallbacks
@@ -317,17 +318,17 @@
              rev.RESOURCE_NAME,
              rev.DELETED],
             From=rev,
-            Where=(rev.REVISION > Parameter("token")).And(
+            Where=(rev.REVISION > Parameter("revision")).And(
                 rev.HOME_RESOURCE_ID == Parameter("resourceID")).And(
                 rev.RESOURCE_ID == rev.HOME_RESOURCE_ID)
         )
 
     @inlineCallbacks
-    def doChangesQuery(self, token):
+    def doChangesQuery(self, revision):
 
         rows = yield self._changesQuery.on(self._txn,
                                          resourceID=self._resourceID,
-                                         token=token)
+                                         revision=revision)
 
         bindName = self.addressbook().name()
         result = [[bindName] + row for row in rows]
@@ -657,7 +658,7 @@
         )
         # get ownerHomeIDs
         for dataRow in dataRows:
-            bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = dataRow[:6]  #@UnusedVariable
+            bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount]  #@UnusedVariable
             ownerHome = yield home.ownerHomeWithChildID(resourceID)
             ownerHomeToDataRowMap[ownerHome] = dataRow
 
@@ -666,7 +667,7 @@
             home._txn, homeID=home._resourceID
         )
         for groupBindRow in groupBindRows:
-            bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = groupBindRow[:6]  #@UnusedVariable
+            bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRow[:cls.bindColumnCount]  #@UnusedVariable
             ownerAddressBookID = yield AddressBookObject.ownerAddressBookFromGroupID(home._txn, resourceID)
             ownerHome = yield home.ownerHomeWithChildID(ownerAddressBookID)
             if ownerHome not in ownerHomeToDataRowMap:
@@ -688,14 +689,15 @@
 
             # Create the actual objects merging in properties
             for ownerHome, dataRow in ownerHomeToDataRowMap.iteritems():
-                bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = dataRow[:6]  #@UnusedVariable
-                metadata = dataRow[6:]
+                bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount]  #@UnusedVariable
+                metadata = dataRow[cls.bindColumnCount:]
 
                 child = cls(
                     home=home,
                     name=ownerHome.shareeAddressBookName(),
                     resourceID=ownerHome._resourceID,
                     mode=bindMode, status=bindStatus,
+                    revision=bindRevision,
                     message=bindMessage, ownerHome=ownerHome,
                     bindName=bindName
                 )
@@ -777,7 +779,7 @@
         if not rows:
             returnValue(None)
 
-        bindMode, homeID, resourceID, bindName, bindStatus, bindMessage, ownerAddressBookID, cachedBindStatus = rows[0]  #@UnusedVariable
+        bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage, ownerAddressBookID, cachedBindStatus = rows[0]  #@UnusedVariable
         # if wrong status, exit here.  Item is in queryCache
         if (cachedBindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
             returnValue(None)
@@ -788,6 +790,7 @@
                 home=home,
                 name=ownerAddressBook.shareeAddressBookName(), resourceID=ownerAddressBookID,
                 mode=bindMode, status=bindStatus,
+                revision=bindRevision,
                 message=bindMessage, ownerHome=ownerHome,
                 bindName=bindName,
             )
@@ -811,7 +814,7 @@
         """
         bindRows = yield cls._bindForNameAndHomeID.on(home._txn, name=name, homeID=home._resourceID)
         if bindRows:
-            bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = bindRows[0]  #@UnusedVariable
+            bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = bindRows[0]  #@UnusedVariable
             if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
                 returnValue(None)
 
@@ -827,7 +830,7 @@
             home._txn, name=name, homeID=home._resourceID
         )
         if groupBindRows:
-            bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = groupBindRows[0]  #@UnusedVariable
+            bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRows[0]  #@UnusedVariable
             if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
                 returnValue(None)
 
@@ -868,7 +871,7 @@
             home._txn, resourceID=resourceID, homeID=home._resourceID
         )
         if bindRows:
-            bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = bindRows[0]  #@UnusedVariable
+            bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = bindRows[0]  #@UnusedVariable
             if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
                 returnValue(None)
 
@@ -882,7 +885,7 @@
                     home._txn, homeID=home._resourceID, addressbookID=resourceID
         )
         if groupBindRows:
-            bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = groupBindRows[0]  #@UnusedVariable
+            bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRows[0]  #@UnusedVariable
             if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
                 returnValue(None)
 
@@ -929,9 +932,11 @@
             Where=(bind.RESOURCE_ID == Parameter("resourceID"))
                   .And(bind.HOME_RESOURCE_ID == Parameter("homeID")),
         ).on(self._txn, resourceID=self._resourceID, homeID=self.viewerHome()._resourceID)
-        '''
+
         yield self.invalidateQueryCache()
         yield self.notifyChanged()
+        '''
+        yield None
 
 
     @classmethod
@@ -950,7 +955,7 @@
         rows.extend((yield AddressBookObject._acceptedBindForHomeID.on(
             home._txn, homeID=home._resourceID
         )))
-        for bindMode, homeID, resourceID, bindName, bindStatus, bindMessage in rows:  #@UnusedVariable
+        for bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage in rows:  #@UnusedVariable
             ownerHome = yield home._txn.homeWithResourceID(home._homeType, homeID)
             names |= set([ownerHome.shareeAddressBookName()])
         returnValue(tuple(names))
@@ -995,7 +1000,7 @@
             groupBindRows = yield AddressBookObject._unacceptedBindWithHomeIDAndAddressBookID.on(
                     self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
             )
-            #for bindMode, homeID, resourceID, bindName, bindStatus, bindMessage in groupBindRows: #@UnusedVariable
+            #for bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage in groupBindRows: #@UnusedVariable
             returnValue([groupBindRow[2] for groupBindRow in groupBindRows])
 
 
@@ -1007,7 +1012,7 @@
             groupBindRows = yield AddressBookObject._acceptedBindWithHomeIDAndAddressBookID.on(
                     self._txn, homeID=self._home._resourceID, addressbookID=self._resourceID
             )
-            #for bindMode, homeID, resourceID, bindName, bindStatus, bindMessage in groupBindRows: #@UnusedVariable
+            #for bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage in groupBindRows: #@UnusedVariable
             returnValue([groupBindRow[2] for groupBindRow in groupBindRows])
 
 
@@ -1021,7 +1026,7 @@
             )
             readWriteGroupIDs = []
             readOnlyGroupIDs = []
-            for bindMode, homeID, resourceID, bindName, bindStatus, bindMessage in groupBindRows:  #@UnusedVariable
+            for bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage in groupBindRows:  #@UnusedVariable
                 if bindMode == _BIND_MODE_WRITE:
                     readWriteGroupIDs.append(resourceID)
                 else:
@@ -1067,7 +1072,7 @@
         )
         readWriteGroupIDs = []
         readOnlyGroupIDs = []
-        for bindMode, homeID, resourceID, bindName, bindStatus, bindMessage in groupBindRows:  #@UnusedVariable
+        for bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage in groupBindRows:  #@UnusedVariable
             if bindMode == _BIND_MODE_WRITE:
                 readWriteGroupIDs.append(resourceID)
             else:
@@ -1162,6 +1167,7 @@
                 if shareeView._bindStatus == _BIND_STATUS_ACCEPTED:
                     if 0 == previouslyAcceptedBindCount:
                         yield shareeView._initSyncToken()
+                        yield shareeView._initBindRevision()
                         shareeView._home._children[shareeView._name] = shareeView
                         shareeView._home._children[shareeView._resourceID] = shareeView
                 elif shareeView._bindStatus == _BIND_STATUS_DECLINED:
@@ -1205,7 +1211,7 @@
             bindRows = yield self._sharedBindForResourceID.on(
                 self._txn, resourceID=self._resourceID, homeID=self._home._resourceID
             )
-            for bindMode, homeID, resourceID, bindName, bindStatus, bindMessage in bindRows:  #@UnusedVariable
+            for bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage in bindRows:  #@UnusedVariable
                 home = yield self._txn.homeWithResourceID(self._home._homeType, homeID)
                 new = yield home.childWithName(self.shareeAddressBookName())
                 result.append(new)
@@ -1231,7 +1237,7 @@
             bindRows = yield self._unacceptedBindForResourceID.on(
                 self._txn, resourceID=self._resourceID
             )
-            for bindMode, homeID, resourceID, bindName, bindStatus, bindMessage in bindRows:  #@UnusedVariable
+            for bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage in bindRows:  #@UnusedVariable
                 home = yield self._txn.homeWithResourceID(self._home._homeType, homeID)
                 new = yield self.objectWithName(home, self.shareeAddressBookName(), accepted=False)
                 result.append(new)
@@ -1532,7 +1538,7 @@
                 )
 
                 if groupBindRows:
-                    bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = groupBindRows[0]  #@UnusedVariable
+                    bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRows[0]  #@UnusedVariable
                     self._bindMode = bindMode
                     self._bindStatus = bindStatus
                     self._bindMessage = bindMessage
@@ -2115,9 +2121,11 @@
             Where=(bind.RESOURCE_ID == Parameter("resourceID"))
                   .And(bind.HOME_RESOURCE_ID == Parameter("homeID")),
         ).on(self._txn, resourceID=self._resourceID, homeID=self.viewerHome()._resourceID)
-        '''
+
         yield self.invalidateQueryCache()
         yield self.notifyChanged()
+        '''
+        yield None
 
     @classmethod
     def metadataColumns(cls):
@@ -2138,7 +2146,7 @@
     def _childrenAndMetadataForHomeID(cls):  #@NoSelf
         bind = cls._bindSchema
         child = cls._objectSchema
-        columns = cls._bindColumns() + cls.metadataColumns()
+        columns = cls.bindColumns() + cls.metadataColumns()
         return Select(columns,
                      From=child.join(
                          bind, child.RESOURCE_ID == bind.RESOURCE_ID,
@@ -2171,7 +2179,7 @@
             groupBindRows = yield self._sharedBindForResourceID.on(
                 self._txn, resourceID=self._resourceID, homeID=self._home._resourceID
             )
-            for bindMode, homeID, resourceID, bindName, bindStatus, bindMessage in groupBindRows:  #@UnusedVariable
+            for bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage in groupBindRows:  #@UnusedVariable
                 home = yield self._txn.homeWithResourceID(self._home._homeType, homeID)
                 addressbook = yield home.childWithName(self._home.shareeAddressBookName())
                 new = yield addressbook.objectResourceWithID(resourceID)
@@ -2199,7 +2207,7 @@
             groupBindRows = yield self._unacceptedBindForResourceID.on(
                 self._txn, resourceID=self._resourceID
             )
-            for bindMode, homeID, resourceID, bindName, bindStatus, bindMessage in groupBindRows:  #@UnusedVariable
+            for bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage in groupBindRows:  #@UnusedVariable
                 home = yield self._txn.homeWithResourceID(self._home._homeType, homeID)
                 addressbook = yield home.childWithName(self._home.shareeAddressBookName())
                 if not addressbook:
@@ -2330,16 +2338,21 @@
             returnValue(newName)
         try:
             bindName = yield self._txn.subtransaction(doInsert)
+            if status == _BIND_STATUS_ACCEPTED:
+                shareeView = yield shareeHome.objectWithShareUID(bindName)
+                yield shareeView._initSyncToken()
+                yield shareeView._initBindRevision()
+
         except AllRetriesFailed:
             # FIXME: catch more specific exception
             groupBindRows = yield self._bindForResourceIDAndHomeID.on(
                 self._txn, resourceID=self._resourceID, homeID=shareeHome._resourceID
             )
-            bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = groupBindRows[0]  #@UnusedVariable
+            bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = groupBindRows[0]  #@UnusedVariable
             if bindStatus == _BIND_STATUS_ACCEPTED:
-                group = yield shareeHome.objectForShareUID(bindName)
+                group = yield shareeHome.objectWithShareUID(bindName)
             else:
-                group = yield shareeHome.invitedObjectForShareUID(bindName)
+                group = yield shareeHome.invitedObjectWithShareUID(bindName)
             bindName = yield self.updateShare(
                 group, mode=mode, status=status,
                 message=message
@@ -2416,6 +2429,7 @@
                 if shareeView._bindStatus == _BIND_STATUS_ACCEPTED:
                     if 0 == previouslyAcceptedBindCount:
                         yield shareeView._addressbook._initSyncToken()
+                        yield shareeView._addressbook._initBindRevision()
                         shareeView._home._children[shareeView._addressbook._name] = shareeView._addressbook
                         shareeView._home._children[shareeView._addressbook._resourceID] = shareeView._addressbook
                 elif shareeView._bindStatus != _BIND_STATUS_INVITED:
@@ -2446,7 +2460,7 @@
         bind = cls._bindSchema
         abo = cls._objectSchema
         return Select(
-                  cls._bindColumns(),
+                  cls.bindColumns(),
                   From=bind.join(abo),
                   Where=(bind.BIND_STATUS == _BIND_STATUS_ACCEPTED)
                         .And(bind.RESOURCE_ID == abo.RESOURCE_ID)
@@ -2460,7 +2474,7 @@
         bind = cls._bindSchema
         abo = cls._objectSchema
         return Select(
-                  cls._bindColumns(),
+                  cls.bindColumns(),
                   From=bind.join(abo),
                   Where=(bind.BIND_STATUS != _BIND_STATUS_ACCEPTED)
                         .And(bind.RESOURCE_ID == abo.RESOURCE_ID)
@@ -2474,7 +2488,7 @@
         bind = cls._bindSchema
         abo = cls._objectSchema
         return Select(
-                  cls._bindColumns(),
+                  cls.bindColumns(),
                   From=bind.join(abo),
                   Where=(bind.RESOURCE_ID == abo.RESOURCE_ID)
                         .And(bind.HOME_RESOURCE_ID == Parameter("homeID"))

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/file.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/file.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -286,7 +286,6 @@
         self._notificationHomes = {}
         self._notifierFactories = notifierFactories
         self._notifiedAlready = set()
-        self._bumpedAlready = set()
         self._migrating = migrating
 
         extraInterfaces = []
@@ -405,25 +404,7 @@
         self._notifiedAlready.add(obj)
 
 
-    def isBumpedAlready(self, obj):
-        """
-        Indicates whether or not bumpAddedForObject has already been
-        called for the given object, in order to facilitate calling
-        bumpModified only once per object.
-        """
-        return obj in self._bumpedAlready
 
-
-    def bumpAddedForObject(self, obj):
-        """
-        Records the fact that a bumpModified( ) call has already been
-        done, in order to facilitate calling bumpModified only once per
-        object.
-        """
-        self._bumpedAlready.add(obj)
-
-
-
 class StubResource(object):
     """
     Just enough resource to keep the shared sql DB classes going.

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -451,7 +451,7 @@
         self._notificationHomes = {}
         self._notifierFactories = notifierFactories
         self._notifiedAlready = set()
-        self._bumpedAlready = set()
+        self._bumpedRevisionAlready = set()
         self._label = label
         self._migrating = migrating
         self._primaryHomeType = None
@@ -885,22 +885,20 @@
         self._notifiedAlready.add(obj.id())
 
 
-    def isBumpedAlready(self, obj):
+    def isRevisionBumpedAlready(self, obj):
         """
-        Indicates whether or not bumpAddedForObject has already been
-        called for the given object, in order to facilitate calling
-        bumpModified only once per object.
+        Indicates whether or not bumpRevisionForObject has already been
+        called for the given object, in order to facilitate changing the
+        revision only once per object.
         """
-        return obj.id() in self._bumpedAlready
+        return obj.id() in self._bumpedRevisionAlready
 
 
-    def bumpAddedForObject(self, obj):
+    def bumpRevisionForObject(self, obj):
         """
-        Records the fact that a bumpModified( ) call has already been
-        done, in order to facilitate calling bumpModified only once per
-        object.
+        Records the fact that a revision token for the object has been bumped.
         """
-        self._bumpedAlready.add(obj.id())
+        self._bumpedRevisionAlready.add(obj.id())
 
     _savepointCounter = 0
 
@@ -1762,8 +1760,8 @@
         child = yield self.childWithName(name)
         if child is None:
             raise NoSuchHomeChildError()
+        resourceID = child._resourceID
 
-        resourceID = child._resourceID
         yield child.remove()
         self._children.pop(name, None)
         self._children.pop(resourceID, None)
@@ -1773,6 +1771,14 @@
     def _syncTokenQuery(cls):  #@NoSelf
         """
         DAL Select statement to find the sync token.
+
+        This is the max(REVISION) from the union of:
+
+        1) REVISION's for all object resources in all home child collections in the targeted home
+        2) REVISION's for all child collections in the targeted home
+
+        Note the later is needed to track changes directly to the home child themselves (e.g.
+        property changes, deletion etc).
         """
         rev = cls._revisionsSchema
         bind = cls._bindSchema
@@ -1802,6 +1808,16 @@
         )
 
 
+    def revisionFromToken(self, token):
+        if token is None:
+            return 0
+        elif isinstance(token, str):
+            _ignore_uuid, revision = token.split("_", 1)
+            return int(revision)
+        else:
+            return token
+
+
     @inlineCallbacks
     def syncToken(self):
         """
@@ -1820,39 +1836,69 @@
         bind = cls._bindSchema
         rev = cls._revisionsSchema
         return Select(
-            [bind.RESOURCE_NAME,
-             rev.COLLECTION_NAME,
-             rev.RESOURCE_NAME,
-             rev.DELETED],
+            [
+                bind.RESOURCE_NAME,
+                rev.COLLECTION_NAME,
+                rev.RESOURCE_NAME,
+                rev.DELETED,
+            ],
             From=rev.join(
                 bind,
-                (
-                    bind.HOME_RESOURCE_ID == Parameter("resourceID")).And(
-                    rev.RESOURCE_ID == bind.RESOURCE_ID
-                ), 'left outer'
+                (bind.HOME_RESOURCE_ID == Parameter("resourceID")).And
+                (rev.RESOURCE_ID == bind.RESOURCE_ID),
+                'left outer'
             ),
-            Where=(rev.REVISION > Parameter("token")).And(
-                rev.HOME_RESOURCE_ID ==
-                Parameter("resourceID")
-            )
+            Where=(rev.REVISION > Parameter("revision")).And
+                  (rev.HOME_RESOURCE_ID == Parameter("resourceID"))
         )
 
 
     @inlineCallbacks
-    def doChangesQuery(self, token):
+    def doChangesQuery(self, revision):
         """
             Do the changes query.
             Subclasses may override.
         """
         result = yield self._changesQuery.on(self._txn,
                                          resourceID=self._resourceID,
-                                         token=token)
+                                         revision=revision)
         returnValue(result)
 
 
+    def resourceNamesSinceToken(self, token, depth):
+        """
+        Return the changed and deleted resources since a particular sync-token. This simply extracts
+        the revision from from the token then calls L{resourceNamesSinceRevision}.
+
+        @param revision: the revision to determine changes since
+        @type revision: C{int}
+        """
+
+        return self.resourceNamesSinceRevision(self.revisionFromToken(token), depth)
+
+
     @inlineCallbacks
-    def resourceNamesSinceToken(self, token, depth):  #@UnusedVariable
+    def resourceNamesSinceRevision(self, revision, depth):
+        """
+        Determine the list of child resources that have changed since the specified sync revision.
+        We do the same SQL query for both depth "1" and "infinity", but filter the results for
+        "1" to only account for a collection change.
 
+        We need to handle shared collection a little differently from owned ones. When a shared collection
+        is bound into a home we record a revision for it using the sharee home id and sharee collection name.
+        That revision is the "starting point" for changes: so if sync occurs with a revision earlier than
+        that, we return the list of all resources in the shared collection since they are all "new" as far
+        as the client is concerned since the shared collection has just appeared. For a later revision, we
+        just report the changes since that one. When a shared collection is removed from a home, we again
+        record a revision for the sharee home and sharee collection name with the "deleted" flag set. That way
+        the shared collection can be reported as removed.
+
+        @param revision: the sync revision to compare to
+        @type revision: C{str}
+        @param depth: depth for determine what changed
+        @type depth: C{str}
+        """
+
         results = [
             (
                 path if path else (collection if collection else ""),
@@ -1860,57 +1906,81 @@
                 wasdeleted
             )
             for path, collection, name, wasdeleted in
-            (yield self.doChangesQuery(token))
+            (yield self.doChangesQuery(revision))
         ]
 
-        deleted = []
+        changed = set()
+        deleted = set()
         deleted_collections = set()
         changed_collections = set()
         for path, name, wasdeleted in results:
             if wasdeleted:
-                if token:
-                    deleted.append("%s/%s" % (path, name,))
-                if not name:
-                    deleted_collections.add(path)
+                if revision:
+                    if name:
+                        # Resource deleted - for depth "1" report collection as changed,
+                        # otherwise report resource as deleted
+                        if depth == "1":
+                            changed.add("%s/" % (path,))
+                        else:
+                            deleted.add("%s/%s" % (path, name,))
+                    else:
+                        # Collection was deleted
+                        deleted.add("%s/" % (path,))
+                        deleted_collections.add(path)
 
-        changed = []
         for path, name, wasdeleted in results:
             if path not in deleted_collections:
-                changed.append("%s/%s" % (path, name,))
-                if not name:
+                # Always report collection as changed
+                changed.add("%s/" % (path,))
+                if name:
+                    # Resource changed - for depth "infinity" report resource as changed
+                    if depth != "1":
+                        changed.add("%s/%s" % (path, name,))
+                else:
+                    # Collection was changed
                     changed_collections.add(path)
 
         # Now deal with shared collections
+        # TODO: think about whether this can be done in one query rather than looping over each share
+        bind = self._bindSchema
         rev = self._revisionsSchema
-        for share in (yield self.children()):
+        shares = yield self.children()
+        for share in shares:
             if not share.owned():
-                sharetoken = 0 if share.name() in changed_collections else token
+                sharerevision = 0 if revision < share._bindRevision else revision
+                shareID = (yield Select(
+                    [bind.RESOURCE_ID], From=bind,
+                    Where=(bind.RESOURCE_NAME == share.name()).And(
+                        bind.HOME_RESOURCE_ID == self._resourceID).And(
+                            bind.BIND_MODE != _BIND_MODE_OWN)
+                ).on(self._txn))[0][0]
                 results = [
                     (
                         share.name(),
                         name if name else "",
                         wasdeleted
                     )
-                    for name, wasdeleted in (
-                        yield Select(
-                            [rev.RESOURCE_NAME, rev.DELETED],
-                            From=rev,
-                            Where=(rev.REVISION > sharetoken).And(
-                                    rev.RESOURCE_ID == share._resourceID)
-                        ).on(self._txn)
-                    ) if name
+                    for name, wasdeleted in
+                    (yield Select([rev.RESOURCE_NAME, rev.DELETED],
+                                     From=rev,
+                                    Where=(rev.REVISION > sharerevision).And(
+                                    rev.RESOURCE_ID == shareID)).on(self._txn))
+                    if name
                 ]
 
                 for path, name, wasdeleted in results:
                     if wasdeleted:
-                        if sharetoken:
-                            deleted.append("%s/%s" % (path, name,))
+                        if sharerevision:
+                            if depth == "1":
+                                changed.add("%s/" % (path,))
+                            else:
+                                deleted.add("%s/%s" % (path, name,))
 
                 for path, name, wasdeleted in results:
-                    changed.append("%s/%s" % (path, name,))
+                    changed.add("%s/%s" % (path, "" if depth == "1" else name,))
 
-        changed.sort()
-        deleted.sort()
+        changed = sorted(changed)
+        deleted = sorted(deleted)
         returnValue((changed, deleted))
 
 
@@ -2117,18 +2187,22 @@
     @inlineCallbacks
     def notifyChanged(self):
         """
-        Trigger a notification of a change
+        Send notifications, change sync token and bump last modified because the resource has changed. We ensure
+        we only do this once per object per transaction.
         """
 
+        if self._txn.isNotifiedAlready(self):
+            returnValue(None)
+        self._txn.notificationAddedForObject(self)
+
         # Update modified if object still exists
         if self._resourceID:
             yield self.bumpModified()
 
-        # Only send one set of change notifications per transaction
-        if self._notifiers and not self._txn.isNotifiedAlready(self):
+        # Send notifications
+        if self._notifiers:
             for notifier in self._notifiers.values():
                 self._txn.postCommit(notifier.notify)
-            self._txn.notificationAddedForObject(self)
 
 
     @classproperty
@@ -2160,10 +2234,6 @@
         delay the transaction whilst waiting for deadlock detection to kick in.
         """
 
-        if self._txn.isBumpedAlready(self):
-            returnValue(None)
-        self._txn.bumpAddedForObject(self)
-
         # NB if modified is bumped we know that sync token will have changed too, so invalidate the cached value
         self._syncTokenRevision = None
 
@@ -2225,8 +2295,13 @@
 
 
     def revisionFromToken(self, token):
-        _ignore_uuid, revision = token.split("_", 1)
-        return int(revision)
+        if token is None:
+            return 0
+        elif isinstance(token, str):
+            _ignore_uuid, revision = token.split("_", 1)
+            return int(revision)
+        else:
+            return token
 
 
     @inlineCallbacks
@@ -2253,19 +2328,32 @@
                           rev.RESOURCE_ID == Parameter("resourceID")))
 
 
-    @inlineCallbacks
     def resourceNamesSinceToken(self, token):
+        """
+        Return the changed and deleted resources since a particular sync-token. This simply extracts
+        the revision from from the token then calls L{resourceNamesSinceRevision}.
 
-        if token is None:
-            token = 0
-        elif isinstance(token, str):
-            token = self.revisionFromToken(token)
+        @param revision: the revision to determine changes since
+        @type revision: C{int}
+        """
 
+        return self.resourceNamesSinceRevision(self.revisionFromToken(token))
+
+
+    @inlineCallbacks
+    def resourceNamesSinceRevision(self, revision):
+        """
+        Return the changed and deleted resources since a particular revision.
+
+        @param revision: the revision to determine changes since
+        @type revision: C{int}
+        """
+
         results = [
             (name if name else "", deleted)
             for name, deleted in
             (yield self._objectNamesSinceRevisionQuery.on(
-                self._txn, revision=token, resourceID=self._resourceID))
+                self._txn, revision=revision, resourceID=self._resourceID))
         ]
         results.sort(key=lambda x: x[1])
 
@@ -2274,7 +2362,7 @@
         for name, wasdeleted in results:
             if name:
                 if wasdeleted:
-                    if token:
+                    if revision:
                         deleted.append(name)
                 else:
                     changed.append(name)
@@ -2312,6 +2400,7 @@
             self._addNewRevision.on(self._txn, homeID=self._home._resourceID,
                                     resourceID=self._resourceID,
                                     collectionName=self._name)))[0][0]
+        self._txn.bumpRevisionForObject(self)
 
 
     @classproperty
@@ -2321,11 +2410,13 @@
         resource name).
         """
         rev = cls._revisionsSchema
-        return Update({
-            rev.REVISION: schema.REVISION_SEQ,
-            rev.COLLECTION_NAME: Parameter("name")},
-            Where=(rev.RESOURCE_ID == Parameter("resourceID")
-                  ).And(rev.RESOURCE_NAME == None),
+        return Update(
+            {
+                rev.REVISION: schema.REVISION_SEQ,
+                rev.COLLECTION_NAME: Parameter("name")
+            },
+            Where=(rev.RESOURCE_ID == Parameter("resourceID")).And
+                  (rev.RESOURCE_NAME == None),
             Return=rev.REVISION
         )
 
@@ -2334,18 +2425,44 @@
     def _renameSyncToken(self):
         self._syncTokenRevision = (yield self._renameSyncTokenQuery.on(
             self._txn, name=self._name, resourceID=self._resourceID))[0][0]
+        self._txn.bumpRevisionForObject(self)
 
 
     @classproperty
+    def _bumpSyncTokenQuery(cls):  #@NoSelf
+        """
+        DAL query to change collection sync token.
+        """
+        rev = cls._revisionsSchema
+        return Update(
+            {rev.REVISION: schema.REVISION_SEQ, },
+            Where=(rev.RESOURCE_ID == Parameter("resourceID")).And
+                  (rev.RESOURCE_NAME == None),
+            Return=rev.REVISION
+        )
+
+
+    @inlineCallbacks
+    def _bumpSyncToken(self):
+
+        if not self._txn.isRevisionBumpedAlready(self):
+            self._txn.bumpRevisionForObject(self)
+            self._syncTokenRevision = (yield self._bumpSyncTokenQuery.on(
+                self._txn, resourceID=self._resourceID))[0][0]
+
+
+    @classproperty
     def _deleteSyncTokenQuery(cls):  #@NoSelf
         """
         DAL query to update a sync revision to be a tombstone instead.
         """
         rev = cls._revisionsSchema
-        return Delete(From=rev, Where=(
-            rev.HOME_RESOURCE_ID == Parameter("homeID")).And(
-                rev.RESOURCE_ID == Parameter("resourceID")).And(
-                rev.COLLECTION_NAME == None))
+        return Delete(
+            From=rev,
+            Where=(rev.HOME_RESOURCE_ID == Parameter("homeID")).And
+                  (rev.RESOURCE_ID == Parameter("resourceID")).And
+                  (rev.COLLECTION_NAME == None)
+        )
 
 
     @classproperty
@@ -2561,20 +2678,9 @@
 
 
     @classmethod
-    def _bindColumns(cls):
-        bind = cls._bindSchema
-        return (bind.BIND_MODE,
-                bind.HOME_RESOURCE_ID,
-                bind.RESOURCE_ID,
-                bind.RESOURCE_NAME,
-                bind.BIND_STATUS,
-                bind.MESSAGE,)
-
-
-    @classmethod
     def _bindFor(cls, condition):  #@NoSelf
         bind = cls._bindSchema
-        columns = cls._bindColumns() + cls.additionalBindColumns()
+        columns = cls.bindColumns() + cls.additionalBindColumns()
         return Select(
             columns,
             From=bind,
@@ -2667,6 +2773,11 @@
             returnValue(newName)
         try:
             bindName = yield self._txn.subtransaction(doInsert)
+            if status == _BIND_STATUS_ACCEPTED:
+                shareeView = yield shareeHome.objectWithShareUID(bindName)
+                yield shareeView._initSyncToken()
+                yield shareeView._initBindRevision()
+
         except AllRetriesFailed:
             # FIXME: catch more specific exception
             child = yield shareeHome.childWithID(self._resourceID)
@@ -2725,7 +2836,6 @@
 
         if len(columnMap):
 
-            #TODO:  with bit of parameter wrangling, call shareWith() here instead.
             sharedname = yield self._updateBindColumnsQuery(columnMap).on(
                             self._txn,
                             resourceID=self._resourceID, homeID=shareeView._home._resourceID
@@ -2739,6 +2849,7 @@
                 shareeView._bindStatus = columnMap[bind.BIND_STATUS]
                 if shareeView._bindStatus == _BIND_STATUS_ACCEPTED:
                     yield shareeView._initSyncToken()
+                    yield shareeView._initBindRevision()
                     shareeView._home._children[shareeView._name] = shareeView
                     shareeView._home._children[shareeView._resourceID] = shareeView
                 elif shareeView._bindStatus == _BIND_STATUS_DECLINED:
@@ -2842,7 +2953,7 @@
 
         result = []
         for row in acceptedRows:
-            bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = row[:6]  #@UnusedVariable
+            bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:self.bindColumnCount]  #@UnusedVariable
             home = yield self._txn.homeWithResourceID(self._home._homeType, homeID)
             new = yield home.objectWithShareUID(bindName)
             result.append(new)
@@ -2871,7 +2982,7 @@
 
         result = []
         for row in rows:
-            bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = row[:6]  #@UnusedVariable
+            bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:self.bindColumnCount]  #@UnusedVariable
             home = yield self._txn.homeWithResourceID(self._home._homeType, homeID)
             new = yield home.invitedObjectWithShareUID(bindName)
             result.append(new)
@@ -2879,6 +2990,23 @@
         returnValue(result)
 
 
+    @inlineCallbacks
+    def _initBindRevision(self):
+        bind = self._bindSchema
+        self._bindRevision = self._syncTokenRevision
+        yield Update(
+            {bind.BIND_REVISION : Parameter("revision"), },
+            Where=(bind.RESOURCE_ID == Parameter("resourceID")).And
+                  (bind.HOME_RESOURCE_ID == Parameter("homeID")),
+        ).on(
+            self._txn,
+            revision=self._bindRevision,
+            resourceID=self._resourceID,
+            homeID=self.viewerHome()._resourceID,
+        )
+        yield self.invalidateQueryCache()
+
+
     def shareMode(self):
         """
         @see: L{ICalendar.shareMode}
@@ -2983,6 +3111,26 @@
 
 
     @classmethod
+    def bindColumns(cls):
+        """
+        Return a list of column names for retrieval during creation. This allows
+        different child classes to have their own type specific data, but still make use of the
+        common base logic.
+        """
+
+        return (
+            cls._bindSchema.BIND_MODE,
+            cls._bindSchema.HOME_RESOURCE_ID,
+            cls._bindSchema.RESOURCE_ID,
+            cls._bindSchema.RESOURCE_NAME,
+            cls._bindSchema.BIND_STATUS,
+            cls._bindSchema.BIND_REVISION,
+            cls._bindSchema.MESSAGE
+        )
+
+    bindColumnCount = 7
+
+    @classmethod
     def additionalBindColumns(cls):
         """
         Return a list of column names for retrieval during creation. This allows
@@ -3009,7 +3157,7 @@
         child = cls._homeChildSchema
         childMetaData = cls._homeChildMetaDataSchema
 
-        columns = cls._bindColumns() + cls.additionalBindColumns() + cls.metadataColumns()
+        columns = cls.bindColumns() + cls.additionalBindColumns() + cls.metadataColumns()
         return Select(columns,
                      From=child.join(
                          bind, child.RESOURCE_ID == bind.RESOURCE_ID,
@@ -3064,13 +3212,14 @@
     _objectSchema = None
 
 
-    def __init__(self, home, name, resourceID, mode, status, message=None, ownerHome=None, ownerName=None):
+    def __init__(self, home, name, resourceID, mode, status, revision=0, message=None, ownerHome=None, ownerName=None):
 
         self._home = home
         self._name = name
         self._resourceID = resourceID
         self._bindMode = mode
         self._bindStatus = status
+        self._bindRevision = revision
         self._bindMessage = message
         self._ownerHome = home if ownerHome is None else ownerHome
         self._ownerName = name if ownerName is None else ownerName
@@ -3156,9 +3305,9 @@
 
         # Create the actual objects merging in properties
         for items in dataRows:
-            bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = items[:6]  #@UnusedVariable
-            additionalBind = items[6:6 + len(cls.additionalBindColumns())]
-            metadata = items[6 + len(cls.additionalBindColumns()):]
+            bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = items[:cls.bindColumnCount]  #@UnusedVariable
+            additionalBind = items[cls.bindColumnCount:cls.bindColumnCount + len(cls.additionalBindColumns())]
+            metadata = items[cls.bindColumnCount + len(cls.additionalBindColumns()):]
 
             if bindMode == _BIND_MODE_OWN:
                 ownerHome = home
@@ -3171,6 +3320,7 @@
                 home=home,
                 name=bindName, resourceID=resourceID,
                 mode=bindMode, status=bindStatus,
+                revision=bindRevision,
                 message=bindMessage, ownerHome=ownerHome,
                 ownerName=ownerName,
             )
@@ -3226,10 +3376,10 @@
             returnValue(None)
 
         row = rows[0]
-        bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = row[:6]  #@UnusedVariable
+        bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:cls.bindColumnCount]  #@UnusedVariable
         if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
             returnValue(None)
-        additionalBind = row[6:6 + len(cls.additionalBindColumns())]
+        additionalBind = row[cls.bindColumnCount:cls.bindColumnCount + len(cls.additionalBindColumns())]
 
         if bindMode == _BIND_MODE_OWN:
             ownerHome = home
@@ -3241,6 +3391,7 @@
             home=home,
             name=name, resourceID=resourceID,
             mode=bindMode, status=bindStatus,
+            revision=bindRevision,
             message=bindMessage, ownerHome=ownerHome,
             ownerName=ownerName
         )
@@ -3266,7 +3417,7 @@
             returnValue(None)
 
         row = rows[0]
-        bindMode, homeID, resourceID, bindName, bindStatus, bindMessage = row[:6]  #@UnusedVariable]
+        bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:cls.bindColumnCount]  #@UnusedVariable]
         if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
             returnValue(None)
 
@@ -3834,6 +3985,22 @@
         return False
 
 
+    def resourceNamesSinceRevision(self, revision):
+        """
+        Return the changed and deleted resources since a particular revision. This implementation takes
+        into account sharing by making use of the bindRevision attribute to determine if the requested
+        revision is earlier than the share acceptance. If so, then we need to return all resources in
+        the results since the collection is in effect "new".
+
+        @param revision: the revision to determine changes since
+        @type revision: C{int}
+        """
+
+        if revision < self._bindRevision:
+            revision = 0
+        return super(CommonHomeChild, self).resourceNamesSinceRevision(revision)
+
+
     @inlineCallbacks
     def _loadPropertyStore(self, props=None):
         if props is None:
@@ -3904,18 +4071,25 @@
     @inlineCallbacks
     def notifyChanged(self):
         """
-        Trigger a notification of a change
+        Send notifications, change sync token and bump last modified because the resource has changed. We ensure
+        we only do this once per object per transaction.
         """
 
+        if self._txn.isNotifiedAlready(self):
+            returnValue(None)
+        self._txn.notificationAddedForObject(self)
+
         # Update modified if object still exists
         if self._resourceID:
             yield self.bumpModified()
 
-        # Only send one set of change notifications per transaction
-        if self._notifiers and not self._txn.isNotifiedAlready(self):
+            # We now also bump the collection level sync token on any change
+            yield self._bumpSyncToken()
+
+        # Send notifications
+        if self._notifiers:
             for notifier in self._notifiers.values():
                 self._txn.postCommit(notifier.notify)
-            self._txn.notificationAddedForObject(self)
 
 
     @classproperty
@@ -3947,10 +4121,6 @@
         delay the transaction whilst waiting for deadlock detection to kick in.
         """
 
-        if self._txn.isBumpedAlready(self):
-            returnValue(None)
-        self._txn.bumpAddedForObject(self)
-
         @inlineCallbacks
         def _bumpModified(subtxn):
             yield self._lockLastModifiedQuery.on(subtxn, resourceID=self._resourceID)
@@ -4729,14 +4899,18 @@
 
     def notifyChanged(self):
         """
-        Trigger a notification of a change
+        Send notifications, change sync token and bump last modified because the resource has changed. We ensure
+        we only do this once per object per transaction.
         """
 
-        # Only send one set of change notifications per transaction
-        if self._notifiers and not self._txn.isNotifiedAlready(self):
+        if self._txn.isNotifiedAlready(self):
+            returnValue(None)
+        self._txn.notificationAddedForObject(self)
+
+        # Send notifications
+        if self._notifiers:
             for notifier in self._notifiers.values():
                 self._txn.postCommit(notifier.notify)
-            self._txn.notificationAddedForObject(self)
 
 
     @classproperty


Property changes on: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql.py
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/common/datastore/sql.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/common/datastore/sql.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py:6167-6191
/CalendarServer/branches/new-store/txdav/common/datastore/sql.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/sql.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/common/datastore/sql.py:5936-5981
/CalendarServer/branches/release/CalendarServer-4.3-dev/txdav/common/datastore/sql.py:10180-10190,10192
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/common/datastore/sql.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/common/datastore/sql.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/sql.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/common/datastore/sql.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/common/datastore/sql.py:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/sql.py:9747-9979
/CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql.py:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/common/datastore/sql.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/common/datastore/sql.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/common/datastore/sql.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/common/datastore/sql.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/common/datastore/sql.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/common/datastore/sql.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/common/datastore/sql.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/common/datastore/sql.py:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging/txdav/common/datastore/sql.py:8730-8743
/CalendarServer/branches/users/glyph/always-abort-txn-on-error/txdav/common/datastore/sql.py:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid/txdav/common/datastore/sql.py:8772-8805
/CalendarServer/branches/users/glyph/conn-limit/txdav/common/datastore/sql.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/common/datastore/sql.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/common/datastore/sql.py:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect/txdav/common/datastore/sql.py:6824-6876
/CalendarServer/branches/users/glyph/deploybuild/txdav/common/datastore/sql.py:7563-7572
/CalendarServer/branches/users/glyph/digest-auth-redux/txdav/common/datastore/sql.py:10624-10635
/CalendarServer/branches/users/glyph/disable-quota/txdav/common/datastore/sql.py:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/common/datastore/sql.py:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html/txdav/common/datastore/sql.py:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client/txdav/common/datastore/sql.py:9054-9105
/CalendarServer/branches/users/glyph/linux-tests/txdav/common/datastore/sql.py:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge/txdav/common/datastore/sql.py:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/common/datastore/sql.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/common/datastore/sql.py:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql.py:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete/txdav/common/datastore/sql.py:8321-8330
/CalendarServer/branches/users/glyph/new-export/txdav/common/datastore/sql.py:7444-7485
/CalendarServer/branches/users/glyph/one-home-list-api/txdav/common/datastore/sql.py:10048-10073
/CalendarServer/branches/users/glyph/oracle/txdav/common/datastore/sql.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/common/datastore/sql.py:7340-7351
/CalendarServer/branches/users/glyph/other-html/txdav/common/datastore/sql.py:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim/txdav/common/datastore/sql.py:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/common/datastore/sql.py:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1/txdav/common/datastore/sql.py:8571-8583
/CalendarServer/branches/users/glyph/q/txdav/common/datastore/sql.py:9560-9688
/CalendarServer/branches/users/glyph/queue-locking-and-timing/txdav/common/datastore/sql.py:10204-10289
/CalendarServer/branches/users/glyph/quota/txdav/common/datastore/sql.py:7604-7637
/CalendarServer/branches/users/glyph/sendfdport/txdav/common/datastore/sql.py:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes/txdav/common/datastore/sql.py:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2/txdav/common/datastore/sql.py:8155-8174
/CalendarServer/branches/users/glyph/sharedpool/txdav/common/datastore/sql.py:6490-6550
/CalendarServer/branches/users/glyph/sharing-api/txdav/common/datastore/sql.py:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones/txdav/common/datastore/sql.py:8524-8535
/CalendarServer/branches/users/glyph/sql-store/txdav/common/datastore/sql.py:5929-6073
/CalendarServer/branches/users/glyph/start-service-start-loop/txdav/common/datastore/sql.py:11060-11065
/CalendarServer/branches/users/glyph/subtransactions/txdav/common/datastore/sql.py:7248-7258
/CalendarServer/branches/users/glyph/table-alias/txdav/common/datastore/sql.py:8651-8664
/CalendarServer/branches/users/glyph/uidexport/txdav/common/datastore/sql.py:7673-7676
/CalendarServer/branches/users/glyph/unshare-when-access-revoked/txdav/common/datastore/sql.py:10562-10595
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/common/datastore/sql.py:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/sql.py:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files/txdav/common/datastore/sql.py:7757-7769
/CalendarServer/branches/users/sagen/applepush/txdav/common/datastore/sql.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/common/datastore/sql.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/common/datastore/sql.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/common/datastore/sql.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/common/datastore/sql.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/common/datastore/sql.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/common/datastore/sql.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/common/datastore/sql.py:5084-5093
/CalendarServer/branches/users/sagen/testing/txdav/common/datastore/sql.py:10827-10851,10853-10855
/CalendarServer/branches/users/wsanchez/transations/txdav/common/datastore/sql.py:5515-5593
/CalendarServer/trunk/txdav/common/datastore/sql.py:11090-11095,11097-11111,11139-11174
   + /CalendarServer/branches/config-separation/txdav/common/datastore/sql.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/common/datastore/sql.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py:6167-6191
/CalendarServer/branches/new-store/txdav/common/datastore/sql.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/sql.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/common/datastore/sql.py:5936-5981
/CalendarServer/branches/release/CalendarServer-4.3-dev/txdav/common/datastore/sql.py:10180-10190,10192
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/common/datastore/sql.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/common/datastore/sql.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/sql.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/common/datastore/sql.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/common/datastore/sql.py:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/sql.py:9747-9979
/CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql.py:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/common/datastore/sql.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/common/datastore/sql.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/common/datastore/sql.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/common/datastore/sql.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/common/datastore/sql.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/common/datastore/sql.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/common/datastore/sql.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/common/datastore/sql.py:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging/txdav/common/datastore/sql.py:8730-8743
/CalendarServer/branches/users/glyph/always-abort-txn-on-error/txdav/common/datastore/sql.py:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid/txdav/common/datastore/sql.py:8772-8805
/CalendarServer/branches/users/glyph/conn-limit/txdav/common/datastore/sql.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/common/datastore/sql.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/common/datastore/sql.py:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect/txdav/common/datastore/sql.py:6824-6876
/CalendarServer/branches/users/glyph/deploybuild/txdav/common/datastore/sql.py:7563-7572
/CalendarServer/branches/users/glyph/digest-auth-redux/txdav/common/datastore/sql.py:10624-10635
/CalendarServer/branches/users/glyph/disable-quota/txdav/common/datastore/sql.py:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/common/datastore/sql.py:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html/txdav/common/datastore/sql.py:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client/txdav/common/datastore/sql.py:9054-9105
/CalendarServer/branches/users/glyph/linux-tests/txdav/common/datastore/sql.py:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge/txdav/common/datastore/sql.py:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/common/datastore/sql.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/common/datastore/sql.py:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql.py:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete/txdav/common/datastore/sql.py:8321-8330
/CalendarServer/branches/users/glyph/new-export/txdav/common/datastore/sql.py:7444-7485
/CalendarServer/branches/users/glyph/one-home-list-api/txdav/common/datastore/sql.py:10048-10073
/CalendarServer/branches/users/glyph/oracle/txdav/common/datastore/sql.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/common/datastore/sql.py:7340-7351
/CalendarServer/branches/users/glyph/other-html/txdav/common/datastore/sql.py:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim/txdav/common/datastore/sql.py:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/common/datastore/sql.py:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1/txdav/common/datastore/sql.py:8571-8583
/CalendarServer/branches/users/glyph/q/txdav/common/datastore/sql.py:9560-9688
/CalendarServer/branches/users/glyph/queue-locking-and-timing/txdav/common/datastore/sql.py:10204-10289
/CalendarServer/branches/users/glyph/quota/txdav/common/datastore/sql.py:7604-7637
/CalendarServer/branches/users/glyph/sendfdport/txdav/common/datastore/sql.py:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes/txdav/common/datastore/sql.py:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2/txdav/common/datastore/sql.py:8155-8174
/CalendarServer/branches/users/glyph/sharedpool/txdav/common/datastore/sql.py:6490-6550
/CalendarServer/branches/users/glyph/sharing-api/txdav/common/datastore/sql.py:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones/txdav/common/datastore/sql.py:8524-8535
/CalendarServer/branches/users/glyph/sql-store/txdav/common/datastore/sql.py:5929-6073
/CalendarServer/branches/users/glyph/start-service-start-loop/txdav/common/datastore/sql.py:11060-11065
/CalendarServer/branches/users/glyph/subtransactions/txdav/common/datastore/sql.py:7248-7258
/CalendarServer/branches/users/glyph/table-alias/txdav/common/datastore/sql.py:8651-8664
/CalendarServer/branches/users/glyph/uidexport/txdav/common/datastore/sql.py:7673-7676
/CalendarServer/branches/users/glyph/unshare-when-access-revoked/txdav/common/datastore/sql.py:10562-10595
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/common/datastore/sql.py:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/sql.py:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files/txdav/common/datastore/sql.py:7757-7769
/CalendarServer/branches/users/sagen/applepush/txdav/common/datastore/sql.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/common/datastore/sql.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/common/datastore/sql.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/common/datastore/sql.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/common/datastore/sql.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/common/datastore/sql.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/common/datastore/sql.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/common/datastore/sql.py:5084-5093
/CalendarServer/branches/users/sagen/testing/txdav/common/datastore/sql.py:10827-10851,10853-10855
/CalendarServer/branches/users/wsanchez/transations/txdav/common/datastore/sql.py:5515-5593
/CalendarServer/trunk/txdav/common/datastore/sql.py:11090-11095,11097-11111,11139-11192

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/current-oracle-dialect.sql	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/current-oracle-dialect.sql	2013-05-15 22:34:00 UTC (rev 11193)
@@ -68,6 +68,7 @@
     "CALENDAR_RESOURCE_NAME" nvarchar2(255),
     "BIND_MODE" integer not null,
     "BIND_STATUS" integer not null,
+    "BIND_REVISION" integer default 0 not null,
     "MESSAGE" nclob,
     "TRANSP" integer default 0 not null,
     "ALARM_VEVENT_TIMED" nclob default null,
@@ -219,6 +220,7 @@
     "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
     "BIND_MODE" integer not null,
     "BIND_STATUS" integer not null,
+    "BIND_REVISION" integer default 0 not null,
     "MESSAGE" nclob, 
     primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID"), 
     unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
@@ -267,6 +269,7 @@
     "GROUP_ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
     "BIND_MODE" integer not null,
     "BIND_STATUS" integer not null,
+    "BIND_REVISION" integer default 0 not null,
     "MESSAGE" nclob, 
     primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"), 
     unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_RESOURCE_NAME")

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/current.sql	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/current.sql	2013-05-15 22:34:00 UTC (rev 11193)
@@ -133,6 +133,7 @@
   CALENDAR_RESOURCE_NAME    varchar(255) not null,
   BIND_MODE                 integer      not null, -- enum CALENDAR_BIND_MODE
   BIND_STATUS               integer      not null, -- enum CALENDAR_BIND_STATUS
+  BIND_REVISION				integer      default 0 not null,
   MESSAGE                   text,
   TRANSP                    integer      default 0 not null, -- enum CALENDAR_TRANSP
   ALARM_VEVENT_TIMED        text         default null,
@@ -392,6 +393,7 @@
   ADDRESSBOOK_RESOURCE_NAME    			varchar(255) 	not null,
   BIND_MODE                    			integer      	not null,	-- enum CALENDAR_BIND_MODE
   BIND_STATUS                  			integer      	not null,	-- enum CALENDAR_BIND_STATUS
+  BIND_REVISION				   			integer      	default 0 not null,
   MESSAGE                      			text,                  		-- FIXME: xml?
 
   primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
@@ -473,6 +475,7 @@
   GROUP_ADDRESSBOOK_RESOURCE_NAME	varchar(255) not null,
   BIND_MODE                    		integer      not null, -- enum CALENDAR_BIND_MODE
   BIND_STATUS                  		integer      not null, -- enum CALENDAR_BIND_STATUS
+  BIND_REVISION				   		integer      default 0 not null,
   MESSAGE                      		text,                  -- FIXME: xml?
 
   primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/old/oracle-dialect/v19.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/old/oracle-dialect/v19.sql	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/old/oracle-dialect/v19.sql	2013-05-15 22:34:00 UTC (rev 11193)
@@ -68,6 +68,7 @@
     "CALENDAR_RESOURCE_NAME" nvarchar2(255),
     "BIND_MODE" integer not null,
     "BIND_STATUS" integer not null,
+    "BIND_REVISION" integer default 0 not null,
     "MESSAGE" nclob,
     "TRANSP" integer default 0 not null,
     "ALARM_VEVENT_TIMED" nclob default null,
@@ -228,6 +229,7 @@
     "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
     "BIND_MODE" integer not null,
     "BIND_STATUS" integer not null,
+    "BIND_REVISION" integer default 0 not null,
     "MESSAGE" nclob, 
     primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_ID"), 
     unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/old/postgres-dialect/v19.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/old/postgres-dialect/v19.sql	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/old/postgres-dialect/v19.sql	2013-05-15 22:34:00 UTC (rev 11193)
@@ -130,6 +130,7 @@
   CALENDAR_RESOURCE_NAME    varchar(255) not null,
   BIND_MODE                 integer      not null, -- enum CALENDAR_BIND_MODE
   BIND_STATUS               integer      not null, -- enum CALENDAR_BIND_STATUS
+  BIND_REVISION				integer      default 0 not null,
   MESSAGE                   text,
   TRANSP                    integer      default 0 not null, -- enum CALENDAR_TRANSP
   ALARM_VEVENT_TIMED        text         default null,
@@ -405,6 +406,7 @@
   ADDRESSBOOK_RESOURCE_NAME    varchar(255) not null,
   BIND_MODE                    integer      not null, -- enum CALENDAR_BIND_MODE
   BIND_STATUS                  integer      not null, -- enum CALENDAR_BIND_STATUS
+  BIND_REVISION				   integer      default 0 not null,
   MESSAGE                      text,                  -- FIXME: xml?
 
   primary key (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID), -- implicit index

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_18_to_19.sql	2013-05-15 22:34:00 UTC (rev 11193)
@@ -32,7 +32,8 @@
 -- Calendar bind related updates
 
 alter table CALENDAR_BIND
- add ("TRANSP" integer default 0 not null,
+ add ("BIND_REVISION" integer default 0 not null,
+      "TRANSP" integer default 0 not null,
       "ALARM_VEVENT_TIMED" nclob default null,
       "ALARM_VEVENT_ALLDAY" nclob default null,
       "ALARM_VTODO_TIMED" nclob default null,
@@ -46,6 +47,13 @@
 insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
 insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
 
+ 	  
+-- Addressbook bind related updates
+
+alter table ADDRESSBOOK_BIND
+ add ("BIND_REVISION" integer default 0 not null);
+
+ 
 -- Now update the version
 -- No data upgrades
 update CALENDARSERVER set VALUE = '19' where NAME = 'VERSION';

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_18_to_19.sql	2013-05-15 22:34:00 UTC (rev 11193)
@@ -33,6 +33,7 @@
 -- Calendar bind related updates
 
 alter table CALENDAR_BIND
+ add column BIND_REVISION			 integer     default 0 not null,
  add column TRANSP                   integer     default 0 not null,
  add column ALARM_VEVENT_TIMED       text        default null,
  add column ALARM_VEVENT_ALLDAY      text        default null,
@@ -47,6 +48,13 @@
 insert into CALENDAR_TRANSP values (0, 'opaque' );
 insert into CALENDAR_TRANSP values (1, 'transparent');
 
+
+-- Addressbook bind related updates
+
+alter table ADDRESSBOOK_BIND
+ add column BIND_REVISION			 integer     default 0 not null;
+
+
 -- Now update the version
 -- No data upgrades
 update CALENDARSERVER set VALUE = '19' where NAME = 'VERSION';

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/upgrade/sql/upgrade.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/upgrade/sql/upgrade.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/upgrade/sql/upgrade.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -25,6 +25,7 @@
 from twext.python.log import LoggingMixIn
 
 from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.python.failure import Failure
 from twisted.python.modules import getModule
 from twisted.python.reflect import namedObject
 
@@ -124,10 +125,11 @@
             actual_version = int(actual_version)
             yield sqlTxn.commit()
         except (RuntimeError, ValueError):
+            f = Failure()
             self.log_error("Database key %s cannot be determined." % (self.versionKey,))
             yield sqlTxn.abort()
             if self.defaultKeyValue is None:
-                raise
+                f.raiseException()
             else:
                 actual_version = self.defaultKeyValue
 
@@ -265,8 +267,9 @@
             yield sqlTxn.execSQLBlock(sql)
             yield sqlTxn.commit()
         except RuntimeError:
+            f = Failure()
             yield sqlTxn.abort()
-            raise
+            f.raiseException()
 
 
 

Modified: CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py	2013-05-15 18:17:40 UTC (rev 11192)
+++ CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py	2013-05-15 22:34:00 UTC (rev 11193)
@@ -109,7 +109,9 @@
 
                                 calendar = (yield calendarHome.calendarWithName(calendarName))
                                 if calendar is not None:
-                                    yield calendarHome.setDefaultCalendar(calendar, tasks=(propname == sqlStore, customxml.ScheduleDefaultTasksURL))
+                                    yield calendarHome.setDefaultCalendar(
+                                        calendar, tasks=(propname ==
+                                            customxml.ScheduleDefaultTasksURL))
 
                 # Always delete the row so that batch processing works correctly
                 yield Delete(
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130515/ddba8a3c/attachment-0001.html>


More information about the calendarserver-changes mailing list