[CalendarServer-changes] [14353] CalendarServer/trunk/txdav/caldav/datastore/scheduling

source_changes at macosforge.org source_changes at macosforge.org
Thu Jan 29 14:44:58 PST 2015


Revision: 14353
          http://trac.calendarserver.org//changeset/14353
Author:   cdaboo at apple.com
Date:     2015-01-29 14:44:58 -0800 (Thu, 29 Jan 2015)
Log Message:
-----------
VPOLL related updates.

Modified Paths:
--------------
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/itip.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/processing.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/scheduler.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_itip.py

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/itip.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/itip.py	2015-01-29 20:22:01 UTC (rev 14352)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/itip.py	2015-01-29 22:44:58 UTC (rev 14353)
@@ -358,6 +358,40 @@
 
 
     @staticmethod
+    def processPollStatus(itip_message, calendar, recipient):
+        """
+        Process a METHOD=POLLSTATUS.
+
+        @param itip_message: the iTIP message to process.
+        @type itip_message: L{Component}
+        @param calendar: the calendar object to apply the POLLSTATUS to
+        @type calendar: L{Component}
+
+        @return: the update calendar component or C{None}
+        """
+
+        # Check sequencing
+        if not iTipProcessing.sequenceComparison(itip_message, calendar):
+            # Ignore out of sequence message
+            return None
+
+        calendar_master = calendar.masterComponent()
+        itip_master = itip_message.masterComponent()
+
+        # Remove each VVOTER in the original (except for the recipients)
+        for component in tuple(calendar_master.subcomponents()):
+            if component.name() == "VVOTER" and component.propertyValue("VOTER") != recipient:
+                calendar_master.removeComponent(component)
+
+        # Add each VVOTER in the iTip message
+        for component in itip_master.subcomponents():
+            if component.name() == "VVOTER" and component.propertyValue("VOTER") != recipient:
+                calendar_master.addComponent(component.duplicate())
+
+        return calendar
+
+
+    @staticmethod
     def processReply(itip_message, calendar):
         """
         Process a METHOD=REPLY.
@@ -576,7 +610,7 @@
             # Do VPOLL transfer
             if reply_component.name() == "VPOLL":
                 # TODO: figure out how to report changes back
-                iTipProcessing.updateVPOLLDataFromReply(reply_component, organizer_component, attendee)
+                partstat_changed = iTipProcessing.updateVPOLLDataFromReply(reply_component, organizer_component, attendee)
 
         return attendee.value(), partstat_changed, private_comment_changed
 
@@ -595,6 +629,8 @@
         @type attendee: L{Property}
         """
 
+        partstat_changed = False
+
         # Get REQUEST-STATUS as we need to write that into the saved ATTENDEE property
         reqstatus = tuple(reply_component.properties("REQUEST-STATUS"))
         if reqstatus:
@@ -607,12 +643,13 @@
         organizerVoter = organizer_component.voterComponentForVoter(attendee.value())
 
         if replyVoter is None:
-            return
+            return partstat_changed
 
         if organizerVoter is None:
             # Add in the new one
             organizerVoter = replyVoter.duplicate()
             reply_component.addComponent(organizerVoter)
+            partstat_changed = True
         else:
             # Merge each vote
             replyMap = replyVoter.voteMap()
@@ -621,9 +658,12 @@
             # Add new ones
             for vote in set(replyMap.keys()) - set(organizerMap.keys()):
                 organizerVoter.addComponent(replyMap[vote].duplicate())
+                partstat_changed = True
 
             # Replace existing ones
             for vote in set(replyMap.keys()) & set(organizerMap.keys()):
+                if organizerMap[vote].propertyValue("RESPONSE") != replyMap[vote].propertyValue("RESPONSE"):
+                    partstat_changed = True
                 organizerVoter.removeComponent(organizerMap[vote])
                 organizerVoter.addComponent(replyMap[vote].duplicate())
 
@@ -635,7 +675,9 @@
         except KeyError:
             pass
 
+        return partstat_changed
 
+
     @staticmethod
     def transferItems(from_calendar, to_component, needs_action_rids, reschedule, master_details, remove_matched=False):
         """

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/processing.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/processing.py	2015-01-29 20:22:01 UTC (rev 14352)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/processing.py	2015-01-29 22:44:58 UTC (rev 14353)
@@ -90,8 +90,9 @@
         @param recipient: calendar user receiving the message
         @type recipient: C{str}
 
-        @return: a C{tuple} of (C{bool}, C{bool}) indicating whether the message was processed, and if it was whether
-            auto-processing has taken place.
+        @return: a C{tuple} of (C{bool}, C{bool}, C{bool}, C{bool}) indicating whether the message was processed,
+            and if it was whether auto-processing has taken place, whether it needs to be stored in the inbox, and
+            the changes property for the inbox item.
         """
 
         self.txn = txn
