[CalendarServer-changes] [14468] CalendarServer/branches/users/sagen/trashcan-4/txdav

source_changes at macosforge.org source_changes at macosforge.org
Mon Feb 23 13:25:17 PST 2015


Revision: 14468
          http://trac.calendarserver.org//changeset/14468
Author:   sagen at apple.com
Date:     2015-02-23 13:25:17 -0800 (Mon, 23 Feb 2015)
Log Message:
-----------
Objects in trash were still appearing in original collection via listObjectResources()

Modified Paths:
--------------
    CalendarServer/branches/users/sagen/trashcan-4/txdav/caldav/datastore/sql.py
    CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/sql.py
    CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/sql_schema/current.sql
    CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/test/test_sql.py

Modified: CalendarServer/branches/users/sagen/trashcan-4/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/sagen/trashcan-4/txdav/caldav/datastore/sql.py	2015-02-23 18:03:06 UTC (rev 14467)
+++ CalendarServer/branches/users/sagen/trashcan-4/txdav/caldav/datastore/sql.py	2015-02-23 21:25:17 UTC (rev 14468)
@@ -2361,6 +2361,8 @@
             obj.CREATED,
             obj.MODIFIED,
             obj.DATAVERSION,
+            obj.TRASHED,
+            obj.IS_TRASH,
         ]
 
 
@@ -2382,6 +2384,8 @@
             "_created",
             "_modified",
             "_dataversion",
+            "_trashed",
+            "_is_trash",
         )
 
 

Modified: CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/sql.py	2015-02-23 18:03:06 UTC (rev 14467)
+++ CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/sql.py	2015-02-23 21:25:17 UTC (rev 14468)
@@ -7015,7 +7015,7 @@
         rows = yield Select(
             [obj.RESOURCE_NAME],
             From=obj,
-            Where=obj.PARENT_RESOURCE_ID == Parameter('parentID')
+            Where=(obj.PARENT_RESOURCE_ID == Parameter('parentID')).And(obj.IS_TRASH == False)
         ).on(parent._txn, parentID=parent.id())
         returnValue(sorted([row[0] for row in rows]))
 
@@ -7498,7 +7498,7 @@
     def _updateIsTrashQuery(cls):
         obj = cls._objectSchema
         return Update(
-            {obj.IS_TRASH: Parameter("isTrash")},
+            {obj.IS_TRASH: Parameter("isTrash"), obj.TRASHED: Parameter("trashed")},
             Where=obj.RESOURCE_ID == Parameter("resourceID"),
         )
 
@@ -7506,7 +7506,7 @@
     @inlineCallbacks
     def toTrash(self):
         yield self._updateIsTrashQuery.on(
-            self._txn, isTrash=True, resourceID=self._resourceID
+            self._txn, isTrash=True, trashed=datetime.datetime.utcnow(), resourceID=self._resourceID
         )
         yield self._parentCollection.removedObjectResource(self)
         yield self._parentCollection._deleteRevision(self.name())
@@ -7526,7 +7526,7 @@
         trash = self._parentCollection
         self._parentCollection = yield trash.originalParentForResource(self)
         yield self._updateIsTrashQuery.on(
-            self._txn, isTrash=False, resourceID=self._resourceID
+            self._txn, isTrash=False, trashed=None, resourceID=self._resourceID
         )
         yield trash._deleteRevision(
             trash.nameForResource(
@@ -7544,7 +7544,7 @@
     @classproperty
     def _selectIsTrashQuery(cls):
         obj = cls._objectSchema
-        return Select((obj.IS_TRASH,), From=obj, Where=obj.RESOURCE_ID == Parameter("resourceID"))
+        return Select((obj.IS_TRASH, obj.TRASHED), From=obj, Where=obj.RESOURCE_ID == Parameter("resourceID"))
 
 
     @inlineCallbacks
@@ -7557,7 +7557,17 @@
             )[0][0]
         )
 
+    @inlineCallbacks
+    def whenTrashed(self):
+        returnValue(
+            (
+                yield self._selectIsTrashQuery.on(
+                    self._txn, resourceID=self._resourceID
+                )
+            )[0][1]
+        )
 
