[CalendarServer-changes] [13753] CalendarServer/branches/users/gaya/groupsharee2/txdav

source_changes at macosforge.org source_changes at macosforge.org
Fri Jul 11 13:46:58 PDT 2014


Revision: 13753
          http://trac.calendarserver.org//changeset/13753
Author:   gaya at apple.com
Date:     2014-07-11 13:46:58 -0700 (Fri, 11 Jul 2014)
Log Message:
-----------
first group sharee tests

Modified Paths:
--------------
    CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/sql.py
    CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/test_sql_sharing.py
    CalendarServer/branches/users/gaya/groupsharee2/txdav/who/groups.py

Added Paths:
-----------
    CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/accounts/
    CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/accounts/groupShareeAccounts.xml

Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/sql.py	2014-07-11 20:03:42 UTC (rev 13752)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/sql.py	2014-07-11 20:46:58 UTC (rev 13753)
@@ -1820,7 +1820,7 @@
         rows = yield Select(
             [Count(gs.GROUP_ID)],
             From=gs,
-            Where=(gs.CALENDAR_HOME_ID == self.ownerHome()._resourceID).And(
+            Where=(gs.CALENDAR_HOME_ID == self.viewerHome()._resourceID).And(
                 gs.CALENDAR_ID == self._resourceID)
         ).on(self._txn)
         if rows[0][0] > 1:
@@ -1855,9 +1855,9 @@
 
         gs = schema.GROUP_SHAREE
         rows = yield Select(
-            [gs.MEMBERSHIP_HASH, gs.MODE],
+            [gs.MEMBERSHIP_HASH, gs.GROUP_BIND_MODE],
             From=gs,
-            Where=(gs.CALENDAR_HOME_ID == self.ownerHome()._resourceID).And(
+            Where=(gs.CALENDAR_HOME_ID == self.viewerHome()._resourceID).And(
                 gs.CALENDAR_ID == self._resourceID).And(
                 gs.GROUP_ID == groupID)
         ).on(self._txn)
@@ -1871,7 +1871,7 @@
             if updateMap:
                 yield Update(
                     updateMap,
-                    Where=(gs.CALENDAR_HOME_ID == self.ownerHome()._resourceID).And(
+                    Where=(gs.CALENDAR_HOME_ID == self.viewerHome()._resourceID).And(
                         gs.CALENDAR_ID == self._resourceID).And(
                         gs.GROUP_ID == groupID
                     )
@@ -1881,7 +1881,7 @@
             yield Insert({
                 gs.MEMBERSHIP_HASH: membershipHash,
                 gs.GROUP_BIND_MODE: mode,
-                gs.CALENDAR_HOME_ID: self.ownerHome()._resourceID,
+                gs.CALENDAR_HOME_ID: self.viewerHome()._resourceID,
                 gs.CALENDAR_ID: self._resourceID,
                 gs.GROUP_ID: groupID,
             }).on(self._txn)
@@ -1899,7 +1899,7 @@
             rows = yield Select(
                 [Max(gs.GROUP_BIND_MODE)], # _BIND_MODE_WRITE > _BIND_MODE_READ
                 From=gs,
-                Where=(gs.CALENDAR_HOME_ID == self.ownerHome()._resourceID).And(
+                Where=(gs.CALENDAR_HOME_ID == self.viewerHome()._resourceID).And(
                     gs.CALENDAR_ID == self._resourceID
                 )
             ).on(self._txn)
@@ -1929,7 +1929,7 @@
         record = yield self._txn.directoryService().recordWithUID(shareeUID.decode("utf-8"))
         if (
             record is None or
-            record.recordType != RecordType.group or not (False and
+            record.recordType != RecordType.group or not (
                 config.Sharing.Enabled and
                 config.Sharing.Calendars.Enabled and
                 config.Sharing.Calendars.Groups.Enabled
@@ -1940,15 +1940,19 @@
             )
 
         # invite every member of group
-        groupID = (yield self._txn.groupByUID(record.uid))[0]
+        shareeViews = set()
+        groupID = (yield self._txn.groupByUID(shareeUID))[0]
         memberUIDs = yield self._txn.groupMemberUIDs(groupID)
         for memberUID in memberUIDs:
-            shareeView = yield self.shareeView(shareeUID)
+            shareeView = yield self.shareeView(memberUID)
             newMode = _BIND_MODE_GROUP if shareeView is None else shareeView.groupModeAfterAddingOneGroupSharee()
             if newMode is not None:
-                yield super(Calendar, self).inviteUIDToShare(memberUID, newMode)
+                # everything but direct share
+                shareeView = yield super(Calendar, self).inviteUIDToShare(memberUID, newMode, summary)
+                yield shareeView.updateShareeGroupLink(shareeUID, mode=mode)
+                shareeViews.add(shareeView)
 
-        returnValue(None)
+        returnValue(shareeViews)
 
 
     @inlineCallbacks
@@ -1987,16 +1991,45 @@
         """
         # Cancel invites - we'll just use whatever userid we are given
 
-        shareeView = yield self.shareeView(shareeUID)
-        if shareeView is not None:
-            newMode = yield shareeView.groupModeAfterRemovingOneGroupSharee()
-            if newMode is None:
-                yield super(Calendar, self).uninviteUIDFromShare(shareeUID)
-            else:
-                yield super(Calendar, self).inviteUIDToShare(shareeUID, newMode)
+        record = yield self._txn.directoryService().recordWithUID(shareeUID.decode("utf-8"))
+        if (
+            record is None or
+            record.recordType != RecordType.group or not (
+                config.Sharing.Enabled and
+                config.Sharing.Calendars.Enabled and
+                config.Sharing.Calendars.Groups.Enabled
+            )
+        ):
+            returnValue(
+                (yield super(Calendar, self).uninviteUIDFromShare(shareeUID))
+            )
 
+        # uninvite every member of group
+        groupID = (yield self._txn.groupByUID(shareeUID))[0]
+        memberUIDs = yield self._txn.groupMemberUIDs(groupID)
+        for memberUID in memberUIDs:
+            shareeView = yield self.shareeView(memberUID)
+            if shareeView is not None:
 
+                newMode = yield shareeView.groupModeAfterRemovingOneGroupSharee()
+                gs = schema.GROUP_SHAREE
+                yield Delete(
+                    From=gs,
+                    Where=(gs.CALENDAR_HOME_ID == shareeView.viewerHome()._resourceID).And(
+                        gs.CALENDAR_ID == self._resourceID).And(
+                        gs.GROUP_ID == groupID
+                    )
+                )
 
+                if newMode is None:
+                    # only group was shared, do delete share
+                    yield super(Calendar, self).uninviteUIDFromShare(memberUID)
+                else:
+                    # multiple groups or group and individual was shared, update to new mode
+                    yield super(Calendar, self).inviteUIDToShare(memberUID, newMode)
+
+
+
 icalfbtype_to_indexfbtype = {
     "UNKNOWN"         : 0,
     "FREE"            : 1,

Added: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/accounts/groupShareeAccounts.xml
===================================================================
--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/accounts/groupShareeAccounts.xml	                        (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/accounts/groupShareeAccounts.xml	2014-07-11 20:46:58 UTC (rev 13753)
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2014 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<directory realm="Test Realm">
+	<record type="user">
+	    <short-name>user01</short-name>
+	    <uid>user01</uid>
+	    <guid>10000000-0000-0000-0000-000000000001</guid>
+	    <password>user01</password>
+	    <full-name>User 01</full-name>
+	    <email>user01 at example.com</email>
+	</record>
+	<record type="user">
+	    <short-name>user02</short-name>
+	    <uid>user02</uid>
+	    <guid>10000000-0000-0000-0000-000000000002</guid>
+	    <password>user02</password>
+	    <full-name>User 02</full-name>
+	    <email>user02 at example.com</email>
+	</record>
+	<record type="user">
+	    <short-name>user03</short-name>
+	    <uid>user03</uid>
+	    <guid>10000000-0000-0000-0000-000000000003</guid>
+	    <password>user03</password>
+	    <full-name>User 03</full-name>
+	    <email>user03 at example.com</email>
+	</record>
+	<record type="user">
+	    <short-name>user04</short-name>
+	    <uid>user04</uid>
+	    <guid>10000000-0000-0000-0000-000000000004</guid>
+	    <password>user04</password>
+	    <full-name>User 04</full-name>
+	    <email>user04 at example.com</email>
+	</record>
+	<record type="user">
+	    <short-name>user05</short-name>
+	    <uid>user05</uid>
+	    <guid>10000000-0000-0000-0000-000000000005</guid>
+	    <password>user05</password>
+	    <full-name>User 05</full-name>
+	    <email>user05 at example.com</email>
+	</record>
+	<record type="user">
+	    <short-name>user06</short-name>
+	    <uid>user06</uid>
+	    <guid>10000000-0000-0000-0000-000000000006</guid>
+	    <password>user06</password>
+	    <full-name>User 06</full-name>
+	    <email>user06 at example.com</email>
+	</record>
+	<record type="user">
+	    <short-name>user07</short-name>
+	    <uid>user07</uid>
+	    <guid>10000000-0000-0000-0000-000000000007</guid>
+	    <password>user07</password>
+	    <full-name>User 07</full-name>
+	    <email>user07 at example.com</email>
+	</record>
+	<record type="user">
+	    <short-name>user08</short-name>
+	    <uid>user08</uid>
+	    <guid>10000000-0000-0000-0000-000000000008</guid>
+	    <password>user08</password>
+	    <full-name>User 08</full-name>
+	    <email>user08 at example.com</email>
+	</record>
+	<record type="user">
+	    <short-name>user09</short-name>
+	    <uid>user09</uid>
+	    <guid>10000000-0000-0000-0000-000000000009</guid>
+	    <password>user09</password>
+	    <full-name>User 09</full-name>
+	    <email>user09 at example.com</email>
+	</record>
+	<record type="user">
+	    <short-name>user10</short-name>
+	    <uid>user10</uid>
+	    <guid>10000000-0000-0000-0000-000000000010</guid>
+	    <password>user10</password>
+	    <full-name>User 10</full-name>
+	    <email>user10 at example.com</email>
+	</record>
+	<record type="group">
+	    <short-name>group01</short-name>
+	    <uid>group01</uid>
+	    <guid>20000000-0000-0000-0000-000000000001</guid>
+	    <full-name>Group 01</full-name>
+	    <email>group01 at example.com</email>
+	    <member-uid>user01</member-uid>
+	</record>
+	<record type="group">
+	    <short-name>group02</short-name>
+	    <uid>group02</uid>
+	    <guid>20000000-0000-0000-0000-000000000002</guid>
+	    <full-name>Group 02</full-name>
+	    <email>group02 at example.com</email>
+	    <member-uid>user06</member-uid>
+	    <member-uid>user07</member-uid>
+	    <member-uid>user08</member-uid>
+	</record>
+	<record type="group">
+	    <short-name>group03</short-name>
+	    <uid>group03</uid>
+	    <guid>20000000-0000-0000-0000-000000000003</guid>
+	    <full-name>Group 03</full-name>
+	    <email>group03 at example.com</email>
+	    <member-uid>user07</member-uid>
+	    <member-uid>user08</member-uid>
+	    <member-uid>user09</member-uid>
+	</record>
+	<record type="group">
+	    <short-name>group04</short-name>
+	    <uid>group04</uid>
+	    <guid>20000000-0000-0000-0000-000000000004</guid>
+	    <full-name>Group 04</full-name>
+	    <member-uid>group02</member-uid>
+	    <member-uid>group03</member-uid>
+	    <member-uid>user10</member-uid>
+	</record>
+</directory>

Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/test_sql_sharing.py
===================================================================
--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/test_sql_sharing.py	2014-07-11 20:03:42 UTC (rev 13752)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/test_sql_sharing.py	2014-07-11 20:46:58 UTC (rev 13753)
@@ -15,20 +15,25 @@
 ##
 
 
+from twext.python.clsprop import classproperty
+from twext.python.filepath import CachingFilePath as FilePath
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.trial.unittest import TestCase
-
-from twext.python.clsprop import classproperty
-from txdav.common.datastore.test.util import CommonCommonTests, \
-    populateCalendarsFrom
-from txdav.common.datastore.sql_tables import _BIND_MODE_READ, \
-    _BIND_STATUS_INVITED, _BIND_MODE_DIRECT, _BIND_STATUS_ACCEPTED
+from twistedcaldav import customxml
+from twistedcaldav.stdconfig import config
 from txdav.base.propertystore.base import PropertyName
+from txdav.common.datastore.sql_tables import _BIND_MODE_DIRECT
+from txdav.common.datastore.sql_tables import _BIND_MODE_GROUP
+from txdav.common.datastore.sql_tables import _BIND_MODE_READ
+from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
+from txdav.common.datastore.sql_tables import _BIND_STATUS_ACCEPTED
+from txdav.common.datastore.sql_tables import _BIND_STATUS_INVITED
+from txdav.common.datastore.test.util import CommonCommonTests
+from txdav.common.datastore.test.util import populateCalendarsFrom
 from txdav.xml.base import WebDAVTextElement
-from twistedcaldav import customxml
 from txdav.xml.element import registerElement, registerElementClass
+import os
 
-
 class BaseSharingTests(CommonCommonTests, TestCase):
     """
     Test store-based calendar sharing.
@@ -581,6 +586,518 @@
 
 
 
+class GroupSharing(BaseSharingTests):
+    """
+    Test store-based group book sharing.
+    """
+
+    @inlineCallbacks
+    def setUp(self):
+        yield super(BaseSharingTests, self).setUp()
+
+        accountsFilePath = FilePath(
+            os.path.join(os.path.dirname(__file__), "accounts")
+        )
+        yield self.buildStoreAndDirectory(
+            accounts=accountsFilePath.child("groupShareeAccounts.xml"),
+            #resources=accountsFilePath.child("resources.xml"),
+        )
+        yield self.populate()
+
+        self.paths = {}
+
+
+    def configure(self):
+        super(GroupSharing, self).configure()
+        config.Sharing.Enabled = True
+        config.Sharing.Calendars.Enabled = True
+        config.Sharing.Calendars.Groups.Enabled = True
+        config.Sharing.Calendars.Groups.ReconciliationDelaySeconds = 0
+
+
+    @inlineCallbacks
+    def _check_notifications(self, home, items):
+        notifyHome = yield self.transactionUnderTest().notificationsWithUID(home)
+        notifications = yield notifyHome.listNotificationObjects()
+        self.assertEqual(set(notifications), set(items))
+
+
+    @inlineCallbacks
+    def test_no_shares(self):
+        """
+        Test that initially there are no shares.
+        """
+
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 0)
+
+
+    @inlineCallbacks
+    def test_invite_sharee(self):
+        """
+        Test invite/uninvite creates/removes shares and notifications.
+        """
+
+        # Invite
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 0)
+        self.assertFalse(calendar.isShared())
+
+        shareeViews = yield calendar.inviteUIDToShare("group02", _BIND_MODE_READ, "summary")
+        self.assertEqual(len(shareeViews), 3)
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 3)
+        shareeViews = sorted(shareeViews, key=lambda shareeView: shareeView.viewerHome().uid())
+        invites = sorted(invites, key=lambda invitee: invitee.shareeUID)
+        for i in range(3):
+            self.assertEqual(invites[i].uid, shareeViews[i].shareUID())
+            self.assertEqual(invites[i].ownerUID, "user01")
+            self.assertEqual(invites[i].shareeUID, shareeViews[i].viewerHome().uid())
+            self.assertEqual(invites[i].mode, _BIND_MODE_GROUP)
+            self.assertEqual((yield shareeViews[i].effectiveShareMode()), _BIND_MODE_READ)
+            self.assertEqual(invites[i].status, _BIND_STATUS_INVITED)
+            self.assertEqual(invites[i].summary, "summary")
+        #inviteUID = shareeView.shareUID()
+
+        self.assertTrue(calendar.isShared())
+
+        yield self.commit()
+
+        '''
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+        #yield self._check_notifications("user02", [inviteUID, ])
+
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        self.assertTrue(group.isShared())
+
+        yield self.commit()
+        '''
+
+        # Uninvite
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 3)
+        self.assertTrue(calendar.isShared())
+
+        yield calendar.uninviteUIDFromShare("group02")
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 0)
+        self.assertTrue(calendar.isShared())
+
+        yield self._check_notifications("user06", [])
+        yield self._check_notifications("user07", [])
+        yield self._check_notifications("user08", [])
+
+        yield self.commit()
+
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        self.assertTrue(calendar.isShared())
+        yield calendar.setShared(False)
+        self.assertFalse(calendar.isShared())
+
+
+    @inlineCallbacks
+    def test_accept_share(self):
+        """
+        Test that invite+accept creates shares and notifications.
+        """
+
+        # Invite
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        invites = yield group.sharingInvites()
+        self.assertEqual(len(invites), 0)
+        self.assertFalse(group.isShared())
+
+        shareeView = yield group.inviteUIDToShare("user02", _BIND_MODE_READ, "summary")
+        invites = yield group.sharingInvites()
+        self.assertEqual(len(invites), 1)
+        inviteUID = shareeView.shareUID()
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+        yield self._check_notifications("user02", [inviteUID, ])
+
+        self.assertTrue(group.isShared())
+
+        yield self.commit()
+
+        # Accept
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.acceptShare(inviteUID)
+
+        yield self._check_calendar("user02", "user01", self.group1_children)
+        yield self._check_notifications("user01", [inviteUID + "-reply", ])
+
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        self.assertTrue(group.isShared())
+
+        yield self.commit()
+
+        # Re-accept
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.acceptShare(inviteUID)
+
+        yield self._check_calendar("user02", "user01", self.group1_children)
+        yield self._check_notifications("user01", [inviteUID + "-reply", ])
+
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        self.assertTrue(group.isShared())
+
+
+    @inlineCallbacks
+    def test_decline_share(self):
+        """
+        Test that invite+decline does not create shares but does create notifications.
+        """
+
+        # Invite
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        invites = yield group.sharingInvites()
+        self.assertEqual(len(invites), 0)
+        self.assertFalse(group.isShared())
+
+        shareeView = yield group.inviteUIDToShare("user02", _BIND_MODE_READ, "summary")
+        invites = yield group.sharingInvites()
+        self.assertEqual(len(invites), 1)
+        inviteUID = shareeView.shareUID()
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+        yield self._check_notifications("user02", [inviteUID, ])
+
+        self.assertTrue(group.isShared())
+
+        yield self.commit()
+
+        # Decline
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.declineShare(inviteUID)
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+        yield self._check_notifications("user01", [inviteUID + "-reply", ])
+
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        self.assertTrue(group.isShared())
+
+        yield self.commit()
+
+        # Re-decline
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.declineShare(inviteUID)
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+        yield self._check_notifications("user01", [inviteUID + "-reply", ])
+
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        self.assertTrue(group.isShared())
+
+
+    @inlineCallbacks
+    def test_accept_decline_share(self):
+        """
+        Test that invite+accept/decline creates/removes shares and notifications.
+        Decline via the home.
+        """
+
+        # Invite
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        invites = yield group.sharingInvites()
+        self.assertEqual(len(invites), 0)
+        self.assertFalse(group.isShared())
+
+        shareeView = yield group.inviteUIDToShare("user02", _BIND_MODE_READ, "summary")
+        invites = yield group.sharingInvites()
+        self.assertEqual(len(invites), 1)
+        inviteUID = shareeView.shareUID()
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+        yield self._check_notifications("user02", [inviteUID, ])
+
+        self.assertTrue(group.isShared())
+
+        yield self.commit()
+
+        # Accept
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.acceptShare(inviteUID)
+
+        yield self._check_calendar("user02", "user01", self.group1_children)
+        yield self._check_notifications("user01", [inviteUID + "-reply", ])
+
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        self.assertTrue(group.isShared())
+
+        yield self.commit()
+
+        # Decline
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.declineShare(inviteUID)
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+        yield self._check_notifications("user01", [inviteUID + "-reply", ])
+
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        self.assertTrue(group.isShared())
+
+
+    @inlineCallbacks
+    def test_accept_remove_share(self):
+        """
+        Test that invite+accept/decline creates/removes shares and notifications.
+        Decline via the shared collection (removal).
+        """
+
+        # Invite
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        invites = yield group.sharingInvites()
+        self.assertEqual(len(invites), 0)
+
+        shareeView = yield group.inviteUIDToShare("user02", _BIND_MODE_READ, "summary")
+        invites = yield group.sharingInvites()
+        self.assertEqual(len(invites), 1)
+        inviteUID = shareeView.shareUID()
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+        yield self._check_notifications("user02", [inviteUID, ])
+
+        yield self.commit()
+
+        # Accept
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.acceptShare(inviteUID)
+
+        yield self._check_calendar("user02", "user01", self.group1_children)
+        yield self._check_notifications("user01", [inviteUID + "-reply", ])
+
+        yield self.commit()
+
+        # Delete
+        group = yield self.calendarObjectUnderTest(home="user02", calendar_name="user01", name="group1.vcf")
+        yield group.deleteShare()
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+        yield self._check_notifications("user01", [inviteUID + "-reply", ])
+
+
+    @inlineCallbacks
+    def test_accept_two_groups(self):
+        """
+        Test that accept of two groups works.
+        """
+
+        # Two shares
+        inviteUID1 = yield self._createGroupShare(groupname="group1.vcf")
+        inviteUID2 = yield self._createGroupShare(groupname="group2.vcf")
+
+        yield self._check_calendar("user02", "user01", self.all_children)
+        yield self._check_notifications("user01", [inviteUID1 + "-reply", inviteUID2 + "-reply", ])
+
+
+    @inlineCallbacks
+    def test_accept_uninvite_two_groups(self):
+        """
+        Test that accept of two groups works, then uninvite each one.
+        """
+
+        # Two shares
+        inviteUID1 = yield self._createGroupShare(groupname="group1.vcf")
+        inviteUID2 = yield self._createGroupShare(groupname="group2.vcf")
+
+        yield self._check_calendar("user02", "user01", self.all_children)
+        yield self._check_notifications("user01", [inviteUID1 + "-reply", inviteUID2 + "-reply", ])
+
+        yield self.commit()
+
+        # Uninvite one
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        yield group.uninviteUIDFromShare("user02")
+        invites = yield group.sharingInvites()
+        self.assertEqual(len(invites), 0)
+
+        yield self._check_calendar("user02", "user01", self.group2_children)
+
+        shared = yield self.calendarObjectUnderTest(home="user02", calendar_name="user01", name="group1.vcf")
+        self.assertTrue(shared is None)
+        shared = yield self.calendarObjectUnderTest(home="user02", calendar_name="user01", name="card2.vcf")
+        self.assertTrue(shared is None)
+
+        yield self.commit()
+
+        # Uninvite other
+        group = yield self.calendarObjectUnderTest(home="user02", calendar_name="user01", name="group2.vcf")
+        yield group.uninviteUIDFromShare("user02")
+        invites = yield group.sharingInvites()
+        self.assertEqual(len(invites), 0)
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+
+    @inlineCallbacks
+    def test_accept_decline_two_groups(self):
+        """
+        Test that accept of two groups works, then decline each one.
+        """
+
+        # Two shares
+        inviteUID1 = yield self._createGroupShare(groupname="group1.vcf")
+        inviteUID2 = yield self._createGroupShare(groupname="group2.vcf")
+
+        yield self._check_calendar("user02", "user01", self.all_children)
+        yield self._check_notifications("user01", [inviteUID1 + "-reply", inviteUID2 + "-reply", ])
+
+        yield self.commit()
+
+        # Decline one
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.declineShare(inviteUID1)
+
+        yield self._check_calendar("user02", "user01", self.group2_children)
+
+        shared = yield self.calendarObjectUnderTest(home="user02", calendar_name="user01", name="group1.vcf")
+        self.assertTrue(shared is None)
+        shared = yield self.calendarObjectUnderTest(home="user02", calendar_name="user01", name="card2.vcf")
+        self.assertTrue(shared is None)
+
+        yield self.commit()
+
+        # Decline other
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.declineShare(inviteUID2)
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+
+    @inlineCallbacks
+    def test_accept_two_groups_different_access(self):
+        """
+        Test that accept of two groups works, then uninvite each one.
+        """
+
+        # Two shares
+        inviteUID1 = yield self._createGroupShare(groupname="group1.vcf")
+        inviteUID2 = yield self._createGroupShare(groupname="group2.vcf", mode=_BIND_MODE_WRITE)
+
+        yield self._check_calendar("user02", "user01", self.all_children)
+        yield self._check_notifications("user01", [inviteUID1 + "-reply", inviteUID2 + "-reply", ])
+
+        # Read only for all, write for group2's items
+        yield self._check_read_only("user02", "user01", ["group1.vcf", "card2.vcf", ])
+        yield self._check_read_write("user02", "user01", ["group2.vcf", "card1.vcf", "card3.vcf", ])
+
+        yield self.commit()
+
+        # Decline one
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.declineShare(inviteUID2)
+
+        yield self._check_calendar("user02", "user01", self.group1_children)
+
+        yield self._check_read_only("user02", "user01", ["group1.vcf", "card1.vcf", "card2.vcf", ])
+
+        shared = yield self.calendarObjectUnderTest(home="user02", calendar_name="user01", name="group2.vcf")
+        self.assertTrue(shared is None)
+        shared = yield self.calendarObjectUnderTest(home="user02", calendar_name="user01", name="card3.vcf")
+        self.assertTrue(shared is None)
+
+        yield self.commit()
+
+        # Decline other
+        shareeHome = yield self.calendarHomeUnderTest(name="user02")
+        yield shareeHome.declineShare(inviteUID1)
+
+        sharedParent = yield self.calendarUnderTest(home="user02", name="user01")
+        self.assertTrue(sharedParent is None)
+
+
+
+class MixedSharing(BaseSharingTests):
+    """
+    Test store-based combined address book and group book sharing.
+    """
+
+    @inlineCallbacks
+    def test_calendar_ro_then_groups(self):
+
+        # Share address book read-only
+        shareeName = yield self._createShare()
+        yield self._check_calendar("user02", "user01", self.fully_shared_children)
+        yield self._check_read_only("user02", "user01", self.all_children)
+        yield self._check_read_write("user02", "user01", [])
+        yield self._check_notifications("user02", [shareeName, ])
+
+        # Add group1 read-write
+        inviteUID1 = yield self._createGroupShare(groupname="group1.vcf", mode=_BIND_MODE_WRITE)
+
+        yield self._check_calendar("user02", "user01", self.fully_shared_children)
+        yield self._check_read_only("user02", "user01", ["group2.vcf", "card3.vcf", ])
+        yield self._check_read_write("user02", "user01", ["group1.vcf", "card1.vcf", "card2.vcf", ])
+        yield self._check_notifications("user02", [shareeName, inviteUID1, ])
+
+        # Add group2 read-write
+        inviteUID2 = yield self._createGroupShare(groupname="group2.vcf", mode=_BIND_MODE_WRITE)
+
+        yield self._check_calendar("user02", "user01", self.fully_shared_children)
+        yield self._check_read_only("user02", "user01", [])
+        yield self._check_read_write("user02", "user01", self.all_children)
+        yield self._check_notifications("user02", [shareeName, inviteUID1, inviteUID2])
+
+        # Uninvite group1
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        yield group.uninviteUIDFromShare("user02")
+
+        yield self._check_calendar("user02", "user01", self.fully_shared_children)
+        yield self._check_read_only("user02", "user01", ["group1.vcf", "card2.vcf", ])
+        yield self._check_read_write("user02", "user01", ["group2.vcf", "card1.vcf", "card3.vcf", ])
+
+        # Uninvite group2
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group2.vcf")
+        yield group.uninviteUIDFromShare("user02")
+
+        yield self._check_calendar("user02", "user01", self.fully_shared_children)
+        yield self._check_read_only("user02", "user01", self.all_children)
+        yield self._check_read_write("user02", "user01", [])
+
+
+    @inlineCallbacks
+    def test_calendar_ro_then_group_no_accept(self):
+
+        # Share address book read-only
+        shareeName = yield self._createShare()
+        yield self._check_calendar("user02", "user01", self.fully_shared_children)
+        yield self._check_read_only("user02", "user01", self.all_children)
+        yield self._check_read_write("user02", "user01", [])
+        yield self._check_notifications("user02", [shareeName, ])
+
+        # Add group1 read-write - but do not accept
+        group = yield self.calendarObjectUnderTest(home="user01", calendar_name="calendar", name="group1.vcf")
+        invited = yield group.inviteUIDToShare("user02", _BIND_MODE_WRITE, "summary")
+        yield self._check_notifications("user02", [shareeName, invited.shareUID(), ])
+
+        yield self._check_calendar("user02", "user01", self.fully_shared_children)
+        yield self._check_read_only("user02", "user01", self.all_children)
+        yield self._check_read_write("user02", "user01", [])
+
+
+
 class SharingRevisions(BaseSharingTests):
     """
     Test store-based sharing and interaction with revision table.

Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/groups.py
===================================================================
--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/groups.py	2014-07-11 20:03:42 UTC (rev 13752)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/groups.py	2014-07-11 20:46:58 UTC (rev 13753)
@@ -453,7 +453,7 @@
         for [calendarID] in rows:
             wp = yield GroupShareeReconciliationWork.reschedule(
                 txn,
-                seconds=float(config.Sharing.Calendar.Groups.ReconciliationDelaySeconds),
+                seconds=float(config.Sharing.Calendars.Groups.ReconciliationDelaySeconds),
                 calendarID=calendarID,
                 groupID=groupID,
             )
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140711/bd90cd9d/attachment-0001.html>


More information about the calendarserver-changes mailing list