@@ -151,7 +152,7 @@
 
 
     def isAttendeeReceivingMessage(self):
-        return self.method in ("REQUEST", "ADD", "CANCEL")
+        return self.method in ("REQUEST", "ADD", "CANCEL", "POLLSTATUS")
 
 
     @inlineCallbacks
@@ -393,6 +394,8 @@
         elif self.method == "ADD":
             # TODO: implement ADD
             result = (False, False, False, None)
+        elif self.method == "POLLSTATUS":
+            result = (yield self.doImplicitAttendeePollStatus())
         else:
             # NB We should never get here as we will have rejected unsupported METHODs earlier.
             result = (True, True, False, None,)
@@ -552,7 +555,7 @@
             else:
                 # Request needs to be ignored
                 log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:REQUEST, UID: '%s' - ignoring" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
-                result = (True, True, False, None,)
+                result = (True, False, False, None,)
 
         returnValue(result)
 
@@ -570,7 +573,7 @@
         # If there is no existing copy, then ignore
         if self.recipient_calendar is None:
             log.debug("ImplicitProcessing - originator '%s' to recipient '%s' ignoring METHOD:CANCEL, UID: '%s' - attendee has no copy" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
-            result = (True, True, True, None)
+            result = (True, False, True, None)
         else:
             # Need to check for auto-respond attendees. These need to suppress the inbox message
             # if the cancel is processed. However, if the principal is a user we always force the
@@ -631,12 +634,36 @@
                     result = (True, autoprocessed, store_inbox, changes)
             else:
                 log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:CANCEL, UID: '%s' - ignoring" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
-                result = (True, True, False, None)
+                result = (True, False, False, None)
 
         returnValue(result)
 
 
     @inlineCallbacks
+    def doImplicitAttendeePollStatus(self):
+        """
+        An iTIP message status update has been sent to an attendee by the organizer. We need to update the
+        attendee state based on the nature of the iTIP message.
+        """
+        # If there is no existing copy, then we must fail
+        if self.new_resource:
+            log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:POLLSTATUS, UID: '%s' - attendee has no copy" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
+            returnValue((True, False, False, None,))
+
+        processed_message = iTipProcessing.processPollStatus(self.message, self.recipient_calendar)
+
+        # Let the store know that no time-range info has changed for a refresh (assuming that
+        # no auto-accept changes were made)
+        processed_message.noInstanceIndexing = True
+
+        # Update the attendee's copy of the event
+        log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:POLLSTATUS, UID: '%s' - updating poll" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
+        yield self.writeCalendarResource(None, self.recipient_calendar_resource, processed_message)
+
+        returnValue((True, False, False, None,))
+
+
+    @inlineCallbacks
     def checkAttendeeAutoReply(self, calendar, automode):
         """
         Check whether a reply to the given iTIP message is needed and if so make the

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/scheduler.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/scheduler.py	2015-01-29 20:22:01 UTC (rev 14352)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/scheduler.py	2015-01-29 22:44:58 UTC (rev 14353)
@@ -515,7 +515,7 @@
             yield self.generateRemoteSchedulingResponses(otherserver_recipients, responses, freebusy, getattr(self.txn, 'doing_attendee_refresh', False))
 
         # To reduce chatter, we suppress certain messages
-        if not self.suppress_refresh:
+        if not self.suppress_refresh or self.calendar.mainType() == "VPOLL":
 
             # Now process remote recipients
             if remote_recipients:

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_itip.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_itip.py	2015-01-29 20:22:01 UTC (rev 14352)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_itip.py	2015-01-29 22:44:58 UTC (rev 14353)
@@ -21,7 +21,7 @@
 from twisted.trial import unittest
 
 from twistedcaldav.stdconfig import config
-from twistedcaldav.ical import Component
+from twistedcaldav.ical import Component, normalize_iCalStr
 
 from txdav.caldav.datastore.scheduling.itip import iTipProcessing, iTipGenerator
 
@@ -1289,6 +1289,7 @@
 END:VCALENDAR
 """,
                 True,
+                None,
             ),
             (
                 "1.2 Simple Reply - recurring no overrides",
@@ -1335,6 +1336,7 @@
 END:VCALENDAR
 """,
                 True,
+                None,
             ),
             (
                 "1.3 Simple Reply - recurring with missing master",
@@ -1389,6 +1391,7 @@
 END:VCALENDAR
 """,
                 True,
+                None,
             ),
             (
                 "1.4 Simple Reply - recurring with overrides in master but not reply",
@@ -1453,6 +1456,7 @@
 END:VCALENDAR
 """,
                 True,
+                None,
             ),
             (
                 "1.5 Simple Reply - recurring with overrides in master invalid in reply",
@@ -1517,6 +1521,7 @@
 END:VCALENDAR
 """,
                 True,
+                None,
             ),
             (
                 "1.6 Simple Reply - recurring with overrides in master, invalid ones in reply",
@@ -1590,6 +1595,7 @@
 END:VCALENDAR
 """,
                 True,
+                None,
             ),
             (
                 "2.1 X-CALENDARSERVER-RESET-PARTSTAT Reply - non recurring",
@@ -1636,6 +1642,7 @@
 END:VCALENDAR
 """,
                 True,
+                None,
             ),
             (
                 "2.2 X-CALENDARSERVER-RESET-PARTSTAT Reply - non recurring",
@@ -1682,6 +1689,7 @@
 END:VCALENDAR
 """,
                 True,
+                None,
             ),
             (
                 "2.3 X-CALENDARSERVER-RESET-PARTSTAT Reply - non recurring",
@@ -1728,6 +1736,7 @@
 END:VCALENDAR
 """,
                 True,
+                None,
             ),
             (
                 "2.4 X-CALENDARSERVER-RESET-PARTSTAT Reply - non recurring",
@@ -1774,6 +1783,7 @@
 END:VCALENDAR
 """,
                 True,
+                None,
             ),
             (
                 "3.1 Simple VPOLL Reply - response added",
@@ -1874,6 +1884,7 @@
 END:VCALENDAR
 """,
                 True,
+                ('mailto:user2 at example.com', set([("", True, False,)])),
             ),
             (
                 "3.2 Simple VPOLL Reply - response changed",
@@ -1978,6 +1989,7 @@
 END:VCALENDAR
 """,
                 True,
+                ('mailto:user2 at example.com', set([("", True, False,)])),
             ),
             (
                 "3.3 Simple VPOLL Reply - response added and changed",
@@ -2090,6 +2102,7 @@
 END:VCALENDAR
 """,
                 True,
+                ('mailto:user2 at example.com', set([("", True, False,)])),
             ),
             (
                 "3.4 Simple VPOLL Reply - response one changed",
@@ -2202,21 +2215,475 @@
 END:VCALENDAR
 """,
                 True,
+                ('mailto:user2 at example.com', set([("", True, False,)])),
             ),
+            (
+                "3.5 Simple VPOLL Reply - no changes",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VPOLL
+UID:8D2B2048-7915-6ECD-A82B-01F4EF8EEBEA
+DTSTAMP:20150113T152404Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+POLL-MODE:BASIC
+POLL-PROPERTIES:DTSTART,DTEND
+SUMMARY:New Poll
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user2 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:50
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:100
+END:VOTE
+END:VVOTER
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+POLL-ITEM-ID:1
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080602T120000Z
+DTEND:20080602T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+POLL-ITEM-ID:2
+END:VEVENT
+END:VPOLL
+END:VCALENDAR
+""",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VPOLL
+UID:8D2B2048-7915-6ECD-A82B-01F4EF8EEBEA
+DTSTAMP:20150113T152404Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+POLL-MODE:BASIC
+POLL-PROPERTIES:DTSTART,DTEND
+SUMMARY:New Poll
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user2 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:50
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:100
+END:VOTE
+END:VVOTER
+END:VPOLL
+END:VCALENDAR
+""",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VPOLL
+UID:8D2B2048-7915-6ECD-A82B-01F4EF8EEBEA
+DTSTAMP:20150113T152404Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+POLL-MODE:BASIC
+POLL-PROPERTIES:DTSTART,DTEND
+SUMMARY:New Poll
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;SCHEDULE-STATUS=2.0:mailto:user2 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:50
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:100
+END:VOTE
+END:VVOTER
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+POLL-ITEM-ID:1
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080602T120000Z
+DTEND:20080602T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+POLL-ITEM-ID:2
+END:VEVENT
+END:VPOLL
+END:VCALENDAR
+""",
+                True,
+                ('mailto:user2 at example.com', set([])),
+            ),
         )
 
