[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