[CalendarServer-changes] [4685] CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/ twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Fri Oct 30 13:11:26 PDT 2009


Revision: 4685
          http://trac.macosforge.org/projects/calendarserver/changeset/4685
Author:   cdaboo at apple.com
Date:     2009-10-30 13:11:23 -0700 (Fri, 30 Oct 2009)
Log Message:
-----------
New datafilters module used to transform data on the way in or out of the server.

Added Paths:
-----------
    CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/
    CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/__init__.py
    CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/calendardata.py
    CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/filter.py
    CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/privateevents.py
    CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/
    CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/__init__.py
    CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/test_calendardata.py
    CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/test_privateevents.py

Added: CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/__init__.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/__init__.py	2009-10-30 20:11:23 UTC (rev 4685)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+Data filtering module.
+"""

Added: CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/calendardata.py
===================================================================
--- CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/calendardata.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/calendardata.py	2009-10-30 20:11:23 UTC (rev 4685)
@@ -0,0 +1,178 @@
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twistedcaldav.ical import Component
+from twistedcaldav.dateops import clipPeriod
+from twistedcaldav.caldavxml import LimitRecurrenceSet, Expand, AllComponents,\
+    AllProperties
+
+__all__ = [
+    "CalendarDataFilter",
+]
+
+class CalendarDataFilter(object):
+    """
+    Filter using the CALDAV:calendar-data element specification
+    """
+
+    def __init__(self, calendardata, timezone=None):
+        """
+        
+        @param calendardata: the XML element describing how to filter
+        @type calendardata: L{CalendarData}
+        @param timezone: the VTIMEZONE to use for floating/all-day
+        @type timezone: L{Component}
+        """
+        
+        self.calendardata = calendardata
+        self.timezone = timezone
+    
+    def filter(self, ical):
+        """
+        Filter the supplied iCalendar (vobject) data using the request information.
+
+        @param ical: iCalendar object
+        @type ical: L{Component} or C{str}
+        
+        @return: L{Component} for the filtered calendar data
+        """
+        
+        # Empty element: get all data
+        if not self.calendardata.children:
+            return ical
+
+        # If we were passed a string, parse it out as a Component
+        if isinstance(ical, str):
+            try:
+                calendar = Component.fromString(ical)
+            except ValueError:
+                raise ValueError("Not a calendar: %r" % (ical,))
+
+        if calendar is None or calendar.name() != "VCALENDAR":
+            raise ValueError("Not a calendar: %r" % (calendar,))
+
+        # Pre-process the calendar data based on expand and limit options
+        if self.calendardata.freebusy_set:
+            calendar = self.limitFreeBusy(calendar)
+
+        # Filter data based on any provided CALDAV:comp element, or use all current data
+        if self.calendardata.component is not None:
+            calendar = self.compFilter(self.calendardata.component, calendar)
+        
+        # Post-process the calendar data based on the expand and limit options
+        if self.calendardata.recurrence_set:
+            if isinstance(self.calendardata.recurrence_set, LimitRecurrenceSet):
+                calendar = self.limitRecurrence(calendar)
+            elif isinstance(self.calendardata.recurrence_set, Expand):
+                calendar = self.expandRecurrence(calendar, self.timezone)
+        
+        return calendar
+
+    def compFilter(self, comp, component):
+        """
+        Returns a calendar component object containing the data in the given
+        component which is specified by this CalendarComponent.
+        """
+        if comp.type != component.name():
+            raise ValueError("%s of type %r can't get data from component of type %r"
+                             % (comp.sname(), comp.type, component.name()))
+
+        result = Component(comp.type)
+
+        xml_components = comp.components
+        xml_properties = comp.properties
+
+        # Empty element means do all properties and components
+        if xml_components is None and xml_properties is None:
+            xml_components = AllComponents()
+            xml_properties = AllProperties()
+
+        if xml_components is not None:
+            if xml_components == AllComponents():
+                for ical_subcomponent in component.subcomponents():
+                    result.addComponent(ical_subcomponent)
+            else:
+                for xml_subcomponent in xml_components:
+                    for ical_subcomponent in component.subcomponents():
+                        if ical_subcomponent.name() == xml_subcomponent.type:
+                            result.addComponent(self.compFilter(xml_subcomponent, ical_subcomponent))
+
+        if xml_properties is not None:
+            if xml_properties == AllProperties():
+                for ical_property in component.properties():
+                    result.addProperty(ical_property)
+            else:
+                for xml_property in xml_properties:
+                    name = xml_property.property_name
+                    for ical_property in component.properties(name):
+                        result.addProperty(ical_property)
+
+        return result
+
+    def expandRecurrence(self, calendar, timezone=None):
+        """
+        Expand the recurrence set into individual items.
+        @param calendar: the L{Component} for the calendar to operate on.
+        @param timezone: the L{Component} the VTIMEZONE to use for floating/all-day.
+        @return: the L{Component} for the result.
+        """
+        return calendar.expand(self.calendardata.recurrence_set.start, self.calendardata.recurrence_set.end, timezone)
+    
+    def limitRecurrence(self, calendar):
+        """
+        Limit the set of overridden instances returned to only those
+        that are needed to describe the range of instances covered
+        by the specified time range.
+        @param calendar: the L{Component} for the calendar to operate on.
+        @return: the L{Component} for the result.
+        """
+        raise NotImplementedError()
+        return calendar
+    
+    def limitFreeBusy(self, calendar):
+        """
+        Limit the range of any FREEBUSY properties in the calendar, returning
+        a new calendar if limits were applied, or the same one if no limits were applied.
+        @param calendar: the L{Component} for the calendar to operate on.
+        @return: the L{Component} for the result.
+        """
+        
+        # First check for any VFREEBUSYs - can ignore limit if there are none
+        if calendar.mainType() != "VFREEBUSY":
+            return calendar
+        
+        # Create duplicate calendar and filter FREEBUSY properties
+        calendar = calendar.duplicate()
+        for component in calendar.subcomponents():
+            if component.name() != "VFREEBUSY":
+                continue
+            for property in component.properties("FREEBUSY"):
+                newvalue = []
+                for period in property.value():
+                    clipped = clipPeriod(period, (self.calendardata.freebusy_set.start, self.calendardata.freebusy_set.end))
+                    if clipped:
+                        newvalue.append(clipped)
+                if len(newvalue):
+                    property.setValue(newvalue)
+                else:
+                    component.removeProperty(property)
+        return calendar
+
+    def merge(self, icalnew, icalold):
+        """
+        Calendar-data merging does not happen
+        """
+        raise NotImplementedError

Added: CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/filter.py
===================================================================
--- CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/filter.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/filter.py	2009-10-30 20:11:23 UTC (rev 4685)
@@ -0,0 +1,50 @@
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+__all__ = [
+    "ICalendarFilter",
+]
+
+class ICalendarFilter(object):
+    """
+    Abstract class that defines an iCalendar filter/merge object
+    """
+
+
+    def __init__(self):
+        pass
+    
+    def filter(self, ical):
+        """
+        Filter the supplied iCalendar (vobject) data using the request information.
+
+        @param ical: iCalendar object
+        @type ical: L{Component}
+        
+        @return: L{Component} for the filtered calendar data
+        """
+        raise NotImplementedError
+    
+    def merge(self, icalnew, icalold):
+        """
+        Merge the old iCalendar (vobject) data into the new iCalendar data using the request information.
+        
+        @param icalnew: new iCalendar object to merge data into
+        @type icalnew: L{Component}
+        @param icalold: old iCalendar data to merge data from
+        @type icalold: L{Component}
+        """
+        raise NotImplementedError

Added: CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/privateevents.py
===================================================================
--- CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/privateevents.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/privateevents.py	2009-10-30 20:11:23 UTC (rev 4685)
@@ -0,0 +1,195 @@
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twisted.web2 import responsecode
+from twisted.web2.http import HTTPError, StatusResponse
+from twistedcaldav.caldavxml import Property, CalendarData, CalendarComponent,\
+    AllProperties, AllComponents
+from twistedcaldav.datafilters.calendardata import CalendarDataFilter
+
+__all__ = [
+    "PrivateEventFilter",
+]
+
+class PrivateEventFilter(object):
+    """
+    Filter a private event to match the rights of the non-owner user accessing the data
+    """
+
+    # Private Event access levels.
+    ACCESS_PROPERTY     = "X-CALENDARSERVER-ACCESS"
+    ACCESS_PUBLIC       = "PUBLIC"
+    ACCESS_PRIVATE      = "PRIVATE"
+    ACCESS_CONFIDENTIAL = "CONFIDENTIAL"
+    ACCESS_RESTRICTED   = "RESTRICTED"
+
+    accessMap = {
+        "PUBLIC"       : ACCESS_PUBLIC,
+        "PRIVATE"      : ACCESS_PRIVATE,
+        "CONFIDENTIAL" : ACCESS_CONFIDENTIAL,
+        "RESTRICTED"   : ACCESS_RESTRICTED,
+    }
+
+    confidentialPropertiesMap = {
+        "VCALENDAR": ("PRODID", "VERSION", "CALSCALE", ACCESS_PROPERTY),
+        "VEVENT":    ("UID", "RECURRENCE-ID", "SEQUENCE", "DTSTAMP", "STATUS", "TRANSP", "DTSTART", "DTEND", "DURATION", "RRULE", "RDATE", "EXRULE", "EXDATE", ),
+        "VTODO":     ("UID", "RECURRENCE-ID", "SEQUENCE", "DTSTAMP", "STATUS", "DTSTART", "COMPLETED", "DUE", "DURATION", "RRULE", "RDATE", "EXRULE", "EXDATE", ),
+        "VJOURNAL":  ("UID", "RECURRENCE-ID", "SEQUENCE", "DTSTAMP", "STATUS", "DTSTART", "RRULE", "RDATE", "EXRULE", "EXDATE", ),
+        "VFREEBUSY": ("UID", "DTSTAMP", "DTSTART", "DTEND", "DURATION", "FREEBUSY", ),
+        "VTIMEZONE": None,
+    }
+    extraRestrictedProperties = ("SUMMARY", "LOCATION",)
+
+    def __init__(self, accessRestriction, isowner):
+        """
+        
+        @param accessRestriction: one of the access levels in L{Component}
+        @type accessRestriction: C{str}
+        @param isowner: whether the current user is the owner of the data
+        @type isowner: C{bool}
+        """
+        
+        self.accessRestriction = accessRestriction
+        self.isowner = isowner
+    
+    def filter(self, ical):
+        """
+        Filter the supplied iCalendar (vobject) data using the request information.
+
+        @param ical: iCalendar object
+        @type ical: L{Component} or C{str}
+        
+        @return: L{Component} for the filtered calendar data
+        """
+        
+        if self.isowner or self.accessRestriction == PrivateEventFilter.ACCESS_PUBLIC:
+            # No need to filter for the owner or public event
+            return ical
+        
+        elif self.accessRestriction == PrivateEventFilter.ACCESS_PRIVATE:
+            # We should never get here because ACCESS_PRIVATE is protected via an ACL
+            raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Access Denied"))
+
+        elif self.accessRestriction == PrivateEventFilter.ACCESS_PUBLIC:
+            return ical
+        elif self.accessRestriction in (PrivateEventFilter.ACCESS_CONFIDENTIAL, PrivateEventFilter.ACCESS_RESTRICTED):
+            # Create a CALDAV:calendar-data element with the appropriate iCalendar Component/Property
+            # filter in place for the access restriction in use
+            
+            extra_access = ()
+            if self.accessRestriction == PrivateEventFilter.ACCESS_RESTRICTED:
+                extra_access = (
+                    Property(name="SUMMARY"),
+                    Property(name="LOCATION"),
+                )
+
+            calendardata = CalendarData(
+                CalendarComponent(
+                    
+                    # VCALENDAR properties
+                    Property(name="PRODID"),
+                    Property(name="VERSION"),
+                    Property(name="CALSCALE"),
+                    Property(name=PrivateEventFilter.ACCESS_PROPERTY),
+
+                    # VEVENT
+                    CalendarComponent(
+                        Property(name="UID"),
+                        Property(name="RECURRENCE-ID"),
+                        Property(name="SEQUENCE"),
+                        Property(name="DTSTAMP"),
+                        Property(name="STATUS"),
+                        Property(name="TRANSP"),
+                        Property(name="DTSTART"),
+                        Property(name="DTEND"),
+                        Property(name="DURATION"),
+                        Property(name="RRULE"),
+                        Property(name="RDATE"),
+                        Property(name="EXRULE"),
+                        Property(name="EXDATE"),
+                        *extra_access,
+                        **{"name":"VEVENT"}
+                    ),
+                    
+                    # VTODO
+                    CalendarComponent(
+                        Property(name="UID"),
+                        Property(name="RECURRENCE-ID"),
+                        Property(name="SEQUENCE"),
+                        Property(name="DTSTAMP"),
+                        Property(name="STATUS"),
+                        Property(name="DTSTART"),
+                        Property(name="COMPLETED"),
+                        Property(name="DUE"),
+                        Property(name="DURATION"),
+                        Property(name="RRULE"),
+                        Property(name="RDATE"),
+                        Property(name="EXRULE"),
+                        Property(name="EXDATE"),
+                        *extra_access,
+                        **{"name":"VTODO"}
+                    ),
+                    
+                    # VJOURNAL
+                    CalendarComponent(
+                        Property(name="UID"),
+                        Property(name="RECURRENCE-ID"),
+                        Property(name="SEQUENCE"),
+                        Property(name="DTSTAMP"),
+                        Property(name="STATUS"),
+                        Property(name="TRANSP"),
+                        Property(name="DTSTART"),
+                        Property(name="RRULE"),
+                        Property(name="RDATE"),
+                        Property(name="EXRULE"),
+                        Property(name="EXDATE"),
+                        *extra_access,
+                        **{"name":"VJOURNAL"}
+                    ),
+                    
+                    # VFREEBUSY
+                    CalendarComponent(
+                        Property(name="UID"),
+                        Property(name="DTSTAMP"),
+                        Property(name="DTSTART"),
+                        Property(name="DTEND"),
+                        Property(name="DURATION"),
+                        Property(name="FREEBUSY"),
+                        *extra_access,
+                        **{"name":"VFREEBUSY"}
+                    ),
+                    
+                    # VTIMEZONE
+                    CalendarComponent(
+                        AllProperties(),
+                        AllComponents(),
+                        name="VTIMEZONE",
+                    ),
+                    name="VCALENDAR",
+                ),
+            )
+
+            # Now "filter" the resource calendar data through the CALDAV:calendar-data element
+            return CalendarDataFilter(calendardata).filter(ical)
+        else:
+            # Unknown access restriction
+            raise HTTPError(StatusResponse(responsecode.FORBIDDEN))
+    
+    def merge(self, icalnew, icalold):
+        """
+        Private event merging does not happen
+        """
+        raise NotImplementedError

Added: CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/__init__.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/__init__.py	2009-10-30 20:11:23 UTC (rev 4685)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+Tests for the twistedcaldav.datafilters module.
+"""

