[CalendarServer-changes] [14169] CalendarServer/branches/release/CalendarServer-5.3-dev
source_changes at macosforge.org
source_changes at macosforge.org
Thu Nov 13 17:18:14 PST 2014
Revision: 14169
http://trac.calendarserver.org//changeset/14169
Author: cdaboo at apple.com
Date: 2014-11-13 17:18:13 -0800 (Thu, 13 Nov 2014)
Log Message:
-----------
Client transp fix.
Modified Paths:
--------------
CalendarServer/branches/release/CalendarServer-5.3-dev/support/build.sh
CalendarServer/branches/release/CalendarServer-5.3-dev/twext/python/launchd.py
CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/stdconfig.py
CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/storebridge.py
CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/util.py
CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/icaldiff.py
CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/implicit.py
CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/test/test_icaldiff.py
Added Paths:
-----------
CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_util.py
Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/support/build.sh
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/support/build.sh 2014-11-14 01:14:48 UTC (rev 14168)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/support/build.sh 2014-11-14 01:18:13 UTC (rev 14169)
@@ -809,7 +809,7 @@
# XXX actually PyCalendar should be imported in-place.
py_dependency -fe -i "src" -r HEAD \
- "PyCalendar" "pycalendar" "pycalendar" \
+ "PyCalendar" "pycalendar" "pycalendar-2" \
"${svn_uri_base}/PyCalendar/branches/CalendarServer-5.2";
#
Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twext/python/launchd.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twext/python/launchd.py 2014-11-14 01:14:48 UTC (rev 14168)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twext/python/launchd.py 2014-11-14 01:18:13 UTC (rev 14169)
@@ -40,9 +40,9 @@
ffi.cdef("""
-static const char* LAUNCH_KEY_CHECKIN;
-static const char* LAUNCH_JOBKEY_LABEL;
-static const char* LAUNCH_JOBKEY_SOCKETS;
+static char const* const LAUNCH_KEY_CHECKIN;
+static char const* const LAUNCH_JOBKEY_LABEL;
+static char const* const LAUNCH_JOBKEY_SOCKETS;
typedef enum {
LAUNCH_DATA_DICTIONARY = 1,
Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/stdconfig.py 2014-11-14 01:14:48 UTC (rev 14168)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/stdconfig.py 2014-11-14 01:18:13 UTC (rev 14169)
@@ -867,6 +867,16 @@
"MaxPrincipalSearchReportResults": 500,
#
+ # Client fixes per user-agent match
+ #
+ "ClientFixes" : {
+ "ForceAttendeeTRANSP" : [
+ "iOS/8\\.0(\\..*)?",
+ "iOS/8\\.1(\\..*)?",
+ ],
+ },
+
+ #
# Localization
#
"Localization" : {
@@ -1427,6 +1437,19 @@
+def _updateClientFixes(configDict, reloading=False):
+ #
+ # Compile ClientFixes expressions for speed
+ #
+ try:
+ configDict.ClientFixesCompiled = {}
+ for key, expressions in configDict.ClientFixes.items():
+ configDict.ClientFixesCompiled[key] = [re.compile("^{}$".format(x)) for x in expressions]
+ except re.error, e:
+ raise ConfigurationError("Invalid regular expression in ClientFixes: %s" % (e,))
+
+
+
def _updateLogLevels(configDict, reloading=False):
log.publisher.levels.clearLogLevels()
@@ -1625,6 +1648,7 @@
_postUpdateProxyDBService,
_updateACLs,
_updateRejectClients,
+ _updateClientFixes,
_updateLogLevels,
_updateNotifications,
_updateScheduling,
Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/storebridge.py 2014-11-14 01:14:48 UTC (rev 14168)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/storebridge.py 2014-11-14 01:18:13 UTC (rev 14169)
@@ -55,6 +55,7 @@
from twistedcaldav.resource import CalDAVResource, GlobalAddressBookResource, \
DefaultAlarmPropertyMixin
from twistedcaldav.scheduling_store.caldav.resource import ScheduleInboxResource
+from twistedcaldav.util import matchClientFixes
from twistedcaldav.vcard import Component as VCard, InvalidVCardDataError
from txdav.base.propertystore.base import PropertyName
@@ -2809,6 +2810,10 @@
"Can't parse calendar data"
))
+ # Look for client fixes
+ ua = request.headers.getHeader("User-Agent")
+ self._newStoreParent._txn._client_fixes = matchClientFixes(config, ua)
+
# storeComponent needs to know who the auth'd user is for access control
# TODO: this needs to be done in a better way - ideally when the txn is created for the request,
# we should set a txn.authzid attribute.
Added: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_util.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_util.py (rev 0)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_util.py 2014-11-14 01:18:13 UTC (rev 14169)
@@ -0,0 +1,98 @@
+##
+# Copyright (c) 2005-2014 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.util import userAgentProductTokens, matchClientFixes
+from twistedcaldav.stdconfig import _updateClientFixes
+from twistedcaldav.config import ConfigDict
+
+class TestUtil(twistedcaldav.test.util.TestCase):
+ """
+ Tests for L{twistedcaldav.util}
+ """
+ def test_userAgentProductTokens(self):
+ """
+ Test that L{userAgentProductTokens} correctly parses a User-Agent header.
+ """
+ for hdr, result in (
+ # Valid syntax
+ ("Client/1.0", ["Client/1.0", ]),
+ ("Client/1.0 FooBar/2", ["Client/1.0", "FooBar/2", ]),
+ ("Client/1.0 (commentary here)", ["Client/1.0", ]),
+ ("Client/1.0 (FooBar/2)", ["Client/1.0", ]),
+ ("Client/1.0 (commentary here) FooBar/2", ["Client/1.0", "FooBar/2", ]),
+ ("Client/1.0 (commentary here) FooBar/2 (more commentary here) ", ["Client/1.0", "FooBar/2", ]),
+
+ # Invalid syntax
+ ("Client/1.0 (commentary here FooBar/2", ["Client/1.0", ]),
+ ("Client/1.0 commentary here) FooBar/2", ["Client/1.0", "commentary", "here)", "FooBar/2", ]),
+ ):
+ self.assertEqual(userAgentProductTokens(hdr), result, msg="Mismatch: {}".format(hdr))
+
+
+ def test_matchClientFixes(self):
+ """
+ Test that L{matchClientFixes} correctly identifies clients with matching fix tokens.
+ """
+ c = ConfigDict()
+ c.ClientFixes = {
+ "fix1": [
+ "Client/1\\.0.*",
+ "Client/1\\.1(\\..*)?",
+ "Client/2",
+ ],
+ "fix2": [
+ "Other/1\\.0.*",
+ ],
+ }
+ _updateClientFixes(c)
+ _updateClientFixes(c)
+
+ # Valid matches
+ for ua in (
+ "Client/1.0 FooBar/2",
+ "Client/1.0.1 FooBar/2",
+ "Client/1.0.1.1 FooBar/2",
+ "Client/1.1 FooBar/2",
+ "Client/1.1.1 FooBar/2",
+ "Client/2 FooBar/2",
+ ):
+ self.assertEqual(
+ matchClientFixes(c, ua),
+ set(("fix1",)),
+ msg="Did not match {}".format(ua),
+ )
+
+ # Valid non-matches
+ for ua in (
+ "Client/1 FooBar/2",
+ "Client/1.10 FooBar/2",
+ "Client/2.0 FooBar/2",
+ "Client/2.0.1 FooBar/2",
+ "Client FooBar/2",
+ "Client/3 FooBar/2",
+ "Client/3.0 FooBar/2",
+ "Client/10 FooBar/2",
+ "Client/10.0 FooBar/2",
+ "Client/10.0.1 FooBar/2",
+ "Client/10.0.1 (Client/1.0) FooBar/2",
+ "Client/10.0.1 (foo Client/1.0 bar) FooBar/2",
+ ):
+ self.assertEqual(
+ matchClientFixes(c, ua),
+ set(),
+ msg="Incorrectly matched {}".format(ua),
+ )
Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/util.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/util.py 2014-11-14 01:14:48 UTC (rev 14168)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/util.py 2014-11-14 01:18:13 UTC (rev 14169)
@@ -18,6 +18,7 @@
import re
import sys
import base64
+import itertools
from subprocess import Popen, PIPE, STDOUT
from hashlib import md5, sha1
@@ -517,3 +518,57 @@
cuas = principal.record.calendarUserAddresses
return (fullName, rec.guid, cuas)
+
+
+
+def userAgentProductTokens(user_agent):
+ """
+ Parse an HTTP User-Agent header to extract the product tokens and ignore
+ any parenthesized comment strings in the header.
+
+ @param user_agent: text of User-Agent header value
+ @type user_agent: L{str}
+
+ @return: list of product tokens extracted from the header
+ @rtype: L{list}
+ """
+
+ ua_hdr = user_agent.split()
+ ua_tokens = []
+ comment = False
+ for token in ua_hdr:
+ if comment:
+ if token.endswith(")"):
+ comment = False
+ elif token.startswith("("):
+ if not token.endswith(")"):
+ comment = True
+ else:
+ ua_tokens.append(token)
+
+ return ua_tokens
+
+
+
+def matchClientFixes(config, user_agent):
+ """
+ Given a user-agent string, see if it matches any of the configured client fixes.
+
+ @param config: the L{config} to match against.
+ @type config: L{ConfigDict}
+ @param user_agent: the HTTP User-Agent header value to test.
+ @type user_agent: L{str}
+ """
+
+ if len(config.ClientFixesCompiled) == 0 or not user_agent:
+ return set()
+
+ ua_tokens = userAgentProductTokens(user_agent)
+
+ client_fixes = set()
+ for fix, patterns in config.ClientFixesCompiled.items():
+ for pattern, token in itertools.product(patterns, ua_tokens):
+ if pattern.match(token) is not None:
+ client_fixes.add(fix)
+ break
+ return client_fixes
Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/icaldiff.py 2014-11-14 01:14:48 UTC (rev 14168)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/icaldiff.py 2014-11-14 01:18:13 UTC (rev 14169)
@@ -27,10 +27,6 @@
from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
from txdav.caldav.datastore.scheduling.itip import iTipGenerator
-"""
-Class that handles diff'ing two calendar objects.
-"""
-
__all__ = [
"iCalDiff",
]
@@ -38,19 +34,33 @@
log = Logger()
class iCalDiff(object):
+ """
+ This object is used for doing comparisons between two calendar objects to
+ work out what the changes are, in order to determine whether a scheduling
+ operation might be needed. The behavior will varying based on whether the
+ change is being triggered by an Organizer or an Attendee.
+ """
- def __init__(self, oldcalendar, newcalendar, smart_merge):
+
+ def __init__(self, oldcalendar, newcalendar, smart_merge, forceTRANSP=False):
"""
+ Note that this object will always duplicate the calendar objects when doing
+ comparisons so as not to change the calendar objects passed in.
- @param oldcalendar:
- @type oldcalendar:
- @param newcalendar:
- @type newcalendar:
+ @param oldcalendar: the existing calendar object to compare to
+ @type oldcalendar: L{Component}
+ @param newcalendar: the new calendar object
+ @type newcalendar: L{Component}
+ @param smart_merge: whether or not a "smart" CalDAV merge is done (If-Schedule-Tag-Match)
+ @type smart_merge: L{bool}
+ @param forceTRANSP: whether or not to apply a fix for clients failing to set TRANSP properly
+ @type forceTRANSP: L{bool}
"""
self.oldcalendar = oldcalendar
self.newcalendar = newcalendar
self.smart_merge = smart_merge
+ self.forceTRANSP = forceTRANSP
def organizerDiff(self):
@@ -501,6 +511,13 @@
replyNeeded = True
+ # Apply client fix only if the PARTSTAT was changed
+ if self.forceTRANSP:
+ if clientAttendee.parameterValue("PARTSTAT", "NEEDS-ACTION") in ("ACCEPTED", "TENTATIVE",):
+ clientComponent.replaceProperty(Property("TRANSP", "OPAQUE"))
+ else:
+ clientComponent.replaceProperty(Property("TRANSP", "TRANSPARENT"))
+
if serverAttendee.parameterValue("RSVP", "FALSE") != clientAttendee.parameterValue("RSVP", "FALSE"):
if clientAttendee.parameterValue("RSVP", "FALSE") == "FALSE":
try:
Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/implicit.py 2014-11-14 01:14:48 UTC (rev 14168)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/implicit.py 2014-11-14 01:18:13 UTC (rev 14169)
@@ -1355,7 +1355,14 @@
(caldav_namespace, "valid-attendee-change"),
"Cannot use an event when not listed as an attendee in the organizer's copy",
))
- differ = iCalDiff(oldcalendar, self.calendar, self.do_smart_merge)
+
+ # Check for a required client fix
+ if hasattr(self.txn, "_client_fixes"):
+ forceTRANSP = "ForceAttendeeTRANSP" in self.txn._client_fixes
+ else:
+ forceTRANSP = False
+
+ differ = iCalDiff(oldcalendar, self.calendar, self.do_smart_merge, forceTRANSP=forceTRANSP)
return differ.attendeeMerge(self.attendee)
Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/test/test_icaldiff.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/test/test_icaldiff.py 2014-11-14 01:14:48 UTC (rev 14168)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/caldav/datastore/scheduling/test/test_icaldiff.py 2014-11-14 01:18:13 UTC (rev 14169)
@@ -4654,6 +4654,391 @@
self.assertEqual(got_rids, rids, msg="%s expected R-IDs: '%s', got: '%s'" % (description, rids, got_rids,))
+ def test_attendee_merge_forceTRANSP(self):
+
+ data = (
+ (
+ "#1.1 Simple component, no change",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, False, (), """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;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.2 Simple component, change to ACCEPTED",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, True, ("",), """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;PARTSTAT=ACCEPTED;X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXXZ:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.3 Simple component, change to DECLINED",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=DECLINED:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, True, ("",), """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;PARTSTAT=DECLINED;X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXXZ:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.4 Simple component, change to TENTATIVE",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=TENTATIVE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, True, ("",), """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;PARTSTAT=TENTATIVE;X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXXZ:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.5 Simple component, change to ACCEPTED with TRANSP",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=TENTATIVE:mailto:user2 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, True, ("",), """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;PARTSTAT=TENTATIVE;X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXXZ:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.6 Simple component, remain as ACCEPTED with TRANSP",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, False, (), """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;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.7 Simple component, remain as ACCEPTED change TRANSP",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, False, (), """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;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.8 Simple component, ACCEPTED->DECLINED with TRANSP",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=DECLINED:mailto:user2 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, True, ("",), """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;PARTSTAT=DECLINED;X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXXZ:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ )
+
+ for description, calendar1, calendar2, attendee, result in data:
+ differ = iCalDiff(
+ Component.fromString(calendar1),
+ Component.fromString(calendar2),
+ False,
+ forceTRANSP=True,
+ )
+ diffResult = differ.attendeeMerge(attendee)
+ diffResult = (
+ diffResult[0],
+ diffResult[1],
+ tuple(sorted(diffResult[2])),
+ re.sub(
+ "X-CALENDARSERVER-DTSTAMP=[^Z]+",
+ "X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXX",
+ str(diffResult[3]).replace("\r", "").replace("\n ", "")
+ ) if diffResult[3] else None,
+ )
+ self.assertEqual(diffResult, result, msg="%s: actual result: (%s)" % (description, ", ".join([str(i).replace("\r", "") for i in diffResult]),))
+
+
def test_organizer_smart_merge(self):
data1 = (
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20141113/c4c8efef/attachment-0001.html>
More information about the calendarserver-changes
mailing list