+
     def removeNotifyCategory(self):
         """
         Indicates what category to use when determining the priority of push

Modified: CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/sql_schema/current.sql	2015-02-23 18:03:06 UTC (rev 14467)
+++ CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/sql_schema/current.sql	2015-02-23 21:25:17 UTC (rev 14468)
@@ -133,7 +133,8 @@
   CREATED               timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
   MODIFIED              timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
   CHILD_TYPE            varchar(10)  default null, -- None, inbox, trash (FIXME: convert this to enumeration)
-  IS_TRASH             boolean      default false not null -- collection is in the trash
+  TRASHED               timestamp    default null,
+  IS_TRASH              boolean      default false not null -- collection is in the trash
 
 );
 
@@ -261,6 +262,7 @@
   CREATED              timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
   MODIFIED             timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
   DATAVERSION          integer      default 0 not null,
+  TRASHED              timestamp    default null,
   IS_TRASH             boolean      default false not null, -- entire resource is in the trash
 
   unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
@@ -482,6 +484,7 @@
   CREATED                       timestamp       default timezone('UTC', CURRENT_TIMESTAMP),
   MODIFIED                      timestamp       default timezone('UTC', CURRENT_TIMESTAMP),
   DATAVERSION                   integer         default 0 not null,
+  TRASHED                       timestamp       default null,
   IS_TRASH                      boolean         default false not null,
 
   unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index

Modified: CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/test/test_sql.py	2015-02-23 18:03:06 UTC (rev 14467)
+++ CalendarServer/branches/users/sagen/trashcan-4/txdav/common/datastore/test/test_sql.py	2015-02-23 21:25:17 UTC (rev 14468)
@@ -633,37 +633,59 @@
 
         # Verify it's not in the trash
         self.assertFalse((yield resource.isTrash()))
+        trashed = yield resource.whenTrashed()
+        self.assertTrue(trashed is None)
 
         # Move object to trash
         yield resource.toTrash()
 
+        yield txn.commit()
+        yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
+
+        txn = self.store.newTransaction()
+
         # Verify it's in the trash
+        resource = yield self._getResource(txn, "user01", "trash", "")
         self.assertTrue((yield resource.isTrash()))
+        trashed = yield resource.whenTrashed()
+        self.assertFalse(trashed is None)
 
         # No objects in collection
-        objects = yield collection.listObjectResources()
-        self.assertEquals(len(objects), 0)
+        resourceNames = yield self._getResourceNames(txn, "user01", "calendar")
+        self.assertEqual(len(resourceNames), 0)
 
         # One object in trash
-        objects = yield trash.listObjectResources()
-        self.assertEquals(len(objects), 1)
+        resourceNames = yield self._getResourceNames(txn, "user01", "trash")
+        self.assertEqual(len(resourceNames), 1)
 
         # Put back from trash
-        resource = yield self._getResource(txn, "user01", "trash", objects[0])
         yield resource.fromTrash()
 
+        yield txn.commit()
+        yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
+
+        txn = self.store.newTransaction()
+
         # Not in trash
-        self.assertFalse((yield resource.isTrash()))
+        resource = yield self._getResource(txn, "user01", "trash", "")
+        self.assertTrue(resource is None)
 
+
         # One object in collection
-        objects = yield collection.listObjectResources()
-        self.assertEquals(len(objects), 1)
+        resourceNames = yield self._getResourceNames(txn, "user01", "calendar")
+        self.assertEqual(len(resourceNames), 1)
+        resource = yield self._getResource(txn, "user01", "calendar", "test.ics")
+        self.assertFalse((yield resource.isTrash()))
+        trashed = yield resource.whenTrashed()
+        self.assertTrue(trashed is None)
 
         # No objects in trash
-        objects = yield trash.listObjectResources()
-        self.assertEquals(len(objects), 0)
+        resourceNames = yield self._getResourceNames(txn, "user01", "trash")
+        self.assertEqual(len(resourceNames), 0)
 
+        yield txn.commit()
 
+
         # Not implemented
 #         #
 #         # Now with addressbook
@@ -809,6 +831,8 @@
         txn = self.store.newTransaction()
         resourceNames = yield self._getResourceNames(txn, "user01", "inbox")
         self.assertEqual(len(resourceNames), 1)
+        resource = yield self._getResource(txn, "user01", "inbox", "")
+        yield resource.remove()
 
         # user01's copy has SCHEDULE-STATUS update
         data = yield self._getResourceData(txn, "user01", "calendar", "test.ics")
@@ -829,6 +853,10 @@
 
         # user01's copy is in the trash, still with user02 accepted
         txn = self.store.newTransaction()
+        resource = yield self._getResource(txn, "user01", "trash", "")
+        self.assertTrue((yield resource.isTrash()))
+        trashed = yield resource.whenTrashed()
+        self.assertFalse(trashed is None)
         data = yield self._getResourceData(txn, "user01", "trash", "")
         self.assertTrue("PARTSTAT=ACCEPTED" in data)
         yield txn.commit()
@@ -859,6 +887,10 @@
         txn = self.store.newTransaction()
 
         # user01's copy should be back on their calendar
+        resource = yield self._getResource(txn, "user01", "calendar", "test.ics")
+        self.assertFalse((yield resource.isTrash()))
+        trashed = yield resource.whenTrashed()
+        self.assertTrue(trashed is None)
         data = yield self._getResourceData(txn, "user01", "calendar", "test.ics")
         self.assertTrue("PARTSTAT=NEEDS-ACTION" in data)
 
@@ -871,6 +903,138 @@
 
 
     @inlineCallbacks
+    def test_trashScheduledFullyInFutureAttendeeRemove(self):
+
+        from twistedcaldav.stdconfig import config
+        self.patch(config, "EnableTrashCollection", True)
+
+        # A month in the future
+        start = DateTime.getNowUTC()
+        start.setHHMMSS(0, 0, 0)
+        start.offsetMonth(1)
+        end = DateTime.getNowUTC()
+        end.setHHMMSS(1, 0, 0)
+        end.offsetMonth(1)
+        subs = {
+            "start": start,
+            "end": end,
+        }
+
+        data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTART;TZID=America/Los_Angeles:%(start)s
+DTEND;TZID=America/Los_Angeles:%(end)s
+DTSTAMP:20150204T192546Z
+SUMMARY:Scheduled
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""" % subs
+
+        data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTART;TZID=America/Los_Angeles:%(start)s
+DTEND;TZID=America/Los_Angeles:%(end)s
+DTSTAMP:20150204T192546Z
+SUMMARY:Scheduled
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""" % subs
+
+        # user01 invites user02
+        txn = self.store.newTransaction()
+        yield self._createResource(
+            txn, "user01", "calendar", "test.ics", data1
+        )
+        yield txn.commit()
+
+        yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
+
+        # user01's copy has SCHEDULE-STATUS update
+        txn = self.store.newTransaction()
+        data = yield self._getResourceData(txn, "user01", "calendar", "test.ics")
+        self.assertTrue("SCHEDULE-STATUS=1.2" in data)
+
+        # user02 has an inbox item
+        resourceNames = yield self._getResourceNames(txn, "user02", "inbox")
+        self.assertEqual(len(resourceNames), 1)
+
+        # user02 accepts
+        yield self._updateResource(txn, "user02", "calendar", "", data2)
+        yield txn.commit()
+
+        yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
+
+        # user01 has an inbox item
+        txn = self.store.newTransaction()
+        resourceNames = yield self._getResourceNames(txn, "user01", "inbox")
+        self.assertEqual(len(resourceNames), 1)
+        resource = yield self._getResource(txn, "user01", "inbox", "")
+        yield resource.remove()
+
+        # user01's copy has SCHEDULE-STATUS update
+        data = yield self._getResourceData(txn, "user01", "calendar", "test.ics")
+        self.assertTrue("SCHEDULE-STATUS=2.0" in data)
+        self.assertTrue("PARTSTAT=ACCEPTED" in data)
+
+        resource = yield self._getResource(txn, "user02", "inbox", "")
+        yield resource.remove()
+
+        yield txn.commit()
+
+        # user02 trashes event
+        txn = self.store.newTransaction()
+        resource = yield self._getResource(txn, "user02", "calendar", "")
+        yield resource.remove()
+
+        yield txn.commit()
+
+        yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
+
+        # user01's calendar copy shows user02 declined
+        txn = self.store.newTransaction()
+
+        data = yield self._getResourceData(txn, "user01", "calendar", "test.ics")
+        self.assertTrue("PARTSTAT=DECLINED" in data)
+
+        # user01's inbox copy also shows user02 declined
+        data = yield self._getResourceData(txn, "user01", "inbox", "")
+        self.assertTrue("PARTSTAT=DECLINED" in data)
+        resource = yield self._getResource(txn, "user01", "inbox", "")
+        yield resource.remove()
+
+        # result = yield txn.execSQL("select * from calendar_object", [])
+        # for row in result:
+        #     print("ROW", row)
+
+        # user02's copy is in the trash only, and still has ACCEPTED
+        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        self.assertEqual(len(resourceNames), 1)
+
+        resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
+        self.assertEqual(len(resourceNames), 0)
+
+        resourceNames = yield self._getResourceNames(txn, "user02", "inbox")
+        self.assertEqual(len(resourceNames), 0)
+
+        data = yield self._getResourceData(txn, "user02", "trash", "")
+        self.assertTrue("PARTSTAT=ACCEPTED" in data)
+
+        yield txn.commit()
+
+
+    @inlineCallbacks
     def test_trashScheduledFullyInPast(self):
 
         from twistedcaldav.stdconfig import config
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150223/dd1dfb5c/attachment-0001.html>


More information about the calendarserver-changes mailing list