[CalendarServer-changes] [2734] CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Sat Jul 19 10:27:37 PDT 2008
Revision: 2734
http://trac.macosforge.org/projects/calendarserver/changeset/2734
Author: cdaboo at apple.com
Date: 2008-07-19 10:27:36 -0700 (Sat, 19 Jul 2008)
Log Message:
-----------
iTIP processing stuff to help with generating iTIP messages.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/ical.py
CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/itip.py
CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/test/test_icalendar.py
Added Paths:
-----------
CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/test/test_itip.py
Modified: CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/ical.py 2008-07-19 00:34:18 UTC (rev 2733)
+++ CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/ical.py 2008-07-19 17:27:36 UTC (rev 2734)
@@ -999,6 +999,32 @@
return None
+ def getOrganizersByInstance(self):
+ """
+ Get the organizer value for each instance.
+
+ @return: a list of tuples of (organizer value, recurrence-id)
+ """
+
+ # Extract appropriate sub-component if this is a VCALENDAR
+ if self.name() == "VCALENDAR":
+ result = ()
+ for component in self.subcomponents():
+ if component.name() != "VTIMEZONE":
+ result += component.getOrganizersByInstance()
+ return result
+ else:
+ try:
+ # Should be just one ORGANIZER
+ org = self.propertyValue("ORGANIZER")
+ rid = self.getRecurrenceIDUTC()
+ if org:
+ return ((org, rid),)
+ except ValueError:
+ pass
+
+ return ()
+
def getOrganizerProperty(self):
"""
Get the organizer value. Works on either a VCALENDAR or on a component.
@@ -1039,6 +1065,27 @@
return None
+ def getAttendeesByInstance(self):
+ """
+ Get the organizer value for each instance.
+
+ @return: a list of tuples of (organizer value, recurrence-id)
+ """
+
+ # Extract appropriate sub-component if this is a VCALENDAR
+ if self.name() == "VCALENDAR":
+ result = ()
+ for component in self.subcomponents():
+ if component.name() != "VTIMEZONE":
+ result += component.getAttendeesByInstance()
+ return result
+ else:
+ result = ()
+ rid = self.getRecurrenceIDUTC()
+ for attendee in self.properties("ATTENDEE"):
+ result += ((attendee.value(), rid),)
+ return result
+
def getAttendeeProperty(self, match):
"""
Get the attendees matching a value. Works on either a VCALENDAR or on a component.
@@ -1114,6 +1161,45 @@
return None
+ def attendeesView(self, attendees):
+ """
+ Filter out any components that all attendees are not present in. Use EXDATEs
+ on the master to account for changes.
+ """
+
+ assert self.name() == "VCALENDAR", "Not a calendar: %r" % (self,)
+
+ # Modify any components that reference the attendee, make note of the ones that don't
+ remove_components = []
+ master_component = None
+ removed_master = False
+ for component in self.subcomponents():
+ if component.name() == "VTIMEZONE":
+ continue
+ found_all_attendees = True
+ for attendee in attendees:
+ if component.getAttendeeProperty((attendee,)) is None:
+ found_all_attendees = False
+ break
+ if not found_all_attendees:
+ remove_components.append(component)
+ if component.getRecurrenceIDUTC() is None:
+ master_component = component
+ if not found_all_attendees:
+ removed_master = True
+
+ # Now remove the unwanted components - but we may need to exdate the master
+ exdates = []
+ for component in remove_components:
+ rid = component.getRecurrenceIDUTC()
+ if rid is not None:
+ exdates.append(rid)
+ self.removeComponent(component)
+
+ if not removed_master and master_component is not None:
+ for exdate in exdates:
+ master_component.addProperty(Property("EXDATE", (exdate,)))
+
##
# Dates and date-times
##
Modified: CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/itip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/itip.py 2008-07-19 00:34:18 UTC (rev 2733)
+++ CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/itip.py 2008-07-19 17:27:36 UTC (rev 2734)
@@ -45,11 +45,13 @@
from twistedcaldav import caldavxml
from twistedcaldav.accounting import accountingEnabled, emitAccounting
from twistedcaldav.log import Logger
-from twistedcaldav.ical import Property, iCalendarProductID
+from twistedcaldav.ical import Property, iCalendarProductID, Component
from twistedcaldav.method import report_common
from twistedcaldav.method.put_common import StoreCalendarObjectResource
from twistedcaldav.resource import isCalendarCollectionResource
+from vobject.icalendar import utc
+
log = Logger()
__version__ = "0.0"
@@ -896,3 +898,60 @@
return -1
return 0
+
+class iTipGenerator(object):
+
+ @staticmethod
+ def generateCancel(original, attendees, instances=None):
+
+ itip = Component("VCALENDAR")
+ itip.addProperty(Property("PRODID", iCalendarProductID))
+ itip.addProperty(Property("METHOD", "CANCEL"))
+
+ if instances is None:
+ instances = (None,)
+
+ for instance_rid in instances:
+
+ # Create a new component matching the type of the original
+ comp = Component(original.mainType())
+ itip.addComponent(comp)
+
+ # Use the master component when the instance is None
+ if not instance_rid:
+ instance = original.masterComponent()
+ else:
+ instance = original.overriddenComponent(instance_rid)
+ if instance is None:
+ instance = original.masterComponent()
+ assert instance is not None
+
+ # Add some required properties extracted from the original
+ comp.addProperty(Property("DTSTAMP", datetime.datetime.now(tz=utc)))
+ comp.addProperty(Property("UID", instance.propertyValue("UID")))
+ seq = instance.propertyValue("SEQUENCE")
+ seq = str(int(seq) + 1) if seq else "1"
+ comp.addProperty(Property("SEQUENCE", seq))
+ comp.addProperty(instance.getOrganizerProperty())
+ if instance_rid:
+ comp.addProperty(Property("RECURRENCE-ID", instance_rid))
+
+ # Extract the matching attendee property
+ for attendee in attendees:
+ attendeeProp = instance.getAttendeeProperty((attendee,))
+ assert attendeeProp is not None
+ comp.addProperty(attendeeProp)
+
+ return itip
+
+ @staticmethod
+ def generateAttendeeRequest(original, attendees):
+
+ # Start with a copy of the original as we may have to modify bits of it
+ itip = original.duplicate()
+ itip.addProperty(Property("METHOD", "REQUEST"))
+
+ # Now filter out components that do not contain every attendee
+ itip.attendeesView(attendees)
+
+ return itip
Modified: CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/test/test_icalendar.py 2008-07-19 00:34:18 UTC (rev 2733)
+++ CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/test/test_icalendar.py 2008-07-19 17:27:36 UTC (rev 2734)
@@ -16,10 +16,12 @@
import os
import datetime
+from dateutil.tz import tzutc
from twisted.trial.unittest import SkipTest
-from twistedcaldav.ical import *
+from twistedcaldav.ical import Component, parse_date, parse_datetime,\
+ parse_date_or_datetime, parse_duration
import twistedcaldav.test.util
from vobject.icalendar import utc
@@ -127,7 +129,7 @@
year = 2002
- instances = calendar.expandTimeRanges(datetime.date(2100, 0, 0))
+ instances = calendar.expandTimeRanges(datetime.date(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
@@ -148,7 +150,7 @@
"""
calendar = Component.fromStream(file(os.path.join(self.data_dir, "Holidays", "C318ABFE-1ED0-11D9-A5E0-000A958A3252.ics")))
- instances = calendar.expandTimeRanges(datetime.date(2100, 0, 0))
+ instances = calendar.expandTimeRanges(datetime.date(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
@@ -157,7 +159,7 @@
self.assertEqual(end, datetime.datetime(2004, 11, 27))
break;
- test_component_timerange.todo = "recurrance expansion should give us no end date here"
+ #test_component_timerange.todo = "recurrance expansion should give us no end date here"
def test_parse_date(self):
"""
@@ -238,3 +240,500 @@
component = Component.fromString(data)
self.assertEqual(component.getAttendeeProperties(("user3 at example.com",)), [])
+
+ def test_organizers_by_instance(self):
+
+ data = (
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ ()
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ (
+ ("mailto:user1 at example.com", None),
+ )
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+ORGANIZER:mailto:user2 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ ()
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ (
+ ("mailto:user1 at example.com", None),
+ ("mailto:user1 at example.com", datetime.datetime(2008, 11, 14, 0, 0, tzinfo=tzutc()))
+ )
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20091114T000000Z
+DTSTART:20071114T020000Z
+ORGANIZER:mailto:user3 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ (
+ ("mailto:user1 at example.com", None),
+ ("mailto:user3 at example.com", datetime.datetime(2009, 11, 14, 0, 0, tzinfo=tzutc()))
+ )
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20091114T000000Z
+DTSTART:20071114T020000Z
+ORGANIZER:mailto:user3 at example.com
+ORGANIZER:mailto:user4 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ (
+ ("mailto:user1 at example.com", None),
+ )
+ ),
+ )
+
+ for caldata, result in data:
+ component = Component.fromString(caldata)
+ self.assertEqual(component.getOrganizersByInstance(), result)
+
+ def test_attendees_by_instance(self):
+
+ data = (
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ ()
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ (
+ ("mailto:user2 at example.com", None),
+ )
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ATTENDEE:mailto:user3 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ (
+ ("mailto:user2 at example.com", None),
+ ("mailto:user3 at example.com", None),
+ )
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ATTENDEE:mailto:user3 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ (
+ ("mailto:user2 at example.com", None),
+ ("mailto:user2 at example.com", datetime.datetime(2008, 11, 14, 0, 0, tzinfo=tzutc())),
+ ("mailto:user3 at example.com", datetime.datetime(2008, 11, 14, 0, 0, tzinfo=tzutc()))
+ )
+ ),
+ )
+
+ for caldata, result in data:
+ component = Component.fromString(caldata)
+ self.assertEqual(component.getAttendeesByInstance(), result)
+
+ def test_attendees_views(self):
+
+ data = (
+ # Simple component, no Attendees - no filtering
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ ()
+ ),
+
+ # Simple component, no Attendees - filtering
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-2
+DTSTART:20071114T000000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ("mailto:user01 at example.com",)
+ ),
+
+ # Simple component, with one attendee - filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",)
+ ),
+
+ # Simple component, with one attendee - no filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ("mailto:user3 at example.com",)
+ ),
+
+ # Recurring component with one instance, each with one attendee - filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",)
+ ),
+
+ # Recurring component with one instance, each with one attendee - no filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ("mailto:user3 at example.com",)
+ ),
+
+ # Recurring component with one instance, master with one attendee, instance without attendee - filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+EXDATE:20081114T000000Z
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",)
+ ),
+
+ # Recurring component with one instance, master with one attendee, instance without attendee - no filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ("mailto:user3 at example.com",)
+ ),
+
+ # Recurring component with one instance, master without attendee, instance with attendee - filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",)
+ ),
+
+ # Recurring component with one instance, master without attendee, instance with attendee - no filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ("mailto:user3 at example.com",)
+ ),
+ )
+
+ for original, filtered, attendees in data:
+ component = Component.fromString(original)
+ component.attendeesView(attendees)
+ self.assertEqual(filtered, str(component).replace("\r", ""))
Added: CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/test/test_itip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/test/test_itip.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/test/test_itip.py 2008-07-19 17:27:36 UTC (rev 2734)
@@ -0,0 +1,515 @@
+##
+# Copyright (c) 2005-2007 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.
+##
+
+from twistedcaldav.ical import Component
+from twistedcaldav.itip import iTipGenerator
+import twistedcaldav.test.util
+from dateutil.tz import tzutc
+import datetime
+import os
+
+class iTIPGenerator (twistedcaldav.test.util.TestCase):
+ """
+ iCalendar support tests
+ """
+ data_dir = os.path.join(os.path.dirname(__file__), "data")
+
+ def test_request(self):
+
+ data = (
+ # Simple component, no Attendees - no filtering
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ ()
+ ),
+
+ # Simple component, no Attendees - filtering
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-2
+DTSTART:20071114T000000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ("mailto:user01 at example.com",)
+ ),
+
+ # Simple component, with one attendee - filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",)
+ ),
+
+ # Simple component, with one attendee - no filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ("mailto:user3 at example.com",)
+ ),
+
+ # Recurring component with one instance, each with one attendee - filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",)
+ ),
+
+ # Recurring component with one instance, each with one attendee - no filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ("mailto:user3 at example.com",)
+ ),
+
+ # Recurring component with one instance, master with one attendee, instance without attendee - filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+EXDATE:20081114T000000Z
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",)
+ ),
+
+ # Recurring component with one instance, master with one attendee, instance without attendee - no filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ("mailto:user3 at example.com",)
+ ),
+
+ # Recurring component with one instance, master without attendee, instance with attendee - filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",)
+ ),
+
+ # Recurring component with one instance, master without attendee, instance with attendee - no filtering match
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+DTSTART:20071114T000000Z
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ("mailto:user3 at example.com",)
+ ),
+ )
+
+ for original, filtered, attendees in data:
+ component = Component.fromString(original)
+ itipped = iTipGenerator.generateAttendeeRequest(component, attendees)
+ self.assertEqual(filtered, str(itipped).replace("\r", ""))
+
+ def test_cancel(self):
+
+ data = (
+ # Simple component, with two attendees - cancel one
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ATTENDEE:mailto:user3 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+SEQUENCE:1
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",),
+ (None,),
+ ),
+
+ # Simple component, with two attendees - cancel two
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-2
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ATTENDEE:mailto:user3 at example.com
+ORGANIZER:mailto:user1 at example.com
+SEQUENCE:1
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-2
+ATTENDEE:mailto:user3 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+SEQUENCE:2
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user3 at example.com", "mailto:user2 at example.com",),
+ (None,)
+ ),
+
+ # Recurring component with no instance, one attendee - cancel instance
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+RECURRENCE-ID:20081114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+SEQUENCE:1
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",),
+ (datetime.datetime(2008, 11, 14, 0, 0, tzinfo=tzutc()), ),
+ ),
+
+ # Recurring component with one instance, each with one attendee - cancel instance
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-4
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+RECURRENCE-ID:20081114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+SEQUENCE:1
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",),
+ (datetime.datetime(2008, 11, 14, 0, 0, tzinfo=tzutc()), ),
+ ),
+
+ # Recurring component with one instance, each with one attendee - cancel master
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-5
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-5
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20071114T010000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-5
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+SEQUENCE:1
+END:VEVENT
+END:VCALENDAR
+""",
+ ("mailto:user2 at example.com",),
+ (None, ),
+ ),
+ )
+
+ for original, filtered, attendees, instances in data:
+ component = Component.fromString(original)
+ itipped = iTipGenerator.generateCancel(component, attendees, instances)
+ itipped = str(itipped).replace("\r", "")
+ itipped = "".join([line for line in itipped.splitlines(True) if not line.startswith("DTSTAMP:")])
+ self.assertEqual(filtered, itipped)
Property changes on: CalendarServer/branches/users/cdaboo/implicit-2660/twistedcaldav/test/test_itip.py
___________________________________________________________________
Name: svn:executable
+ *
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080719/f6ec7a11/attachment-0001.html
More information about the calendarserver-changes
mailing list