Added: CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/test_calendardata.py
===================================================================
--- CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/test_calendardata.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/test_calendardata.py	2009-10-30 20:11:23 UTC (rev 4685)
@@ -0,0 +1,361 @@
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+import twistedcaldav.test.util
+from twistedcaldav.datafilters.calendardata import CalendarDataFilter
+from twistedcaldav.caldavxml import CalendarData, CalendarComponent,\
+    AllComponents, AllProperties, Property
+
+class CalendarDataTest (twistedcaldav.test.util.TestCase):
+
+    def test_empty(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
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        empty = CalendarData()
+        self.assertEqual(str(CalendarDataFilter(empty).filter(data)), data)
+
+    def test_vcalendar_no_effect(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
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        no_effect = CalendarData(
+            CalendarComponent(
+                name="VCALENDAR"
+            )
+        )
+        self.assertEqual(str(CalendarDataFilter(no_effect).filter(data)), data)
+ 
+        no_effect = CalendarData(
+            CalendarComponent(
+                AllComponents(),
+                AllProperties(),
+                name="VCALENDAR"
+            )
+        )
+        self.assertEqual(str(CalendarDataFilter(no_effect).filter(data)), data)
+
+    def test_vcalendar_no_props(self):
+        
+        data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-WR-CALNAME:Help
+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")
+        
+        result = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//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")
+        
+        empty = CalendarData(
+            CalendarComponent(
+                AllComponents(),
+                name="VCALENDAR"
+            )
+        )
+        self.assertEqual(str(CalendarDataFilter(empty).filter(data)), result)
+
+    def test_vcalendar_no_comp(self):
+        
+        data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-WR-CALNAME:Help
+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")
+        
+        result = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-WR-CALNAME:Help
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        empty = CalendarData(
+            CalendarComponent(
+                AllProperties(),
+                name="VCALENDAR"
+            )
+        )
+        self.assertEqual(str(CalendarDataFilter(empty).filter(data)), result)
+
+    def test_vevent_no_effect(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
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        no_effect = CalendarData(
+            CalendarComponent(
+                CalendarComponent(
+                    name="VEVENT"
+                ),
+                AllProperties(),
+                name="VCALENDAR"
+            )
+        )
+        self.assertEqual(str(CalendarDataFilter(no_effect).filter(data)), data)
+
+    def test_vevent_other_component(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
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        result = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        other_component = CalendarData(
+            CalendarComponent(
+                CalendarComponent(
+                    name="VTODO"
+                ),
+                AllProperties(),
+                name="VCALENDAR"
+            )
+        )
+        self.assertEqual(str(CalendarDataFilter(other_component).filter(data)), result)
+
+    def test_vevent_no_props(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
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        result = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        empty = CalendarData(
+            CalendarComponent(
+                CalendarComponent(
+                    AllComponents(),
+                    name="VEVENT"
+                ),
+                AllProperties(),
+                name="VCALENDAR"
+            )
+        )
+        
+        filtered = str(CalendarDataFilter(empty).filter(data))
+        filtered = "".join([line for line in filtered.splitlines(True) if not line.startswith("UID:")])
+
+        self.assertEqual(str(CalendarDataFilter(empty).filter(data)), result)
+
+    def test_vevent_no_comp(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
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        result = """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")
+        
+        empty = CalendarData(
+            CalendarComponent(
+                CalendarComponent(
+                    AllProperties(),
+                    name="VEVENT"
+                ),
+                AllProperties(),
+                name="VCALENDAR"
+            )
+        )
+        self.assertEqual(str(CalendarDataFilter(empty).filter(data)), result)
+
+    def test_vevent_some_props(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
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        result = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        empty = CalendarData(
+            CalendarComponent(
+                CalendarComponent(
+                    AllComponents(),
+                    Property(
+                        name="UID",
+                    ),
+                    Property(
+                        name="DTSTART",
+                    ),
+                    Property(
+                        name="DTEND",
+                    ),
+                    name="VEVENT"
+                ),
+                AllProperties(),
+                name="VCALENDAR"
+            )
+        )
+        
+        self.assertEqual(str(CalendarDataFilter(empty).filter(data)), result)
+

Added: CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/test_privateevents.py
===================================================================
--- CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/test_privateevents.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/per-user-icalendar-4669/twistedcaldav/datafilters/test/test_privateevents.py	2009-10-30 20:11:23 UTC (rev 4685)
@@ -0,0 +1,153 @@
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twisted.web2.http import HTTPError
+import twistedcaldav.test.util
+from twistedcaldav.datafilters.privateevents import PrivateEventFilter
+
+class PrivateEventsTest (twistedcaldav.test.util.TestCase):
+
+    def test_public_default(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
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        self.assertEqual(str(PrivateEventFilter(PrivateEventFilter.ACCESS_PUBLIC, True).filter(data)), data)
+        self.assertEqual(str(PrivateEventFilter(PrivateEventFilter.ACCESS_PUBLIC, False).filter(data)), data)
+
+    def test_public(self):
+        
+        data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:PUBLIC
+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")
+        
+        self.assertEqual(str(PrivateEventFilter(PrivateEventFilter.ACCESS_PUBLIC, True).filter(data)), data)
+        self.assertEqual(str(PrivateEventFilter(PrivateEventFilter.ACCESS_PUBLIC, False).filter(data)), data)
+
+    def test_private(self):
+        
+        data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:PRIVATE
+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")
+        
+        self.assertEqual(str(PrivateEventFilter(PrivateEventFilter.ACCESS_PRIVATE, True).filter(data)), data)
+        pfilter = PrivateEventFilter(PrivateEventFilter.ACCESS_PRIVATE, False)
+        self.assertRaises(HTTPError, pfilter.filter, data)
+
+    def test_confidential(self):
+        
+        data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:CONFIDENTIAL
+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
+SUMMARY:Confidential
+DESCRIPTION:In confidence
+LOCATION:My office
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        filtered = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+END:VEVENT
+X-CALENDARSERVER-ACCESS:CONFIDENTIAL
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        self.assertEqual(str(PrivateEventFilter(PrivateEventFilter.ACCESS_CONFIDENTIAL, True).filter(data)), data)
+        self.assertEqual(str(PrivateEventFilter(PrivateEventFilter.ACCESS_CONFIDENTIAL, False).filter(data)), filtered)
+
+    def test_restricted(self):
+        
+        data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:CONFIDENTIAL
+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
+SUMMARY:Confidential
+DESCRIPTION:In confidence
+LOCATION:My office
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        filtered = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+LOCATION:My office
+SUMMARY:Confidential
+END:VEVENT
+X-CALENDARSERVER-ACCESS:CONFIDENTIAL
+END:VCALENDAR
+""".replace("\n", "\r\n")
+        
+        self.assertEqual(str(PrivateEventFilter(PrivateEventFilter.ACCESS_RESTRICTED, True).filter(data)), data)
+        self.assertEqual(str(PrivateEventFilter(PrivateEventFilter.ACCESS_RESTRICTED, False).filter(data)), filtered)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20091030/9658ad35/attachment-0001.html>


More information about the calendarserver-changes mailing list