-        for title, calendar_txt, itip_txt, changed_txt, expected in data:
+        for title, calendar_txt, itip_txt, changed_txt, expected, processed in data:
             calendar = Component.fromString(calendar_txt)
             itip = Component.fromString(itip_txt)
             if expected:
                 changed = Component.fromString(changed_txt)
 
-            result, _ignore = iTipProcessing.processReply(itip, calendar)
+            result, result_processed = iTipProcessing.processReply(itip, calendar)
             self.assertEqual(result, expected, msg="Result mismatch: %s" % (title,))
             if expected:
                 self.assertEqual(changed, calendar, msg="Calendar mismatch: %s" % (title,))
+            if processed is not None:
+                self.assertEqual(result_processed, processed, msg="Process mismatch: %s" % (title,))
 
 
+    def test_processPollStatus(self):
+        """
+        Test iTIPProcessing.processPollStatus
+        """
+
+        data = (
+            (
+                "3.1 Simple VPOLL - response added",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VPOLL
+UID:8D2B2048-7915-6ECD-A82B-01F4EF8EEBEA
+DTSTAMP:20150113T152404Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+POLL-MODE:BASIC
+POLL-PROPERTIES:DTSTART,DTEND
+SUMMARY:New Poll
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user1 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:100
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:100
+END:VOTE
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user2 at example.com
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user3 at example.com
+END:VVOTER
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+POLL-ITEM-ID:1
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080602T120000Z
+DTEND:20080602T130000Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+POLL-ITEM-ID:2
+END:VEVENT
+END:VPOLL
+END:VCALENDAR
+""",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:POLLSTATUS
+BEGIN:VPOLL
+UID:8D2B2048-7915-6ECD-A82B-01F4EF8EEBEA
+DTSTAMP:20150113T152404Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+POLL-MODE:BASIC
+POLL-PROPERTIES:DTSTART,DTEND
+SUMMARY:New Poll
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user1 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:100
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:100
+END:VOTE
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user2 at example.com
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user3 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:100
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:0
+END:VOTE
+END:VVOTER
+END:VPOLL
+END:VCALENDAR
+""",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VPOLL
+UID:8D2B2048-7915-6ECD-A82B-01F4EF8EEBEA
+DTSTAMP:20150113T152404Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+POLL-MODE:BASIC
+POLL-PROPERTIES:DTSTART,DTEND
+SUMMARY:New Poll
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user1 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:100
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:100
+END:VOTE
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user2 at example.com
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user3 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:100
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:0
+END:VOTE
+END:VVOTER
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+POLL-ITEM-ID:1
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080602T120000Z
+DTEND:20080602T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+POLL-ITEM-ID:2
+END:VEVENT
+END:VPOLL
+END:VCALENDAR
+""",
+            ),
+            (
+                "3.2 Simple VPOLL - recipient response not changed",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VPOLL
+UID:8D2B2048-7915-6ECD-A82B-01F4EF8EEBEA
+DTSTAMP:20150113T152404Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+POLL-MODE:BASIC
+POLL-PROPERTIES:DTSTART,DTEND
+SUMMARY:New Poll
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user1 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:100
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:100
+END:VOTE
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user2 at example.com
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user3 at example.com
+END:VVOTER
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+POLL-ITEM-ID:1
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080602T120000Z
+DTEND:20080602T130000Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+POLL-ITEM-ID:2
+END:VEVENT
+END:VPOLL
+END:VCALENDAR
+""",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:POLLSTATUS
+BEGIN:VPOLL
+UID:8D2B2048-7915-6ECD-A82B-01F4EF8EEBEA
+DTSTAMP:20150113T152404Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+POLL-MODE:BASIC
+POLL-PROPERTIES:DTSTART,DTEND
+SUMMARY:New Poll
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user1 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:100
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:100
+END:VOTE
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user2 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:50
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:0
+END:VOTE
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user3 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:100
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:0
+END:VOTE
+END:VVOTER
+END:VPOLL
+END:VCALENDAR
+""",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VPOLL
+UID:8D2B2048-7915-6ECD-A82B-01F4EF8EEBEA
+DTSTAMP:20150113T152404Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+POLL-MODE:BASIC
+POLL-PROPERTIES:DTSTART,DTEND
+SUMMARY:New Poll
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user1 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:100
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:100
+END:VOTE
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user2 at example.com
+END:VVOTER
+BEGIN:VVOTER
+DTSTAMP:20150113T152404Z
+VOTER;RSVP=TRUE:mailto:user3 at example.com
+BEGIN:VOTE
+POLL-ITEM-ID:1
+RESPONSE:100
+END:VOTE
+BEGIN:VOTE
+POLL-ITEM-ID:2
+RESPONSE:0
+END:VOTE
+END:VVOTER
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+POLL-ITEM-ID:1
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080602T120000Z
+DTEND:20080602T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+POLL-ITEM-ID:2
+END:VEVENT
+END:VPOLL
+END:VCALENDAR
+""",
+            ),
+        )
+
+        for title, calendar_txt, itip_txt, changed_txt in data:
+            calendar = Component.fromString(calendar_txt)
+            itip = Component.fromString(itip_txt)
+
+            result = iTipProcessing.processPollStatus(itip, calendar, "mailto:user2 at example.com")
+            self.assertEqual(normalize_iCalStr(result), normalize_iCalStr(changed_txt), msg="Calendar mismatch: %s" % (title,))
+
+
     def test_update_attendee_partstat(self):
 
         data = (
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150129/a65cea5e/attachment-0001.html>


More information about the calendarserver-changes mailing list