[CalendarServer-changes] [5387] CalendarServer/branches/users/cdaboo/shared-calendars-5187/ twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Tue Mar 23 15:46:31 PDT 2010
Revision: 5387
http://trac.macosforge.org/projects/calendarserver/changeset/5387
Author: cdaboo at apple.com
Date: 2010-03-23 15:46:30 -0700 (Tue, 23 Mar 2010)
Log Message:
-----------
Per-user calendar data support. This includes per-user freebusy in the index (with a major
revision to the schema there).
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/datafilters/peruserdata.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/datafilters/test/test_peruserdata.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/ical.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/icaldav.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/index.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/delete_common.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/get.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/put_common.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/report_calquery.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/report_common.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/query/sqlgenerator.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/implicit.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/itip.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/processing.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/utils.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sql.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_icalendar.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_index.py
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/datafilters/peruserdata.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/datafilters/peruserdata.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/datafilters/peruserdata.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -59,6 +59,7 @@
"""
# If any of these change also change the vobject behaviors in this module's __init__.py
+ # and update usage in ical.py
PERUSER_COMPONENT = "X-CALENDARSERVER-PERUSER"
PERUSER_UID = "X-CALENDARSERVER-PERUSER-UID"
PERINSTANCE_COMPONENT = "X-CALENDARSERVER-PERINSTANCE"
@@ -206,22 +207,21 @@
def _splitPerUserData(self, ical):
- peruser_component = None
- perinstance_components = {}
-
def init_peruser_component():
peruser = Component(PerUserDataFilter.PERUSER_COMPONENT)
peruser.addProperty(Property("UID", ical.resourceUID()))
peruser.addProperty(Property(PerUserDataFilter.PERUSER_UID, self.uid))
ical.addComponent(peruser)
return peruser
-
- for component in ical.subcomponents():
+
+ components = tuple(ical.subcomponents())
+ peruser_component = init_peruser_component() if self.uid else None
+ perinstance_components = {}
+
+ for component in components:
if component.name() == "VTIMEZONE":
continue
- perinstance_component = None
-
def init_perinstance_component():
peruser = Component(PerUserDataFilter.PERINSTANCE_COMPONENT)
rid = component.getRecurrenceIDUTC()
@@ -230,41 +230,38 @@
perinstance_components[rid] = peruser
return peruser
+ perinstance_component = init_perinstance_component() if self.uid else None
+
# Transfer per-user properties from main component to per-instance component
for property in tuple(component.properties()):
if property.name() in PerUserDataFilter.PERUSER_PROPERTIES or property.name().startswith("X-"):
- if peruser_component is None:
- peruser_component = init_peruser_component()
- if perinstance_component is None:
- perinstance_component = init_perinstance_component()
- perinstance_component.addProperty(property)
+ if self.uid:
+ perinstance_component.addProperty(property)
component.removeProperty(property)
# Transfer per-user components from main component to per-instance component
for subcomponent in tuple(component.subcomponents()):
if subcomponent.name() in PerUserDataFilter.PERUSER_SUBCOMPONENTS or subcomponent.name().startswith("X-"):
- if peruser_component is None:
- peruser_component = init_peruser_component()
- if perinstance_component is None:
- perinstance_component = init_perinstance_component()
- perinstance_component.addComponent(subcomponent)
+ if self.uid:
+ perinstance_component.addComponent(subcomponent)
component.removeComponent(subcomponent)
- # Add unique per-instance components into the per-user component
- master_perinstance = perinstance_components.get(None)
- master_perinstance_txt = str(master_perinstance)
- if master_perinstance:
- peruser_component.addComponent(master_perinstance)
- for rid, perinstance in perinstance_components.iteritems():
- if rid is None:
- continue
- perinstance_txt = str(perinstance)
- perinstance_txt = "".join([line for line in perinstance_txt.splitlines(True) if not line.startswith("RECURRENCE-ID:")])
- if master_perinstance is None or perinstance_txt != master_perinstance_txt:
- peruser_component.addComponent(perinstance)
+ if self.uid:
+ # Add unique per-instance components into the per-user component
+ master_perinstance = perinstance_components.get(None)
+ master_perinstance_txt = str(master_perinstance)
+ if master_perinstance:
+ peruser_component.addComponent(master_perinstance)
+ for rid, perinstance in perinstance_components.iteritems():
+ if rid is None:
+ continue
+ perinstance_txt = str(perinstance)
+ perinstance_txt = "".join([line for line in perinstance_txt.splitlines(True) if not line.startswith("RECURRENCE-ID:")])
+ if master_perinstance is None or perinstance_txt != master_perinstance_txt:
+ peruser_component.addComponent(perinstance)
+
+ self._compactInstances(ical)
- self._compactInstances(ical)
-
def _compactInstances(self, ical):
"""
Remove recurrences instances that are the same as their master-derived counterparts. This gives the most
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/datafilters/test/test_peruserdata.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/datafilters/test/test_peruserdata.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/datafilters/test/test_peruserdata.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -38,6 +38,8 @@
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user01").filter(item)), data)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").filter(item)), data)
def test_public_oneuser(self):
@@ -103,6 +105,8 @@
self.assertEqual(str(PerUserDataFilter("user01").filter(item)), result01)
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user02").filter(item)), result02)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").filter(item)), result02)
def test_public_twousers(self):
@@ -201,6 +205,8 @@
self.assertEqual(str(PerUserDataFilter("user02").filter(item)), result02)
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user03").filter(item)), result03)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").filter(item)), result03)
class PerUserDataFilterTestRecurring (twistedcaldav.test.util.TestCase):
@@ -232,6 +238,8 @@
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user01").filter(item)), data)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").filter(item)), data)
def test_public_oneuser_master(self):
@@ -333,6 +341,8 @@
self.assertEqual(str(PerUserDataFilter("user01").filter(item)), result01)
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user02").filter(item)), result02)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").filter(item)), result02)
def test_public_oneuser_master_and_override(self):
@@ -443,6 +453,8 @@
self.assertEqual(str(PerUserDataFilter("user01").filter(item)), result01)
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user02").filter(item)), result02)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").filter(item)), result02)
def test_public_oneuser_override(self):
@@ -539,6 +551,8 @@
self.assertEqual(str(PerUserDataFilter("user01").filter(item)), result01)
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user02").filter(item)), result02)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").filter(item)), result02)
def test_public_oneuser_master_derived_override(self):
@@ -631,6 +645,8 @@
self.assertEqual(str(PerUserDataFilter("user01").filter(item)), result01)
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user02").filter(item)), result02)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").filter(item)), result02)
def test_public_oneuser_master_derived_override_x2(self):
@@ -760,11 +776,27 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ result03 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user01").filter(item)), result01)
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user02").filter(item)), result02)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").filter(item)), result03)
def test_public_oneuser_no_master_and_override(self):
@@ -834,6 +866,8 @@
self.assertEqual(str(PerUserDataFilter("user01").filter(item)), result01)
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user02").filter(item)), result02)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").filter(item)), result02)
class PerUserDataMergeTestNewNotRecurring (twistedcaldav.test.util.TestCase):
@@ -852,9 +886,30 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ result01 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
for item in (data, Component.fromString(data),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(item, None)), data)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(item, None)), result01)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").merge(item, None)), data)
def test_public_oneuser(self):
@@ -902,9 +957,24 @@
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""".replace("\n", "\r\n")
+ result02 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user01").merge(item, None)), result01)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").merge(item, None)), result02)
class PerUserDataMergeTestNewRecurring (twistedcaldav.test.util.TestCase):
@@ -933,9 +1003,40 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ result01 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
for item in (data, Component.fromString(data),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(item, None)), data)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(item, None)), result01)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").merge(item, None)), data)
def test_public_oneuser_master(self):
@@ -1009,9 +1110,34 @@
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""".replace("\n", "\r\n")
+ result02 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user01").merge(item, None)), result01)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").merge(item, None)), result02)
def test_public_oneuser_master_and_override(self):
@@ -1094,9 +1220,34 @@
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""".replace("\n", "\r\n")
+ result02 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user01").merge(item, None)), result01)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").merge(item, None)), result02)
def test_public_oneuser_override(self):
@@ -1154,6 +1305,8 @@
UID:12345-67890
X-CALENDARSERVER-PERUSER-UID:user01
BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
RECURRENCE-ID:20080602T120000Z
TRANSP:TRANSPARENT
BEGIN:VALARM
@@ -1165,9 +1318,34 @@
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""".replace("\n", "\r\n")
+ result02 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user01").merge(item, None)), result01)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").merge(item, None)), result02)
def test_public_oneuser_master_compact_override(self):
@@ -1241,9 +1419,34 @@
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""".replace("\n", "\r\n")
+ result02 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T120000Z
+DTEND:20080602T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user01").merge(item, None)), result01)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").merge(item, None)), result02)
def test_public_oneuser_master_noncompact_override(self):
@@ -1326,9 +1529,34 @@
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""".replace("\n", "\r\n")
+ result02 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("user01").merge(item, None)), result01)
+ for item in (data, Component.fromString(data),):
+ self.assertEqual(str(PerUserDataFilter("").merge(item, None)), result02)
class PerUserDataMergeTestExistingNotRecurring (twistedcaldav.test.util.TestCase):
@@ -1347,6 +1575,25 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ newresult = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
olddata = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -1363,7 +1610,7 @@
for olditem in (olddata, Component.fromString(olddata),):
for newitem in (newdata, Component.fromString(newdata),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newdata)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newresult)
def test_public_oneuser(self):
@@ -1606,6 +1853,12 @@
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
TRANSP:OPAQUE
@@ -1641,6 +1894,26 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ newresult = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
olddata = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -1657,7 +1930,7 @@
for olditem in (olddata, Component.fromString(olddata),):
for newitem in (newdata, Component.fromString(newdata),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newdata)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newresult)
def test_public_noperuser_master_with_override(self):
@@ -1684,6 +1957,35 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ newresult = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
olddata = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -1700,7 +2002,7 @@
for olditem in (olddata, Component.fromString(olddata),):
for newitem in (newdata, Component.fromString(newdata),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newdata)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newresult)
def test_public_noperuser_only_override(self):
@@ -1718,6 +2020,27 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ newresult = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080602T120000Z
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
olddata = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -1734,7 +2057,7 @@
for olditem in (olddata, Component.fromString(olddata),):
for newitem in (newdata, Component.fromString(newdata),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newdata)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newresult)
def test_public_oneuser_master(self):
@@ -2401,6 +2724,12 @@
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
TRANSP:OPAQUE
@@ -2503,6 +2832,12 @@
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
TRANSP:OPAQUE
@@ -2586,6 +2921,13 @@
ATTENDEE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080602T120000Z
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""".replace("\n", "\r\n")
@@ -2611,6 +2953,26 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ newresult = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
olddata = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -2628,7 +2990,7 @@
for olditem in (olddata, Component.fromString(olddata),):
for newitem in (newdata, Component.fromString(newdata),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newdata)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newresult)
def test_public_noperuser_master_with_override(self):
@@ -2646,6 +3008,26 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ newresult = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
olddata = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -2672,7 +3054,7 @@
for olditem in (olddata, Component.fromString(olddata),):
for newitem in (newdata, Component.fromString(newdata),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newdata)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newresult)
def test_public_noperuser_only_override(self):
@@ -2689,6 +3071,25 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ newresult = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T110000Z
+DTEND:20080601T120000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
olddata = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -2706,7 +3107,7 @@
for olditem in (olddata, Component.fromString(olddata),):
for newitem in (newdata, Component.fromString(newdata),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newdata)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newresult)
def test_public_oneuser_master(self):
@@ -3336,6 +3737,12 @@
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
TRANSP:OPAQUE
@@ -3438,6 +3845,12 @@
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
TRANSP:OPAQUE
@@ -3522,6 +3935,12 @@
ATTENDEE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""".replace("\n", "\r\n")
@@ -3547,6 +3966,26 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ newresult = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
olddata = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -3564,7 +4003,7 @@
for olditem in (olddata, Component.fromString(olddata),):
for newitem in (newdata, Component.fromString(newdata),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newdata)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newresult)
def test_public_oneuser(self):
@@ -3816,6 +4255,12 @@
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
TRANSP:OPAQUE
@@ -3910,6 +4355,12 @@
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
TRANSP:OPAQUE
@@ -3954,6 +4405,35 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ newresult = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
olddata = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -3980,7 +4460,7 @@
for olditem in (olddata, Component.fromString(olddata),):
for newitem in (newdata, Component.fromString(newdata),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newdata)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newresult)
def test_public_oneuser(self):
@@ -4388,6 +4868,12 @@
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
TRANSP:OPAQUE
@@ -4423,6 +4909,27 @@
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
+ newresult = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080602T120000Z
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
olddata = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -4440,7 +4947,7 @@
for olditem in (olddata, Component.fromString(olddata),):
for newitem in (newdata, Component.fromString(newdata),):
- self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newdata)
+ self.assertEqual(str(PerUserDataFilter("user01").merge(newitem, olditem)), newresult)
def test_public_oneuser(self):
@@ -4685,6 +5192,13 @@
ATTENDEE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080602T120000Z
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""".replace("\n", "\r\n")
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/ical.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/ical.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -131,6 +131,8 @@
"ORGANIZER": normalizeCUAddr,
}
+ignoredComponents = ("VTIMEZONE", "X-CALENDARSERVER-PERUSER",)
+
class InvalidICalendarDataError(ValueError):
pass
@@ -423,7 +425,7 @@
mtype = None
for component in self.subcomponents():
- if component.name() == "VTIMEZONE" or component.name().startswith("X-"):
+ if component.name() in ignoredComponents:
continue
elif mtype and (mtype != component.name()):
raise InvalidICalendarDataError("Component contains more than one type of primary type: %r" % (self,))
@@ -442,7 +444,7 @@
result = None
for component in self.subcomponents():
- if component.name() == "VTIMEZONE" or component.name().startswith("X-"):
+ if component.name() in ignoredComponents:
continue
elif not allow_multiple and (result is not None):
raise InvalidICalendarDataError("Calendar contains more than one primary component: %r" % (self,))
@@ -462,7 +464,7 @@
assert self.name() == "VCALENDAR", "Must be a VCALENDAR: %r" % (self,)
for component in self.subcomponents():
- if component.name() == "VTIMEZONE" or component.name().startswith("X-"):
+ if component.name() in ignoredComponents:
continue
if not component.hasProperty("RECURRENCE-ID"):
return component
@@ -481,7 +483,7 @@
assert self.name() == "VCALENDAR", "Must be a VCALENDAR: %r" % (self,)
for component in self.subcomponents():
- if component.name() == "VTIMEZONE" or component.name().startswith("X-"):
+ if component.name() in ignoredComponents:
continue
rid = component.getRecurrenceIDUTC()
if rid and recurrence_id and dateordatetime(rid) == recurrence_id:
@@ -758,10 +760,6 @@
if self.name() not in ("VEVENT", ):
return "FREE"
- # If it is TRANSPARENT we always ignore it
- if self.propertyValue("TRANSP") == "TRANSPARENT":
- return "FREE"
-
# Handle status
status = self.propertyValue("STATUS")
if status == "CANCELLED":
@@ -1023,7 +1021,7 @@
if self.name() == "VCALENDAR":
result = ()
for component in self.subcomponents():
- if component.name() != "VTIMEZONE" and not component.name().startswith("X-"):
+ if component.name() not in ignoredComponents:
result += component.getComponentInstances()
return result
else:
@@ -1038,7 +1036,7 @@
# Extract appropriate sub-component if this is a VCALENDAR
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() != "VTIMEZONE" and not component.name().startswith("X-") and component.isRecurring():
+ if component.name() not in ignoredComponents and component.isRecurring():
return True
else:
for propname in ("RRULE", "RDATE", "EXDATE", "RECURRENCE-ID",):
@@ -1211,7 +1209,7 @@
if not hasattr(self, "_resource_uid"):
for subcomponent in self.subcomponents():
- if subcomponent.name() != "VTIMEZONE" and not subcomponent.name().startswith("X-"):
+ if subcomponent.name() not in ignoredComponents:
self._resource_uid = subcomponent.propertyValue("UID")
break
else:
@@ -1233,7 +1231,7 @@
name = subcomponent.name()
if name == "VTIMEZONE":
has_timezone = True
- elif subcomponent.name().startswith("X-"):
+ elif subcomponent.name() in ignoredComponents:
continue
else:
self._resource_type = name
@@ -1306,6 +1304,8 @@
if subcomponent.name() == "VTIMEZONE":
timezones.add(subcomponent.propertyValue("TZID"))
+ elif subcomponent.name() in ignoredComponents:
+ continue
else:
if ctype is None:
ctype = subcomponent.name()
@@ -1524,7 +1524,7 @@
# Extract appropriate sub-component if this is a VCALENDAR
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() != "VTIMEZONE":
+ if component.name() not in ignoredComponents:
return component.getOrganizer()
else:
try:
@@ -1546,7 +1546,7 @@
if self.name() == "VCALENDAR":
result = ()
for component in self.subcomponents():
- if component.name() != "VTIMEZONE":
+ if component.name() not in ignoredComponents:
result += component.getOrganizersByInstance()
return result
else:
@@ -1570,7 +1570,7 @@
# Extract appropriate sub-component if this is a VCALENDAR
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() != "VTIMEZONE":
+ if component.name() not in ignoredComponents:
return component.getOrganizerProperty()
else:
try:
@@ -1604,7 +1604,7 @@
# Extract appropriate sub-component if this is a VCALENDAR
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() != "VTIMEZONE":
+ if component.name() not in ignoredComponents:
return component.getAttendees()
else:
# Find the property values
@@ -1627,7 +1627,7 @@
if self.name() == "VCALENDAR":
result = ()
for component in self.subcomponents():
- if component.name() != "VTIMEZONE":
+ if component.name() not in ignoredComponents:
result += component.getAttendeesByInstance(makeUnique, onlyScheduleAgentServer)
return result
else:
@@ -1665,7 +1665,7 @@
# Extract appropriate sub-component if this is a VCALENDAR
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() != "VTIMEZONE":
+ if component.name() not in ignoredComponents:
attendee = component.getAttendeeProperty(match)
if attendee is not None:
return attendee
@@ -1690,7 +1690,7 @@
# Extract appropriate sub-component if this is a VCALENDAR
results = []
for component in self.subcomponents():
- if component.name() != "VTIMEZONE":
+ if component.name() not in ignoredComponents:
attendee = component.getAttendeeProperty(match)
if attendee:
results.append(attendee)
@@ -1707,7 +1707,7 @@
# Extract appropriate sub-component if this is a VCALENDAR
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() != "VTIMEZONE":
+ if component.name() not in ignoredComponents:
for attendee in component.getAllAttendeeProperties():
yield attendee
else:
@@ -1726,7 +1726,7 @@
# Extract appropriate sub-component if this is a VCALENDAR
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() != "VTIMEZONE":
+ if component.name() not in ignoredComponents:
return component.getMaskUID()
else:
try:
@@ -1752,7 +1752,7 @@
"""
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
for property in component.properties(propname):
if propvalue is None or property.value() == propvalue:
@@ -1785,7 +1785,7 @@
"""
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
found = component.getProperty(property.name())
if not found or found.value() != property.value():
@@ -1802,7 +1802,7 @@
"""
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
component.addProperty(property)
@@ -1813,7 +1813,7 @@
"""
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
component.replaceProperty(property)
@@ -1832,7 +1832,7 @@
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
component.transferProperties(from_calendar, properties)
else:
@@ -1862,7 +1862,7 @@
master_component = None
removed_master = False
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
found_all_attendees = True
for attendee in attendees:
@@ -1906,7 +1906,7 @@
components = tuple(self.subcomponents())
remaining = len(components)
for component in components:
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
remaining -= 1
continue
rid = component.getRecurrenceIDUTC()
@@ -1924,7 +1924,7 @@
assert self.name() == "VCALENDAR", "Not a calendar: %r" % (self,)
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
[component.removeProperty(p) for p in tuple(component.properties("ATTENDEE")) if p.value().lower() != attendee.lower()]
@@ -1935,7 +1935,7 @@
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
component.removeAlarms()
else:
@@ -1952,13 +1952,22 @@
for component in self.subcomponents():
component.filterProperties(remove, keep, do_subcomponents=False)
else:
- if self.name() == "VTIMEZONE":
+ if self.name() in ignoredComponents:
return
for p in tuple(self.properties()):
if (keep and p.name() not in keep) or (remove and p.name() in remove):
self.removeProperty(p)
+ def removeXComponents(self, keep_components=()):
+ """
+ Remove all X- properties except the specified ones
+ """
+
+ for component in tuple(self.subcomponents()):
+ if component.name().startswith("X-") and component.name() not in keep_components:
+ self.removeComponent(component)
+
def removeXProperties(self, keep_properties=(), remove_x_parameters=True, do_subcomponents=True):
"""
Remove all X- properties except the specified ones
@@ -1968,7 +1977,7 @@
for component in self.subcomponents():
component.removeXProperties(keep_properties, remove_x_parameters, do_subcomponents=False)
else:
- if self.name() == "VTIMEZONE":
+ if self.name() in ignoredComponents:
return
for p in tuple(self.properties()):
xpname = p.name().startswith("X-")
@@ -1986,7 +1995,7 @@
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
component.removePropertyParameters(property, params)
else:
@@ -2005,7 +2014,7 @@
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
component.removePropertyParametersByValue(property, paramvalues)
else:
@@ -2126,7 +2135,7 @@
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
component.normalizePropertyValueLists(propname)
else:
@@ -2143,7 +2152,7 @@
if self.name() == "VCALENDAR":
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
component.normalizeAttachments()
else:
@@ -2165,7 +2174,7 @@
@type lookupFunction: L{Function}
"""
for component in self.subcomponents():
- if component.name() == "VTIMEZONE":
+ if component.name() in ignoredComponents:
continue
for prop in itertools.chain(
component.properties("ORGANIZER"),
@@ -2250,7 +2259,49 @@
except KeyError:
pass
+
+ def allPerUserUIDs(self):
+ results = set()
+ for component in self.subcomponents():
+ if component.name() == "X-CALENDARSERVER-PERUSER":
+ results.add(component.propertyValue("X-CALENDARSERVER-PERUSER-UID"))
+ return results
+
+ def perUserTransparency(self, rid):
+
+ # We will create a cache of all user/rid/transparency values as we will likely
+ # be calling this a lot
+ if not hasattr(self, "_perUserTransparency"):
+ self._perUserTransparency = {}
+
+ # Do per-user data
+ for component in self.subcomponents():
+ if component.name() == "X-CALENDARSERVER-PERUSER":
+ uid = component.propertyValue("X-CALENDARSERVER-PERUSER-UID")
+ for subcomponent in component.subcomponents():
+ if subcomponent.name() == "X-CALENDARSERVER-PERINSTANCE":
+ instancerid = subcomponent.propertyValue("RECURRENCE-ID")
+ transp = subcomponent.propertyValue("TRANSP") == "TRANSPARENT"
+ self._perUserTransparency.setdefault(uid, {})[instancerid] = transp
+ elif component.name() not in ignoredComponents:
+ instancerid = component.propertyValue("RECURRENCE-ID")
+ transp = component.propertyValue("TRANSP") == "TRANSPARENT"
+ self._perUserTransparency.setdefault("", {})[instancerid] = transp
+
+ # Now lookup in cache
+ results = []
+ for uid, cachedRids in sorted(self._perUserTransparency.items(), key=lambda x:x[0]):
+ lookupRid = rid
+ if lookupRid not in cachedRids:
+ lookupRid = None
+ if lookupRid in cachedRids:
+ results.append((uid, cachedRids[lookupRid],))
+ else:
+ results.append((uid, False,))
+
+ return tuple(results)
+
##
# Dates and date-times
##
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/icaldav.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/icaldav.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/icaldav.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -94,20 +94,6 @@
of type C{"VCALENDAR"}.
"""
- def iCalendarXML(name=None):
- """
- Constructs a CalDAV XML element representing this resource or its child
- with the given name.
- The behavior of this method is not specified if it is called on a
- resource that is not a calendar collection or a calendar resource within
- a calendar collection.
- @param name: the name of the desired child of this resource, or None
- if this resource is desired. Must be None if this resource is
- not a calendar collection.
- @return: a L{twistedcaldav.caldavxml.CalendarData} containing the
- iCalendar data for the requested resource.
- """
-
class ICalendarPrincipalResource(IDAVResource):
"""
CalDAV principle resource.
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/index.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/index.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/index.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -57,7 +57,7 @@
log = Logger()
db_basename = db_prefix + "sqlite"
-schema_version = "9"
+schema_version = "10"
collection_types = {"Calendar": "Regular Calendar Collection", "iTIP": "iTIP Calendar Collection"}
icalfbtype_to_indexfbtype = {
@@ -297,7 +297,7 @@
return changed, deleted,
- def indexedSearch(self, filter, fbtype=False):
+ def indexedSearch(self, filter, useruid="", fbtype=False):
"""
Finds resources matching the given qualifiers.
@param filter: the L{Filter} for the calendar-query to execute.
@@ -334,10 +334,25 @@
rowiter = self._db_execute("select NAME, UID, TYPE from RESOURCE")
else:
if fbtype:
+ # Lookup the useruid - try the empty (default) one if needed
+ dbuseruid = self._db_value_for_sql(
+ "select PERUSERID from PERUSER where USERUID == :1",
+ useruid,
+ )
+ count = self._db_value_for_sql(
+ "select COUNT(PERUSERID) from TRANSPARENCY where PERUSERID == :1",
+ dbuseruid,
+ )
+ if dbuseruid is None or count == 0:
+ dbuseruid = self._db_value_for_sql(
+ "select PERUSERID from PERUSER where USERUID == :1",
+ "",
+ )
+
# For a free-busy time-range query we return all instances
rowiter = self._db_execute(
- "select RESOURCE.NAME, RESOURCE.UID, RESOURCE.TYPE, RESOURCE.ORGANIZER, TIMESPAN.FLOAT, TIMESPAN.START, TIMESPAN.END, TIMESPAN.FBTYPE" +
- qualifiers[0],
+ "select RESOURCE.NAME, RESOURCE.UID, RESOURCE.TYPE, RESOURCE.ORGANIZER, TIMESPAN.FLOAT, TIMESPAN.START, TIMESPAN.END, TIMESPAN.FBTYPE, TRANSPARENCY.TRANSPARENT" +
+ qualifiers[0] + " AND TRANSPARENCY.PERUSERID == '%s'" % (dbuseruid,),
*qualifiers[1]
)
else:
@@ -429,6 +444,7 @@
q.execute(
"""
create table RESOURCE (
+ RESOURCEID integer primary key autoincrement,
NAME text unique,
UID text%s,
TYPE text,
@@ -454,11 +470,12 @@
q.execute(
"""
create table TIMESPAN (
- NAME text,
- FLOAT text(1),
- START date,
- END date,
- FBTYPE text(1)
+ INSTANCEID integer primary key autoincrement,
+ RESOURCEID integer,
+ FLOAT text(1),
+ START date,
+ END date,
+ FBTYPE text(1)
)
"""
)
@@ -469,6 +486,41 @@
)
#
+ # PERUSER table tracks per-user ids
+ # PERUSERID: autoincrement primary key
+ # UID: User ID used in calendar data
+ #
+ q.execute(
+ """
+ create table PERUSER (
+ PERUSERID integer primary key autoincrement,
+ USERUID text
+ )
+ """
+ )
+ q.execute(
+ """
+ create index PERUSER_UID on PERUSER (USERUID)
+ """
+ )
+
+ #
+ # TRANSPARENCY table tracks per-user per-instance transparency
+ # PERUSERID: user id key
+ # INSTANCEID: instance id key
+ # TRANSPARENT: Y if transparent, N if opaque
+ #
+ q.execute(
+ """
+ create table TRANSPARENCY (
+ PERUSERID integer,
+ INSTANCEID integer,
+ TRANSPARENT text(1)
+ )
+ """
+ )
+
+ #
# REVISIONS table tracks changes
# NAME: Last URI component (eg. <uid>.ics, RESOURCE primary key)
# REVISION: revision number
@@ -505,42 +557,42 @@
"""
)
+ # Cascading triggers to help on delete
+ q.execute(
+ """
+ create trigger resourceDelete after delete on RESOURCE
+ for each row
+ begin
+ delete from TIMESPAN where TIMESPAN.RESOURCEID = OLD.RESOURCEID;
+ end
+ """
+ )
+ q.execute(
+ """
+ create trigger timespanDelete after delete on TIMESPAN
+ for each row
+ begin
+ delete from TRANSPARENCY where INSTANCEID = OLD.INSTANCEID;
+ end
+ """
+ )
+
def _db_can_upgrade(self, old_version):
"""
Can we do an in-place upgrade
"""
- # Previous versions can be upgraded as per _db_upgrade_data_tables
- return True
+ # v10 is a big change - no upgrade possible
+ return False
def _db_upgrade_data_tables(self, q, old_version):
"""
Upgrade the data from an older version of the DB.
"""
- # When going to version 7+ all we need to do is add a column to the resource and timespan
- if old_version < "7":
- q.execute("alter table RESOURCE add column ORGANIZER text default '?'")
- q.execute("alter table TIMESPAN add column FBTYPE text(1) default '?'")
+ # v10 is a big change - no upgrade possible
+ pass
- # When going to version 8+ all we need to do is add an index
- if old_version < "8":
- q.execute("create index STARTENDFLOAT on TIMESPAN (START, END, FLOAT)")
-
- # When going to version 9+ all we need to do is add revision table and index
- if old_version < "9":
- q.execute(
- """
- create table REVISIONS (
- NAME text unique,
- REVISION integer,
- CREATEDREVISION integer,
- WASDELETED text(1)
- )
- """
- )
- q.execute("create index REVISION on REVISIONS (REVISION)")
-
def notExpandedBeyond(self, minDate):
"""
Gives all resources which have not been expanded beyond a given date
@@ -597,6 +649,35 @@
self._delete_from_db(name, uid, None)
+ # Add RESOURCE item
+ self._db_execute(
+ """
+ insert into RESOURCE (NAME, UID, TYPE, RECURRANCE_MAX, ORGANIZER)
+ values (:1, :2, :3, :4, :5)
+ """, name, uid, calendar.resourceType(), instances.limit, organizer
+ )
+ resourceid = self.lastrowid
+
+ # Get a set of all referenced per-user UIDs and map those to entries already
+ # in the DB and add new ones as needed
+ useruids = calendar.allPerUserUIDs()
+ useruids.add("")
+ useruidmap = {}
+ for useruid in useruids:
+ peruserid = self._db_value_for_sql(
+ "select PERUSERID from PERUSER where USERUID = :1",
+ useruid
+ )
+ if peruserid is None:
+ self._db_execute(
+ """
+ insert into PERUSER (USERUID)
+ values (:1)
+ """, useruid
+ )
+ peruserid = self.lastrowid
+ useruidmap[useruid] = peruserid
+
for key in instances:
instance = instances[key]
start = instance.start.replace(tzinfo=utc)
@@ -604,10 +685,21 @@
float = 'Y' if instance.start.tzinfo is None else 'N'
self._db_execute(
"""
- insert into TIMESPAN (NAME, FLOAT, START, END, FBTYPE)
+ insert into TIMESPAN (RESOURCEID, FLOAT, START, END, FBTYPE)
values (:1, :2, :3, :4, :5)
- """, name, float, start, end, icalfbtype_to_indexfbtype.get(instance.component.getFBType(), 'F')
+ """, resourceid, float, start, end, icalfbtype_to_indexfbtype.get(instance.component.getFBType(), 'F')
)
+ instanceid = self.lastrowid
+ peruserdata = calendar.perUserTransparency(instance.rid)
+ for useruid, transp in peruserdata:
+ peruserid = useruidmap[useruid]
+ self._db_execute(
+ """
+ insert into TRANSPARENCY (PERUSERID, INSTANCEID, TRANSPARENT)
+ values (:1, :2, :3)
+ """, peruserid, instanceid, 'T' if transp else 'F'
+ )
+
# Special - for unbounded recurrence we insert a value for "infinity"
# that will allow an open-ended time-range to always match it.
@@ -617,18 +709,21 @@
float = 'N'
self._db_execute(
"""
- insert into TIMESPAN (NAME, FLOAT, START, END, FBTYPE)
+ insert into TIMESPAN (RESOURCEID, FLOAT, START, END, FBTYPE)
values (:1, :2, :3, :4, :5)
- """, name, float, start, end, '?'
+ """, resourceid, float, start, end, '?'
)
+ instanceid = self.lastrowid
+ peruserdata = calendar.perUserTransparency(None)
+ for useruid, transp in peruserdata:
+ peruserid = useruidmap[useruid]
+ self._db_execute(
+ """
+ insert into TRANSPARENCY (PERUSERID, INSTANCEID, TRANSPARENT)
+ values (:1, :2, :3)
+ """, peruserid, instanceid, 'T' if transp else 'F'
+ )
- self._db_execute(
- """
- insert into RESOURCE (NAME, UID, TYPE, RECURRANCE_MAX, ORGANIZER)
- values (:1, :2, :3, :4, :5)
- """, name, uid, calendar.resourceType(), instances.limit, organizer
- )
-
if revision is not None:
created = self._db_value_for_sql("select CREATEDREVISION from REVISIONS where NAME = :1", name)
if created is None:
@@ -646,7 +741,6 @@
@param name: the name of the resource to delete.
@param uid: the uid of the resource to delete.
"""
- self._db_execute("delete from TIMESPAN where NAME = :1", name)
self._db_execute("delete from RESOURCE where NAME = :1", name)
if revision is not None:
self._db_execute(
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/delete_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/delete_common.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/delete_common.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -143,7 +143,7 @@
lock = None
if not self.internal_request and self.allowImplicitSchedule:
# Get data we need for implicit scheduling
- calendar = delresource.iCalendar()
+ calendar = (yield delresource.iCalendarForUser(self.request))
scheduler = ImplicitScheduler()
do_implicit_action, _ignore = (yield scheduler.testImplicitSchedulingDELETE(self.request, delresource, calendar))
if do_implicit_action:
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/get.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/get.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/get.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -22,6 +22,7 @@
from twisted.internet.defer import inlineCallbacks, returnValue
from twext.web2.dav import davxml
+from twext.web2.dav.util import parentForURL
from twext.web2.http import HTTPError
from twext.web2.http import Response
from twext.web2.http_headers import MimeType
@@ -30,41 +31,47 @@
from twistedcaldav.caldavxml import ScheduleTag
from twistedcaldav.customxml import TwistedCalendarAccessProperty
from twistedcaldav.datafilters.privateevents import PrivateEventFilter
+from twistedcaldav.resource import isPseudoCalendarCollectionResource
@inlineCallbacks
def http_GET(self, request):
# Look for calendar access restriction on existing resource.
if self.exists():
- try:
- access = self.readDeadProperty(TwistedCalendarAccessProperty)
- except HTTPError:
- access = None
-
- if access:
+ parentURL = parentForURL(request.uri)
+ parent = (yield request.locateResource(parentURL))
+ if isPseudoCalendarCollectionResource(parent):
# Check authorization first
yield self.authorize(request, (davxml.Read(),))
- # Non DAV:owner's have limited access to the data
- isowner = (yield self.isOwner(request, adminprincipals=True, readprincipals=True))
-
- # Now "filter" the resource calendar data
- caldata = PrivateEventFilter(access, isowner).filter(self.iCalendarText())
+ caldata = (yield self.iCalendarForUser(request))
+ try:
+ access = self.readDeadProperty(TwistedCalendarAccessProperty)
+ except HTTPError:
+ access = None
+
+ if access:
+
+ # Non DAV:owner's have limited access to the data
+ isowner = (yield self.isOwner(request, adminprincipals=True, readprincipals=True))
+
+ # Now "filter" the resource calendar data
+ caldata = PrivateEventFilter(access, isowner).filter(caldata)
+
response = Response()
response.stream = MemoryStream(str(caldata))
response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8"))
+
+ # Add Schedule-Tag header if property is present
+ if self.hasDeadProperty(ScheduleTag):
+ scheduletag = self.readDeadProperty(ScheduleTag)
+ if scheduletag:
+ response.headers.setHeader("Schedule-Tag", str(scheduletag))
+
returnValue(response)
-
# Do normal GET behavior
response = (yield super(CalDAVFile, self).http_GET(request))
-
- # Add Schedule-Tag header if property is present
- if self.exists() and self.hasDeadProperty(ScheduleTag):
- scheduletag = self.readDeadProperty(ScheduleTag)
- if scheduletag:
- response.headers.setHeader("Schedule-Tag", str(scheduletag))
-
returnValue(response)
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/put_common.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/put_common.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -55,6 +55,7 @@
TwistedCalendarHasPrivateCommentsProperty, TwistedSchedulingObjectResource,\
TwistedScheduleMatchETags
from twistedcaldav.customxml import TwistedCalendarAccessProperty
+from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
from twistedcaldav.fileops import copyToWithXAttrs, copyXAttrs
from twistedcaldav.fileops import putWithXAttrs
from twistedcaldav.fileops import copyWithXAttrs
@@ -315,7 +316,11 @@
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data")))
# At this point we need the calendar data to do more tests
- self.calendar = self.source.iCalendar()
+ try:
+ self.calendar = (yield self.source.iCalendarForUser(self.request))
+ except ValueError, e:
+ log.err(str(e))
+ raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"), description="Can't parse calendar data"))
else:
try:
if type(self.calendar) in (types.StringType, types.UnicodeType,):
@@ -364,7 +369,7 @@
# FIXME: We need this here because we have to re-index the destination. Ideally it
# would be better to copy the index entries from the source and add to the destination.
- self.calendar = self.source.iCalendar()
+ self.calendar = (yield self.source.iCalendarForUser(self.request))
# Check access
if self.destinationcal and config.EnablePrivateEvents:
@@ -375,7 +380,7 @@
elif self.sourcecal:
self.source_index = self.sourceparent.index()
- self.calendar = self.source.iCalendar()
+ self.calendar = (yield self.source.iCalendarForUser(self.request))
@inlineCallbacks
def validCopyMoveOperation(self):
@@ -692,6 +697,7 @@
else:
return False
+ @inlineCallbacks
def preservePrivateComments(self):
# Check for private comments on the old resource and the new resource and re-insert
# ones that are lost.
@@ -709,14 +715,14 @@
if old_has_private_comments and not new_has_private_comments:
# Transfer old comments to new calendar
log.debug("Private Comments properties were entirely removed by the client. Restoring existing properties.")
- old_calendar = self.destination.iCalendar()
+ old_calendar = (yield self.destination.iCalendarForUser(self.request))
self.calendar.transferProperties(old_calendar, (
"X-CALENDARSERVER-PRIVATE-COMMENT",
"X-CALENDARSERVER-ATTENDEE-COMMENT",
))
self.calendardata = None
- return new_has_private_comments
+ returnValue(new_has_private_comments)
@inlineCallbacks
def doImplicitScheduling(self):
@@ -789,7 +795,26 @@
returnValue((is_scheduling_resource, data_changed, did_implicit_action,))
@inlineCallbacks
+ def mergePerUserData(self):
+
+ if self.calendar:
+ accessUID = (yield self.destination.resourceOwnerPrincipal(self.request))
+ accessUID = accessUID.principalUID() if accessUID else ""
+ oldCal = self.destination.iCalendar() if self.destination.exists() and self.destinationcal else None
+
+ # Duplicate before we do the merge because someone else may "own" the calendar object
+ # and we should not change it. This is not ideal as we may duplicate it unnecessarily
+ # but we currently have no api to let the caller tell us whether it cares about the
+ # whether the calendar data is changed or not.
+ self.calendar = PerUserDataFilter(accessUID).merge(self.calendar.duplicate(), oldCal)
+ self.calendardata = None
+
+ @inlineCallbacks
def doStore(self, implicit):
+
+ # Always do the per-user data merge right before we store
+ yield self.mergePerUserData()
+
# Do put or copy based on whether source exists
if self.source is not None:
if implicit:
@@ -966,7 +991,7 @@
rruleChanged = self.truncateRecurrence()
# Preserve private comments
- new_has_private_comments = self.preservePrivateComments()
+ new_has_private_comments = (yield self.preservePrivateComments())
# Do scheduling
implicit_result = (yield self.doImplicitScheduling())
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/report_calquery.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/report_calquery.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/report_calquery.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -202,7 +202,7 @@
child_path_name = urllib.unquote(child_uri_name)
if generate_calendar_data or not index_query_ok:
- calendar = calresource.iCalendar(child_path_name)
+ calendar = (yield calresource.iCalendarForUser(request, child_path_name))
assert calendar is not None, "Calendar %s is missing from calendar collection %r" % (child_uri_name, self)
else:
calendar = None
@@ -226,7 +226,7 @@
# Check private events access status
isowner = (yield calresource.isOwner(request, adminprincipals=True, readprincipals=True))
- calendar = calresource.iCalendar()
+ calendar = (yield calresource.iCalendarForUser(request))
yield queryCalendarObjectResource(calresource, uri, None, calendar, timezone)
returnValue(True)
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/report_common.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/method/report_common.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -344,7 +344,7 @@
access = None
if calendar is None:
- calendar = resource.iCalendarText()
+ calendar = (yield resource.iCalendarForUser(request))
filtered = PrivateEventFilter(access, isowner).filter(calendar)
filtered = CalendarDataFilter(property, timezone).filter(filtered)
propvalue = CalendarData().fromCalendar(filtered)
@@ -453,13 +453,17 @@
filteredaces = (yield calresource.inheritedACEsforChildren(request))
try:
- resources = calresource.index().indexedSearch(filter, fbtype=True)
+ useruid = (yield calresource.resourceOwnerPrincipal(request))
+ useruid = useruid.principalUID() if useruid else ""
+ resources = calresource.index().indexedSearch(filter, useruid=useruid, fbtype=True)
except IndexedSearchException:
resources = calresource.index().bruteForceSearch()
# We care about separate instances for VEVENTs only
aggregated_resources = {}
- for name, uid, type, test_organizer, float, start, end, fbtype in resources:
+ for name, uid, type, test_organizer, float, start, end, fbtype, transp in resources:
+ if transp == 'T' and fbtype != '?':
+ fbtype = 'F'
aggregated_resources.setdefault((name, uid, type, test_organizer,), []).append((float, start, end, fbtype,))
for key in aggregated_resources.iterkeys():
@@ -519,7 +523,7 @@
fbinfo[fbtype_index_mapper.get(fbtype, 0)].append(clipped)
else:
- calendar = calresource.iCalendar(name)
+ calendar = (yield calresource.iCalendarForUser(request, name))
# The calendar may come back as None if the resource is being changed, or was deleted
# between our initial index query and getting here. For now we will ignore this error, but in
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/query/sqlgenerator.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/query/sqlgenerator.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/query/sqlgenerator.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -30,23 +30,26 @@
class sqlgenerator(object):
- FROM =" from "
- WHERE =" where "
- RESOURCEDB = "RESOURCE"
- TIMESPANDB = "TIMESPAN"
- NOTOP = "NOT "
- ANDOP = " AND "
- OROP = " OR "
- CONTAINSOP = " GLOB "
- NOTCONTAINSOP = " NOT GLOB "
- ISOP = " == "
- ISNOTOP = " != "
- INOP = " IN "
- NOTINOP = " NOT IN "
+ FROM =" from "
+ WHERE =" where "
+ RESOURCEDB = "RESOURCE"
+ TIMESPANDB = "TIMESPAN"
+ TRANSPARENCYDB = "TRANSPARENCY"
+ PERUSERDB = "PERUSER"
+ NOTOP = "NOT "
+ ANDOP = " AND "
+ OROP = " OR "
+ CONTAINSOP = " GLOB "
+ NOTCONTAINSOP = " NOT GLOB "
+ ISOP = " == "
+ ISNOTOP = " != "
+ INOP = " IN "
+ NOTINOP = " NOT IN "
- TIMESPANTEST = "((TIMESPAN.FLOAT == 'N' AND TIMESPAN.START < %s AND TIMESPAN.END > %s) OR (TIMESPAN.FLOAT == 'Y' AND TIMESPAN.START < %s AND TIMESPAN.END > %s)) AND TIMESPAN.NAME == RESOURCE.NAME"
- TIMESPANTEST_NOEND = "((TIMESPAN.FLOAT == 'N' AND TIMESPAN.END > %s) OR (TIMESPAN.FLOAT == 'Y' AND TIMESPAN.END > %s)) AND TIMESPAN.NAME == RESOURCE.NAME"
- TIMESPANTEST_NOSTART = "((TIMESPAN.FLOAT == 'N' AND TIMESPAN.START < %s) OR (TIMESPAN.FLOAT == 'Y' AND TIMESPAN.START < %s)) AND TIMESPAN.NAME == RESOURCE.NAME"
+ TIMESPANTEST = "((TIMESPAN.FLOAT == 'N' AND TIMESPAN.START < %s AND TIMESPAN.END > %s) OR (TIMESPAN.FLOAT == 'Y' AND TIMESPAN.START < %s AND TIMESPAN.END > %s))"
+ TIMESPANTEST_NOEND = "((TIMESPAN.FLOAT == 'N' AND TIMESPAN.END > %s) OR (TIMESPAN.FLOAT == 'Y' AND TIMESPAN.END > %s))"
+ TIMESPANTEST_NOSTART = "((TIMESPAN.FLOAT == 'N' AND TIMESPAN.START < %s) OR (TIMESPAN.FLOAT == 'Y' AND TIMESPAN.START < %s))"
+ TIMESPANTEST_TAIL_PIECE = " AND TIMESPAN.RESOURCEID == RESOURCE.RESOURCEID AND TIMESPAN.INSTANCEID == TRANSPARENCY.INSTANCEID"
def __init__(self, expr):
self.expression = expr
@@ -72,7 +75,7 @@
# Prefix with ' from ...' partial statement
select = self.FROM + self.RESOURCEDB
if self.usedtimespan:
- select += ", " + self.TIMESPANDB
+ select += ", %s, %s, %s" % (self.TIMESPANDB, self.TRANSPARENCYDB, self.PERUSERDB,)
select += self.sout.getvalue()
return select, self.arguments
@@ -134,6 +137,7 @@
arg1 = self.setArgument(expr.end)
arg2 = self.setArgument(expr.endfloat)
test = self.TIMESPANTEST_NOSTART % (arg1, arg2)
+ test += self.TIMESPANTEST_TAIL_PIECE
self.sout.write(test)
self.usedtimespan = True
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -64,6 +64,7 @@
from twistedcaldav.config import config
from twistedcaldav.customxml import TwistedCalendarAccessProperty
from twistedcaldav.customxml import calendarserver_namespace
+from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
from twistedcaldav.extensions import DAVResource, DAVPrincipalResource,\
PropertyNotFoundError
from twistedcaldav.ical import Component
@@ -829,6 +830,19 @@
except ValueError:
return None
+ @inlineCallbacks
+ def iCalendarForUser(self, request, name=None):
+
+ caldata = self.iCalendar(name)
+
+ accessUID = (yield self.resourceOwnerPrincipal(request))
+ if accessUID is None:
+ accessUID = ""
+ else:
+ accessUID = accessUID.principalUID()
+
+ returnValue(PerUserDataFilter(accessUID).filter(caldata))
+
def iCalendarRolledup(self, request):
"""
See L{ICalDAVResource.iCalendarRolledup}.
@@ -852,14 +866,6 @@
"""
return str(self.iCalendar(name))
- def iCalendarXML(self, name=None):
- """
- See L{ICalDAVResource.iCalendarXML}.
- This implementation returns an XML element constructed from the object
- returned by L{iCalendar} when given the same arguments.
- """
- return caldavxml.CalendarData.fromCalendar(self.iCalendar(name))
-
def iCalendarAddressDoNormalization(self, ical):
"""
Normalize calendar user addresses in the supplied iCalendar object into their
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/implicit.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/implicit.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -71,7 +71,8 @@
self.internal_request = internal_request
existing_resource = resource.exists()
- existing_type = "schedule" if self.checkSchedulingObjectResource(resource) else "calendar"
+ is_scheduling_object = (yield self.checkSchedulingObjectResource(resource))
+ existing_type = "schedule" if is_scheduling_object else "calendar"
new_type = "schedule" if (yield self.checkImplicitState()) else "calendar"
if existing_type == "calendar":
@@ -88,7 +89,7 @@
# Also make sure that we return the new calendar being written rather than the old one
# when the implicit action is executed
self.return_calendar = calendar
- self.calendar = resource.iCalendar()
+ self.calendar = (yield resource.iCalendarForUser(request))
yield self.checkImplicitState()
# Attendees are not allowed to overwrite one type with another
@@ -108,8 +109,8 @@
new_type = "schedule" if (yield self.checkImplicitState()) else "calendar"
dest_exists = destresource.exists()
- dest_is_implicit = self.checkSchedulingObjectResource(destresource)
- src_is_implicit = self.checkSchedulingObjectResource(srcresource) or new_type == "schedule"
+ dest_is_implicit = (yield self.checkSchedulingObjectResource(destresource))
+ src_is_implicit = (yield self.checkSchedulingObjectResource(srcresource)) or new_type == "schedule"
if srccal and destcal:
if src_is_implicit and dest_exists or dest_is_implicit:
@@ -138,8 +139,8 @@
new_type = "schedule" if (yield self.checkImplicitState()) else "calendar"
- dest_is_implicit = self.checkSchedulingObjectResource(destresource)
- src_is_implicit = self.checkSchedulingObjectResource(srcresource) or new_type == "schedule"
+ dest_is_implicit = (yield self.checkSchedulingObjectResource(destresource))
+ src_is_implicit = (yield self.checkSchedulingObjectResource(srcresource)) or new_type == "schedule"
if srccal and destcal:
if src_is_implicit or dest_is_implicit:
@@ -167,11 +168,13 @@
yield self.checkImplicitState()
- resource_type = "schedule" if self.checkSchedulingObjectResource(resource) else "calendar"
+ is_scheduling_object = (yield self.checkSchedulingObjectResource(resource))
+ resource_type = "schedule" if is_scheduling_object else "calendar"
self.action = "remove" if resource_type == "schedule" else "none"
returnValue((self.action != "none", False,))
+ @inlineCallbacks
def checkSchedulingObjectResource(self, resource):
if resource and resource.exists():
@@ -180,24 +183,24 @@
except HTTPError:
implicit = None
if implicit is not None:
- return implicit != "false"
+ returnValue(implicit != "false")
else:
- calendar = resource.iCalendar()
+ calendar = (yield resource.iCalendarForUser(self.request))
# Get the ORGANIZER and verify it is the same for all components
try:
organizer = calendar.validOrganizerForScheduling()
except ValueError:
# We have different ORGANIZERs in the same iCalendar object - this is an error
- return False
+ returnValue(False)
organizerPrincipal = resource.principalForCalendarUserAddress(organizer) if organizer else None
resource.writeDeadProperty(TwistedSchedulingObjectResource("true" if organizerPrincipal != None else "false"))
log.debug("Implicit - checked scheduling object resource state for UID: '%s', result: %s" % (
calendar.resourceUID(),
"true" if organizerPrincipal != None else "false",
))
- return organizerPrincipal != None
+ returnValue(organizerPrincipal != None)
- return False
+ returnValue(False)
@inlineCallbacks
def checkImplicitState(self):
@@ -397,7 +400,8 @@
child = (yield self.request.locateResource(joinURL(collection_uri, rname)))
if child == check_resource:
returnValue(True)
- matched_type = "schedule" if self.checkSchedulingObjectResource(child) else "calendar"
+ is_scheduling_object = (yield self.checkSchedulingObjectResource(child))
+ matched_type = "schedule" if is_scheduling_object else "calendar"
if (
collection_uri != check_parent_uri and
(type == "schedule" or matched_type == "schedule")
@@ -494,7 +498,7 @@
elif self.action == "modify":
# Read in existing data
- self.oldcalendar = self.resource.iCalendar()
+ self.oldcalendar = (yield self.resource.iCalendarForUser(self.request))
# Significant change
no_change, self.changed_rids, reinvites, recurrence_reschedule = self.isOrganizerChangeInsignificant()
@@ -780,7 +784,7 @@
else:
# Make sure ORGANIZER is not changed
if self.resource.exists():
- self.oldcalendar = self.resource.iCalendar()
+ self.oldcalendar = (yield self.resource.iCalendarForUser(self.request))
oldOrganizer = self.oldcalendar.getOrganizer()
newOrganizer = self.calendar.getOrganizer()
if oldOrganizer != newOrganizer:
@@ -883,7 +887,7 @@
self.organizer_calendar = None
calendar_resource, _ignore_name, _ignore_collection, _ignore_uri = (yield getCalendarObjectForPrincipals(self.request, self.organizerPrincipal, self.uid))
if calendar_resource:
- self.organizer_calendar = calendar_resource.iCalendar()
+ self.organizer_calendar = (yield calendar_resource.iCalendarForUser(self.request))
elif isinstance(self.organizerAddress, PartitionedCalendarUser):
# For partitioning where the organizer is on a different node, we will assume that the attendee's copy
# of the event is up to date and "authoritative". So we pretend that is the organizer copy
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/itip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/itip.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/itip.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -667,12 +667,15 @@
Remove properties and parameters that should not be sent in an iTIP message
"""
+ # All X- components go away
+ itip.removeXComponents()
+
# Alarms
itip.removeAlarms()
# Top-level properties - remove all X-
itip.removeXProperties(do_subcomponents=False)
-
+
# Component properties - remove all X- except for those specified
if not reply:
# Organizer properties that need to go to the Attendees
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/processing.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/processing.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -136,7 +136,7 @@
self.recipient_calendar_name = None
calendar_resource, resource_name, calendar_collection, calendar_collection_uri = (yield getCalendarObjectForPrincipals(self.request, self.recipient.principal, self.uid))
if calendar_resource:
- self.recipient_calendar = calendar_resource.iCalendar()
+ self.recipient_calendar = (yield calendar_resource.iCalendarForUser(self.request))
self.recipient_calendar_collection = calendar_collection
self.recipient_calendar_collection_uri = calendar_collection_uri
self.recipient_calendar_name = resource_name
@@ -709,7 +709,7 @@
calendar_resource, _ignore_name, _ignore_collection, _ignore_uri = (yield getCalendarObjectForPrincipals(self.request, self.originator.principal, self.uid))
if not calendar_resource:
raise ImplicitProcessorException("5.1;Service unavailable")
- originator_calendar = calendar_resource.iCalendar()
+ originator_calendar = (yield calendar_resource.iCalendarForUser(self.request))
# Get attendee's view of that
originator_calendar.attendeesView((self.recipient.cuaddr,))
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/utils.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/utils.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/utils.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -16,6 +16,7 @@
from twisted.internet.defer import inlineCallbacks, succeed, returnValue
from twistedcaldav.method import report_common
+from twext.web2.dav.util import joinURL
@inlineCallbacks
def getCalendarObjectForPrincipals(request, principal, uid):
@@ -43,7 +44,10 @@
def queryCalendarCollection(collection, uri):
rname = collection.index().resourceNameForUID(uid)
if rname:
- result["resource"] = collection.getChild(rname)
+ resource = collection.getChild(rname)
+ request._rememberResource(resource, joinURL(uri, rname))
+
+ result["resource"] = resource
result["resource_name"] = rname
result["calendar_collection"] = collection
result["calendar_collection_uri"] = uri
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sql.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sql.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -327,6 +327,7 @@
q = self._db().cursor()
try:
q.execute(sql, query_params)
+ self.lastrowid = q.lastrowid
return q.fetchall()
except DatabaseError:
log.err("Exception while executing SQL on DB %s: %r %r" % (self, sql, query_params))
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -71,6 +71,7 @@
from twistedcaldav.client.reverseproxy import ReverseProxyResource
from twistedcaldav.config import config
from twistedcaldav.customxml import TwistedCalendarAccessProperty, TwistedScheduleMatchETags
+from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
from twistedcaldav.extensions import DAVFile, CachingPropertyStore
from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
from twistedcaldav.memcacheprops import MemcachePropertyCollection
@@ -277,6 +278,7 @@
tzids = set()
isowner = (yield self.isOwner(request, adminprincipals=True, readprincipals=True))
+ accessPrincipal = (yield self.resourceOwnerPrincipal(request))
for name, uid, type in self.index().bruteForceSearch(): #@UnusedVariable
try:
@@ -293,7 +295,7 @@
continue
# Get the access filtered view of the data
- caldata = child.iCalendarTextFiltered(isowner)
+ caldata = child.iCalendarTextFiltered(isowner, accessPrincipal.principalUID() if accessPrincipal else "")
try:
subcalendar = iComponent.fromString(caldata)
except ValueError:
@@ -315,7 +317,7 @@
raise HTTPError(ErrorResponse(responsecode.BAD_REQUEST))
- def iCalendarTextFiltered(self, isowner):
+ def iCalendarTextFiltered(self, isowner, accessUID=None):
try:
access = self.readDeadProperty(TwistedCalendarAccessProperty)
except HTTPError:
@@ -323,7 +325,8 @@
# Now "filter" the resource calendar data
caldata = PrivateEventFilter(access, isowner).filter(self.iCalendarText())
-
+ if accessUID:
+ caldata = PerUserDataFilter(accessUID).filter(caldata)
return str(caldata)
def iCalendarText(self, name=None):
@@ -354,9 +357,6 @@
return calendar_data
- def iCalendarXML(self, name=None):
- return caldavxml.CalendarData.fromCalendar(self.iCalendarText(name))
-
def createAddressBook(self, request):
#
# request object is required because we need to validate against parent
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_icalendar.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_icalendar.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -3459,3 +3459,519 @@
calendar.validateForCalDAV()
except:
self.fail("Valid calendar should validate")
+
+ def test_allperuseruids(self):
+ data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:OPAQUE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+ calendar = Component.fromString(data)
+ self.assertEqual(calendar.allPerUserUIDs(), set((
+ "user01",
+ "user02",
+ )))
+
+ def test_perUserTransparency(self):
+ data = (
+ (
+ "No per-user, not recurring 1.1",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+ (
+ (
+ None,
+ (
+ ("", True,),
+ ),
+ ),
+ ),
+ ),
+ (
+ "Single user, not recurring 1.2",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:OPAQUE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+ (
+ (
+ None,
+ (
+ ("", False,),
+ ("user01", False,),
+ ),
+ ),
+ ),
+ ),
+ (
+ "Two users, not recurring 1.3",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:OPAQUE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+ (
+ (
+ None,
+ (
+ ("", False,),
+ ("user01", False,),
+ ("user02", True,),
+ ),
+ ),
+ ),
+ ),
+ (
+ "No per-user, simple recurring 2.1",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+ (
+ (
+ None,
+ (
+ ("", False,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", False,),
+ ),
+ ),
+ ),
+ ),
+ (
+ "Single user, simple recurring 2.2",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:OPAQUE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+ (
+ (
+ None,
+ (
+ ("", False,),
+ ("user01", False,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", False,),
+ ("user01", False,),
+ ),
+ ),
+ ),
+ ),
+ (
+ "Two users, simple recurring 2.3",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:OPAQUE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+ (
+ (
+ None,
+ (
+ ("", False,),
+ ("user01", False,),
+ ("user02", True,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", False,),
+ ("user01", False,),
+ ("user02", True,),
+ ),
+ ),
+ ),
+ ),
+ (
+ "No per-user, complex recurring 3.1",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+ (
+ (
+ None,
+ (
+ ("", True,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", False,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 3, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", True,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 4, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", True,),
+ ),
+ ),
+ ),
+ ),
+ (
+ "Single user, complex recurring 3.2",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:OPAQUE
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080602T120000Z
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080603T120000Z
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+ (
+ (
+ None,
+ (
+ ("", False,),
+ ("user01", False,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", False,),
+ ("user01", True,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 3, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", False,),
+ ("user01", True,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 4, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", False,),
+ ("user01", False,),
+ ),
+ ),
+ ),
+ ),
+ (
+ "Two users, complex recurring 3.3",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:OPAQUE
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080602T120000Z
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080603T120000Z
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080602T120000Z
+TRANSP:OPAQUE
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080604T120000Z
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+ (
+ (
+ None,
+ (
+ ("", False,),
+ ("user01", False,),
+ ("user02", True,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", False,),
+ ("user01", True,),
+ ("user02", False,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 3, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", False,),
+ ("user01", True,),
+ ("user02", True,),
+ ),
+ ),
+ (
+ datetime.datetime(2008, 6, 4, 12, 0, 0, tzinfo=tzutc()),
+ (
+ ("", False,),
+ ("user01", False,),
+ ("user02", True,),
+ ),
+ ),
+ ),
+ ),
+ )
+
+ for title, text, results in data:
+ calendar = Component.fromString(text)
+ for rid, result in results:
+ self.assertEqual(calendar.perUserTransparency(rid), result, "Failed comparison: %s %s" % (title, rid,))
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_index.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_index.py 2010-03-23 22:40:37 UTC (rev 5386)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_index.py 2010-03-23 22:46:30 UTC (rev 5387)
@@ -280,7 +280,7 @@
""",
"20080601T000000Z", "20080602T000000Z",
"mailto:user1 at example.com",
- (('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B'),),
+ (('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'F'),),
),
(
"#1.2 Simple component - transparent",
@@ -301,7 +301,7 @@
""",
"20080602T000000Z", "20080603T000000Z",
"mailto:user1 at example.com",
- (('N', "2008-06-02 12:00:00+00:00", "2008-06-02 13:00:00+00:00", 'F'),),
+ (('N', "2008-06-02 12:00:00+00:00", "2008-06-02 13:00:00+00:00", 'B', 'T'),),
),
(
"#1.3 Simple component - canceled",
@@ -322,7 +322,7 @@
""",
"20080603T000000Z", "20080604T000000Z",
"mailto:user1 at example.com",
- (('N', "2008-06-03 12:00:00+00:00", "2008-06-03 13:00:00+00:00", 'F'),),
+ (('N', "2008-06-03 12:00:00+00:00", "2008-06-03 13:00:00+00:00", 'F', 'F'),),
),
(
"#1.4 Simple component - tentative",
@@ -343,7 +343,7 @@
""",
"20080604T000000Z", "20080605T000000Z",
"mailto:user1 at example.com",
- (('N', "2008-06-04 12:00:00+00:00", "2008-06-04 13:00:00+00:00", 'T'),),
+ (('N', "2008-06-04 12:00:00+00:00", "2008-06-04 13:00:00+00:00", 'T', 'F'),),
),
(
"#2.1 Recurring component - busy",
@@ -365,8 +365,8 @@
"20080605T000000Z", "20080607T000000Z",
"mailto:user1 at example.com",
(
- ('N', "2008-06-05 12:00:00+00:00", "2008-06-05 13:00:00+00:00", 'B'),
- ('N', "2008-06-06 12:00:00+00:00", "2008-06-06 13:00:00+00:00", 'B'),
+ ('N', "2008-06-05 12:00:00+00:00", "2008-06-05 13:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-06 12:00:00+00:00", "2008-06-06 13:00:00+00:00", 'B', 'F'),
),
),
(
@@ -399,8 +399,8 @@
"20080607T000000Z", "20080609T000000Z",
"mailto:user1 at example.com",
(
- ('N', "2008-06-07 12:00:00+00:00", "2008-06-07 13:00:00+00:00", 'B'),
- ('N', "2008-06-08 14:00:00+00:00", "2008-06-08 15:00:00+00:00", 'F'),
+ ('N', "2008-06-07 12:00:00+00:00", "2008-06-07 13:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-08 14:00:00+00:00", "2008-06-08 15:00:00+00:00", 'B', 'T'),
),
),
)
@@ -434,12 +434,415 @@
resources = self.db.indexedSearch(filter, fbtype=True)
index_results = set()
- for _ignore_name, _ignore_uid, type, test_organizer, float, start, end, fbtype in resources:
+ for _ignore_name, _ignore_uid, type, test_organizer, float, start, end, fbtype, transp in resources:
self.assertEqual(test_organizer, organizer, msg=description)
- index_results.add((float, start, end, fbtype,))
+ index_results.add((float, start, end, fbtype, transp,))
self.assertEqual(set(instances), index_results, msg=description)
+ def test_index_timespan_per_user(self):
+ data = (
+ (
+ "#1.1 Single per-user non-recurring component",
+ "1.1",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1.1
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890-1.1
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""",
+ "20080601T000000Z", "20080602T000000Z",
+ "mailto:user1 at example.com",
+ (
+ (
+ "user01",
+ (('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'T'),),
+ ),
+ (
+ "user02",
+ (('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'F'),),
+ ),
+ ),
+ ),
+ (
+ "#1.2 Two per-user non-recurring component",
+ "1.2",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1.2
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890-1.2
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890-1.2
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""",
+ "20080601T000000Z", "20080602T000000Z",
+ "mailto:user1 at example.com",
+ (
+ (
+ "user01",
+ (('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'T'),),
+ ),
+ (
+ "user02",
+ (('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'F'),),
+ ),
+ (
+ "user03",
+ (('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'F'),),
+ ),
+ ),
+ ),
+ (
+ "#2.1 Single per-user simple recurring component",
+ "2.1",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1.1
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:FREQ=DAILY;COUNT=10
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890-1.1
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""",
+ "20080601T000000Z", "20080603T000000Z",
+ "mailto:user1 at example.com",
+ (
+ (
+ "user01",
+ (
+ ('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'T'),
+ ('N', "2008-06-02 12:00:00+00:00", "2008-06-02 13:00:00+00:00", 'B', 'T'),
+ ),
+ ),
+ (
+ "user02",
+ (
+ ('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-02 12:00:00+00:00", "2008-06-02 13:00:00+00:00", 'B', 'F'),
+ ),
+ ),
+ ),
+ ),
+ (
+ "#2.2 Two per-user simple recurring component",
+ "2.2",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1.2
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:FREQ=DAILY;COUNT=10
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890-1.2
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890-1.2
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""",
+ "20080601T000000Z", "20080603T000000Z",
+ "mailto:user1 at example.com",
+ (
+ (
+ "user01",
+ (
+ ('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'T'),
+ ('N', "2008-06-02 12:00:00+00:00", "2008-06-02 13:00:00+00:00", 'B', 'T'),
+ ),
+ ),
+ (
+ "user02",
+ (
+ ('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-02 12:00:00+00:00", "2008-06-02 13:00:00+00:00", 'B', 'F'),
+ ),
+ ),
+ (
+ "user03",
+ (
+ ('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-02 12:00:00+00:00", "2008-06-02 13:00:00+00:00", 'B', 'F'),
+ ),
+ ),
+ ),
+ ),
+ (
+ "#3.1 Single per-user complex recurring component",
+ "3.1",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1.1
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:FREQ=DAILY;COUNT=10
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1.1
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890-1.1
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080602T120000Z
+TRANSP:OPAQUE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""",
+ "20080601T000000Z", "20080604T000000Z",
+ "mailto:user1 at example.com",
+ (
+ (
+ "user01",
+ (
+ ('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'T'),
+ ('N', "2008-06-02 13:00:00+00:00", "2008-06-02 14:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-03 12:00:00+00:00", "2008-06-03 13:00:00+00:00", 'B', 'T'),
+ ),
+ ),
+ (
+ "user02",
+ (
+ ('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-02 13:00:00+00:00", "2008-06-02 14:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-03 12:00:00+00:00", "2008-06-03 13:00:00+00:00", 'B', 'F'),
+ ),
+ ),
+ ),
+ ),
+ (
+ "#3.2 Two per-user complex recurring component",
+ "3.2",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1.2
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:FREQ=DAILY;COUNT=10
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1.2
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890-1.2
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080602T120000Z
+TRANSP:OPAQUE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890-1.2
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:20080603T120000Z
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""",
+ "20080601T000000Z", "20080604T000000Z",
+ "mailto:user1 at example.com",
+ (
+ (
+ "user01",
+ (
+ ('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'T'),
+ ('N', "2008-06-02 13:00:00+00:00", "2008-06-02 14:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-03 12:00:00+00:00", "2008-06-03 13:00:00+00:00", 'B', 'T'),
+ ),
+ ),
+ (
+ "user02",
+ (
+ ('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-02 13:00:00+00:00", "2008-06-02 14:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-03 12:00:00+00:00", "2008-06-03 13:00:00+00:00", 'B', 'T'),
+ ),
+ ),
+ (
+ "user03",
+ (
+ ('N', "2008-06-01 12:00:00+00:00", "2008-06-01 13:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-02 13:00:00+00:00", "2008-06-02 14:00:00+00:00", 'B', 'F'),
+ ('N', "2008-06-03 12:00:00+00:00", "2008-06-03 13:00:00+00:00", 'B', 'F'),
+ ),
+ ),
+ ),
+ ),
+ )
+
+ revision = 0
+ for description, name, calendar_txt, trstart, trend, organizer, peruserinstances in data:
+ revision += 1
+ calendar = Component.fromString(calendar_txt)
+
+ f = open(os.path.join(self.site.resource.fp.path, name), "w")
+ f.write(calendar_txt)
+ del f
+
+ self.db.addResource(name, calendar, revision)
+ self.assertTrue(self.db.resourceExists(name), msg=description)
+
+ # Create fake filter element to match time-range
+ filter = caldavxml.Filter(
+ caldavxml.ComponentFilter(
+ caldavxml.ComponentFilter(
+ TimeRange(
+ start=trstart,
+ end=trend,
+ ),
+ name=("VEVENT", "VFREEBUSY", "VAVAILABILITY"),
+ ),
+ name="VCALENDAR",
+ )
+ )
+ filter = queryfilter.Filter(filter)
+
+ for useruid, instances in peruserinstances:
+ resources = self.db.indexedSearch(filter, useruid=useruid, fbtype=True)
+ index_results = set()
+ for _ignore_name, _ignore_uid, type, test_organizer, float, start, end, fbtype, transp in resources:
+ self.assertEqual(test_organizer, organizer, msg=description)
+ index_results.add((str(float), str(start), str(end), str(fbtype), str(transp),))
+
+ self.assertEqual(set(instances), index_results, msg="%s, user:%s" % (description, useruid,))
+
+ revision += 1
+ self.db.deleteResource(name, revision)
+
def test_index_revisions(self):
data1 = """BEGIN:VCALENDAR
VERSION:2.0
@@ -503,424 +906,6 @@
for revision, results in tests:
self.assertEquals(self.db.whatchanged(revision), results, "Mismatched results for whatchanged with revision %d" % (revision,))
-class SQLIndexUpgradeTests (twistedcaldav.test.util.TestCase):
- """
- Test abstract SQL DB class
- """
-
- class OldIndexv6(Index):
-
- def _db_version(self):
- """
- @return: the schema version assigned to this index.
- """
- return "6"
-
- def _db_init_data_tables_base(self, q, uidunique):
- """
- Initialise the underlying database tables.
- @param q: a database cursor to use.
- """
- #
- # RESOURCE table is the primary index table
- # NAME: Last URI component (eg. <uid>.ics, RESOURCE primary key)
- # UID: iCalendar UID (may or may not be unique)
- # TYPE: iCalendar component type
- # RECURRANCE_MAX: Highest date of recurrence expansion
- #
- if uidunique:
- q.execute(
- """
- create table RESOURCE (
- NAME text unique,
- UID text unique,
- TYPE text,
- RECURRANCE_MAX date
- )
- """
- )
- else:
- q.execute(
- """
- create table RESOURCE (
- NAME text unique,
- UID text,
- TYPE text,
- RECURRANCE_MAX date
- )
- """
- )
-
- #
- # TIMESPAN table tracks (expanded) timespans for resources
- # NAME: Related resource (RESOURCE foreign key)
- # FLOAT: 'Y' if start/end are floating, 'N' otherwise
- # START: Start date
- # END: End date
- #
- q.execute(
- """
- create table TIMESPAN (
- NAME text,
- FLOAT text(1),
- START date,
- END date
- )
- """
- )
-
- if uidunique:
- #
- # RESERVED table tracks reserved UIDs
- # UID: The UID being reserved
- # TIME: When the reservation was made
- #
- q.execute(
- """
- create table RESERVED (
- UID text unique,
- TIME date
- )
- """
- )
-
- def _db_upgrade(self, old_version):
- """
- Upgrade the database tables.
- """
-
- return super(AbstractCalendarIndex, self)._db_upgrade(old_version)
-
- def _add_to_db(self, name, calendar, cursor = None, expand_until=None, reCreate=False):
- """
- Records the given calendar resource in the index with the given name.
- Resource names and UIDs must both be unique; only one resource name may
- be associated with any given UID and vice versa.
- NB This method does not commit the changes to the db - the caller
- MUST take care of that
- @param name: the name of the resource to add.
- @param calendar: a L{Calendar} object representing the resource
- contents.
- """
- uid = calendar.resourceUID()
-
- # Decide how far to expand based on the component
- master = calendar.masterComponent()
- if master is None or not calendar.isRecurring() and not calendar.isRecurringUnbounded():
- # When there is no master we have a set of overridden components - index them all.
- # When there is one instance - index it.
- # When bounded - index all.
- expand = datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=utc)
- else:
- if expand_until:
- expand = expand_until
- else:
- expand = datetime.date.today() + default_future_expansion_duration
-
- if expand > (datetime.date.today() + maximum_future_expansion_duration):
- raise IndexedSearchException
-
- try:
- instances = calendar.expandTimeRanges(expand, ignoreInvalidInstances=reCreate)
- except InvalidOverriddenInstanceError:
- raise
-
- self._delete_from_db(name, uid, None)
-
- for key in instances:
- instance = instances[key]
- start = instance.start.replace(tzinfo=utc)
- end = instance.end.replace(tzinfo=utc)
- float = 'Y' if instance.start.tzinfo is None else 'N'
- self._db_execute(
- """
- insert into TIMESPAN (NAME, FLOAT, START, END)
- values (:1, :2, :3, :4)
- """, name, float, start, end
- )
-
- # Special - for unbounded recurrence we insert a value for "infinity"
- # that will allow an open-ended time-range to always match it.
- if calendar.isRecurringUnbounded():
- start = datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=utc)
- end = datetime.datetime(2100, 1, 1, 1, 0, 0, tzinfo=utc)
- float = 'N'
- self._db_execute(
- """
- insert into TIMESPAN (NAME, FLOAT, START, END)
- values (:1, :2, :3, :4)
- """, name, float, start, end
- )
-
- self._db_execute(
- """
- insert into RESOURCE (NAME, UID, TYPE, RECURRANCE_MAX)
- values (:1, :2, :3, :4)
- """, name, uid, calendar.resourceType(), instances.limit
- )
-
- class OldIndexv7(Index):
-
- def _db_version(self):
- """
- @return: the schema version assigned to this index.
- """
- return "7"
-
- def _db_init_data_tables_base(self, q, uidunique):
- """
- Initialise the underlying database tables.
- @param q: a database cursor to use.
- """
- #
- # RESOURCE table is the primary index table
- # NAME: Last URI component (eg. <uid>.ics, RESOURCE primary key)
- # UID: iCalendar UID (may or may not be unique)
- # TYPE: iCalendar component type
- # RECURRANCE_MAX: Highest date of recurrence expansion
- # ORGANIZER: cu-address of the Organizer of the event
- #
- if uidunique:
- q.execute(
- """
- create table RESOURCE (
- NAME text unique,
- UID text unique,
- TYPE text,
- RECURRANCE_MAX date,
- ORGANIZER text
- )
- """
- )
- else:
- q.execute(
- """
- create table RESOURCE (
- NAME text unique,
- UID text,
- TYPE text,
- RECURRANCE_MAX date
- )
- """
- )
-
- #
- # TIMESPAN table tracks (expanded) time spans for resources
- # NAME: Related resource (RESOURCE foreign key)
- # FLOAT: 'Y' if start/end are floating, 'N' otherwise
- # START: Start date
- # END: End date
- # FBTYPE: FBTYPE value:
- # '?' - unknown
- # 'F' - free
- # 'B' - busy
- # 'U' - busy-unavailable
- # 'T' - busy-tentative
- #
- q.execute(
- """
- create table TIMESPAN (
- NAME text,
- FLOAT text(1),
- START date,
- END date,
- FBTYPE text(1)
- )
- """
- )
-
- if uidunique:
- #
- # RESERVED table tracks reserved UIDs
- # UID: The UID being reserved
- # TIME: When the reservation was made
- #
- q.execute(
- """
- create table RESERVED (
- UID text unique,
- TIME date
- )
- """
- )
-
- def _add_to_db(self, name, calendar, cursor = None, expand_until=None, reCreate=False):
- """
- Records the given calendar resource in the index with the given name.
- Resource names and UIDs must both be unique; only one resource name may
- be associated with any given UID and vice versa.
- NB This method does not commit the changes to the db - the caller
- MUST take care of that
- @param name: the name of the resource to add.
- @param calendar: a L{Calendar} object representing the resource
- contents.
- """
- uid = calendar.resourceUID()
- organizer = calendar.getOrganizer()
- if not organizer:
- organizer = ""
-
- # Decide how far to expand based on the component
- master = calendar.masterComponent()
- if master is None or not calendar.isRecurring() and not calendar.isRecurringUnbounded():
- # When there is no master we have a set of overridden components - index them all.
- # When there is one instance - index it.
- # When bounded - index all.
- expand = datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=utc)
- else:
- if expand_until:
- expand = expand_until
- else:
- expand = datetime.date.today() + default_future_expansion_duration
-
- if expand > (datetime.date.today() + maximum_future_expansion_duration):
- raise IndexedSearchException
-
- try:
- instances = calendar.expandTimeRanges(expand, ignoreInvalidInstances=reCreate)
- except InvalidOverriddenInstanceError:
- raise
-
- self._delete_from_db(name, uid, None)
-
- for key in instances:
- instance = instances[key]
- start = instance.start.replace(tzinfo=utc)
- end = instance.end.replace(tzinfo=utc)
- float = 'Y' if instance.start.tzinfo is None else 'N'
- self._db_execute(
- """
- insert into TIMESPAN (NAME, FLOAT, START, END, FBTYPE)
- values (:1, :2, :3, :4, :5)
- """, name, float, start, end, icalfbtype_to_indexfbtype.get(instance.component.getFBType(), 'F')
- )
-
- # Special - for unbounded recurrence we insert a value for "infinity"
- # that will allow an open-ended time-range to always match it.
- if calendar.isRecurringUnbounded():
- start = datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=utc)
- end = datetime.datetime(2100, 1, 1, 1, 0, 0, tzinfo=utc)
- float = 'N'
- self._db_execute(
- """
- insert into TIMESPAN (NAME, FLOAT, START, END, FBTYPE)
- values (:1, :2, :3, :4, :5)
- """, name, float, start, end, '?'
- )
-
- self._db_execute(
- """
- insert into RESOURCE (NAME, UID, TYPE, RECURRANCE_MAX, ORGANIZER)
- values (:1, :2, :3, :4, :5)
- """, name, uid, calendar.resourceType(), instances.limit, organizer
- )
-
- def setUp(self):
- super(SQLIndexUpgradeTests, self).setUp()
- self.site.resource.isCalendarCollection = lambda: True
- self.db = Index(self.site.resource)
- self.olddbv6 = SQLIndexUpgradeTests.OldIndexv6(self.site.resource)
- self.olddbv7 = SQLIndexUpgradeTests.OldIndexv7(self.site.resource)
-
- def prepareOldDB(self):
- if os.path.exists(self.olddbv6.dbpath):
- os.remove(self.olddbv6.dbpath)
-
- def test_old_schema(self):
-
- for olddb in (self.olddbv6, self.olddbv7):
- self.prepareOldDB()
-
- schema = olddb._db_value_for_sql(
- """
- select VALUE from CALDAV
- where KEY = 'SCHEMA_VERSION'
- """)
- self.assertEqual(schema, olddb._db_version())
-
- def test_empty_upgrade(self):
-
- for olddb in (self.olddbv6, self.olddbv7):
- self.prepareOldDB()
-
- schema = olddb._db_value_for_sql(
- """
- select VALUE from CALDAV
- where KEY = 'SCHEMA_VERSION'
- """)
- self.assertEqual(schema, olddb._db_version())
-
- if olddb._db_version() == "6":
- self.assertRaises(sqlite3.OperationalError, olddb._db_value_for_sql, "select ORGANIZER from RESOURCE")
- self.assertRaises(sqlite3.OperationalError, olddb._db_value_for_sql, "select FBTYPE from TIMESPAN")
- elif olddb._db_version() == "7":
- olddb._db_value_for_sql("select ORGANIZER from RESOURCE")
- olddb._db_value_for_sql("select FBTYPE from TIMESPAN")
- self.assertEqual(set([row[1] for row in olddb._db_execute("PRAGMA index_list(TIMESPAN)")]), set())
-
- schema = self.db._db_value_for_sql(
- """
- select VALUE from CALDAV
- where KEY = 'SCHEMA_VERSION'
- """)
- self.assertEqual(schema, self.db._db_version())
-
- value = self.db._db_value_for_sql("select ORGANIZER from RESOURCE")
- self.assertEqual(value, None)
- self.assertEqual(set([row[1] for row in self.db._db_execute("PRAGMA index_list(TIMESPAN)")]), set(("STARTENDFLOAT",)))
-
- def test_basic_upgrade(self):
-
- for olddb in (self.olddbv6, self.olddbv7):
- self.prepareOldDB()
-
- calendar_name = "1.ics"
- calendar_data = """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890-1.1
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-END:VEVENT
-END:VCALENDAR
-"""
-
- olddb.addResource(calendar_name, Component.fromString(calendar_data), 1)
- self.assertTrue(olddb.resourceExists(calendar_name))
-
- if olddb._db_version() == "6":
- self.assertRaises(sqlite3.OperationalError, olddb._db_value_for_sql, "select ORGANIZER from RESOURCE")
- self.assertRaises(sqlite3.OperationalError, olddb._db_value_for_sql, "select FBTYPE from TIMESPAN")
- elif olddb._db_version() == "7":
- olddb._db_value_for_sql("select ORGANIZER from RESOURCE")
- olddb._db_value_for_sql("select FBTYPE from TIMESPAN")
- self.assertEqual(set([row[1] for row in olddb._db_execute("PRAGMA index_list(TIMESPAN)")]), set())
-
- value = self.db._db_value_for_sql("select ORGANIZER from RESOURCE where NAME = :1", calendar_name)
- if olddb._db_version() == "6":
- self.assertEqual(value, "?")
- else:
- self.assertEqual(value, "mailto:user1 at example.com")
-
- value = self.db._db_value_for_sql("select FBTYPE from TIMESPAN where NAME = :1", calendar_name)
- if olddb._db_version() == "6":
- self.assertEqual(value, "?")
- else:
- self.assertEqual(value, "B")
-
- self.db.addResource(calendar_name, Component.fromString(calendar_data), 2)
- self.assertTrue(olddb.resourceExists(calendar_name))
-
- value = self.db._db_value_for_sql("select ORGANIZER from RESOURCE where NAME = :1", calendar_name)
- self.assertEqual(value, "mailto:user1 at example.com")
-
- value = self.db._db_value_for_sql("select FBTYPE from TIMESPAN where NAME = :1", calendar_name)
- self.assertEqual(value, "B")
-
class MemcacheTests(SQLIndexTests):
def setUp(self):
super(MemcacheTests, self).setUp()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100323/588f5b87/attachment-0001.html>
More information about the calendarserver-changes
mailing list