<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[12110] CalendarServer/branches/users/gaya/sharedgroupfixes</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/12110">12110</a></dd>
<dt>Author</dt> <dd>gaya@apple.com</dd>
<dt>Date</dt> <dd>2013-12-13 22:28:16 -0800 (Fri, 13 Dec 2013)</dd>
</dl>
<h3>Log Message</h3>
<pre>merge in 11861 to 12016 from trunk</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterREADMEtxt">CalDAVTester/branches/users/gaya/sharedgroupfixestester/README.txt</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterResourceCalDAVprivatecomments32ics">CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/32.ics</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterResourceCalDAVprivatecomments35ics">CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/35.ics</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterResourceCalDAVprivatecomments37ics">CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/37.ics</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptsserverserverinfotemplatexml">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-template.xml</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptsserverserverinfodtd">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.dtd</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptsserverserverinfoxml">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.xml</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVcaldavtestdtd">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/caldavtest.dtd</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVimplicitdefaultcalendarxml">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/implicitdefaultcalendar.xml</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVprivatecommentsxml">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/privatecomments.xml</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVsharingnotificationsyncxml">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sharing-notification-sync.xml</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVsyncreportxml">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sync-report.xml</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCardDAVcaldavtestdtd">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/caldavtest.dtd</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestestersrccaldavtestpy">CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/caldavtest.py</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestestersrcmanagerpy">CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/manager.py</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestestersrcrequestpy">CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/request.py</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestestersrcserverinfopy">CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/serverinfo.py</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestestersrcxmlDefspy">CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/xmlDefs.py</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterverifiersaddressDataMatchpy">CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/addressDataMatch.py</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterverifierscalendarDataMatchpy">CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/calendarDataMatch.py</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterverifiersfreeBusypy">CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/freeBusy.py</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterverifierspostFreeBusypy">CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/postFreeBusy.py</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterverifiersxmlElementMatchpy">CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/xmlElementMatch.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesHACKING">CalendarServer/branches/users/gaya/sharedgroupfixes/HACKING</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesbin_calendarserver_preamblepy">CalendarServer/branches/users/gaya/sharedgroupfixes/bin/_calendarserver_preamble.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushamppushpy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/amppush.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushapplepushpy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/applepush.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushnotifierpy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/notifier.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushtesttest_amppushpy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_amppush.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushtesttest_applepushpy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_applepush.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushtesttest_notifierpy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_notifier.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushutilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertapcaldavpy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/caldav.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertaputilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsampnotificationspy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/ampnotifications.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsanonymizepy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/anonymize.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolscalverifypy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/calverify.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsdbinspectpy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/dbinspect.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsgatewaypy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/gateway.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsmanagetimezonespy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/managetimezones.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolspurgepy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/purge.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsshelldirectorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/shell/directory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstestdeprovisioncaldavdplist">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/deprovision/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstestgatewaycaldavdplist">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/gateway/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstestprincipalscaldavdplist">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/principals/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstesttest_calverifypy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_calverify.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstesttest_gatewaypy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_gateway.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstesttest_purgepy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstesttest_purge_old_eventspy">CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge_old_events.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfauthaugmentsdtd">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments.dtd</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfcaldavdtestplist">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconflocalserverstestxml">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers-test.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconflocalserversxml">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfremoteserverstestxml">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers-test.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfremoteserversxml">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfresourcescaldavdresourcesplist">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/resources/caldavd-resources.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformanceloadtesticalpy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/ical.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformanceloadtestprofilespy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/profiles.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformanceloadtestsimpy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/sim.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformanceloadtesttest_icalpy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/test_ical.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformancesqlusagerequestsinvitepy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/invite.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformancesqlusagerequestsputpy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/put.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformancesqlusagerequestsquerypy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/query.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformancesqlusagerequestssyncpy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/sync.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformancesqlusagesqlusagepy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/sqlusage.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformancestatspy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/stats.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribperformancetest_statspy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/test_stats.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribtoolsrequest_monitorpy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/request_monitor.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixescontribtoolssortrecurrencespy">CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/sortrecurrences.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixessetuppy">CalendarServer/branches/users/gaya/sharedgroupfixes/setup.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixessupportbuildsh">CalendarServer/branches/users/gaya/sharedgroupfixes/support/build.sh</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixessupportversionpy">CalendarServer/branches/users/gaya/sharedgroupfixes/support/version.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestest">CalendarServer/branches/users/gaya/sharedgroupfixes/test</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextenterprisedalsyntaxpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/dal/syntax.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextenterprisefixturespy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/fixtures.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextenterprisequeuepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/queue.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextenterprisetesttest_queuepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/test/test_queue.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextinternetsendfdportpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/internet/sendfdport.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextprotocolstesttest_memcachepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/test/test_memcache.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhoaggregatepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/aggregate.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhodirectorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/directory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhoexpressionpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/expression.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhoidirectorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/idirectory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhoindexpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/index.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_aggregatepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_aggregate.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_directorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_directory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_expressionpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_expression.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_utilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_xmlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_xml.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhoutilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhoxmlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/xml.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldav__init__py">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/__init__.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavaccountingpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/accounting.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavauthkerbpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/authkerb.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavbackuppy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/backup.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavcaldavxmlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/caldavxml.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavcarddavxmlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/carddavxml.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavconfigpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/config.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavcustomxmlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/customxml.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdatabasepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/database.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdatafilterscalendardatapy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/datafilters/calendardata.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdateopspy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dateops.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryaddressbookpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/addressbook.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryaugmentpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/augment.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorycommonpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/common.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorydirectoryprincipalresourcehtml">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory-principal-resource.html</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorydirectorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryidirectorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/idirectory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryldapdirectorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/ldapdirectory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryopendirectorybackerpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/opendirectorybacker.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryprincipalpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/principal.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryresourcepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/resource.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestaugmentstestdefaultxml">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test-default.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestaugmentstestxml">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestresourcescaldavdplist">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/resources/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_aggregatepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_aggregate.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_augmentpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_augment.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_cachedirectorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_cachedirectory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_digestpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_digest.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_directorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_directory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_guidchangepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_guidchange.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_ldapdirectorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_ldapdirectory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_livedirectorypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_livedirectory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_modifypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_modify.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_opendirectorybackerpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_opendirectorybacker.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_proxyprincipaldbpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_proxyprincipaldb.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_resourcespy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_resources.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_xmlfilepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_xmlfile.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestutilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryxmlaugmentsparserpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/xmlaugmentsparser.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdropboxpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dropbox.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavextensionspy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/extensions.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavfreebusyurlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/freebusyurl.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavicalpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/ical.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavicaldavpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/icaldav.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavinstancepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/instance.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavlinkresourcepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/linkresource.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavlocalizationpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/localization.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmemcachelockpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachelock.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmemcachepoolpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachepool.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmethodgetpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/get.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmethodpropfindpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/propfind.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmethodreport_calendar_querypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_calendar_query.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmethodreport_commonpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_common.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmethodreport_freebusypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_freebusy.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmkcolxmlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/mkcolxml.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavnotificationspy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/notifications.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavquerycalendarqueryfilterpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/calendarqueryfilter.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavquerytesttest_calendarquerypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_calendarquery.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavquerytesttest_queryfilterpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_queryfilter.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavresourcepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavscheduling_storecaldavresourcepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/scheduling_store/caldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavsharingpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sharing.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavsqlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavstdconfigpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavstorebridgepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_addressbookmultigetpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_addressbookmultiget.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_caldavxmlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_caldavxml.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_calendarquerypy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_calendarquery.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_configpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_config.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_dateopspy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_dateops.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_icalendarpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_icalendar.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_localizationpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_localization.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_multigetpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_multiget.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_propspy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_props.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_timezonespy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_timezones.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_upgradepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_upgrade.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtimezonespy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezones.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtimezoneservicepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezoneservice.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtimezonestdservicepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezonestdservice.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavupgradepy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/upgrade.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavutilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavvcardpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/vcard.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavxmlutilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/xmlutil.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAfricaCasablancaics">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Casablanca.ics</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAfricaEl_Aaiunics">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAfricaTripoliics">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Tripoli.ics</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAmericaEirunepeics">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Eirunepe.ics</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAmericaPorto_Acreics">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Porto_Acre.ics</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAmericaRio_Brancoics">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Rio_Branco.ics</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoBrazilAcreics">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Brazil/Acre.ics</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoLibyaics">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Libya.ics</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfotimezonesxml">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/timezones.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoversiontxt">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/version.txt</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavbasepropertystorebasepy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/base.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavbasepropertystoretestbasepy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/test/base.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastorefilepy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/file.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreindex_filepy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/index_file.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingaddressmappingpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/addressmapping.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingcaldavdeliverypy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/delivery.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingcaldavschedulerpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/scheduler.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingcaldavtesttest_schedulerpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingcuaddresspy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/cuaddress.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingfreebusypy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/freebusy.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingicaldiffpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icaldiff.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingicalsplitterpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icalsplitter.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingimipoutboundpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/outbound.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingimiptesttest_deliverypy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_delivery.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingimiptesttest_inboundpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingimiptesttest_outboundpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_outbound.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingimplicitpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/implicit.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduledeliverypy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/delivery.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischedulelocalserverspy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/localservers.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduleremoteserverspy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduleresourcepy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/resource.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduleschedulerpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/scheduler.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduletesttest_deliverypy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduletesttest_localserverspy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduletesttest_resourcepy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingitippy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/itip.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingprocessingpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/processing.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingschedulerpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/scheduler.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingtesttest_freebusypy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_freebusy.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingtesttest_icalsplitterpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingtesttest_implicitpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_implicit.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingtesttest_itippy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_itip.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingtesttest_utilspy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_utils.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingutilspy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/utils.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoresqlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretestcommonpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/common.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_attachmentspy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_attachments.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_filepy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_file.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_implicitpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_implicit.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_index_filepy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_index_file.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_sqlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_utilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretestutilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreutilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavicalendardirectoryservicepy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendardirectoryservice.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavicalendarstorepy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendarstore.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcarddavdatastoretestcommonpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/common.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresqlpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_legacypy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_legacy.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemacurrentoracledialectsql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current-oracle-dialect.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemacurrentsql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_25_to_26sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_25_to_26sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoretestutilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/test/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_3_to_4py">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_4_to_5py">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_4_to_5py">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavidavpy">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/idav.py</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li>CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/podding/</li>
<li>CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/polls/</li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptsserverserverinfopodxml">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-pod.xml</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVpollsxml">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/polls.xml</a></li>
<li>CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests-pod/</li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesbintrial">CalendarServer/branches/users/gaya/sharedgroupfixes/bin/trial</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesbintwistd">CalendarServer/branches/users/gaya/sharedgroupfixes/bin/twistd</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfauthaccountstestpodxml">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/accounts-test-pod.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfauthaugmentstestpodxml">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments-test-pod.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfauthproxiestestpodxml">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/proxies-test-pod.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfauthresourcestestpodxml">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/resources-test-pod.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfcaldavdtestpodAplist">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podA.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfcaldavdtestpodBplist">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podB.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconflocalserversdtd">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.dtd</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfremoteserversdtd">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.dtd</a></li>
<li>CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/webpoll/</li>
<li>CalendarServer/branches/users/gaya/sharedgroupfixes/twext/application/</li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextprotocolsechopy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/echo.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_indexpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_index.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedpluginsmasterchildpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twisted/plugins/masterchild.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_utilpy">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldoracledialectv24sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldoracledialectv26sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldoracledialectv27sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v27.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldoracledialectv28sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v28.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldpostgresdialectv24sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldpostgresdialectv26sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldpostgresdialectv27sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v27.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldpostgresdialectv28sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v28.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_24_to_25sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_26_to_27sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_27_to_28sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_27_to_28.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_28_to_29sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_28_to_29.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_24_to_25sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_26_to_27sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_27_to_28sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_27_to_28.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_28_to_29sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_28_to_29.sql</a></li>
</ul>
<h3>Removed Paths</h3>
<ul>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptsserverserverinfopartitioningxml">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-partitioning.xml</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesbincalendarserver_make_partition">CalendarServer/branches/users/gaya/sharedgroupfixes/bin/calendarserver_make_partition</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfcaldavdpartitioningprimaryplist">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-primary.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfcaldavdpartitioningsecondaryplist">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-secondary.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfserversdtd">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servers.dtd</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfservertoserverdtd">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servertoserver.dtd</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixesconfsudoersplist">CalendarServer/branches/users/gaya/sharedgroupfixes/conf/sudoers.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestsudoersplist">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestsudoers2plist">CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers2.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldoracledialectv24sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldpostgresdialectv24sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_24_to_25sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_24_to_25sql">CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql</a></li>
</ul>
<h3>Property Changed</h3>
<ul>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestester">CalDAVTester/branches/users/gaya/sharedgroupfixestester/</a></li>
<li><a href="#CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCardDAV">CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/</a></li>
<li><a href="#CalendarServerbranchesusersgayasharedgroupfixes">CalendarServer/branches/users/gaya/sharedgroupfixes/</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestester"></a>
<div class="propset"><h4>Property changes: CalDAVTester/branches/users/gaya/sharedgroupfixestester</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnmergeinfo"></a>
<div class="modfile"><h4>Modified: svn:mergeinfo</h4></div>
<span class="cx">/CalDAVTester/branches/release/CalDAVTester-4.3-dev:10193
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/attendee-comments-2887:2888-2910
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/better-proxy-3148:3149-3163
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/component-set-fixes:8221-8346
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/conditional-4466:4467-4469
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/implicitauto-2948:2949-2989
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/location-partial-accept-3574:3575-3581
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/managed-attachments:9986-10145
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/normalize-cuaddr-3533:3534-3558
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/pycalendar:7160-7206
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/pycard:7226-7237
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/sharing-5228:5229-5440
</span><span class="cx">/CalDAVTester/branches/users/gaya/sharedgroupstester-3:11181-11204
</span><span class="cx">/CalDAVTester/trunk:11742-11861
</span><span class="cx"> + /CalDAVTester/branches/release/CalDAVTester-3.0-dev:7584
</span><span class="cx">/CalDAVTester/branches/release/CalDAVTester-4.3-dev:10193
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/attendee-comments-2887:2888-2910
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/better-proxy-3148:3149-3163
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/component-set-fixes:8221-8346
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/conditional-4466:4467-4469
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/implicitauto-2948:2949-2989
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/location-partial-accept-3574:3575-3581
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/managed-attachments:9986-10145
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/normalize-cuaddr-3533:3534-3558
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/pycalendar:7160-7206
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/pycard:7226-7237
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/sharing-5228:5229-5440
</span><span class="cx">/CalDAVTester/branches/users/gaya/sharedgroupstester-3:11181-11204
</span><span class="cx">/CalDAVTester/trunk:11742-12016
</span><a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterREADMEtxt"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/README.txt (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/README.txt        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/README.txt        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -791,6 +791,7 @@
</span><span class="cx">         [+text] - node text starts with "text".
</span><span class="cx">         [^tag] - node has child element "tag".
</span><span class="cx">         [^tag=text] - node has child element "tag" with text "text".
</span><ins>+        [|] - node is empty.
</ins><span class="cx">         [json] - node contains valid JSON data.
</span><span class="cx">         [icalendar] - node contains valid iCalendare data.
</span><span class="cx">         
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterResourceCalDAVprivatecomments32ics"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/32.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/32.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/32.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,7 +1,7 @@
</span><span class="cx"> BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> CALSCALE:GREGORIAN
</span><del>-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
</del><ins>+PRODID:-//Example Inc.//Example Calendar//EN
</ins><span class="cx"> BEGIN:VTIMEZONE
</span><span class="cx"> TZID:US/Eastern
</span><span class="cx"> LAST-MODIFIED:20040110T032845Z
</span><span class="lines">@@ -25,11 +25,10 @@
</span><span class="cx"> DTSTART;TZID=US/Eastern:$now.year.1:0101T100000
</span><span class="cx"> DURATION:PT1H
</span><span class="cx"> ATTENDEE;CN=$username1:;PARTSTAT=ACCEPTED;EMAIL=$email1::$cuaddrurn1:
</span><del>-ATTENDEE;CN=$username2:;PARTSTAT=ACCEPTED;EMAIL=$email2::$cuaddrurn2:
</del><ins>+ATTENDEE;CN=$username2:;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0;EMAIL=$email2::$cuaddrurn2:
</ins><span class="cx"> CREATED:20060101T150000Z
</span><span class="cx"> DTSTAMP:20051222T205953Z
</span><del>-ORGANIZER;CN=$username1:;SCHEDULE-STATUS=1.2;EMAIL=$email1::$cuaddrurn1:
</del><ins>+ORGANIZER;CN=$username1:;EMAIL=$email1::$cuaddrurn1:
</ins><span class="cx"> SUMMARY:event 3
</span><del>-X-CALENDARSERVER-PRIVATE-COMMENT:I have accepted.
</del><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterResourceCalDAVprivatecomments35ics"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/35.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/35.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/35.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -31,6 +31,6 @@
</span><span class="cx"> ORGANIZER;CN=$username1:;EMAIL=$email1::$cuaddrurn1:
</span><span class="cx"> SUMMARY:event 3
</span><span class="cx"> X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="$cuaddrurn2:
</span><del>- ";X-CALENDARSERVER-DTSTAMP=20080827T171052Z:
</del><ins>+ ";X-CALENDARSERVER-DTSTAMP=20080827T171052Z:I have accepted.
</ins><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterResourceCalDAVprivatecomments37ics"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/37.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/37.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/Resource/CalDAV/privatecomments/37.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -31,6 +31,6 @@
</span><span class="cx"> ORGANIZER;CN=$username1:;EMAIL=$email1::$cuaddrurn1:
</span><span class="cx"> SUMMARY:event 3
</span><span class="cx"> X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="$cuaddrurn2:
</span><del>- ";X-CALENDARSERVER-DTSTAMP=20080827T171052Z:
</del><ins>+ ";X-CALENDARSERVER-DTSTAMP=20080827T171052Z:I have accepted.
</ins><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptsserverserverinfopartitioningxml"></a>
<div class="delfile"><h4>Deleted: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-partitioning.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-partitioning.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-partitioning.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,896 +0,0 @@
</span><del>-<?xml version="1.0" standalone="no"?>
-
-<!DOCTYPE serverinfo SYSTEM "serverinfo.dtd">
-
-<!--
- Copyright (c) 2006-2013 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.
- -->
-
-<serverinfo>
-        <host>localhost</host>
-        <nonsslport>8008</nonsslport>
-        <sslport>8443</sslport>
-        <authtype>basic</authtype>
-
-        <features>
-                <!-- Generic WebDAV extensions -->
-                <feature>COPY Method</feature>                                                        <!-- COPY method -->
-                <feature>MOVE Method</feature>                                                        <!-- MOVE method -->
-                <feature>Extended MKCOL</feature>                                                <!-- Extended MKCOL -->
-                
-                <!-- ACL related -->
-                <feature>ACL Method</feature>                                                        <!-- ACL method -->
-                <feature>acl-principal-prop-set REPORT</feature>                <!-- ACL acl-principal-prop-set REPORT -->
-                <feature>principal-match REPORT</feature>                                <!-- ACL principal-match REPORT -->
-                <feature>principal-property-search REPORT</feature>                <!-- ACL principal-property-search REPORT -->
-                <feature>principal-search-property-set REPORT</feature>        <!-- ACL principal-search-property-set REPORT -->
-
-                <feature>add-member</feature>                                        <!-- Add-member used to create resources -->
-                <!-- <feature>auth-on-root</feature> -->                <!-- Whether the server requires authentication on the root URI -->
-                <feature>brief</feature>                                         <!-- Brief header for PROPFIND, REPORT -->
-                <feature>bulk-post</feature>                                        <!-- Bulk POST requests -->
-                <feature>ctag</feature>                                                        <!-- ctag extension -->
-                <feature>current-user-principal</feature>                <!-- current-user-principal extension -->
-                <feature>directory listing</feature>                         <!-- GET on collection -->
-                <feature>extended-principal-search</feature>        <!-- Extended principal-property-search REPORT extension -->
-                <feature>expand-property</feature>                                <!-- Expand property REPORT -->
-                <feature>only-proxy-groups</feature>                        <!-- Group-membership only includes delegated-to groups -->
-                <feature>limits</feature>                                                <!-- max-collections and max-resources limits -->
-                <feature>prefer</feature>                                                <!-- Prefer header overall support -->
-                <feature>prefer-minimal</feature>                                <!-- Prefer header return=minimal -->
-                <feature>prefer-representation</feature>                <!-- Prefer header return=representation -->
-                <feature>prefer-noroot</feature>                                <!-- Prefer header depth-noroot -->
-                <feature>quota</feature>                                                <!-- WebDAV QUOTA -->
-                <!-- <feature>quota-on-resources</feature> -->        <!-- WebDAV QUOTA on calendar and address book object resources -->
-                <feature>resource-id</feature>                                        <!-- WebDAV BIND DAV:resource-id property -->
-                <feature>sync-report</feature>                                        <!-- WebDAV collection sync REPORT -->
-                <!-- <feature>sync-report-limit</feature> -->        <!-- WebDAV collection sync REPORT DAV:limit support -->
-                <feature>sync-report-home</feature>                                <!-- WebDAV collection sync REPORT on Homes -->
-                <feature>well-known</feature>                                        <!-- well-known feature -->
-
-                <!-- <feature>per-object-ACLs</feature> -->                <!-- ACL for objects in calendar/address books -->
-                <!-- <feature>regular-collection</feature> --> <!-- Regular collections allowed in calendar/address book homes -->
-
-                <!-- <feature>json-data</feature> -->                        <!-- jCal and jCard support -->
-
-                <!-- CalDAV specific extension -->
-                <feature>caldav</feature>                                         <!-- Basic CalDAV feature enabler -->
-                <feature>attachments-collection</feature>                <!-- Server uses a collection in same WebDAV tree to store attachments -->
-                <feature>auto-accept</feature>                                        <!-- Auto-accept for rooms & locations -->
-                <feature>auto-accept-modes</feature>                        <!-- Auto-accept modes -->
-                <!-- <feature>dropbox</feature> -->                                <!-- dropbox extension -->
-                <feature>default-alarms</feature>                                <!-- default alarms extension -->
-                <feature>EMAIL parameter</feature>                                <!-- Server normalizes cuaddress and adds EMAIL parameter -->
-                <feature>extended-freebusy</feature>                 <!-- Extended freebusy response -->
-                <feature>implicit-scheduling</feature>                        <!-- CalDAV scheduling - implicit -->
-                <feature>location-resource-tracking</feature> <!-- Server tracks who makes unscheduled changes to locations and resources -->
-                <feature>managed-attachments</feature>                        <!-- CalDAV Managed Attachments -->
-                <feature>maskuid</feature>                                                <!-- maskuid extension -->
-                <feature>no-duplicate-uids</feature>                        <!-- duplicate UIDs in same home not supported -->
-                <feature>partitioning</feature>                                        <!-- Partitioned server -->
-                <feature>partstat-timestamp</feature>                        <!-- Time stamps when PARTSTAT changes extension -->
-                <feature>private-comments</feature>                                <!-- private-comments extension -->
-                <feature>private-events</feature>                                <!-- private-events extension -->
-                <feature>proxy</feature>                                                <!-- calendar-user-proxy extension -->
-                <!-- <feature>proxy-authz</feature> -->                        <!-- sudo user extension -->
-                <feature>remove-duplicate-alarms</feature>         <!-- Server removes any duplicate alarms on PUT -->
-                <feature>query-extended</feature>                                <!-- calendar-query-extended extension -->
-                <feature>shared-calendars</feature>                                <!-- Shared calendars extension -->
-                <feature>schedule-changes</feature>                                <!-- schedule-changes property extension -->
-                <feature>split-calendars</feature>                                <!-- Calendars are split by component type -->
-                <feature>supported-component-sets</feature>                <!-- CALDAV:supported-calendar-component-sets on calendar homes -->
-                <feature>supported-component-sets-one</feature>        <!-- Only single component calendars allowed to be created -->
-                <feature>timerange-low-limit</feature>                        <!-- Time-range only valid one year back -->
-                <feature>timerange-high-limit</feature>                        <!-- Time-range only valid 5 years ahead -->
-                <feature>timezones-by-reference</feature>                <!-- Timezones by reference enabled -->
-                <feature>timezone-service</feature>                                <!-- Timezone service extension for Wiki -->
-                <feature>timezone-std-service</feature>                        <!-- Timezone standard service extension -->
-                <feature>vavailability</feature>                                <!-- VAVAILABILITY on inbox -->
-                <feature>webcal</feature>                                                <!-- Internet calendar subscription via GET on calendar collection -->
-
-                <!-- CardDAV specific extension -->
-                <feature>carddav</feature>                                                 <!-- Basic CardDAV feature enabler -->
-                <feature>default-addressbook</feature>                         <!-- Default address book behavior -->
-                <!-- <feature>global-addressbook</feature> -->         <!-- Global address book for each user -->
-                <feature>shared-addressbooks</feature>                        <!-- Shared address books extension -->
-                <!-- <feature>directory-gateway</feature> -->        <!-- Directory gateway extension -->
-
-        </features>
-
-        <substitutions>
-                <!-- Useful xpath shortcuts for verifiers -->
-                <substitution>
-                        <key>$multistatus-response-prefix:</key>
-                        <value>/{DAV:}multistatus/{DAV:}response</value>
-                </substitution>
-                <substitution>
-                        <key>$multistatus-href-prefix:</key>
-                        <value>/{DAV:}multistatus/{DAV:}response/{DAV:}href</value>
-                </substitution>
-                <substitution>
-                        <key>$verify-response-prefix:</key>
-                        <value>{DAV:}response/{DAV:}propstat/{DAV:}prop</value>
-                </substitution>
-                <substitution>
-                        <key>$verify-property-prefix:</key>
-                        <value>/{DAV:}multistatus/{DAV:}response/{DAV:}propstat/{DAV:}prop</value>
-                </substitution>
-                <substitution>
-                        <key>$verify-bad-response:</key>
-                        <value>/{DAV:}multistatus/{DAV:}response/{DAV:}status</value>
-                </substitution>
-                <substitution>
-                        <key>$verify-error-response:</key>
-                        <value>/{DAV:}multistatus/{DAV:}response/{DAV:}error</value>
-                </substitution>
-                <substitution>
-                        <key>$CALDAV:</key>
-                        <value>urn:ietf:params:xml:ns:caldav</value>
-                </substitution>
-                <substitution>
-                        <key>$CARDDAV:</key>
-                        <value>urn:ietf:params:xml:ns:carddav</value>
-                </substitution>
-                <substitution>
-                        <key>$CS:</key>
-                        <value>http://calendarserver.org/ns/</value>
-                </substitution>
-
-                <!-- Server configuration settings -->
-                <!-- $host: and $hostssl: are implicitly added by CalDAVTester based
-                 on the host/nonsslport/sslport values and ssl command line switch -->
-
-                <!-- relative path to caldav root-->
-                <substitution>
-                        <key>$root:</key>
-                        <value>/</value>
-                </substitution>
-
-                <!-- relative path to main principal collection-->
-                <substitution>
-                        <key>$principalcollection:</key>
-                        <value>$root:principals/</value>
-                </substitution>
-
-                <!-- the core recored type collections-->
-                <substitution>
-                        <key>$uidstype:</key>
-                        <value>__uids__</value>
-                </substitution>
-                <substitution>
-                        <key>$userstype:</key>
-                        <value>users</value>
-                </substitution>
-                <substitution>
-                        <key>$groupstype:</key>
-                        <value>groups</value>
-                </substitution>
-                <substitution>
-                        <key>$locationstype:</key>
-                        <value>locations</value>
-                </substitution>
-                <substitution>
-                        <key>$resourcestype:</key>
-                        <value>resources</value>
-                </substitution>
-
-                <!-- relative path to record type principal collections-->
-                <substitution>
-                        <key>$principals_uids:</key>
-                        <value>$principalcollection:$uidstype:/</value>
-                </substitution>
-                <substitution>
-                        <key>$principals_users:</key>
-                        <value>$principalcollection:$userstype:/</value>
-                </substitution>
-                <substitution>
-                        <key>$principals_groups:</key>
-                        <value>$principalcollection:$groupstype:/</value>
-                </substitution>
-                <substitution>
-                        <key>$principals_resources:</key>
-                        <value>$principalcollection:$resourcestype:/</value>
-                </substitution>
-                <substitution>
-                        <key>$principals_locations:</key>
-                        <value>$principalcollection:$locationstype:/</value>
-                </substitution>
-
-                <!-- relative path to calendars collection-->
-                <substitution>
-                        <key>$calendars:</key>
-                        <value>$root:calendars/</value>
-                </substitution>
-
-                <!-- relative path to record type calendar collections-->
-                <substitution>
-                        <key>$calendars_uids:</key>
-                        <value>$calendars:$uidstype:/</value>
-                </substitution>
-                <substitution>
-                        <key>$calendars_users:</key>
-                        <value>$calendars:$userstype:/</value>
-                </substitution>
-                <substitution>
-                        <key>$calendars_groups:</key>
-                        <value>$calendars:$groupstype:/</value>
-                </substitution>
-                <substitution>
-                        <key>$calendars_resources:</key>
-                        <value>$calendars:$resourcestype:/</value>
-                </substitution>
-                <substitution>
-                        <key>$calendars_locations:</key>
-                        <value>$calendars:$locationstype:/</value>
-                </substitution>
-
-                <!-- primary calendar name-->
-                <substitution>
-                        <key>$calendar:</key>
-                        <value>calendar</value>
-                </substitution>
-
-                <!-- primary tasks-only calendar name-->
-                <substitution>
-                        <key>$tasks:</key>
-                        <value>tasks</value>
-                </substitution>
-
-                <!-- inbox name-->
-                <substitution>
-                        <key>$inbox:</key>
-                        <value>inbox</value>
-                </substitution>
-
-                <!-- outbox name-->
-                <substitution>
-                        <key>$outbox:</key>
-                        <value>outbox</value>
-                </substitution>
-
-                <!-- dropbox name-->
-                <substitution>
-                        <key>$dropbox:</key>
-                        <value>dropbox</value>
-                </substitution>
-
-                <!-- attachments name-->
-                <substitution>
-                        <key>$attachments:</key>
-                        <value>dropbox</value>
-                </substitution>
-
-                <!-- notification name-->
-                <substitution>
-                        <key>$notification:</key>
-                        <value>notification</value>
-                </substitution>
-
-                <!-- freebusy name-->
-                <substitution>
-                        <key>$freebusy:</key>
-                        <value>freebusy</value>
-                </substitution>
-
-                <!-- server-to-server inbox-->
-                <substitution>
-                        <key>$servertoserver:</key>
-                        <value>$root:inbox</value>
-                </substitution>
-
-                <!-- timezone service-->
-                <substitution>
-                        <key>$timezoneservice:</key>
-                        <value>$root:timezones</value>
-                </substitution>
-
-                <!-- timezone std service-->
-                <substitution>
-                        <key>$timezonestdservice:</key>
-                        <value>$root:stdtimezones</value>
-                </substitution>
-
-                <!-- relative path to addressbooks collection-->
-                <substitution>
-                        <key>$addressbooks:</key>
-                        <value>$root:addressbooks/</value>
-                </substitution>
-
-                <!-- relative path to record type addressbook collections-->
-                <substitution>
-                        <key>$addressbooks_uids:</key>
-                        <value>$addressbooks:$uidstype:/</value>
-                </substitution>
-                <substitution>
-                        <key>$addressbooks_users:</key>
-                        <value>$addressbooks:$userstype:/</value>
-                </substitution>
-                <substitution>
-                        <key>$addressbooks_groups:</key>
-                        <value>$addressbooks:$groupstype:/</value>
-                </substitution>
-
-                <!-- primary addressbook name -->
-                <substitution>
-                        <key>$addressbook:</key>
-                        <value>addressbook</value>
-                </substitution>
-
-                <!-- directory name -->
-                <substitution>
-                        <key>$directory:</key>
-                        <value>$root:directory/</value>
-                </substitution>
-
-                <!-- global-addressbook name -->
-                <substitution>
-                        <key>$global_addressbook:</key>
-                        <value>global-addressbook</value>
-                </substitution>
-
-                <!-- POST add-member URI suffix -->
-                <substitution>
-                        <key>$add-member:</key>
-                        <value>;add-member</value>
-                </substitution>
-
-                <!-- user id for admin user -->
-                <substitution>
-                        <key>$useradmin:</key>
-                        <value>admin</value>
-                </substitution>
-                <!-- guid for admin user -->
-                <substitution>
-                        <key>$useradminguid:</key>
-                        <value>admin</value>
-                </substitution>
-                <!-- password for admin user -->
-                <substitution>
-                        <key>$pswdadmin:</key>
-                        <value>admin</value>
-                </substitution>
-
-                <!-- relative path to admin principal resource-->
-                <substitution>
-                        <key>$principal_admin:</key>
-                        <value>$principals_users:$useradmin:/</value>
-                </substitution>
-                <substitution>
-                        <key>$principaluri_admin:</key>
-                        <value>$principals_uids:$useradminguid:/</value>
-                </substitution>
-
-                <!-- user id for apprentice user -->
-                <substitution>
-                        <key>$userapprentice:</key>
-                        <value>apprentice</value>
-                </substitution>
-                <!-- guid for apprentice user -->
-                <substitution>
-                        <key>$userapprenticeguid:</key>
-                        <value>apprentice</value>
-                </substitution>
-                <!-- password for admin user -->
-                <substitution>
-                        <key>$pswdapprentice:</key>
-                        <value>apprentice</value>
-                </substitution>
-
-                <!-- relative path to apprentice principal resource-->
-                <substitution>
-                        <key>$principal_apprentice:</key>
-                        <value>$principals_users:$userapprentice:/</value>
-                </substitution>
-                <substitution>
-                        <key>$principaluri_apprentice:</key>
-                        <value>$principals_uids:$userapprenticeguid:/</value>
-                </substitution>
-
-                <!-- user id for proxy user -->
-                <substitution>
-                        <key>$userproxy:</key>
-                        <value>superuser</value>
-                </substitution>
-                <!-- password for proxy user -->
-                <substitution>
-                        <key>$pswdproxy:</key>
-                        <value>superuser</value>
-                </substitution>
-
-                <!-- Forty user accounts -->
-                <repeat count="40">
-                        <!-- user id -->
-                        <substitution>
-                                <key>$userid%d:</key>
-                                <value>user%02d</value>
-                        </substitution>
-                        <!-- user guid -->
-                        <substitution>
-                                <key>$userguid%d:</key>
-                                <value>user%02d</value>
-                        </substitution>
-                        <!-- user name -->
-                        <substitution>
-                                <key>$username%d:</key>
-                                <value>User %02d</value>
-                        </substitution>
-                        <!-- user name URI encoded -->
-                        <substitution>
-                                <key>$username-encoded%d:</key>
-                                <value>User%%20%02d</value>
-                        </substitution>
-                        <!-- first name -->
-                        <substitution>
-                                <key>$firstname%d:</key>
-                                <value>User</value>
-                        </substitution>
-                        <!-- last name -->
-                        <substitution>
-                                <key>$lastname%d:</key>
-                                <value>%02d</value>
-                        </substitution>
-                        <!-- password -->
-                        <substitution>
-                                <key>$pswd%d:</key>
-                                <value>user%02d</value>
-                        </substitution>
-                        <!-- relative path to user principal resource-->
-                        <substitution>
-                                <key>$principal%d:</key>
-                                <value>$principals_users:$userid%d:/</value>
-                        </substitution>
-                        <substitution>
-                                <key>$principaluri%d:</key>
-                                <value>$principals_uids:$userguid%d:/</value>
-                        </substitution>
-                        <substitution>
-                                <key>$principal%dnoslash:</key>
-                                <value>$principals_users:$userid%d:</value>
-                        </substitution>
-
-                        <!-- relative path to user calendar home-->
-                        <substitution>
-                                <key>$calendarhome%d:</key>
-                                <value>$calendars_uids:$userguid%d:</value>
-                        </substitution>
-                        <!-- relative path to user alternate calendar home-->
-                        <substitution>
-                                <key>$calendarhomealt%d:</key>
-                                <value>$calendars_users:$userid%d:</value>
-                        </substitution>
-                        <!-- relative path to user calendar-->
-                        <substitution>
-                                <key>$calendarpath%d:</key>
-                                <value>$calendarhome%d:/$calendar:</value>
-                        </substitution>
-                        <!-- relative path to user alternate calendar-->
-                        <substitution>
-                                <key>$calendarpathalt%d:</key>
-                                <value>$calendarhomealt%d:/$calendar:</value>
-                        </substitution>
-                        <!-- relative path to user tasks calendar-->
-                        <substitution>
-                                <key>$taskspath%d:</key>
-                                <value>$calendarhome%d:/$tasks:</value>
-                        </substitution>
-                        <!-- relative path to user inbox-->
-                        <substitution>
-                                <key>$inboxpath%d:</key>
-                                <value>$calendarhome%d:/$inbox:</value>
-                        </substitution>
-                        <!-- relative path to user outbox-->
-                        <substitution>
-                                <key>$outboxpath%d:</key>
-                                <value>$calendarhome%d:/$outbox:</value>
-                        </substitution>
-                        <!-- relative path to user dropbox-->
-                        <substitution>
-                                <key>$dropboxpath%d:</key>
-                                <value>$calendarhome%d:/$dropbox:</value>
-                        </substitution>
-                        <!-- relative path to user notification-->
-                        <substitution>
-                                <key>$notificationpath%d:</key>
-                                <value>$calendarhome%d:/$notification:</value>
-                        </substitution>
-                        <!-- relative path to user freebusy-->
-                        <substitution>
-                                <key>$freebusypath%d:</key>
-                                <value>$calendarhome%d:/$freebusy:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$email%d:</key>
-                                <value>$userid%d:@example.com</value>
-                        </substitution>
-                        <!-- calendar user address of user-->
-                        <substitution>
-                                <key>$cuaddr%d:</key>
-                                <value>mailto:$email%d:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$cuaddralt%d:</key>
-                                <value>$principaluri%d:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$cuaddraltnoslash%d:</key>
-                                <value>$principals_uids:$userguid%d:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$cuaddrurn%d:</key>
-                                <value>urn:uuid:$userguid%d:</value>
-                        </substitution>
-
-                        <!-- relative path to user addressbook home-->
-                        <substitution>
-                                <key>$addressbookhome%d:</key>
-                                <value>$addressbooks_uids:$userguid%d:</value>
-                        </substitution>
-                        <!-- relative path to user addressbook-->
-                        <substitution>
-                                <key>$addressbookpath%d:</key>
-                                <value>$addressbooks_uids:$userguid%d:/$addressbook:</value>
-                        </substitution>
-                </repeat>
-
-                <!-- Ten public accounts -->
-                <repeat count="10">
-                        <!-- user id -->
-                        <substitution>
-                                <key>$publicuserid%d:</key>
-                                <value>public%02d</value>
-                        </substitution>
-                        <!-- user guid -->
-                        <substitution>
-                                <key>$publicuserguid%d:</key>
-                                <value>public%02d</value>
-                        </substitution>
-                        <!-- user name -->
-                        <substitution>
-                                <key>$publicusername%d:</key>
-                                <value>Public %02d</value>
-                        </substitution>
-                        <!-- password -->
-                        <substitution>
-                                <key>$publicpswd%d:</key>
-                                <value>public%02d</value>
-                        </substitution>
-                        <!-- relative path to user principal resource-->
-                        <substitution>
-                                <key>$publicprincipal%d:</key>
-                                <value>$principals_users:$publicuserid%d:/</value>
-                        </substitution>
-                        <substitution>
-                                <key>$publicprincipaluri%d:</key>
-                                <value>$principals_uids:$publicuserguid%d:/</value>
-                        </substitution>
-                        <!-- relative path to user calendar home-->
-                        <substitution>
-                                <key>$publiccalendarhome%d:</key>
-                                <value>$calendars_uids:$publicuserguid%d:</value>
-                        </substitution>
-                        <!-- relative path to user calendar-->
-                        <substitution>
-                                <key>$publiccalendarpath%d:</key>
-                                <value>$calendars_uids:$publicuserguid%d:/$calendar:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$publicemail%d:</key>
-                                <value>$publicuserid%d:@example.com</value>
-                        </substitution>
-                        <!-- calendar user address of user-->
-                        <substitution>
-                                <key>$publiccuaddr%d:</key>
-                                <value>mailto:$publicemail%d:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$publiccuaddralt%d:</key>
-                                <value>$publicprincipaluri%d:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$publiccuaddrurn%d:</key>
-                                <value>urn:uuid:$publicuserguid%d:</value>
-                        </substitution>
-                </repeat>
-
-                <!-- Twenty resource accounts -->
-                <repeat count="20">
-                        <substitution>
-                                <key>$resourceid%d:</key>
-                                <value>resource%02d</value>
-                        </substitution>
-                        <!-- resource guid-->
-                        <substitution>
-                                <key>$resourceguid%d:</key>
-                                <value>resource%02d</value>
-                        </substitution>
-                        <!-- resource name-->
-                        <substitution>
-                                <key>$resourcename%d:</key>
-                                <value>Resource %02d</value>
-                        </substitution>
-                        <!-- relative path to first resource calendar home-->
-                        <substitution>
-                                <key>$rcalendarhome%d:</key>
-                                <value>$calendars_uids:$resourceguid%d:</value>
-                        </substitution>
-                        <!-- relative path to first resource calendar home-->
-                        <substitution>
-                                <key>$rcalendarpath%d:</key>
-                                <value>$calendars_uids:$resourceguid%d:/$calendar:</value>
-                        </substitution>
-                        <!-- relative path to first resource inbox-->
-                        <substitution>
-                                <key>$rinboxpath%d:</key>
-                                <value>$calendars_uids:$resourceguid%d:/$inbox:</value>
-                        </substitution>
-                        <!-- relative path to first resource outbox-->
-                        <substitution>
-                                <key>$routboxpath%d:</key>
-                                <value>$calendars_uids:$resourceguid%d:/$outbox:</value>
-                        </substitution>
-                        <!-- relative path to first resource principal resource-->
-                        <substitution>
-                                <key>$rprincipal%d:</key>
-                                <value>$principals_resources:$resourceid%d:/</value>
-                        </substitution>
-                        <substitution>
-                                <key>$rprincipaluri%d:</key>
-                                <value>$principals_uids:$resourceguid%d:/</value>
-                        </substitution>
-                        <substitution>
-                                <key>$rcuaddralt%d:</key>
-                                <value>$rprincipaluri%d:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$rcuaddrurn%d:</key>
-                                <value>urn:uuid:$resourceguid%d:</value>
-                        </substitution>
-                </repeat>
-
-                <!-- Ten Location accounts -->
-                <repeat count="10">
-                        <substitution>
-                                <key>$locationid%d:</key>
-                                <value>location%02d</value>
-                        </substitution>
-                        <!-- location guid-->
-                        <substitution>
-                                <key>$locationguid%d:</key>
-                                <value>location%02d</value>
-                        </substitution>
-                        <!-- location name-->
-                        <substitution>
-                                <key>$locationname%d:</key>
-                                <value>Location %02d</value>
-                        </substitution>
-                        <!-- relative path to first location calendar home-->
-                        <substitution>
-                                <key>$lcalendarhome%d:</key>
-                                <value>$calendars_uids:$locationguid%d:</value>
-                        </substitution>
-                        <!-- relative path to first location calendar home-->
-                        <substitution>
-                                <key>$lcalendarpath%d:</key>
-                                <value>$calendars_uids:$locationguid%d:/$calendar:</value>
-                        </substitution>
-                        <!-- relative path to first location inbox-->
-                        <substitution>
-                                <key>$linboxpath%d:</key>
-                                <value>$calendars_uids:$locationguid%d:/$inbox:</value>
-                        </substitution>
-                        <!-- relative path to first location outbox-->
-                        <substitution>
-                                <key>$loutboxpath%d:</key>
-                                <value>$calendars_uids:$locationguid%d:/$outbox:</value>
-                        </substitution>
-                        <!-- relative path to first location principal resource-->
-                        <substitution>
-                                <key>$lprincipal%d:</key>
-                                <value>$principals_resources:$locationid%d:/</value>
-                        </substitution>
-                        <substitution>
-                                <key>$lprincipaluri%d:</key>
-                                <value>$principals_uids:$locationguid%d:/</value>
-                        </substitution>
-                        <substitution>
-                                <key>$lcuaddralt%d:</key>
-                                <value>$lprincipaluri%d:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$lcuaddrurn%d:</key>
-                                <value>urn:uuid:$locationguid%d:</value>
-                        </substitution>
-                </repeat>
-
-
-                <!-- Ten Group accounts -->
-                <repeat count="10">
-                        <substitution>
-                                <key>$groupid%d:</key>
-                                <value>group%02d</value>
-                        </substitution>
-                        <!-- group guid-->
-                        <substitution>
-                                <key>$groupguid%d:</key>
-                                <value>group%02d</value>
-                        </substitution>
-                        <!-- group name-->
-                        <substitution>
-                                <key>$groupname%d:</key>
-                                <value>Group %02d</value>
-                        </substitution>
-                        <!-- relative path to first group principal resource-->
-                        <substitution>
-                                <key>$gprincipal%d:</key>
-                                <value>$principals_resources:$groupid%d:/</value>
-                        </substitution>
-                        <substitution>
-                                <key>$gprincipaluri%d:</key>
-                                <value>$principals_uids:$groupguid%d:/</value>
-                        </substitution>
-                        <substitution>
-                                <key>$gcuaddralt%d:</key>
-                                <value>$gprincipaluri%d:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$gcuaddrurn%d:</key>
-                                <value>urn:uuid:$groupguid%d:</value>
-                        </substitution>
-                </repeat>
-
-                <!-- User with non-ascii name -->
-                <substitution>
-                        <key>$i18nid:</key>
-                        <value>i18nuser</value>
-                </substitution>
-                <!-- group guid-->
-                <substitution>
-                        <key>$i18nguid:</key>
-                        <value>i18nuser</value>
-                </substitution>
-                <!-- group name-->
-                <substitution>
-                        <key>$i18nname:</key>
-                        <value>まだ</value>
-                </substitution>
-                <!-- password -->
-                <substitution>
-                        <key>$i18npswd:</key>
-                        <value>i18nuser</value>
-                </substitution>
-                <!-- relative path to user calendar-->
-                <substitution>
-                        <key>$i18ncalendarpath:</key>
-                        <value>$calendars_uids:$i18nguid:/$calendar:</value>
-                </substitution>
-                <substitution>
-                        <key>$i18nemail:</key>
-                        <value>$i18nid:@example.com</value>
-                </substitution>
-                <!-- CUAddrs -->
-                <substitution>
-                        <key>$i18ncuaddr:</key>
-                        <value>mailto:$i18nemail:</value>
-                </substitution>
-                <substitution>
-                        <key>$i18ncuaddrurn:</key>
-                        <value>urn:uuid:$i18nguid:</value>
-                </substitution>
-
-                <!-- relative path to disabled group principal resource-->
-                <substitution>
-                        <key>$principaldisabled:</key>
-                        <value>$principals_groups:disabledgroup/</value>
-                </substitution>
-                <substitution>
-                        <key>$principaluridisabled:</key>
-                        <value>$principals_uids:disabledgroup/</value>
-                </substitution>
-                <!-- calendar user address of disabled group-->
-                <substitution>
-                        <key>$cuaddrdisabled:</key>
-                        <value>$principals_uids:disabledgroup/</value>
-                </substitution>
-
-                <!-- Ten other accounts -->
-                <repeat count="10">
-                        <!-- user id -->
-                        <substitution>
-                                <key>$otherid%d:</key>
-                                <value>other%02d</value>
-                        </substitution>
-                        <!-- user guid -->
-                        <substitution>
-                                <key>$otherguid%d:</key>
-                                <value>other%02d</value>
-                        </substitution>
-                        <!-- user name -->
-                        <substitution>
-                                <key>$otherusername%d:</key>
-                                <value>Other %02d</value>
-                        </substitution>
-                        <!-- password -->
-                        <substitution>
-                                <key>$otherpswd%d:</key>
-                                <value>other%02d</value>
-                        </substitution>
-                        <!-- relative path to user principal resource-->
-                        <substitution>
-                                <key>$otherprincipal%d:</key>
-                                <value>$principals_users:$otherid%d:/</value>
-                        </substitution>
-                        <substitution>
-                                <key>$otherprincipaluri%d:</key>
-                                <value>$principals_uids:$otherguid%d:/</value>
-                        </substitution>
-                        <!-- relative path to user calendar home-->
-                        <substitution>
-                                <key>$othercalendarhome%d:</key>
-                                <value>$calendars_uids:$otherguid%d:</value>
-                        </substitution>
-                        <!-- relative path to user calendar-->
-                        <substitution>
-                                <key>$othercalendarpath%d:</key>
-                                <value>$calendars_uids:$otherguid%d:/$calendar:</value>
-                        </substitution>
-                        <!-- relative path to user inbox-->
-                        <substitution>
-                                <key>$otherinboxpath%d:</key>
-                                <value>$calendars_uids:$otherguid%d:/$inbox:</value>
-                        </substitution>
-                        <!-- relative path to user outbox-->
-                        <substitution>
-                                <key>$otheroutboxpath%d:</key>
-                                <value>$calendars_uids:$otherguid%d:/$outbox:</value>
-                        </substitution>
-                        <!-- relative path to user dropbox-->
-                        <substitution>
-                                <key>$otherdropboxpath%d:</key>
-                                <value>$calendars_uids:$otherguid%d:/$dropbox:</value>
-                        </substitution>
-                        <!-- relative path to user freebusy-->
-                        <substitution>
-                                <key>$otherfreebusypath%d:</key>
-                                <value>$calendars_uids:$otherguid%d:/$freebusy:</value>
-                        </substitution>
-                        <!-- calendar user address of user-->
-                        <substitution>
-                                <key>$otheremail%d:</key>
-                                <value>$otherid%d:@example.com</value>
-                        </substitution>
-                        <substitution>
-                                <key>$othercuaddr%d:</key>
-                                <value>mailto:$otheremail%d:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$othercuaddralt%d:</key>
-                                <value>$otherprincipaluri%d:</value>
-                        </substitution>
-                        <substitution>
-                                <key>$othercuaddrurn%d:</key>
-                                <value>urn:uuid:$otherguid%d:</value>
-                        </substitution>
-                </repeat>
-
-                <!-- Override some of the above definitions for special cases -->
-
-                <!-- calendar user address of second user-->
-                <substitution>
-                        <key>$cuaddr2:</key>
-                        <value>MAILTO:$email2:</value>
-                </substitution>
-
-        </substitutions>
-</serverinfo>
</del></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptsserverserverinfopodxmlfromrev12016CalDAVTestertrunkscriptsserverserverinfopodxml"></a>
<div class="copfile"><h4>Copied: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-pod.xml (from rev 12016, CalDAVTester/trunk/scripts/server/serverinfo-pod.xml) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-pod.xml         (rev 0)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-pod.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,652 @@
</span><ins>+<?xml version="1.0" standalone="no"?>
+
+<!DOCTYPE serverinfo SYSTEM "serverinfo.dtd">
+
+<!--
+ Copyright (c) 2006-2013 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.
+ -->
+
+<serverinfo>
+        <host>localhost</host>
+        <nonsslport>8008</nonsslport>
+        <sslport>8443</sslport>
+
+        <host2>localhost</host2>
+        <nonsslport2>8108</nonsslport2>
+        <sslport2>8543</sslport2>
+
+        <authtype>basic</authtype>
+
+        <features>
+                <!-- Generic WebDAV extensions -->
+                <feature>COPY Method</feature>                                                        <!-- COPY method -->
+                <feature>MOVE Method</feature>                                                        <!-- MOVE method -->
+                <feature>Extended MKCOL</feature>                                                <!-- Extended MKCOL -->
+                
+                <!-- ACL related -->
+                <feature>ACL Method</feature>                                                        <!-- ACL method -->
+                <feature>acl-principal-prop-set REPORT</feature>                <!-- ACL acl-principal-prop-set REPORT -->
+                <feature>principal-match REPORT</feature>                                <!-- ACL principal-match REPORT -->
+                <feature>principal-property-search REPORT</feature>                <!-- ACL principal-property-search REPORT -->
+                <feature>principal-search-property-set REPORT</feature>        <!-- ACL principal-search-property-set REPORT -->
+
+                <feature>add-member</feature>                                        <!-- Add-member used to create resources -->
+                <!-- <feature>auth-on-root</feature> -->                <!-- Whether the server requires authentication on the root URI -->
+                <feature>brief</feature>                                         <!-- Brief header for PROPFIND, REPORT -->
+                <feature>bulk-post</feature>                                        <!-- Bulk POST requests -->
+                <feature>ctag</feature>                                                        <!-- ctag extension -->
+                <feature>current-user-principal</feature>                <!-- current-user-principal extension -->
+                <feature>directory listing</feature>                         <!-- GET on collection -->
+                <feature>extended-principal-search</feature>        <!-- Extended principal-property-search REPORT extension -->
+                <feature>expand-property</feature>                                <!-- Expand property REPORT -->
+                <feature>only-proxy-groups</feature>                        <!-- Group-membership only includes delegated-to groups -->
+                <feature>limits</feature>                                                <!-- max-collections and max-resources limits -->
+                <feature>prefer</feature>                                                <!-- Prefer header overall support -->
+                <feature>prefer-minimal</feature>                                <!-- Prefer header return=minimal -->
+                <feature>prefer-representation</feature>                <!-- Prefer header return=representation -->
+                <feature>prefer-noroot</feature>                                <!-- Prefer header depth-noroot -->
+                <feature>quota</feature>                                                <!-- WebDAV QUOTA -->
+                <!-- <feature>quota-on-resources</feature> -->        <!-- WebDAV QUOTA on calendar and address book object resources -->
+                <feature>resource-id</feature>                                        <!-- WebDAV BIND DAV:resource-id property -->
+                <feature>sync-report</feature>                                        <!-- WebDAV collection sync REPORT -->
+                <!-- <feature>sync-report-limit</feature> -->        <!-- WebDAV collection sync REPORT DAV:limit support -->
+                <feature>sync-report-home</feature>                                <!-- WebDAV collection sync REPORT on Homes -->
+                <feature>well-known</feature>                                        <!-- well-known feature -->
+
+                <!-- <feature>per-object-ACLs</feature> -->                <!-- ACL for objects in calendar/address books -->
+                <!-- <feature>regular-collection</feature> --> <!-- Regular collections allowed in calendar/address book homes -->
+
+                <!-- <feature>json-data</feature> -->                        <!-- jCal and jCard support -->
+
+                <!-- CalDAV specific extension -->
+                <feature>caldav</feature>                                         <!-- Basic CalDAV feature enabler -->
+                <feature>attachments-collection</feature>                <!-- Server uses a collection in same WebDAV tree to store attachments -->
+                <feature>auto-accept</feature>                                        <!-- Auto-accept for rooms & locations -->
+                <feature>auto-accept-modes</feature>                        <!-- Auto-accept modes -->
+                <!-- <feature>dropbox</feature> -->                                <!-- dropbox extension -->
+                <feature>default-alarms</feature>                                <!-- default alarms extension -->
+                <feature>EMAIL parameter</feature>                                <!-- Server normalizes cuaddress and adds EMAIL parameter -->
+                <feature>extended-freebusy</feature>                 <!-- Extended freebusy response -->
+                <feature>implicit-scheduling</feature>                        <!-- CalDAV scheduling - implicit -->
+                <feature>location-resource-tracking</feature> <!-- Server tracks who makes unscheduled changes to locations and resources -->
+                <feature>managed-attachments</feature>                        <!-- CalDAV Managed Attachments -->
+                <feature>maskuid</feature>                                                <!-- maskuid extension -->
+                <feature>no-duplicate-uids</feature>                        <!-- duplicate UIDs in same home not supported -->
+                <feature>partstat-timestamp</feature>                        <!-- Time stamps when PARTSTAT changes extension -->
+                <feature>podding</feature>                                                <!-- Podded server -->
+                <feature>private-comments</feature>                                <!-- private-comments extension -->
+                <feature>private-events</feature>                                <!-- private-events extension -->
+                <feature>proxy</feature>                                                <!-- calendar-user-proxy extension -->
+                <!-- <feature>proxy-authz</feature> -->                        <!-- sudo user extension -->
+                <feature>remove-duplicate-alarms</feature>         <!-- Server removes any duplicate alarms on PUT -->
+                <feature>query-extended</feature>                                <!-- calendar-query-extended extension -->
+                <feature>shared-calendars</feature>                                <!-- Shared calendars extension -->
+                <feature>schedule-changes</feature>                                <!-- schedule-changes property extension -->
+                <feature>split-calendars</feature>                                <!-- Calendars are split by component type -->
+                <feature>supported-component-sets</feature>                <!-- CALDAV:supported-calendar-component-sets on calendar homes -->
+                <feature>supported-component-sets-one</feature>        <!-- Only single component calendars allowed to be created -->
+                <feature>timerange-low-limit</feature>                        <!-- Time-range only valid one year back -->
+                <feature>timerange-high-limit</feature>                        <!-- Time-range only valid 5 years ahead -->
+                <feature>timezones-by-reference</feature>                <!-- Timezones by reference enabled -->
+                <feature>timezone-service</feature>                                <!-- Timezone service extension for Wiki -->
+                <feature>timezone-std-service</feature>                        <!-- Timezone standard service extension -->
+                <feature>vavailability</feature>                                <!-- VAVAILABILITY on inbox -->
+                <!-- <feature>vpoll</feature> -->                                <!-- VPOLL support for store and scheduling -->
+                <feature>webcal</feature>                                                <!-- Internet calendar subscription via GET on calendar collection -->
+
+                <!-- CardDAV specific extension -->
+                <feature>carddav</feature>                                                 <!-- Basic CardDAV feature enabler -->
+                <feature>default-addressbook</feature>                         <!-- Default address book behavior -->
+                <!-- <feature>global-addressbook</feature> -->         <!-- Global address book for each user -->
+                <feature>shared-addressbooks</feature>                        <!-- Shared address books extension -->
+                <!-- <feature>directory-gateway</feature> -->        <!-- Directory gateway extension -->
+
+        </features>
+
+        <substitutions>
+                <!-- Useful xpath shortcuts for verifiers -->
+                <substitution>
+                        <key>$multistatus-response-prefix:</key>
+                        <value>/{DAV:}multistatus/{DAV:}response</value>
+                </substitution>
+                <substitution>
+                        <key>$multistatus-href-prefix:</key>
+                        <value>/{DAV:}multistatus/{DAV:}response/{DAV:}href</value>
+                </substitution>
+                <substitution>
+                        <key>$verify-response-prefix:</key>
+                        <value>{DAV:}response/{DAV:}propstat/{DAV:}prop</value>
+                </substitution>
+                <substitution>
+                        <key>$verify-property-prefix:</key>
+                        <value>/{DAV:}multistatus/{DAV:}response/{DAV:}propstat/{DAV:}prop</value>
+                </substitution>
+                <substitution>
+                        <key>$verify-bad-response:</key>
+                        <value>/{DAV:}multistatus/{DAV:}response/{DAV:}status</value>
+                </substitution>
+                <substitution>
+                        <key>$verify-error-response:</key>
+                        <value>/{DAV:}multistatus/{DAV:}response/{DAV:}error</value>
+                </substitution>
+                <substitution>
+                        <key>$CALDAV:</key>
+                        <value>urn:ietf:params:xml:ns:caldav</value>
+                </substitution>
+                <substitution>
+                        <key>$CARDDAV:</key>
+                        <value>urn:ietf:params:xml:ns:carddav</value>
+                </substitution>
+                <substitution>
+                        <key>$CS:</key>
+                        <value>http://calendarserver.org/ns/</value>
+                </substitution>
+
+                <!-- Server configuration settings -->
+                <!-- $host: and $hostssl: are implicitly added by CalDAVTester based
+                 on the host/nonsslport/sslport values and ssl command line switch -->
+
+                <!-- relative path to caldav root-->
+                <substitution>
+                        <key>$root:</key>
+                        <value>/</value>
+                </substitution>
+
+                <!-- relative path to main principal collection-->
+                <substitution>
+                        <key>$principalcollection:</key>
+                        <value>$root:principals/</value>
+                </substitution>
+
+                <!-- the core recored type collections-->
+                <substitution>
+                        <key>$uidstype:</key>
+                        <value>__uids__</value>
+                </substitution>
+                <substitution>
+                        <key>$userstype:</key>
+                        <value>users</value>
+                </substitution>
+                <substitution>
+                        <key>$groupstype:</key>
+                        <value>groups</value>
+                </substitution>
+                <substitution>
+                        <key>$locationstype:</key>
+                        <value>locations</value>
+                </substitution>
+                <substitution>
+                        <key>$resourcestype:</key>
+                        <value>resources</value>
+                </substitution>
+
+                <!-- relative path to record type principal collections-->
+                <substitution>
+                        <key>$principals_uids:</key>
+                        <value>$principalcollection:$uidstype:/</value>
+                </substitution>
+                <substitution>
+                        <key>$principals_users:</key>
+                        <value>$principalcollection:$userstype:/</value>
+                </substitution>
+                <substitution>
+                        <key>$principals_groups:</key>
+                        <value>$principalcollection:$groupstype:/</value>
+                </substitution>
+                <substitution>
+                        <key>$principals_resources:</key>
+                        <value>$principalcollection:$resourcestype:/</value>
+                </substitution>
+                <substitution>
+                        <key>$principals_locations:</key>
+                        <value>$principalcollection:$locationstype:/</value>
+                </substitution>
+
+                <!-- relative path to calendars collection-->
+                <substitution>
+                        <key>$calendars:</key>
+                        <value>$root:calendars/</value>
+                </substitution>
+
+                <!-- relative path to record type calendar collections-->
+                <substitution>
+                        <key>$calendars_uids:</key>
+                        <value>$calendars:$uidstype:/</value>
+                </substitution>
+                <substitution>
+                        <key>$calendars_users:</key>
+                        <value>$calendars:$userstype:/</value>
+                </substitution>
+                <substitution>
+                        <key>$calendars_groups:</key>
+                        <value>$calendars:$groupstype:/</value>
+                </substitution>
+                <substitution>
+                        <key>$calendars_resources:</key>
+                        <value>$calendars:$resourcestype:/</value>
+                </substitution>
+                <substitution>
+                        <key>$calendars_locations:</key>
+                        <value>$calendars:$locationstype:/</value>
+                </substitution>
+
+                <!-- primary calendar name-->
+                <substitution>
+                        <key>$calendar:</key>
+                        <value>calendar</value>
+                </substitution>
+
+                <!-- primary tasks-only calendar name-->
+                <substitution>
+                        <key>$tasks:</key>
+                        <value>tasks</value>
+                </substitution>
+
+                <!-- inbox name-->
+                <substitution>
+                        <key>$inbox:</key>
+                        <value>inbox</value>
+                </substitution>
+
+                <!-- outbox name-->
+                <substitution>
+                        <key>$outbox:</key>
+                        <value>outbox</value>
+                </substitution>
+
+                <!-- dropbox name-->
+                <substitution>
+                        <key>$dropbox:</key>
+                        <value>dropbox</value>
+                </substitution>
+
+                <!-- attachments name-->
+                <substitution>
+                        <key>$attachments:</key>
+                        <value>dropbox</value>
+                </substitution>
+
+                <!-- notification name-->
+                <substitution>
+                        <key>$notification:</key>
+                        <value>notification</value>
+                </substitution>
+
+                <!-- freebusy name-->
+                <substitution>
+                        <key>$freebusy:</key>
+                        <value>freebusy</value>
+                </substitution>
+
+                <!-- server-to-server inbox-->
+                <substitution>
+                        <key>$servertoserver:</key>
+                        <value>$root:inbox</value>
+                </substitution>
+
+                <!-- timezone service-->
+                <substitution>
+                        <key>$timezoneservice:</key>
+                        <value>$root:timezones</value>
+                </substitution>
+
+                <!-- timezone std service-->
+                <substitution>
+                        <key>$timezonestdservice:</key>
+                        <value>$root:stdtimezones</value>
+                </substitution>
+
+                <!-- relative path to addressbooks collection-->
+                <substitution>
+                        <key>$addressbooks:</key>
+                        <value>$root:addressbooks/</value>
+                </substitution>
+
+                <!-- relative path to record type addressbook collections-->
+                <substitution>
+                        <key>$addressbooks_uids:</key>
+                        <value>$addressbooks:$uidstype:/</value>
+                </substitution>
+                <substitution>
+                        <key>$addressbooks_users:</key>
+                        <value>$addressbooks:$userstype:/</value>
+                </substitution>
+                <substitution>
+                        <key>$addressbooks_groups:</key>
+                        <value>$addressbooks:$groupstype:/</value>
+                </substitution>
+
+                <!-- primary addressbook name -->
+                <substitution>
+                        <key>$addressbook:</key>
+                        <value>addressbook</value>
+                </substitution>
+
+                <!-- directory name -->
+                <substitution>
+                        <key>$directory:</key>
+                        <value>$root:directory/</value>
+                </substitution>
+
+                <!-- global-addressbook name -->
+                <substitution>
+                        <key>$global_addressbook:</key>
+                        <value>global-addressbook</value>
+                </substitution>
+
+                <!-- POST add-member URI suffix -->
+                <substitution>
+                        <key>$add-member:</key>
+                        <value>;add-member</value>
+                </substitution>
+
+                <!-- user id for admin user -->
+                <substitution>
+                        <key>$useradmin:</key>
+                        <value>admin</value>
+                </substitution>
+                <!-- guid for admin user -->
+                <substitution>
+                        <key>$useradminguid:</key>
+                        <value>admin</value>
+                </substitution>
+                <!-- password for admin user -->
+                <substitution>
+                        <key>$pswdadmin:</key>
+                        <value>admin</value>
+                </substitution>
+
+                <!-- relative path to admin principal resource-->
+                <substitution>
+                        <key>$principal_admin:</key>
+                        <value>$principals_users:$useradmin:/</value>
+                </substitution>
+                <substitution>
+                        <key>$principaluri_admin:</key>
+                        <value>$principals_uids:$useradminguid:/</value>
+                </substitution>
+
+                <!-- Forty podA user accounts -->
+                <repeat count="40">
+                        <!-- user id -->
+                        <substitution>
+                                <key>$userid%d:</key>
+                                <value>user%02d</value>
+                        </substitution>
+                        <!-- user guid -->
+                        <substitution>
+                                <key>$userguid%d:</key>
+                                <value>user%02d</value>
+                        </substitution>
+                        <!-- user name -->
+                        <substitution>
+                                <key>$username%d:</key>
+                                <value>User %02d</value>
+                        </substitution>
+                        <!-- user name URI encoded -->
+                        <substitution>
+                                <key>$username-encoded%d:</key>
+                                <value>User%%20%02d</value>
+                        </substitution>
+                        <!-- first name -->
+                        <substitution>
+                                <key>$firstname%d:</key>
+                                <value>User</value>
+                        </substitution>
+                        <!-- last name -->
+                        <substitution>
+                                <key>$lastname%d:</key>
+                                <value>%02d</value>
+                        </substitution>
+                        <!-- password -->
+                        <substitution>
+                                <key>$pswd%d:</key>
+                                <value>user%02d</value>
+                        </substitution>
+                        <!-- relative path to user principal resource-->
+                        <substitution>
+                                <key>$principal%d:</key>
+                                <value>$principals_users:$userid%d:/</value>
+                        </substitution>
+                        <substitution>
+                                <key>$principaluri%d:</key>
+                                <value>$principals_uids:$userguid%d:/</value>
+                        </substitution>
+                        <substitution>
+                                <key>$principal%dnoslash:</key>
+                                <value>$principals_users:$userid%d:</value>
+                        </substitution>
+
+                        <!-- relative path to user calendar home-->
+                        <substitution>
+                                <key>$calendarhome%d:</key>
+                                <value>$calendars_uids:$userguid%d:</value>
+                        </substitution>
+                        <!-- relative path to user alternate calendar home-->
+                        <substitution>
+                                <key>$calendarhomealt%d:</key>
+                                <value>$calendars_users:$userid%d:</value>
+                        </substitution>
+                        <!-- relative path to user calendar-->
+                        <substitution>
+                                <key>$calendarpath%d:</key>
+                                <value>$calendarhome%d:/$calendar:</value>
+                        </substitution>
+                        <!-- relative path to user alternate calendar-->
+                        <substitution>
+                                <key>$calendarpathalt%d:</key>
+                                <value>$calendarhomealt%d:/$calendar:</value>
+                        </substitution>
+                        <!-- relative path to user tasks calendar-->
+                        <substitution>
+                                <key>$taskspath%d:</key>
+                                <value>$calendarhome%d:/$tasks:</value>
+                        </substitution>
+                        <!-- relative path to user inbox-->
+                        <substitution>
+                                <key>$inboxpath%d:</key>
+                                <value>$calendarhome%d:/$inbox:</value>
+                        </substitution>
+                        <!-- relative path to user outbox-->
+                        <substitution>
+                                <key>$outboxpath%d:</key>
+                                <value>$calendarhome%d:/$outbox:</value>
+                        </substitution>
+                        <!-- relative path to user dropbox-->
+                        <substitution>
+                                <key>$dropboxpath%d:</key>
+                                <value>$calendarhome%d:/$dropbox:</value>
+                        </substitution>
+                        <!-- relative path to user notification-->
+                        <substitution>
+                                <key>$notificationpath%d:</key>
+                                <value>$calendarhome%d:/$notification:</value>
+                        </substitution>
+                        <!-- relative path to user freebusy-->
+                        <substitution>
+                                <key>$freebusypath%d:</key>
+                                <value>$calendarhome%d:/$freebusy:</value>
+                        </substitution>
+                        <substitution>
+                                <key>$email%d:</key>
+                                <value>$userid%d:@example.com</value>
+                        </substitution>
+                        <!-- calendar user address of user-->
+                        <substitution>
+                                <key>$cuaddr%d:</key>
+                                <value>mailto:$email%d:</value>
+                        </substitution>
+                        <substitution>
+                                <key>$cuaddralt%d:</key>
+                                <value>$principaluri%d:</value>
+                        </substitution>
+                        <substitution>
+                                <key>$cuaddraltnoslash%d:</key>
+                                <value>$principals_uids:$userguid%d:</value>
+                        </substitution>
+                        <substitution>
+                                <key>$cuaddrurn%d:</key>
+                                <value>urn:uuid:$userguid%d:</value>
+                        </substitution>
+
+                        <!-- relative path to user addressbook home-->
+                        <substitution>
+                                <key>$addressbookhome%d:</key>
+                                <value>$addressbooks_uids:$userguid%d:</value>
+                        </substitution>
+                        <!-- relative path to user addressbook-->
+                        <substitution>
+                                <key>$addressbookpath%d:</key>
+                                <value>$addressbooks_uids:$userguid%d:/$addressbook:</value>
+                        </substitution>
+                </repeat>
+
+                <!-- Forty podB user accounts -->
+                <repeat count="40">
+                        <!-- user id -->
+                        <substitution>
+                                <key>$puserid%d:</key>
+                                <value>puser%02d</value>
+                        </substitution>
+                        <!-- user guid -->
+                        <substitution>
+                                <key>$puserguid%d:</key>
+                                <value>puser%02d</value>
+                        </substitution>
+                        <!-- user name -->
+                        <substitution>
+                                <key>$pusername%d:</key>
+                                <value>Puser %02d</value>
+                        </substitution>
+                        <!-- user name URI encoded -->
+                        <substitution>
+                                <key>$pusername-encoded%d:</key>
+                                <value>Puser%%20%02d</value>
+                        </substitution>
+                        <!-- first name -->
+                        <substitution>
+                                <key>$pfirstname%d:</key>
+                                <value>Puser</value>
+                        </substitution>
+                        <!-- last name -->
+                        <substitution>
+                                <key>$plastname%d:</key>
+                                <value>%02d</value>
+                        </substitution>
+                        <!-- password -->
+                        <substitution>
+                                <key>$ppswd%d:</key>
+                                <value>puser%02d</value>
+                        </substitution>
+                        <!-- relative path to user principal resource-->
+                        <substitution>
+                                <key>$pprincipal%d:</key>
+                                <value>$principals_users:$puserid%d:/</value>
+                        </substitution>
+                        <substitution>
+                                <key>$pprincipaluri%d:</key>
+                                <value>$principals_uids:$puserguid%d:/</value>
+                        </substitution>
+                        <substitution>
+                                <key>$pprincipal%dnoslash:</key>
+                                <value>$principals_users:$puserid%d:</value>
+                        </substitution>
+
+                        <!-- relative path to user calendar home-->
+                        <substitution>
+                                <key>$pcalendarhome%d:</key>
+                                <value>$calendars_uids:$puserguid%d:</value>
+                        </substitution>
+                        <!-- relative path to user alternate calendar home-->
+                        <substitution>
+                                <key>$pcalendarhomealt%d:</key>
+                                <value>$calendars_users:$puserid%d:</value>
+                        </substitution>
+                        <!-- relative path to user calendar-->
+                        <substitution>
+                                <key>$pcalendarpath%d:</key>
+                                <value>$pcalendarhome%d:/$calendar:</value>
+                        </substitution>
+                        <!-- relative path to user alternate calendar-->
+                        <substitution>
+                                <key>$pcalendarpathalt%d:</key>
+                                <value>$pcalendarhomealt%d:/$calendar:</value>
+                        </substitution>
+                        <!-- relative path to user tasks calendar-->
+                        <substitution>
+                                <key>$ptaskspath%d:</key>
+                                <value>$pcalendarhome%d:/$tasks:</value>
+                        </substitution>
+                        <!-- relative path to user inbox-->
+                        <substitution>
+                                <key>$pinboxpath%d:</key>
+                                <value>$pcalendarhome%d:/$inbox:</value>
+                        </substitution>
+                        <!-- relative path to user outbox-->
+                        <substitution>
+                                <key>$poutboxpath%d:</key>
+                                <value>$pcalendarhome%d:/$outbox:</value>
+                        </substitution>
+                        <!-- relative path to user dropbox-->
+                        <substitution>
+                                <key>$pdropboxpath%d:</key>
+                                <value>$pcalendarhome%d:/$dropbox:</value>
+                        </substitution>
+                        <!-- relative path to user notification-->
+                        <substitution>
+                                <key>$pnotificationpath%d:</key>
+                                <value>$pcalendarhome%d:/$notification:</value>
+                        </substitution>
+                        <!-- relative path to user freebusy-->
+                        <substitution>
+                                <key>$pfreebusypath%d:</key>
+                                <value>$pcalendarhome%d:/$freebusy:</value>
+                        </substitution>
+                        <substitution>
+                                <key>$pemail%d:</key>
+                                <value>$puserid%d:@example.com</value>
+                        </substitution>
+                        <!-- calendar user address of user-->
+                        <substitution>
+                                <key>$pcuaddr%d:</key>
+                                <value>mailto:$pemail%d:</value>
+                        </substitution>
+                        <substitution>
+                                <key>$pcuaddralt%d:</key>
+                                <value>$pprincipaluri%d:</value>
+                        </substitution>
+                        <substitution>
+                                <key>$pcuaddraltnoslash%d:</key>
+                                <value>$principals_uids:$puserguid%d:</value>
+                        </substitution>
+                        <substitution>
+                                <key>$pcuaddrurn%d:</key>
+                                <value>urn:uuid:$puserguid%d:</value>
+                        </substitution>
+
+                        <!-- relative path to user addressbook home-->
+                        <substitution>
+                                <key>$paddressbookhome%d:</key>
+                                <value>$addressbooks_uids:$puserguid%d:</value>
+                        </substitution>
+                        <!-- relative path to user addressbook-->
+                        <substitution>
+                                <key>$paddressbookpath%d:</key>
+                                <value>$paddressbookhome%d:/$addressbook:</value>
+                        </substitution>
+                </repeat>
+
+        </substitutions>
+</serverinfo>
</ins></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptsserverserverinfotemplatexml"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-template.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-template.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo-template.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -79,8 +79,8 @@
</span><span class="cx">                 <feature>managed-attachments</feature>                        <!-- CalDAV Managed Attachments -->
</span><span class="cx">                 <feature>maskuid</feature>                                                <!-- maskuid extension -->
</span><span class="cx">                 <feature>no-duplicate-uids</feature>                        <!-- duplicate UIDs in same home not supported -->
</span><del>-                <!-- <feature>partitioning</feature> -->                <!-- Partitioned server -->
</del><span class="cx">                 <feature>partstat-timestamp</feature>                        <!-- Time stamps when PARTSTAT changes extension -->
</span><ins>+                <!-- <feature>podding</feature> -->                         <!-- Podded server -->
</ins><span class="cx">                 <feature>private-comments</feature>                                <!-- private-comments extension -->
</span><span class="cx">                 <feature>private-events</feature>                                <!-- private-events extension -->
</span><span class="cx">                 <feature>proxy</feature>                                                <!-- calendar-user-proxy extension -->
</span><span class="lines">@@ -98,13 +98,14 @@
</span><span class="cx">                 <feature>timezone-service</feature>                                <!-- Timezone service extension for Wiki -->
</span><span class="cx">                 <feature>timezone-std-service</feature>                        <!-- Timezone standard service extension -->
</span><span class="cx">                 <feature>vavailability</feature>                                <!-- VAVAILABILITY on inbox -->
</span><ins>+                <!-- <feature>vpoll</feature> -->                                <!-- VPOLL support for store and scheduling -->
</ins><span class="cx">                 <feature>webcal</feature>                                                <!-- Internet calendar subscription via GET on calendar collection -->
</span><span class="cx">
</span><span class="cx">                 <!-- CardDAV specific extension -->
</span><span class="cx">                 <feature>carddav</feature>                                                 <!-- Basic CardDAV feature enabler -->
</span><span class="cx">                 <feature>default-addressbook</feature>                         <!-- Default address book behavior -->
</span><span class="cx">                 <!-- <feature>global-addressbook</feature> -->         <!-- Global address book for each user -->
</span><del>-                <!-- <feature>shared-addressbooks</feature> -->        <!-- Shared address books extension -->
</del><ins>+                <!-- <feature>shared-addressbooks</feature> --> <!-- Shared address books extension -->
</ins><span class="cx">                 <!-- <feature>directory-gateway</feature> -->        <!-- Directory gateway extension -->
</span><span class="cx">
</span><span class="cx">         </features>
</span><span class="lines">@@ -539,7 +540,7 @@
</span><span class="cx">                         <!-- relative path to user addressbook-->
</span><span class="cx">                         <substitution>
</span><span class="cx">                                 <key>$addressbookpath%%d:</key>
</span><del>-                                <value>$addressbooks_uids:$userguid%%d:/$addressbook:</value>
</del><ins>+                                <value>$addressbookhome%%d:/$addressbook:</value>
</ins><span class="cx">                         </substitution>
</span><span class="cx">                 </repeat>
</span><span class="cx">
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptsserverserverinfodtd"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.dtd (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.dtd        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.dtd        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,11 +14,14 @@
</span><span class="cx"> limitations under the License.
</span><span class="cx"> -->
</span><span class="cx">
</span><del>-<!ELEMENT serverinfo (host, nonsslport, sslport, authtype?, waitime?, features?, substitutions)? >
</del><ins>+<!ELEMENT serverinfo (host, nonsslport, sslport, host2?, nonsslport2?, sslport2?, authtype?, waitime?, features?, substitutions)? >
</ins><span class="cx">
</span><span class="cx">         <!ELEMENT host                        (#PCDATA)>
</span><span class="cx">         <!ELEMENT nonsslport        (#PCDATA)>
</span><span class="cx">         <!ELEMENT sslport                (#PCDATA)>
</span><ins>+        <!ELEMENT host2                        (#PCDATA)>
+        <!ELEMENT nonsslport2        (#PCDATA)>
+        <!ELEMENT sslport2                (#PCDATA)>
</ins><span class="cx">         <!ELEMENT authtype                (#PCDATA)>
</span><span class="cx">         <!ELEMENT waittime (#PCDATA)>
</span><span class="cx">         <!ELEMENT features (feature*)>
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptsserverserverinfoxml"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/server/serverinfo.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -78,9 +78,9 @@
</span><span class="cx">                 <feature>location-resource-tracking</feature> <!-- Server tracks who makes unscheduled changes to locations and resources -->
</span><span class="cx">                 <feature>managed-attachments</feature>                        <!-- CalDAV Managed Attachments -->
</span><span class="cx">                 <feature>maskuid</feature>                                                <!-- maskuid extension -->
</span><del>-                <!-- <feature>partitioning</feature> -->                <!-- Partitioned server -->
</del><span class="cx">                 <feature>no-duplicate-uids</feature>                        <!-- duplicate UIDs in same home not supported -->
</span><span class="cx">                 <feature>partstat-timestamp</feature>                        <!-- Time stamps when PARTSTAT changes extension -->
</span><ins>+                <!-- <feature>podding</feature> -->                         <!-- Podded server -->
</ins><span class="cx">                 <feature>private-comments</feature>                                <!-- private-comments extension -->
</span><span class="cx">                 <feature>private-events</feature>                                <!-- private-events extension -->
</span><span class="cx">                 <feature>proxy</feature>                                                <!-- calendar-user-proxy extension -->
</span><span class="lines">@@ -98,6 +98,7 @@
</span><span class="cx">                 <feature>timezone-service</feature>                                <!-- Timezone service extension for Wiki -->
</span><span class="cx">                 <feature>timezone-std-service</feature>                        <!-- Timezone standard service extension -->
</span><span class="cx">                 <feature>vavailability</feature>                                <!-- VAVAILABILITY on inbox -->
</span><ins>+                <!-- <feature>vpoll</feature> -->                                <!-- VPOLL support for store and scheduling -->
</ins><span class="cx">                 <feature>webcal</feature>                                                <!-- Internet calendar subscription via GET on calendar collection -->
</span><span class="cx">
</span><span class="cx">                 <!-- CardDAV specific extension -->
</span><span class="lines">@@ -248,6 +249,12 @@
</span><span class="cx">                         <value>tasks</value>
</span><span class="cx">                 </substitution>
</span><span class="cx">
</span><ins>+                <!-- primary polls-only calendar name-->
+                <substitution>
+                        <key>$polls:</key>
+                        <value>polls</value>
+                </substitution>
+
</ins><span class="cx">                 <!-- inbox name-->
</span><span class="cx">                 <substitution>
</span><span class="cx">                         <key>$inbox:</key>
</span><span class="lines">@@ -302,13 +309,13 @@
</span><span class="cx">                         <value>$root:stdtimezones</value>
</span><span class="cx">                 </substitution>
</span><span class="cx">
</span><del>-                <!-- relative path to calendars collection-->
</del><ins>+                <!-- relative path to addressbooks collection-->
</ins><span class="cx">                 <substitution>
</span><span class="cx">                         <key>$addressbooks:</key>
</span><span class="cx">                         <value>$root:addressbooks/</value>
</span><span class="cx">                 </substitution>
</span><span class="cx">
</span><del>-                <!-- relative path to record type calendar collections-->
</del><ins>+                <!-- relative path to record type addressbook collections-->
</ins><span class="cx">                 <substitution>
</span><span class="cx">                         <key>$addressbooks_uids:</key>
</span><span class="cx">                         <value>$addressbooks:$uidstype:/</value>
</span><span class="lines">@@ -485,6 +492,11 @@
</span><span class="cx">                                 <key>$taskspath%d:</key>
</span><span class="cx">                                 <value>$calendarhome%d:/$tasks:</value>
</span><span class="cx">                         </substitution>
</span><ins>+                        <!-- relative path to user polls calendar-->
+                        <substitution>
+                                <key>$pollspath%d:</key>
+                                <value>$calendarhome%d:/$polls:</value>
+                        </substitution>
</ins><span class="cx">                         <!-- relative path to user inbox-->
</span><span class="cx">                         <substitution>
</span><span class="cx">                                 <key>$inboxpath%d:</key>
</span><span class="lines">@@ -540,7 +552,7 @@
</span><span class="cx">                         <!-- relative path to user addressbook-->
</span><span class="cx">                         <substitution>
</span><span class="cx">                                 <key>$addressbookpath%d:</key>
</span><del>-                                <value>$addressbooks_uids:$userguid%d:/$addressbook:</value>
</del><ins>+                                <value>$addressbookhome%d:/$addressbook:</value>
</ins><span class="cx">                         </substitution>
</span><span class="cx">                 </repeat>
</span><span class="cx">
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVcaldavtestdtd"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/caldavtest.dtd (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/caldavtest.dtd        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/caldavtest.dtd        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -31,7 +31,8 @@
</span><span class="cx">
</span><span class="cx">         <!ELEMENT request (require-feature?, exclude-feature?, method, ruri*, header*, data?, verify*,
</span><span class="cx">                                                 graburi?, grabcount?, grabheader*, grabproperty*, grabelement*, grabcalproperty*, grabcalparameter*)>
</span><del>-                <!ATTLIST request auth (yes|no) "yes"
</del><ins>+                <!ATTLIST request host2 (yes|no) "no"
+                                                 auth (yes|no) "yes"
</ins><span class="cx">                                                  user CDATA ""
</span><span class="cx">                                                  pswd CDATA ""
</span><span class="cx">                                                  end-delete (yes|no) "no"
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVimplicitdefaultcalendarxml"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/implicitdefaultcalendar.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/implicitdefaultcalendar.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/implicitdefaultcalendar.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -357,7 +357,7 @@
</span><span class="cx">                                         </arg>
</span><span class="cx">                                         <arg>
</span><span class="cx">                                                 <name>nohrefs</name>
</span><del>-                                                <value>events/</value>
</del><ins>+                                                <value>$calendar:/</value>
</ins><span class="cx">                                         </arg>
</span><span class="cx">                                 </verify>
</span><span class="cx">                         </request>
</span><span class="lines">@@ -424,7 +424,7 @@
</span><span class="cx">                                         </arg>
</span><span class="cx">                                         <arg>
</span><span class="cx">                                                 <name>okhrefs</name>
</span><del>-                                                <value>events/</value>
</del><ins>+                                                <value>$calendar:/</value>
</ins><span class="cx">                                         </arg>
</span><span class="cx">                                 </verify>
</span><span class="cx">                         </request>
</span><span class="lines">@@ -433,7 +433,7 @@
</span><span class="cx">                         <description>Attendee has data</description>
</span><span class="cx">                         <request user="$userid10:" pswd="$pswd10:" print-response='no'>
</span><span class="cx">                                 <method>GETNEW</method>
</span><del>-                                <ruri>$calendarhome10:/events/</ruri>
</del><ins>+                                <ruri>$calendarpath10:/</ruri>
</ins><span class="cx">                                 <verify>
</span><span class="cx">                                         <callback>calendarDataMatch</callback>
</span><span class="cx">                                         <arg>
</span><span class="lines">@@ -451,7 +451,7 @@
</span><span class="cx">                         </request>
</span><span class="cx">                         <request user="$userid10:" pswd="$pswd10:">
</span><span class="cx">                                 <method>DELETEALL</method>
</span><del>-                                <ruri>$calendarhome10:/events/</ruri>
</del><ins>+                                <ruri>$calendarpath10:/</ruri>
</ins><span class="cx">                         </request>
</span><span class="cx">                         <request user="$userid1:" pswd="$pswd1:">
</span><span class="cx">                                 <method>DELETEALL</method>
</span><span class="lines">@@ -461,14 +461,6 @@
</span><span class="cx">                                 <method>DELETEALL</method>
</span><span class="cx">                                 <ruri>$inboxpath10:/</ruri>
</span><span class="cx">                         </request>
</span><del>-                        <request user="$userid10:" pswd="$pswd10:" print-response='no'>
-                                <method>MKCALENDAR</method>
-                                <ruri>$calendarpath10:/</ruri>
-                        </request>
-                        <request user="$userid10:" pswd="$pswd10:">
-                                <method>DELETE</method>
-                                <ruri>$calendarhome10:/events/</ruri>
-                        </request>
</del><span class="cx">                 </test>
</span><span class="cx">         </test-suite>
</span><span class="cx">         
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVpollsxmlfromrev12016CalDAVTestertrunkscriptstestsCalDAVpollsxml"></a>
<div class="copfile"><h4>Copied: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/polls.xml (from rev 12016, CalDAVTester/trunk/scripts/tests/CalDAV/polls.xml) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/polls.xml         (rev 0)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/polls.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,171 @@
</span><ins>+<?xml version="1.0" standalone="no"?>
+
+<!DOCTYPE caldavtest SYSTEM "caldavtest.dtd">
+
+<!--
+ Copyright (c) 2006-2013 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.
+ -->
+
+<caldavtest>
+        <description>Test PUT method in CalDAV</description>
+
+        <require-feature>
+                <feature>caldav</feature>
+                <feature>vpoll</feature>
+        </require-feature>
+
+        <start/>
+
+        <test-suite name='supported-component-set'>
+                <test name='1'>
+                        <require-feature>
+                                <feature>supported-component-sets</feature>
+                        </require-feature>
+                        <description>Check calendar home for property</description>
+                        <request>
+                                <method>PROPFIND</method>
+                                <ruri>$calendarhome1:/</ruri>
+                                <header>
+                                        <name>Depth</name>
+                                        <value>0</value>
+                                </header>
+                                <data>
+                                        <content-type>text/xml; charset=utf-8</content-type>
+                                        <filepath>Resource/CalDAV/polls/supported-component-set/1.xml</filepath>
+                                </data>
+                                <verify>
+                                        <exclude-feature>
+                                                <feature>supported-component-sets-one</feature>
+                                        </exclude-feature>
+                                        <callback>propfindItems</callback>
+                                        <arg>
+                                                <name>okprops</name>
+                                                <value>{urn:ietf:params:xml:ns:caldav}supported-calendar-component-sets$</value>
+                                        </arg>
+                                </verify>
+                                <verify>
+                                        <require-feature>
+                                                <feature>supported-component-sets-one</feature>
+                                        </require-feature>
+                                        <callback>xmlElementMatch</callback>
+                                        <arg>
+                                                <name>exists</name>
+                                                <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-sets/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VEVENT"]</value>
+                                                <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-sets/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VTODO"]</value>
+                                                <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-sets/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VPOLL"]</value>
+                                        </arg>
+                                </verify>
+                        </request>
+                </test>
+                <test name='2'>
+                        <description>Check calendar for property</description>
+                        <request>
+                                <method>PROPFIND</method>
+                                <ruri>$pollspath1:/</ruri>
+                                <header>
+                                        <name>Depth</name>
+                                        <value>0</value>
+                                </header>
+                                <data>
+                                        <content-type>text/xml; charset=utf-8</content-type>
+                                        <filepath>Resource/CalDAV/polls/supported-component-set/2.xml</filepath>
+                                </data>
+                                <verify>
+                                        <require-feature>
+                                                <feature>exclude-calendars</feature>
+                                        </require-feature>
+                                        <callback>xmlElementMatch</callback>
+                                        <arg>
+                                                <name>exists</name>
+                                                <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VEVENT"]</value>
+                                                <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VTODO"]</value>
+                                                <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VPOLL"]</value>
+                                        </arg>
+                                </verify>
+                                <verify>
+                                        <require-feature>
+                                                <feature>split-calendars</feature>
+                                        </require-feature>
+                                        <callback>xmlElementMatch</callback>
+                                        <arg>
+                                                <name>exists</name>
+                                                <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VPOLL"]</value>
+                                        </arg>
+                                        <arg>
+                                                <name>notexists</name>
+                                                <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VEVENT"]</value>
+                                                <value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set/{urn:ietf:params:xml:ns:caldav}comp[@name="VTODO"]</value>
+                                        </arg>
+                                </verify>
+                        </request>
+                </test>
+        </test-suite>
+
+        <test-suite name='PUT VPOLL' ignore='no'>
+                <test name='1' ignore='no'>
+                        <description>PUT unscheduled empty poll</description>
+                        <request end-delete='yes'>
+                                <method>PUT</method>
+                                <ruri>$pollspath1:/1.ics</ruri>
+                                <data>
+                                        <content-type>text/calendar; charset=utf-8</content-type>
+                                        <filepath>Resource/CalDAV/polls/put/1.ics</filepath>
+                                </data>
+                                <verify>
+                                        <callback>statusCode</callback>
+                                </verify>
+                        </request>
+                        <request>
+                                <method>GET</method>
+                                <ruri>$pollspath1:/1.ics</ruri>
+                                <verify>
+                                        <callback>calendarDataMatch</callback>
+                                        <arg>
+                                                <name>filepath</name>
+                                                <value>Resource/CalDAV/polls/put/2.ics</value>
+                                        </arg>
+                                </verify>
+                        </request>
+                </test>
+                <test name='2' ignore='no'>
+                        <description>PUT unscheduled event poll</description>
+                        <request end-delete='yes'>
+                                <method>PUT</method>
+                                <ruri>$pollspath1:/2.ics</ruri>
+                                <data>
+                                        <content-type>text/calendar; charset=utf-8</content-type>
+                                        <filepath>Resource/CalDAV/polls/put/3.ics</filepath>
+                                </data>
+                                <verify>
+                                        <callback>statusCode</callback>
+                                </verify>
+                        </request>
+                        <request>
+                                <method>GET</method>
+                                <ruri>$pollspath1:/2.ics</ruri>
+                                <verify>
+                                        <callback>calendarDataMatch</callback>
+                                        <arg>
+                                                <name>filepath</name>
+                                                <value>Resource/CalDAV/polls/put/4.ics</value>
+                                        </arg>
+                                </verify>
+                        </request>
+                </test>
+        </test-suite>
+        
+        <end/>
+        
+</caldavtest>
</ins></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVprivatecommentsxml"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/privatecomments.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/privatecomments.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/privatecomments.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1006,55 +1006,34 @@
</span><span class="cx">                         </request>
</span><span class="cx">                 </test>
</span><span class="cx">                 <test name='8'>
</span><del>-                        <description>Attendee removes comment property entirely</description>
-                        <request user="$userid2:" pswd="$pswd2:" print-response='no'>
-                                <method>GETNEW</method>
-                                <ruri>$calendarpath2:/</ruri>
-                                <verify>
-                                        <callback>statusCode</callback>
-                                </verify>
</del><ins>+                        <description>Organizer Inbox Item</description>
+                        <request print-response='no'>
+                                <method>WAITDELETEALL 1</method>
+                                <ruri>$inboxpath1:/</ruri>
</ins><span class="cx">                         </request>
</span><del>-                        <request user="$userid2:" pswd="$pswd2:" print-response='no'>
-                                <method>PUT</method>
-                                <ruri>$</ruri>
-                                <data>
-                                        <content-type>text/calendar; charset=utf-8</content-type>
-                                        <filepath>Resource/CalDAV/privatecomments/31.ics</filepath>
-                                </data>
-                                <verify>
-                                        <callback>statusCode</callback>
-                                </verify>
-                        </request>
</del><span class="cx">                 </test>
</span><span class="cx">                 <test name='9'>
</span><del>-                        <description>Attendee has data with comment preserved</description>
-                        <request user="$userid2:" pswd="$pswd2:" print-response='no'>
-                                <method>GETNEW</method>
-                                <ruri>$calendarpath2:/</ruri>
</del><ins>+                        <description>Organizer data changed</description>
+                        <request print-response='no'>
+                                <method>GET</method>
+                                <ruri>$calendarpath1:/3.ics</ruri>
</ins><span class="cx">                                 <verify>
</span><span class="cx">                                         <callback>calendarDataMatch</callback>
</span><span class="cx">                                         <arg>
</span><span class="cx">                                                 <name>filepath</name>
</span><del>-                                                <value>Resource/CalDAV/privatecomments/32.ics</value>
</del><ins>+                                                <value>Resource/CalDAV/privatecomments/35.ics</value>
</ins><span class="cx">                                         </arg>
</span><span class="cx">                                 </verify>
</span><span class="cx">                         </request>
</span><span class="cx">                 </test>
</span><span class="cx">                 <test name='10'>
</span><del>-                        <description>Attendee changes comment</description>
-                        <request user="$userid2:" pswd="$pswd2:" print-response='no'>
-                                <method>GETNEW</method>
-                                <ruri>$calendarpath2:/</ruri>
-                                <verify>
-                                        <callback>statusCode</callback>
-                                </verify>
-                        </request>
-                        <request user="$userid2:" pswd="$pswd2:" print-response='no'>
</del><ins>+                        <description>Organizer removes comments entirely</description>
+                        <request print-response='no'>
</ins><span class="cx">                                 <method>PUT</method>
</span><del>-                                <ruri>$</ruri>
</del><ins>+                                <ruri>$calendarpath1:/3.ics</ruri>
</ins><span class="cx">                                 <data>
</span><span class="cx">                                         <content-type>text/calendar; charset=utf-8</content-type>
</span><del>-                                        <filepath>Resource/CalDAV/privatecomments/33.ics</filepath>
</del><ins>+                                        <filepath>Resource/CalDAV/privatecomments/36.ics</filepath>
</ins><span class="cx">                                 </data>
</span><span class="cx">                                 <verify>
</span><span class="cx">                                         <callback>statusCode</callback>
</span><span class="lines">@@ -1062,55 +1041,34 @@
</span><span class="cx">                         </request>
</span><span class="cx">                 </test>
</span><span class="cx">                 <test name='11'>
</span><del>-                        <description>Attendee has data with comment preserved</description>
-                        <request user="$userid2:" pswd="$pswd2:" print-response='no'>
-                                <method>GETNEW</method>
-                                <ruri>$calendarpath2:/</ruri>
-                                <verify>
-                                        <callback>calendarDataMatch</callback>
-                                        <arg>
-                                                <name>filepath</name>
-                                                <value>Resource/CalDAV/privatecomments/34.ics</value>
-                                        </arg>
-                                </verify>
-                        </request>
-                </test>
-                <test name='12'>
-                        <description>Organizer Inbox Items</description>
</del><ins>+                        <description>Organizer checks data</description>
</ins><span class="cx">                         <request print-response='no'>
</span><del>-                                <method>WAITDELETEALL 2</method>
-                                <ruri>$inboxpath1:/</ruri>
-                        </request>
-                </test>
-                <test name='13'>
-                        <description>Organizer data changed</description>
-                        <request print-response='no'>
</del><span class="cx">                                 <method>GET</method>
</span><span class="cx">                                 <ruri>$calendarpath1:/3.ics</ruri>
</span><span class="cx">                                 <verify>
</span><span class="cx">                                         <callback>calendarDataMatch</callback>
</span><span class="cx">                                         <arg>
</span><span class="cx">                                                 <name>filepath</name>
</span><del>-                                                <value>Resource/CalDAV/privatecomments/35.ics</value>
</del><ins>+                                                <value>Resource/CalDAV/privatecomments/37.ics</value>
</ins><span class="cx">                                         </arg>
</span><span class="cx">                                 </verify>
</span><span class="cx">                         </request>
</span><span class="cx">                 </test>
</span><del>-                <test name='15'>
-                        <description>Organizer removes comments entirely</description>
</del><ins>+                <test name='12'>
+                        <description>Organizer changes comment</description>
</ins><span class="cx">                         <request print-response='no'>
</span><span class="cx">                                 <method>PUT</method>
</span><span class="cx">                                 <ruri>$calendarpath1:/3.ics</ruri>
</span><span class="cx">                                 <data>
</span><span class="cx">                                         <content-type>text/calendar; charset=utf-8</content-type>
</span><del>-                                        <filepath>Resource/CalDAV/privatecomments/36.ics</filepath>
</del><ins>+                                        <filepath>Resource/CalDAV/privatecomments/38.ics</filepath>
</ins><span class="cx">                                 </data>
</span><span class="cx">                                 <verify>
</span><span class="cx">                                         <callback>statusCode</callback>
</span><span class="cx">                                 </verify>
</span><span class="cx">                         </request>
</span><span class="cx">                 </test>
</span><del>-                <test name='16'>
</del><ins>+                <test name='13'>
</ins><span class="cx">                         <description>Organizer checks data</description>
</span><span class="cx">                         <request print-response='no'>
</span><span class="cx">                                 <method>GET</method>
</span><span class="lines">@@ -1119,35 +1077,49 @@
</span><span class="cx">                                         <callback>calendarDataMatch</callback>
</span><span class="cx">                                         <arg>
</span><span class="cx">                                                 <name>filepath</name>
</span><del>-                                                <value>Resource/CalDAV/privatecomments/37.ics</value>
</del><ins>+                                                <value>Resource/CalDAV/privatecomments/38.ics</value>
</ins><span class="cx">                                         </arg>
</span><span class="cx">                                 </verify>
</span><span class="cx">                         </request>
</span><span class="cx">                 </test>
</span><del>-                <test name='17'>
-                        <description>Organizer changes comment</description>
-                        <request print-response='no'>
</del><ins>+                <test name='14'>
+                        <description>Attendee removes comment property entirely</description>
+                        <request user="$userid2:" pswd="$pswd2:" print-response='no'>
+                                <method>GETNEW</method>
+                                <ruri>$calendarpath2:/</ruri>
+                                <verify>
+                                        <callback>statusCode</callback>
+                                </verify>
+                        </request>
+                        <request user="$userid2:" pswd="$pswd2:" print-response='no'>
</ins><span class="cx">                                 <method>PUT</method>
</span><del>-                                <ruri>$calendarpath1:/3.ics</ruri>
</del><ins>+                                <ruri>$</ruri>
</ins><span class="cx">                                 <data>
</span><span class="cx">                                         <content-type>text/calendar; charset=utf-8</content-type>
</span><del>-                                        <filepath>Resource/CalDAV/privatecomments/38.ics</filepath>
</del><ins>+                                        <filepath>Resource/CalDAV/privatecomments/31.ics</filepath>
</ins><span class="cx">                                 </data>
</span><span class="cx">                                 <verify>
</span><span class="cx">                                         <callback>statusCode</callback>
</span><span class="cx">                                 </verify>
</span><span class="cx">                         </request>
</span><span class="cx">                 </test>
</span><del>-                <test name='18'>
-                        <description>Organizer checks data</description>
</del><ins>+                <test name='15'>
+                        <description>Organizer Inbox Item</description>
</ins><span class="cx">                         <request print-response='no'>
</span><ins>+                                <method>WAITDELETEALL 1</method>
+                                <ruri>$inboxpath1:/</ruri>
+                        </request>
+                </test>
+                <test name='16'>
+                        <description>Organizer data changed</description>
+                        <request print-response='no'>
</ins><span class="cx">                                 <method>GET</method>
</span><span class="cx">                                 <ruri>$calendarpath1:/3.ics</ruri>
</span><span class="cx">                                 <verify>
</span><span class="cx">                                         <callback>calendarDataMatch</callback>
</span><span class="cx">                                         <arg>
</span><span class="cx">                                                 <name>filepath</name>
</span><del>-                                                <value>Resource/CalDAV/privatecomments/38.ics</value>
</del><ins>+                                                <value>Resource/CalDAV/privatecomments/32.ics</value>
</ins><span class="cx">                                         </arg>
</span><span class="cx">                                 </verify>
</span><span class="cx">                         </request>
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVsharingnotificationsyncxml"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sharing-notification-sync.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sharing-notification-sync.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sharing-notification-sync.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -239,36 +239,12 @@
</span><span class="cx">                                         <filepath>Resource/CalDAV/sharing/notification-sync/2.xml</filepath>
</span><span class="cx">                                 </data>
</span><span class="cx">                                 <verify>
</span><del>-                                        <exclude-feature>
-                                                <feature>split-calendars</feature>
-                                        </exclude-feature>
</del><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><del>-                                                <name>okhrefs</name>
-                                                <value>$calendar:/</value>
-                                                <value>$inbox:/</value>
-                                                <value>$outbox:/</value>
-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
-                                                <value>shared/</value>
</del><ins>+                                                <name>ignoremissing</name>
</ins><span class="cx">                                         </arg>
</span><span class="cx">                                         <arg>
</span><del>-                                                <name>badhrefs</name>
-                                                <value>$dropbox:/</value>
-                                        </arg>
-                                </verify>
-                                <verify>
-                                        <require-feature>
-                                                <feature>split-calendars</feature>
-                                        </require-feature>
-                                        <callback>multistatusItems</callback>
-                                        <arg>
</del><span class="cx">                                                 <name>okhrefs</name>
</span><del>-                                                <value>$calendar:/</value>
-                                                <value>$tasks:/</value>
-                                                <value>$inbox:/</value>
-                                                <value>$outbox:/</value>
-                                                <value>$freebusy:</value>
</del><span class="cx">                                                 <value>$notification:/</value>
</span><span class="cx">                                                 <value>shared/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -300,35 +276,12 @@
</span><span class="cx">                                         <filepath>Resource/CalDAV/sharing/notification-sync/2.xml</filepath>
</span><span class="cx">                                 </data>
</span><span class="cx">                                 <verify>
</span><del>-                                        <exclude-feature>
-                                                <feature>split-calendars</feature>
-                                        </exclude-feature>
</del><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><del>-                                                <name>okhrefs</name>
-                                                <value>$calendar:/</value>
-                                                <value>$inbox:/</value>
-                                                <value>$outbox:/</value>
-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><ins>+                                                <name>ignoremissing</name>
</ins><span class="cx">                                         </arg>
</span><span class="cx">                                         <arg>
</span><del>-                                                <name>badhrefs</name>
-                                                <value>$dropbox:/</value>
-                                        </arg>
-                                </verify>
-                                <verify>
-                                        <require-feature>
-                                                <feature>split-calendars</feature>
-                                        </require-feature>
-                                        <callback>multistatusItems</callback>
-                                        <arg>
</del><span class="cx">                                                 <name>okhrefs</name>
</span><del>-                                                <value>$calendar:/</value>
-                                                <value>$tasks:/</value>
-                                                <value>$inbox:/</value>
-                                                <value>$outbox:/</value>
-                                                <value>$freebusy:</value>
</del><span class="cx">                                                 <value>$notification:/</value>
</span><span class="cx">                                         </arg>
</span><span class="cx">                                         <arg>
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCalDAVsyncreportxml"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sync-report.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sync-report.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CalDAV/sync-report.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -262,12 +262,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -282,13 +283,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -321,12 +323,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -341,13 +344,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -380,12 +384,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -400,13 +405,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -439,12 +445,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -463,13 +470,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -506,12 +514,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -530,13 +539,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -573,12 +583,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -597,13 +608,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -841,12 +853,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -861,13 +874,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -900,12 +914,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -924,13 +939,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -1005,12 +1021,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -1025,13 +1042,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -1064,12 +1082,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -1089,13 +1108,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -1166,12 +1186,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -1186,13 +1207,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -1225,12 +1247,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -1249,13 +1272,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -1329,12 +1353,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -1349,13 +1374,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar2/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -1388,12 +1414,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -1412,13 +1439,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar1/</value>
</span><span class="cx">                                                 <value>synccalendar1/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar1/2.ics</value>
</span><span class="lines">@@ -2063,12 +2091,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar3/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar3/2.ics</value>
</span><span class="lines">@@ -2087,13 +2116,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar3/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar3/2.ics</value>
</span><span class="lines">@@ -2298,12 +2328,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar3/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar3/2.ics</value>
</span><span class="lines">@@ -2322,13 +2353,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar3/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar3/2.ics</value>
</span><span class="lines">@@ -2579,12 +2611,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar3/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar3/2.ics</value>
</span><span class="lines">@@ -2603,13 +2636,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar3/1.ics</value>
</span><span class="cx">                                                 <value>synccalendar3/2.ics</value>
</span><span class="lines">@@ -2795,12 +2829,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -2815,13 +2850,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -3012,12 +3048,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -3032,13 +3069,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -3275,12 +3313,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -3295,13 +3334,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -3481,12 +3521,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                                 <value>synccalendar4/1.ics</value>
</span><span class="lines">@@ -3503,13 +3544,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                                 <value>synccalendar4/1.ics</value>
</span><span class="lines">@@ -3639,12 +3681,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -3659,13 +3702,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -3794,12 +3838,13 @@
</span><span class="cx">                                         </exclude-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                         </arg>
</span><span class="lines">@@ -3814,13 +3859,14 @@
</span><span class="cx">                                         </require-feature>
</span><span class="cx">                                         <callback>multistatusItems</callback>
</span><span class="cx">                                         <arg>
</span><ins>+                                                <name>ignoremissing</name>
+                                        </arg>
+                                        <arg>
</ins><span class="cx">                                                 <name>okhrefs</name>
</span><span class="cx">                                                 <value>$calendar:/</value>
</span><span class="cx">                                                 <value>$tasks:/</value>
</span><span class="cx">                                                 <value>$inbox:/</value>
</span><span class="cx">                                                 <value>$outbox:/</value>
</span><del>-                                                <value>$freebusy:</value>
-                                                <value>$notification:/</value>
</del><span class="cx">                                                 <value>synccalendar3/</value>
</span><span class="cx">                                                 <value>synccalendar4/</value>
</span><span class="cx">                                         </arg>
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCardDAV"></a>
<div class="propset"><h4>Property changes: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnmergeinfo"></a>
<div class="modfile"><h4>Modified: svn:mergeinfo</h4></div>
<span class="cx">/CalDAVTester/branches/release/CalDAVTester-4.3-dev/scripts/tests/CardDAV:10193
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/attendee-comments-2887/scripts/tests/CardDAV:2888-2910
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/better-proxy-3148/scripts/tests/CardDAV:3149-3163
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/component-set-fixes/scripts/tests/CardDAV:8221-8346
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/conditional-4466/scripts/tests/CardDAV:4467-4469
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/implicitauto-2948/scripts/tests/CardDAV:2949-2989
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/location-partial-accept-3574/scripts/tests/CardDAV:3575-3581
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/managed-attachments/scripts/tests/CardDAV:9986-10145
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/normalize-cuaddr-3533/scripts/tests/CardDAV:3534-3558
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/pycalendar/scripts/tests/CardDAV:7160-7206
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/pycard/scripts/tests/CardDAV:7226-7237
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/sharing-5228/scripts/tests/CardDAV:5229-5440
</span><span class="cx">/CalDAVTester/branches/users/gaya/sharedgroupstester-2/scripts/tests/CardDAV:11078-11181
</span><span class="cx">/CalDAVTester/branches/users/gaya/sharedgroupstester-3/scripts/tests/CardDAV:11181-11204
</span><span class="cx">/CalDAVTester/trunk/scripts/tests/CardDAV:11742-11861
</span><span class="cx"> + /CalDAVTester/branches/release/CalDAVTester-3.0-dev/scripts/tests/CardDAV:7584
</span><span class="cx">/CalDAVTester/branches/release/CalDAVTester-4.3-dev/scripts/tests/CardDAV:10193
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/attendee-comments-2887/scripts/tests/CardDAV:2888-2910
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/better-proxy-3148/scripts/tests/CardDAV:3149-3163
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/component-set-fixes/scripts/tests/CardDAV:8221-8346
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/conditional-4466/scripts/tests/CardDAV:4467-4469
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/implicitauto-2948/scripts/tests/CardDAV:2949-2989
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/location-partial-accept-3574/scripts/tests/CardDAV:3575-3581
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/managed-attachments/scripts/tests/CardDAV:9986-10145
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/normalize-cuaddr-3533/scripts/tests/CardDAV:3534-3558
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/pycalendar/scripts/tests/CardDAV:7160-7206
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/pycard/scripts/tests/CardDAV:7226-7237
</span><span class="cx">/CalDAVTester/branches/users/cdaboo/sharing-5228/scripts/tests/CardDAV:5229-5440
</span><span class="cx">/CalDAVTester/branches/users/gaya/sharedgroupstester-2/scripts/tests/CardDAV:11078-11181
</span><span class="cx">/CalDAVTester/branches/users/gaya/sharedgroupstester-3/scripts/tests/CardDAV:11181-11204
</span><span class="cx">/CalDAVTester/trunk/scripts/tests/CardDAV:11742-12016
</span><a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterscriptstestsCardDAVcaldavtestdtd"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/caldavtest.dtd (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/caldavtest.dtd        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/scripts/tests/CardDAV/caldavtest.dtd        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -31,7 +31,8 @@
</span><span class="cx">
</span><span class="cx">         <!ELEMENT request (require-feature?, exclude-feature?, method, ruri*, header*, data?, verify*,
</span><span class="cx">                                                 graburi?, grabcount?, grabheader*, grabproperty*, grabelement*, grabcalproperty*, grabcalparameter*)>
</span><del>-                <!ATTLIST request auth (yes|no) "yes"
</del><ins>+                <!ATTLIST request host2 (yes|no) "no"
+                                                 auth (yes|no) "yes"
</ins><span class="cx">                                                  user CDATA ""
</span><span class="cx">                                                  pswd CDATA ""
</span><span class="cx">                                                  end-delete (yes|no) "no"
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestestersrccaldavtestpy"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/caldavtest.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/caldavtest.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/caldavtest.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -19,7 +19,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> from cStringIO import StringIO
</span><del>-from pycalendar.calendar import PyCalendar
</del><ins>+from pycalendar.icalendar.calendar import Calendar
</ins><span class="cx"> from src.httpshandler import SmartHTTPConnection
</span><span class="cx"> from src.manager import manager
</span><span class="cx"> from src.request import data, pause
</span><span class="lines">@@ -583,7 +583,7 @@
</span><span class="cx"> stats.startTimer()
</span><span class="cx">
</span><span class="cx"> # Do the http request
</span><del>- http = SmartHTTPConnection(self.manager.server_info.host, self.manager.server_info.port, self.manager.server_info.ssl)
</del><ins>+ http = SmartHTTPConnection(req.host, req.port, self.manager.server_info.ssl)
</ins><span class="cx">
</span><span class="cx"> if not 'User-Agent' in headers and label is not None:
</span><span class="cx"> headers['User-Agent'] = label.encode("utf-8")
</span><span class="lines">@@ -916,7 +916,7 @@
</span><span class="cx"> prop = self._calProperty(propertyname, respdata)
</span><span class="cx">
</span><span class="cx"> try:
</span><del>- return prop.getAttributeValue(pname) if prop else None
</del><ins>+ return prop.getParameterValue(pname) if prop else None
</ins><span class="cx"> except KeyError:
</span><span class="cx"> return None
</span><span class="cx">
</span><span class="lines">@@ -924,7 +924,7 @@
</span><span class="cx"> def _calProperty(self, propertyname, respdata):
</span><span class="cx">
</span><span class="cx"> try:
</span><del>- cal = PyCalendar.parseText(respdata)
</del><ins>+ cal = Calendar.parseText(respdata)
</ins><span class="cx"> except Exception:
</span><span class="cx"> return None
</span><span class="cx">
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestestersrcmanagerpy"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/manager.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/manager.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/manager.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -100,12 +100,22 @@
</span><span class="cx"> # Setup ssl stuff
</span><span class="cx"> self.server_info.ssl = ssl
</span><span class="cx"> self.server_info.port = self.server_info.sslport if ssl else self.server_info.nonsslport
</span><ins>+ self.server_info.port2 = self.server_info.sslport2 if ssl else self.server_info.nonsslport2
+
</ins><span class="cx"> moresubs["$host:"] = "%s://%s:%d" % (
</span><span class="cx"> "https" if ssl else "http",
</span><span class="cx"> self.server_info.host,
</span><span class="cx"> self.server_info.port,
</span><span class="cx"> )
</span><span class="cx"> moresubs["$hostssl:"] = "https://%s:%d" % (self.server_info.host, self.server_info.sslport,)
</span><ins>+
+ moresubs["$host2:"] = "%s://%s:%d" % (
+ "https" if ssl else "http",
+ self.server_info.host2,
+ self.server_info.port2,
+ )
+ moresubs["$hostssl2:"] = "https://%s:%d" % (self.server_info.host2, self.server_info.sslport2,)
+
</ins><span class="cx"> self.server_info.addsubs(moresubs)
</span><span class="cx">
</span><span class="cx"> for testfile in testfiles:
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestestersrcrequestpy"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/request.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/request.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/request.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -142,6 +142,8 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, manager):
</span><span class="cx"> self.manager = manager
</span><ins>+ self.host = self.manager.server_info.host
+ self.port = self.manager.server_info.port
</ins><span class="cx"> self.auth = True
</span><span class="cx"> self.user = ""
</span><span class="cx"> self.pswd = ""
</span><span class="lines">@@ -345,6 +347,10 @@
</span><span class="cx"> self.iterate_data = getYesNoAttributeValue(node, src.xmlDefs.ATTR_ITERATE_DATA)
</span><span class="cx"> self.wait_for_success = getYesNoAttributeValue(node, src.xmlDefs.ATTR_WAIT_FOR_SUCCESS)
</span><span class="cx">
</span><ins>+ if node.get(src.xmlDefs.ATTR_HOST2, src.xmlDefs.ATTR_VALUE_NO) == src.xmlDefs.ATTR_VALUE_YES:
+ self.host = self.manager.server_info.host2
+ self.port = self.manager.server_info.port2
+
</ins><span class="cx"> for child in node.getchildren():
</span><span class="cx"> if child.tag == src.xmlDefs.ELEMENT_REQUIRE_FEATURE:
</span><span class="cx"> self.parseFeatures(child, require=True)
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestestersrcserverinfopy"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/serverinfo.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/serverinfo.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/serverinfo.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -30,6 +30,9 @@
</span><span class="cx"> self.host = ""
</span><span class="cx"> self.nonsslport = 80
</span><span class="cx"> self.sslport = 443
</span><ins>+ self.host2 = ""
+ self.nonsslport2 = 80
+ self.sslport2 = 443
</ins><span class="cx"> self.authtype = "basic"
</span><span class="cx"> self.features = set()
</span><span class="cx"> self.user = ""
</span><span class="lines">@@ -131,6 +134,15 @@
</span><span class="cx"> self.nonsslport = int(child.text)
</span><span class="cx"> elif child.tag == src.xmlDefs.ELEMENT_SSLPORT:
</span><span class="cx"> self.sslport = int(child.text)
</span><ins>+ elif child.tag == src.xmlDefs.ELEMENT_HOST2:
+ try:
+ self.host2 = child.text.encode("utf-8")
+ except:
+ self.host2 = "localhost"
+ elif child.tag == src.xmlDefs.ELEMENT_NONSSLPORT2:
+ self.nonsslport2 = int(child.text)
+ elif child.tag == src.xmlDefs.ELEMENT_SSLPORT2:
+ self.sslport2 = int(child.text)
</ins><span class="cx"> elif child.tag == src.xmlDefs.ELEMENT_AUTHTYPE:
</span><span class="cx"> self.authtype = child.text.encode("utf-8")
</span><span class="cx"> elif child.tag == src.xmlDefs.ELEMENT_WAITTIME:
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestestersrcxmlDefspy"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/xmlDefs.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/xmlDefs.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/src/xmlDefs.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -39,6 +39,7 @@
</span><span class="cx"> ELEMENT_GRABURI = "graburi"
</span><span class="cx"> ELEMENT_HEADER = "header"
</span><span class="cx"> ELEMENT_HOST = "host"
</span><ins>+ELEMENT_HOST2 = "host2"
</ins><span class="cx"> ELEMENT_KEY = "key"
</span><span class="cx"> ELEMENT_LOGGING = "logging"
</span><span class="cx"> ELEMENT_MAILFROM = "mailfrom"
</span><span class="lines">@@ -46,6 +47,7 @@
</span><span class="cx"> ELEMENT_METHOD = "method"
</span><span class="cx"> ELEMENT_NAME = "name"
</span><span class="cx"> ELEMENT_NONSSLPORT = "nonsslport"
</span><ins>+ELEMENT_NONSSLPORT2 = "nonsslport2"
</ins><span class="cx"> ELEMENT_NOTIFY = "notify"
</span><span class="cx"> ELEMENT_PARENT = "parent"
</span><span class="cx"> ELEMENT_PAUSE = "pause"
</span><span class="lines">@@ -59,6 +61,7 @@
</span><span class="cx"> ELEMENT_SERVERINFO = "serverinfo"
</span><span class="cx"> ELEMENT_SPREAD = "spread"
</span><span class="cx"> ELEMENT_SSLPORT = "sslport"
</span><ins>+ELEMENT_SSLPORT2 = "sslport2"
</ins><span class="cx"> ELEMENT_START = "start"
</span><span class="cx"> ELEMENT_SUBJECT = "subject"
</span><span class="cx"> ELEMENT_SUBSTITUTIONS = "substitutions"
</span><span class="lines">@@ -75,6 +78,7 @@
</span><span class="cx"> ELEMENT_WAITTIME = "waittime"
</span><span class="cx"> ELEMENT_WARNINGTIME = "warningtime"
</span><span class="cx">
</span><ins>+ATTR_HOST2 = "host2"
</ins><span class="cx"> ATTR_AUTH = "auth"
</span><span class="cx"> ATTR_COUNT = "count"
</span><span class="cx"> ATTR_DETAILS = "details"
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterverifiersaddressDataMatchpy"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/addressDataMatch.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/addressDataMatch.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/addressDataMatch.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -73,8 +73,8 @@
</span><span class="cx"> if ":" in filter:
</span><span class="cx"> propname, parameter = filter.split(":")
</span><span class="cx"> if property.getName() == propname:
</span><del>- if property.hasAttribute(parameter):
- property.removeAttributes(parameter)
</del><ins>+ if property.hasParameter(parameter):
+ property.removeParameters(parameter)
</ins><span class="cx"> else:
</span><span class="cx"> if property.getName() == filter:
</span><span class="cx"> component.removeProperty(property)
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterverifierscalendarDataMatchpy"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/calendarDataMatch.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/calendarDataMatch.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/calendarDataMatch.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -15,8 +15,8 @@
</span><span class="cx"> ##
</span><span class="cx">
</span><span class="cx"> from difflib import unified_diff
</span><del>-from pycalendar.calendar import PyCalendar
-from pycalendar.attribute import PyCalendarAttribute
</del><ins>+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.parameter import Parameter
</ins><span class="cx">
</span><span class="cx"> """
</span><span class="cx"> Verifier that checks the response body for a semantic match to data in a file.
</span><span class="lines">@@ -99,25 +99,25 @@
</span><span class="cx"> for property in allProps:
</span><span class="cx"> # Always reset DTSTAMP on these properties
</span><span class="cx"> if property.getName() in ("ATTENDEE", "X-CALENDARSERVER-ATTENDEE-COMMENT"):
</span><del>- if property.hasAttribute("X-CALENDARSERVER-DTSTAMP"):
- property.replaceAttribute(PyCalendarAttribute("X-CALENDARSERVER-DTSTAMP", "20080101T000000Z"))
</del><ins>+ if property.hasParameter("X-CALENDARSERVER-DTSTAMP"):
+ property.replaceParameter(Parameter("X-CALENDARSERVER-DTSTAMP", "20080101T000000Z"))
</ins><span class="cx">
</span><span class="cx"> for filter in filters:
</span><span class="cx"> if ":" in filter:
</span><span class="cx"> propname, parameter = filter.split(":")
</span><span class="cx"> if property.getName() == propname:
</span><del>- if property.hasAttribute(parameter):
- property.removeAttributes(parameter)
</del><ins>+ if property.hasParameter(parameter):
+ property.removeParameters(parameter)
</ins><span class="cx"> else:
</span><span class="cx"> if property.getName() == filter:
</span><span class="cx"> component.removeProperty(property)
</span><span class="cx">
</span><span class="cx"> try:
</span><del>- resp_calendar = PyCalendar.parseText(respdata)
</del><ins>+ resp_calendar = Calendar.parseText(respdata)
</ins><span class="cx"> removePropertiesParameters(resp_calendar)
</span><span class="cx"> respdata = resp_calendar.getText()
</span><span class="cx">
</span><del>- data_calendar = PyCalendar.parseText(data)
</del><ins>+ data_calendar = Calendar.parseText(data)
</ins><span class="cx"> removePropertiesParameters(data_calendar)
</span><span class="cx"> data = data_calendar.getText()
</span><span class="cx">
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterverifiersfreeBusypy"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/freeBusy.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/freeBusy.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/freeBusy.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,8 +18,8 @@
</span><span class="cx"> Verifier that checks the response of a free-busy-query.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-from pycalendar.calendar import PyCalendar
-from pycalendar.exceptions import PyCalendarInvalidData
</del><ins>+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.exceptions import InvalidData
</ins><span class="cx">
</span><span class="cx"> class Verifier(object):
</span><span class="cx">
</span><span class="lines">@@ -36,7 +36,7 @@
</span><span class="cx">
</span><span class="cx"> # Parse data as calendar object
</span><span class="cx"> try:
</span><del>- calendar = PyCalendar.parseText(respdata)
</del><ins>+ calendar = Calendar.parseText(respdata)
</ins><span class="cx">
</span><span class="cx"> # Check for calendar
</span><span class="cx"> if calendar is None:
</span><span class="lines">@@ -61,8 +61,8 @@
</span><span class="cx"> periods[i].getValue().setUseDuration(False)
</span><span class="cx"> # Check param
</span><span class="cx"> fbtype = "BUSY"
</span><del>- if fp.hasAttribute("FBTYPE"):
- fbtype = fp.getAttributeValue("FBTYPE")
</del><ins>+ if fp.hasParameter("FBTYPE"):
+ fbtype = fp.getParameterValue("FBTYPE")
</ins><span class="cx"> if fbtype == "BUSY":
</span><span class="cx"> busyp.extend(periods)
</span><span class="cx"> elif fbtype == "BUSY-TENTATIVE":
</span><span class="lines">@@ -97,7 +97,7 @@
</span><span class="cx"> elif len(unavailablep.symmetric_difference(unavailable)):
</span><span class="cx"> raise ValueError("Busy-unavailable periods do not match")
</span><span class="cx">
</span><del>- except PyCalendarInvalidData:
</del><ins>+ except InvalidData:
</ins><span class="cx"> return False, " HTTP response data is not a calendar"
</span><span class="cx"> except ValueError, txt:
</span><span class="cx"> return False, " HTTP response data is invalid: %s" % (txt,)
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterverifierspostFreeBusypy"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/postFreeBusy.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/postFreeBusy.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/postFreeBusy.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,8 +18,8 @@
</span><span class="cx"> Verifier that checks the response of a free-busy-query.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-from pycalendar.calendar import PyCalendar
-from pycalendar.exceptions import PyCalendarInvalidData
</del><ins>+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.exceptions import InvalidData
</ins><span class="cx"> from xml.etree.ElementTree import ElementTree
</span><span class="cx"> from xml.parsers.expat import ExpatError
</span><span class="cx"> import StringIO
</span><span class="lines">@@ -47,7 +47,7 @@
</span><span class="cx"> for calendar in tree.findall("./{urn:ietf:params:xml:ns:caldav}response/{urn:ietf:params:xml:ns:caldav}calendar-data"):
</span><span class="cx"> # Parse data as calendar object
</span><span class="cx"> try:
</span><del>- calendar = PyCalendar.parseText(calendar.text)
</del><ins>+ calendar = Calendar.parseText(calendar.text)
</ins><span class="cx">
</span><span class="cx"> # Check for calendar
</span><span class="cx"> if calendar is None:
</span><span class="lines">@@ -80,8 +80,8 @@
</span><span class="cx"> periods[i].getValue().setUseDuration(False)
</span><span class="cx"> # Check param
</span><span class="cx"> fbtype = "BUSY"
</span><del>- if fp.hasAttribute("FBTYPE"):
- fbtype = fp.getAttributeValue("FBTYPE")
</del><ins>+ if fp.hasParameter("FBTYPE"):
+ fbtype = fp.getParameterValue("FBTYPE")
</ins><span class="cx"> if fbtype == "BUSY":
</span><span class="cx"> busyp.extend(periods)
</span><span class="cx"> elif fbtype == "BUSY-TENTATIVE":
</span><span class="lines">@@ -118,7 +118,7 @@
</span><span class="cx">
</span><span class="cx"> break
</span><span class="cx">
</span><del>- except PyCalendarInvalidData:
</del><ins>+ except InvalidData:
</ins><span class="cx"> return False, " HTTP response data is not a calendar"
</span><span class="cx"> except ValueError, txt:
</span><span class="cx"> return False, " HTTP response data is invalid: %s" % (txt,)
</span></span></pre></div>
<a id="CalDAVTesterbranchesusersgayasharedgroupfixestesterverifiersxmlElementMatchpy"></a>
<div class="modfile"><h4>Modified: CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/xmlElementMatch.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/xmlElementMatch.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalDAVTester/branches/users/gaya/sharedgroupfixestester/verifiers/xmlElementMatch.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -19,7 +19,7 @@
</span><span class="cx"> Verifier that checks the response body for an exact match to data in a file.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-from pycalendar.calendar import PyCalendar
</del><ins>+from pycalendar.icalendar.calendar import Calendar
</ins><span class="cx"> from xml.etree.ElementTree import ElementTree
</span><span class="cx"> import json
</span><span class="cx"> import StringIO
</span><span class="lines">@@ -141,6 +141,9 @@
</span><span class="cx"> for child in node.getchildren():
</span><span class="cx"> if child.tag == element and (value is None or child.text == value):
</span><span class="cx"> results.append(node)
</span><ins>+ elif test[0] == '|':
+ if node.text is None and len(node.getchildren()) == 0:
+ results.append(node)
</ins><span class="cx"> else:
</span><span class="cx"> results = nodes
</span><span class="cx">
</span><span class="lines">@@ -222,7 +225,7 @@
</span><span class="cx"> # Try to parse as iCalendar
</span><span class="cx"> elif test == 'icalendar':
</span><span class="cx"> try:
</span><del>- PyCalendar.parseText(node.text)
</del><ins>+ Calendar.parseText(node.text)
</ins><span class="cx"> except:
</span><span class="cx"> result = " Incorrect value returned in iCalendar for %s\n" % (path,)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixes"></a>
<div class="propset"><h4>Property changes: CalendarServer/branches/users/gaya/sharedgroupfixes</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnmergeinfo"></a>
<div class="modfile"><h4>Modified: svn:mergeinfo</h4></div>
<span class="cx">/CalendarServer/branches/config-separation:4379-4443
</span><span class="cx">/CalendarServer/branches/egg-info-351:4589-4625
</span><span class="cx">/CalendarServer/branches/generic-sqlstore:6167-6191
</span><span class="cx">/CalendarServer/branches/new-store:5594-5934
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile:5911-5935
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
</span><span class="cx">/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
</span><span class="cx">/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
</span><span class="cx">/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
</span><span class="cx">/CalendarServer/branches/users/cdaboo/fix-no-ischedule:11612
</span><span class="cx">/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
</span><span class="cx">/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
</span><span class="cx">/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
</span><span class="cx">/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
</span><span class="cx">/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
</span><span class="cx">/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pods:7297-7377
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycard:7227-7237
</span><span class="cx">/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
</span><span class="cx">/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
</span><span class="cx">/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
</span><span class="cx">/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
</span><span class="cx">/CalendarServer/branches/users/cdaboo/timezones:7443-7699
</span><span class="cx">/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
</span><span class="cx">/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
</span><span class="cx">/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
</span><span class="cx">/CalendarServer/branches/users/glyph/conn-limit:6574-6577
</span><span class="cx">/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
</span><span class="cx">/CalendarServer/branches/users/glyph/dalify:6932-7023
</span><span class="cx">/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
</span><span class="cx">/CalendarServer/branches/users/glyph/deploybuild:7563-7572
</span><span class="cx">/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
</span><span class="cx">/CalendarServer/branches/users/glyph/disable-quota:7718-7727
</span><span class="cx">/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
</span><span class="cx">/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
</span><span class="cx">/CalendarServer/branches/users/glyph/hang-fix:11465-11491
</span><span class="cx">/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
</span><span class="cx">/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
</span><span class="cx">/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
</span><span class="cx">/CalendarServer/branches/users/glyph/linux-tests:6893-6900
</span><span class="cx">/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
</span><span class="cx">/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
</span><span class="cx">/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
</span><span class="cx">/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
</span><span class="cx">/CalendarServer/branches/users/glyph/new-export:7444-7485
</span><span class="cx">/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle:7106-7155
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
</span><span class="cx">/CalendarServer/branches/users/glyph/other-html:8062-8091
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
</span><span class="cx">/CalendarServer/branches/users/glyph/q:9560-9688
</span><span class="cx">/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
</span><span class="cx">/CalendarServer/branches/users/glyph/quota:7604-7637
</span><span class="cx">/CalendarServer/branches/users/glyph/sendfdport:5388-5424
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
</span><span class="cx">/CalendarServer/branches/users/glyph/sharedpool:6490-6550
</span><span class="cx">/CalendarServer/branches/users/glyph/sharing-api:9192-9205
</span><span class="cx">/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
</span><span class="cx">/CalendarServer/branches/users/glyph/sql-store:5929-6073
</span><span class="cx">/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
</span><span class="cx">/CalendarServer/branches/users/glyph/subtransactions:7248-7258
</span><span class="cx">/CalendarServer/branches/users/glyph/table-alias:8651-8664
</span><span class="cx">/CalendarServer/branches/users/glyph/uidexport:7673-7676
</span><span class="cx">/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
</span><span class="cx">/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
</span><span class="cx">/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
</span><span class="cx">/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
</span><span class="cx">/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
</span><span class="cx">/CalendarServer/branches/users/sagen/applepush:8126-8184
</span><span class="cx">/CalendarServer/branches/users/sagen/inboxitems:7380-7381
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources:5032-5051
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
</span><span class="cx">/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
</span><span class="cx">/CalendarServer/branches/users/sagen/resources-2:5084-5093
</span><span class="cx">/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
</span><span class="cx">/CalendarServer/branches/users/wsanchez/transations:5515-5593
</span><span class="cx">/CalendarServer/trunk:11632-11860
</span><span class="cx"> + /CalDAVTester/trunk:11193-11198
</span><span class="cx">/CalendarServer/branches/config-separation:4379-4443
</span><span class="cx">/CalendarServer/branches/egg-info-351:4589-4625
</span><span class="cx">/CalendarServer/branches/generic-sqlstore:6167-6191
</span><span class="cx">/CalendarServer/branches/new-store:5594-5934
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile:5911-5935
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
</span><span class="cx">/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
</span><span class="cx">/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
</span><span class="cx">/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
</span><span class="cx">/CalendarServer/branches/users/cdaboo/fix-no-ischedule:11607-11871
</span><span class="cx">/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
</span><span class="cx">/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
</span><span class="cx">/CalendarServer/branches/users/cdaboo/json:11622-11912
</span><span class="cx">/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
</span><span class="cx">/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
</span><span class="cx">/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
</span><span class="cx">/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pods:7297-7377
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycard:7227-7237
</span><span class="cx">/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
</span><span class="cx">/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
</span><span class="cx">/CalendarServer/branches/users/cdaboo/reverse-proxy-pods:11875-11900
</span><span class="cx">/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
</span><span class="cx">/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
</span><span class="cx">/CalendarServer/branches/users/cdaboo/timezones:7443-7699
</span><span class="cx">/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
</span><span class="cx">/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
</span><span class="cx">/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
</span><span class="cx">/CalendarServer/branches/users/glyph/conn-limit:6574-6577
</span><span class="cx">/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
</span><span class="cx">/CalendarServer/branches/users/glyph/dalify:6932-7023
</span><span class="cx">/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
</span><span class="cx">/CalendarServer/branches/users/glyph/deploybuild:7563-7572
</span><span class="cx">/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
</span><span class="cx">/CalendarServer/branches/users/glyph/disable-quota:7718-7727
</span><span class="cx">/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
</span><span class="cx">/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
</span><span class="cx">/CalendarServer/branches/users/glyph/hang-fix:11465-11491
</span><span class="cx">/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
</span><span class="cx">/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
</span><span class="cx">/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
</span><span class="cx">/CalendarServer/branches/users/glyph/linux-tests:6893-6900
</span><span class="cx">/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
</span><span class="cx">/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
</span><span class="cx">/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
</span><span class="cx">/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
</span><span class="cx">/CalendarServer/branches/users/glyph/new-export:7444-7485
</span><span class="cx">/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle:7106-7155
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
</span><span class="cx">/CalendarServer/branches/users/glyph/other-html:8062-8091
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
</span><span class="cx">/CalendarServer/branches/users/glyph/q:9560-9688
</span><span class="cx">/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
</span><span class="cx">/CalendarServer/branches/users/glyph/quota:7604-7637
</span><span class="cx">/CalendarServer/branches/users/glyph/sendfdport:5388-5424
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
</span><span class="cx">/CalendarServer/branches/users/glyph/sharedpool:6490-6550
</span><span class="cx">/CalendarServer/branches/users/glyph/sharing-api:9192-9205
</span><span class="cx">/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
</span><span class="cx">/CalendarServer/branches/users/glyph/sql-store:5929-6073
</span><span class="cx">/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
</span><span class="cx">/CalendarServer/branches/users/glyph/subtransactions:7248-7258
</span><span class="cx">/CalendarServer/branches/users/glyph/table-alias:8651-8664
</span><span class="cx">/CalendarServer/branches/users/glyph/uidexport:7673-7676
</span><span class="cx">/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
</span><span class="cx">/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
</span><span class="cx">/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
</span><span class="cx">/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
</span><span class="cx">/CalendarServer/branches/users/glyph/whenNotProposed:11881-11897
</span><span class="cx">/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
</span><span class="cx">/CalendarServer/branches/users/sagen/applepush:8126-8184
</span><span class="cx">/CalendarServer/branches/users/sagen/inboxitems:7380-7381
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources:5032-5051
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
</span><span class="cx">/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
</span><span class="cx">/CalendarServer/branches/users/sagen/resources-2:5084-5093
</span><span class="cx">/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
</span><span class="cx">/CalendarServer/branches/users/wsanchez/transations:5515-5593
</span><span class="cx">/CalendarServer/trunk:11632-11860,11862-12016
</span><a id="CalendarServerbranchesusersgayasharedgroupfixesHACKING"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/HACKING (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/HACKING        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/HACKING        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -144,10 +144,6 @@
</span><span class="cx">
</span><span class="cx"> PEP-8 items we do not follow:
</span><span class="cx">
</span><del>- * Lines need not be limited to 79 spaces, but longer lines are
- undesirable. If you can easily do so, try to keep lines under 80
- columns.
-
</del><span class="cx"> * PEP-8 recommends using a backslash to break long lines up:
</span><span class="cx">
</span><span class="cx"> ::
</span><span class="lines">@@ -181,14 +177,6 @@
</span><span class="cx">
</span><span class="cx"> Because that's just silly.
</span><span class="cx">
</span><del>- * Lining up assignments is OK, within reason:
-
- ::
-
- cars = 4
- motorbikes = 8
- bicycles = 18
-
</del><span class="cx"> Additions:
</span><span class="cx">
</span><span class="cx"> * Close parentheses and brackets such as ``()``, ``[]`` and ``{}`` at the
</span><span class="lines">@@ -248,9 +236,8 @@
</span><span class="cx">
</span><span class="cx"> process = subprocess.Popen(...)
</span><span class="cx">
</span><del>- This makes code shorter and removes the runtime indirection (which
- can be relevant in tight loops). It also makes it easier to replace
- one implementation with another.
</del><ins>+ This makes code shorter and makes it easier to replace one implementation
+ with another.
</ins><span class="cx">
</span><span class="cx"> * All files should have an ``__all__`` specification. Put them at the
</span><span class="cx"> top of the file, before imports (PEP-8 puts them at the top, but
</span><span class="lines">@@ -259,8 +246,8 @@
</span><span class="cx">
</span><span class="cx"> * It is more important that symbol names are meaningful than it is
</span><span class="cx"> that they be concise. ``x`` is rarely an appropriate name for a
</span><del>- variable. ``transmogrifierStatus`` is more useful to the reader
- than ``trmgStat``; avoid contractions.
</del><ins>+ variable. Avoid contractions: ``transmogrifierStatus`` is more useful
+ to the reader than ``trmgStat``.
</ins><span class="cx">
</span><span class="cx"> * A deferred that will be immediately returned may be called ``d``:
</span><span class="cx">
</span><span class="lines">@@ -271,9 +258,7 @@
</span><span class="cx"> d.addErrback(onError)
</span><span class="cx"> return d
</span><span class="cx">
</span><del>- * We prefer ``inlineCallbacks`` over ``deferredGenerator``.
- ``inlineCallbacks`` are more readable, and we do not support Python
- versions old enough that ``deferredGenerator`` would be necessary.
</del><ins>+ * Do not use ``deferredGenerator``. Use ``inlineCallbacks`` instead.
</ins><span class="cx">
</span><span class="cx"> * That said, avoid using ``inlineCallbacks`` when chaining deferreds
</span><span class="cx"> is straightforward, as they are more expensive. Use
</span><span class="lines">@@ -306,17 +291,29 @@
</span><span class="cx"> Use of underscores is reserved for implied dispatching and the like
</span><span class="cx"> (eg. ``http_FOO()``). See the Twisted Coding Standard for details.
</span><span class="cx">
</span><del>- * Always use a tuple when using ``%``-formatting, even when only one
- value is being provided:
</del><ins>+ * Do not use ``%``-formatting:
</ins><span class="cx">
</span><span class="cx"> ::
</span><span class="cx">
</span><span class="cx"> error = "Unexpected value: %s" % (value,)
</span><span class="cx">
</span><del>- Do not use the non-tuple form:
</del><ins>+ Use PEP-3101 formatting instead:
</ins><span class="cx">
</span><span class="cx"> ::
</span><span class="cx">
</span><ins>+ error = "Unexpected value: {value}".format(value=value)
+
+ * If you must use ``%``-formatting for some reason, always use a tuple as
+ the format argument, even when only one value is being provided:
+
+ ::
+
+ error = "Unexpected value: %s" % (value,)
+
+ Never use the non-tuple form:
+
+ ::
+
</ins><span class="cx"> error = "Unexpected value: %s" % value
</span><span class="cx">
</span><span class="cx"> Which is allowed in Python, but results in a programming error if
</span><span class="lines">@@ -329,8 +326,9 @@
</span><span class="cx"> numbers = (1,2,3,) # No
</span><span class="cx"> numbers = (1,2,3) # Yes
</span><span class="cx">
</span><del>- It's desirable on multiple lines, though, as that makes re-ordering
- items easy, and avoids a diff on the last line when adding another:
</del><ins>+ The trailing comma is desirable on multiple lines, though, as that makes
+ re-ordering items easy, and avoids a diff on the last line when adding
+ another:
</ins><span class="cx">
</span><span class="cx"> ::
</span><span class="cx">
</span><span class="lines">@@ -368,11 +366,11 @@
</span><span class="cx"> ==============
</span><span class="cx">
</span><span class="cx"> * If a callable is going to return a Deferred some of the time, it
</span><del>- should probably return a deferred all of the time. Return
- ``succeed(value)`` instead of ``value`` if necessary. This avoids
- forcing the caller to check as to whether the value is a deferred
- or not (eg. by using ``maybeDeferred()``), which is both annoying
- to code and potentially expensive at runtime.
</del><ins>+ should return a deferred all of the time. Return ``succeed(value)``
+ instead of ``value`` if necessary. This avoids forcing the caller
+ to check as to whether the value is a deferred or not (eg. by using
+ ``maybeDeferred()``), which is both annoying to code and potentially
+ expensive at runtime.
</ins><span class="cx">
</span><span class="cx"> * Be proactive about closing files and file-like objects.
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesbin_calendarserver_preamblepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/bin/_calendarserver_preamble.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/bin/_calendarserver_preamble.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/bin/_calendarserver_preamble.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -60,11 +60,11 @@
</span><span class="cx"> noConfigOption = [
</span><span class="cx"> "calendarserver_bootstrap_database",
</span><span class="cx"> "calendarserver_load_augmentdb",
</span><del>- "calendarserver_make_partition",
</del><span class="cx"> "calendarserver_manage_augments",
</span><span class="cx"> "calendarserver_manage_postgres",
</span><span class="cx"> "calendarserver_manage_timezones",
</span><span class="cx"> "icalendar_split",
</span><ins>+ "twistd", "trial",
</ins><span class="cx"> ]
</span><span class="cx">
</span><span class="cx"> if split(sys.argv[0])[-1] not in noConfigOption:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesbincalendarserver_make_partition"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/bin/calendarserver_make_partition (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/bin/calendarserver_make_partition        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/bin/calendarserver_make_partition        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,32 +0,0 @@
</span><del>-#!/usr/bin/env python
-##
-# Copyright (c) 2010-2013 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 sys
-
-#PYTHONPATH
-
-if __name__ == "__main__":
- if "PYTHONPATH" in globals():
- sys.path.insert(0, PYTHONPATH)
- else:
- try:
- import _calendarserver_preamble
- except ImportError:
- sys.exc_clear()
-
- from calendarserver.tools.makepartition import main
- main()
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesbintrialfromrev12016CalendarServertrunkbintrial"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/bin/trial (from rev 12016, CalendarServer/trunk/bin/trial) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/bin/trial         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/bin/trial        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2013 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 sys
+
+#PYTHONPATH
+
+if __name__ == "__main__":
+ if "PYTHONPATH" in globals():
+ sys.path.insert(0, PYTHONPATH)
+ else:
+ try:
+ import _calendarserver_preamble
+ except ImportError:
+ sys.exc_clear()
+
+ from twisted.scripts.trial import run
+ run()
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesbintwistdfromrev12016CalendarServertrunkbintwistd"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/bin/twistd (from rev 12016, CalendarServer/trunk/bin/twistd) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/bin/twistd         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/bin/twistd        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2013 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 sys
+
+#PYTHONPATH
+
+if __name__ == "__main__":
+ if "PYTHONPATH" in globals():
+ sys.path.insert(0, PYTHONPATH)
+ else:
+ try:
+ import _calendarserver_preamble
+ except ImportError:
+ sys.exc_clear()
+
+ from twisted.scripts.twistd import run
+ run()
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushamppushpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/amppush.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/amppush.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/amppush.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -23,7 +23,9 @@
</span><span class="cx"> import time
</span><span class="cx"> import uuid
</span><span class="cx">
</span><ins>+from calendarserver.push.util import PushPriority
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -49,7 +51,8 @@
</span><span class="cx">
</span><span class="cx"> class NotificationForID(amp.Command):
</span><span class="cx"> arguments = [('id', amp.String()),
</span><del>- ('dataChangedTimestamp', amp.Integer(optional=True))]
</del><ins>+ ('dataChangedTimestamp', amp.Integer(optional=True)),
+ ('priority', amp.Integer(optional=True))]
</ins><span class="cx"> response = [('status', amp.String())]
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -82,12 +85,14 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def enqueue(self, transaction, id, dataChangedTimestamp=None):
</del><ins>+ def enqueue(self, transaction, id, dataChangedTimestamp=None,
+ priority=PushPriority.high):
</ins><span class="cx"> if dataChangedTimestamp is None:
</span><span class="cx"> dataChangedTimestamp = int(time.time())
</span><span class="cx"> for protocol in self.protocols:
</span><span class="cx"> yield protocol.callRemote(NotificationForID, id=id,
</span><del>- dataChangedTimestamp=dataChangedTimestamp)
</del><ins>+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=priority.value)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -103,10 +108,12 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @NotificationForID.responder
</span><del>- def enqueueFromWorker(self, id, dataChangedTimestamp=None):
</del><ins>+ def enqueueFromWorker(self, id, dataChangedTimestamp=None,
+ priority=PushPriority.high.value):
</ins><span class="cx"> if dataChangedTimestamp is None:
</span><span class="cx"> dataChangedTimestamp = int(time.time())
</span><del>- self.master.enqueue(None, id, dataChangedTimestamp=dataChangedTimestamp)
</del><ins>+ self.master.enqueue(None, id, dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.lookupByValue(priority))
</ins><span class="cx"> return {"status" : "OK"}
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -167,7 +174,8 @@
</span><span class="cx"> self.subscribers.remove(p)
</span><span class="cx">
</span><span class="cx">
</span><del>- def enqueue(self, transaction, pushKey, dataChangedTimestamp=None):
</del><ins>+ def enqueue(self, transaction, pushKey, dataChangedTimestamp=None,
+ priority=PushPriority.high):
</ins><span class="cx"> """
</span><span class="cx"> Sends an AMP push notification to any clients subscribing to this pushKey.
</span><span class="cx">
</span><span class="lines">@@ -192,23 +200,26 @@
</span><span class="cx"> if token is not None:
</span><span class="cx"> tokens.append(token)
</span><span class="cx"> if tokens:
</span><del>- return self.scheduleNotifications(tokens, pushKey, dataChangedTimestamp)
</del><ins>+ return self.scheduleNotifications(tokens, pushKey,
+ dataChangedTimestamp, priority)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def sendNotification(self, token, id, dataChangedTimestamp):
</del><ins>+ def sendNotification(self, token, id, dataChangedTimestamp, priority):
</ins><span class="cx"> for subscriber in self.subscribers:
</span><span class="cx"> if subscriber.subscribedToID(id):
</span><del>- yield subscriber.notify(token, id, dataChangedTimestamp)
</del><ins>+ yield subscriber.notify(token, id, dataChangedTimestamp,
+ priority)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def scheduleNotifications(self, tokens, id, dataChangedTimestamp):
</del><ins>+ def scheduleNotifications(self, tokens, id, dataChangedTimestamp, priority):
</ins><span class="cx"> if self.scheduler is not None:
</span><del>- self.scheduler.schedule(tokens, id, dataChangedTimestamp)
</del><ins>+ self.scheduler.schedule(tokens, id, dataChangedTimestamp, priority)
</ins><span class="cx"> else:
</span><span class="cx"> for token in tokens:
</span><del>- yield self.sendNotification(token, id, dataChangedTimestamp)
</del><ins>+ yield self.sendNotification(token, id, dataChangedTimestamp,
+ priority)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -238,11 +249,12 @@
</span><span class="cx"> return {"status" : "OK"}
</span><span class="cx"> UnsubscribeFromID.responder(unsubscribe)
</span><span class="cx">
</span><del>- def notify(self, token, id, dataChangedTimestamp):
</del><ins>+ def notify(self, token, id, dataChangedTimestamp, priority):
</ins><span class="cx"> if self.subscribedToID(id) == token:
</span><span class="cx"> self.log.debug("Sending notification for %s to %s" % (id, token))
</span><span class="cx"> return self.callRemote(NotificationForID, id=id,
</span><del>- dataChangedTimestamp=dataChangedTimestamp)
</del><ins>+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=priority.value)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def subscribedToID(self, id):
</span><span class="lines">@@ -288,8 +300,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def notificationForID(self, id, dataChangedTimestamp):
- yield self.callback(id, dataChangedTimestamp)
</del><ins>+ def notificationForID(self, id, dataChangedTimestamp, priority):
+ yield self.callback(id, dataChangedTimestamp, PushPriority.lookupByValue(priority))
</ins><span class="cx"> returnValue({"status" : "OK"})
</span><span class="cx">
</span><span class="cx"> NotificationForID.responder(notificationForID)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushapplepushpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/applepush.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/applepush.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/applepush.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -36,16 +36,27 @@
</span><span class="cx"> import struct
</span><span class="cx"> import time
</span><span class="cx"> from txdav.common.icommondatastore import InvalidSubscriptionValues
</span><del>-
-from calendarserver.push.util import validToken, TokenHistory, PushScheduler
-
</del><ins>+from calendarserver.push.util import (
+ validToken, TokenHistory, PushScheduler, PushPriority
+)
</ins><span class="cx"> from twext.internet.adaptendpoint import connect
</span><span class="cx"> from twext.internet.gaiendpoint import GAIEndpoint
</span><ins>+from twisted.python.constants import Values, ValueConstant
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><ins>+class ApplePushPriority(Values):
+ """
+ Maps calendarserver.push.util.PushPriority values to APNS-specific values
+ """
+ low = ValueConstant(PushPriority.low.value)
+ medium = ValueConstant(PushPriority.medium.value)
+ high = ValueConstant(PushPriority.high.value)
+
+
+
</ins><span class="cx"> class ApplePushNotifierService(service.MultiService):
</span><span class="cx"> """
</span><span class="cx"> ApplePushNotifierService is a MultiService responsible for
</span><span class="lines">@@ -55,7 +66,7 @@
</span><span class="cx">
</span><span class="cx"> The Apple Push Notification protocol is described here:
</span><span class="cx">
</span><del>- http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html
</del><ins>+ https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/CommunicatingWIthAPS.html
</ins><span class="cx"> """
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -177,7 +188,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def enqueue(self, transaction, pushKey, dataChangedTimestamp=None):
</del><ins>+ def enqueue(self, transaction, pushKey, dataChangedTimestamp=None,
+ priority=PushPriority.high):
</ins><span class="cx"> """
</span><span class="cx"> Sends an Apple Push Notification to any device token subscribed to
</span><span class="cx"> this pushKey.
</span><span class="lines">@@ -191,6 +203,8 @@
</span><span class="cx"> @param dataChangedTimestamp: Timestamp (epoch seconds) for the data change
</span><span class="cx"> which triggered this notification (Only used for unit tests)
</span><span class="cx"> @type key: C{int}
</span><ins>+ @param priority: the priority level
+ @type priority: L{PushPriority}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> try:
</span><span class="lines">@@ -219,7 +233,8 @@
</span><span class="cx"> if token and uid:
</span><span class="cx"> tokens.append(token)
</span><span class="cx"> if tokens:
</span><del>- provider.scheduleNotifications(tokens, pushKey, dataChangedTimestamp)
</del><ins>+ provider.scheduleNotifications(tokens, pushKey,
+ dataChangedTimestamp, priority)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -230,8 +245,7 @@
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="cx"> # Sent by provider
</span><del>- COMMAND_SIMPLE = 0
- COMMAND_ENHANCED = 1
</del><ins>+ COMMAND_PROVIDER = 2
</ins><span class="cx">
</span><span class="cx"> # Received by provider
</span><span class="cx"> COMMAND_ERROR = 8
</span><span class="lines">@@ -333,7 +347,7 @@
</span><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="cx">
</span><del>- def sendNotification(self, token, key, dataChangedTimestamp):
</del><ins>+ def sendNotification(self, token, key, dataChangedTimestamp, priority):
</ins><span class="cx"> """
</span><span class="cx"> Sends a push notification message for the key to the device associated
</span><span class="cx"> with the token.
</span><span class="lines">@@ -357,6 +371,7 @@
</span><span class="cx"> return
</span><span class="cx">
</span><span class="cx"> identifier = self.history.add(token)
</span><ins>+ apnsPriority = ApplePushPriority.lookupByValue(priority.value).value
</ins><span class="cx"> payload = json.dumps(
</span><span class="cx"> {
</span><span class="cx"> "key" : key,
</span><span class="lines">@@ -365,23 +380,79 @@
</span><span class="cx"> }
</span><span class="cx"> )
</span><span class="cx"> payloadLength = len(payload)
</span><del>- self.log.debug("Sending APNS notification to {token}: id={id} payload={payload}",
- token=token, id=identifier, payload=payload)
</del><ins>+ self.log.debug("Sending APNS notification to {token}: id={id} payload={payload} priority={priority}",
+ token=token, id=identifier, payload=payload, priority=apnsPriority)
</ins><span class="cx">
</span><ins>+ """
+ Notification format
+
+ Top level: Command (1 byte), Frame length (4 bytes), Frame data (variable)
+ Within Frame data: Item ...
+ Item: Item number (1 byte), Item data length (2 bytes), Item data (variable)
+ Item 1: Device token (32 bytes)
+ Item 2: Payload (variable length) in JSON format, not null-terminated
+ Item 3: Notification ID (4 bytes) an opaque value used for reporting errors
+ Item 4: Expiration date (4 bytes) UNIX epoch in secondcs UTC
+ Item 5: Priority (1 byte): 10 (push sent immediately) or 5 (push sent
+ at a time that conservces power on the device receiving it)
+ """
+
+ # Frame struct.pack format
+ # ! Network byte order
+ command = self.COMMAND_PROVIDER # B
+ frameLength = ( # I
+ # Item 1 (Device token)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 32 + # device token # 32s
+ # Item 2 (Payload)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ payloadLength + # the JSON payload # %d s
+ # Item 3 (Notification ID)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 4 + # Notification ID # I
+ # Item 4 (Expiration)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 4 + # Expiration seconds since epoch # I
+ # Item 5 (Priority)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 1 # Priority # B
+ )
+
</ins><span class="cx"> self.transport.write(
</span><del>- struct.pack("!BIIH32sH%ds" % (payloadLength,),
- self.COMMAND_ENHANCED, # Command
- identifier, # Identifier
- int(time.time()) + 72 * 60 * 60, # Expires in 72 hours
</del><ins>+ struct.pack("!BIBH32sBH%dsBHIBHIBHB" % (payloadLength,),
+
+ command, # Command
+ frameLength, # Frame length
+
+ 1, # Item 1 (Device token)
</ins><span class="cx"> 32, # Token Length
</span><span class="cx"> binaryToken, # Token
</span><del>- payloadLength, # Payload Length
- payload, # Payload in JSON format
</del><ins>+
+ 2, # Item 2 (Payload)
+ payloadLength, # Payload length
+ payload, # Payload
+
+ 3, # Item 3 (Notification ID)
+ 4, # Notification ID Length
+ identifier, # Notification ID
+
+ 4, # Item 4 (Expiration)
+ 4, # Expiration length
+ int(time.time()) + 72 * 60 * 60, # Expires in 72 hours
+
+ 5, # Item 5 (Priority)
+ 1, # Priority length
+ apnsPriority, # Priority
+
</ins><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> class APNProviderFactory(ReconnectingClientFactory):
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -509,12 +580,13 @@
</span><span class="cx"> # sent will be put back into the queue.
</span><span class="cx"> queued = list(self.queue)
</span><span class="cx"> self.queue = []
</span><del>- for (token, key), dataChangedTimestamp in queued:
- if token and key and dataChangedTimestamp:
- self.sendNotification(token, key, dataChangedTimestamp)
</del><ins>+ for (token, key), dataChangedTimestamp, priority in queued:
+ if token and key and dataChangedTimestamp and priority:
+ self.sendNotification(token, key, dataChangedTimestamp,
+ priority)
</ins><span class="cx">
</span><span class="cx">
</span><del>- def scheduleNotifications(self, tokens, key, dataChangedTimestamp):
</del><ins>+ def scheduleNotifications(self, tokens, key, dataChangedTimestamp, priority):
</ins><span class="cx"> """
</span><span class="cx"> The starting point for getting notifications to the APNS server. If there is
</span><span class="cx"> a connection to the APNS server, these notifications are scheduled (or directly
</span><span class="lines">@@ -533,15 +605,15 @@
</span><span class="cx"> connection = getattr(self.factory, "connection", None)
</span><span class="cx"> if connection is not None:
</span><span class="cx"> if self.scheduler is not None:
</span><del>- self.scheduler.schedule(tokens, key, dataChangedTimestamp)
</del><ins>+ self.scheduler.schedule(tokens, key, dataChangedTimestamp, priority)
</ins><span class="cx"> else:
</span><span class="cx"> for token in tokens:
</span><del>- self.sendNotification(token, key, dataChangedTimestamp)
</del><ins>+ self.sendNotification(token, key, dataChangedTimestamp, priority)
</ins><span class="cx"> else:
</span><del>- self._saveForWhenConnected(tokens, key, dataChangedTimestamp)
</del><ins>+ self._saveForWhenConnected(tokens, key, dataChangedTimestamp, priority)
</ins><span class="cx">
</span><span class="cx">
</span><del>- def _saveForWhenConnected(self, tokens, key, dataChangedTimestamp):
</del><ins>+ def _saveForWhenConnected(self, tokens, key, dataChangedTimestamp, priority):
</ins><span class="cx"> """
</span><span class="cx"> Called in order to save notifications that can't be sent now because there
</span><span class="cx"> is no connection to the APNS server. (token, key) tuples are appended to
</span><span class="lines">@@ -557,16 +629,16 @@
</span><span class="cx"> """
</span><span class="cx"> for token in tokens:
</span><span class="cx"> tokenKeyPair = (token, key)
</span><del>- for existingPair, ignored in self.queue:
</del><ins>+ for existingPair, timstamp, priority in self.queue:
</ins><span class="cx"> if tokenKeyPair == existingPair:
</span><span class="cx"> self.log.debug("APNProviderService has no connection; skipping duplicate: %s %s" % (token, key))
</span><span class="cx"> break # Already scheduled
</span><span class="cx"> else:
</span><span class="cx"> self.log.debug("APNProviderService has no connection; queuing: %s %s" % (token, key))
</span><del>- self.queue.append(((token, key), dataChangedTimestamp))
</del><ins>+ self.queue.append(((token, key), dataChangedTimestamp, priority))
</ins><span class="cx">
</span><span class="cx">
</span><del>- def sendNotification(self, token, key, dataChangedTimestamp):
</del><ins>+ def sendNotification(self, token, key, dataChangedTimestamp, priority):
</ins><span class="cx"> """
</span><span class="cx"> If there is a connection the notification is sent right away, otherwise
</span><span class="cx"> the notification is saved for later.
</span><span class="lines">@@ -579,15 +651,15 @@
</span><span class="cx"> which triggered this notification
</span><span class="cx"> @type key: C{int}
</span><span class="cx"> """
</span><del>- if not (token and key and dataChangedTimestamp):
</del><ins>+ if not (token and key and dataChangedTimestamp, priority):
</ins><span class="cx"> return
</span><span class="cx">
</span><span class="cx"> # Service has reference to factory has reference to protocol instance
</span><span class="cx"> connection = getattr(self.factory, "connection", None)
</span><span class="cx"> if connection is None:
</span><del>- self._saveForWhenConnected([token], key, dataChangedTimestamp)
</del><ins>+ self._saveForWhenConnected([token], key, dataChangedTimestamp, priority)
</ins><span class="cx"> else:
</span><del>- connection.sendNotification(token, key, dataChangedTimestamp)
</del><ins>+ connection.sendNotification(token, key, dataChangedTimestamp, priority)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushnotifierpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/notifier.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/notifier.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/notifier.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -19,7 +19,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> from twext.enterprise.dal.record import fromTable
</span><del>-from twext.enterprise.dal.syntax import Delete
</del><ins>+from twext.enterprise.dal.syntax import Delete, Select, Parameter
</ins><span class="cx"> from twext.enterprise.queue import WorkItem
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx">
</span><span class="lines">@@ -32,10 +32,13 @@
</span><span class="cx">
</span><span class="cx"> import datetime
</span><span class="cx">
</span><ins>+from calendarserver.push.util import PushPriority
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class PushNotificationWork(WorkItem, fromTable(schema.PUSH_NOTIFICATION_WORK)):
</span><span class="cx">
</span><span class="cx"> group = property(lambda self: self.pushID)
</span><span class="lines">@@ -43,14 +46,35 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def doWork(self):
</span><span class="cx">
</span><del>- # Delete all other work items with the same pushID
- yield Delete(From=self.table,
- Where=self.table.PUSH_ID == self.pushID
- ).on(self.transaction)
</del><ins>+ # Find all work items with the same push ID and find the highest
+ # priority. Delete matching work items.
+ results = (yield Select([self.table.WORK_ID, self.table.PRIORITY],
+ From=self.table, Where=self.table.PUSH_ID == self.pushID).on(
+ self.transaction))
</ins><span class="cx">
</span><ins>+ maxPriority = self.priority
+
+ # If there are other enqueued work items for this push ID, find the
+ # highest priority one and use that value
+ if results:
+ workIDs = []
+ for workID, priority in results:
+ if priority > maxPriority:
+ maxPriority = priority
+ workIDs.append(workID)
+
+ # Delete the work items we selected
+ yield Delete(From=self.table,
+ Where=self.table.WORK_ID.In(
+ Parameter("workIDs", len(workIDs)))
+ ).on(self.transaction, workIDs=workIDs)
+
</ins><span class="cx"> pushDistributor = self.transaction._pushDistributor
</span><span class="cx"> if pushDistributor is not None:
</span><del>- yield pushDistributor.enqueue(self.transaction, self.pushID)
</del><ins>+ # Convert the integer priority value back into a constant
+ priority = PushPriority.lookupByValue(maxPriority)
+ yield pushDistributor.enqueue(self.transaction, self.pushID,
+ priority=priority)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -84,13 +108,15 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def notify(self, txn):
</del><ins>+ def notify(self, txn, priority=PushPriority.high):
</ins><span class="cx"> """
</span><span class="cx"> Send the notification. For a home object we just push using the home id. For a home
</span><span class="cx"> child we push both the owner home id and the owned home child id.
</span><span class="cx">
</span><span class="cx"> @param txn: The transaction to create the work item with
</span><span class="cx"> @type txn: L{CommonStoreTransaction}
</span><ins>+ @param priority: the priority level
+ @type priority: L{PushPriority}
</ins><span class="cx"> """
</span><span class="cx"> # Push ids from the store objects are a tuple of (prefix, name,) and we need to compose that
</span><span class="cx"> # into a single token.
</span><span class="lines">@@ -102,10 +128,13 @@
</span><span class="cx">
</span><span class="cx"> for prefix, id in ids:
</span><span class="cx"> if self._notify:
</span><del>- self.log.debug("Notifications are enabled: %s %s/%s" % (self._storeObject, prefix, id,))
- yield self._notifierFactory.send(prefix, id, txn)
</del><ins>+ self.log.debug("Notifications are enabled: %s %s/%s priority=%d" %
+ (self._storeObject, prefix, id, priority.value))
+ yield self._notifierFactory.send(prefix, id, txn,
+ priority=priority)
</ins><span class="cx"> else:
</span><del>- self.log.debug("Skipping notification for: %s %s/%s" % (self._storeObject, prefix, id,))
</del><ins>+ self.log.debug("Skipping notification for: %s %s/%s" %
+ (self._storeObject, prefix, id,))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def clone(self, storeObject):
</span><span class="lines">@@ -150,12 +179,14 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def send(self, prefix, id, txn):
</del><ins>+ def send(self, prefix, id, txn, priority=PushPriority.high):
</ins><span class="cx"> """
</span><span class="cx"> Enqueue a push notification work item on the provided transaction.
</span><span class="cx"> """
</span><span class="cx"> notBefore = datetime.datetime.utcnow() + datetime.timedelta(seconds=self.coalesceSeconds)
</span><del>- yield txn.enqueue(PushNotificationWork, pushID=self.pushKeyForId(prefix, id), notBefore=notBefore)
</del><ins>+ yield txn.enqueue(PushNotificationWork,
+ pushID=self.pushKeyForId(prefix, id), notBefore=notBefore,
+ priority=priority.value)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def newNotifier(self, storeObject):
</span><span class="lines">@@ -212,7 +243,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def enqueue(self, transaction, pushKey):
</del><ins>+ def enqueue(self, transaction, pushKey, priority=PushPriority.high):
</ins><span class="cx"> """
</span><span class="cx"> Pass along enqueued pushKey to any observers
</span><span class="cx">
</span><span class="lines">@@ -221,6 +252,10 @@
</span><span class="cx">
</span><span class="cx"> @param pushKey: the push key to distribute to the observers
</span><span class="cx"> @type pushKey: C{str}
</span><ins>+
+ @param priority: the priority level
+ @type priority: L{PushPriority}
</ins><span class="cx"> """
</span><span class="cx"> for observer in self.observers:
</span><del>- yield observer.enqueue(transaction, pushKey)
</del><ins>+ yield observer.enqueue(transaction, pushKey,
+ dataChangedTimestamp=None, priority=priority)
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushtesttest_amppushpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_amppush.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_amppush.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_amppush.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,6 +18,7 @@
</span><span class="cx"> from calendarserver.push.amppush import NotificationForID
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase
</span><span class="cx"> from twisted.internet.task import Clock
</span><ins>+from calendarserver.push.util import PushPriority
</ins><span class="cx">
</span><span class="cx"> class AMPPushMasterTests(StoreTestCase):
</span><span class="cx">
</span><span class="lines">@@ -57,27 +58,81 @@
</span><span class="cx"> self.assertTrue(client3.subscribedToID("/CalDAV/localhost/user03/"))
</span><span class="cx">
</span><span class="cx"> dataChangedTimestamp = 1354815999
</span><del>- service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
</del><ins>+ service.enqueue(None, "/CalDAV/localhost/user01/",
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.high)
</ins><span class="cx"> self.assertEquals(len(client1.history), 0)
</span><span class="cx"> self.assertEquals(len(client2.history), 0)
</span><span class="cx"> self.assertEquals(len(client3.history), 0)
</span><span class="cx"> clock.advance(1)
</span><del>- self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp': 1354815999})])
</del><ins>+ self.assertEquals(
+ client1.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.high.value,
+ }
+ )
+ ]
+ )
</ins><span class="cx"> self.assertEquals(len(client2.history), 0)
</span><span class="cx"> self.assertEquals(len(client3.history), 0)
</span><span class="cx"> clock.advance(3)
</span><del>- self.assertEquals(client2.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp': 1354815999})])
</del><ins>+ self.assertEquals(
+ client2.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.high.value,
+ }
+ )
+ ]
+ )
+
</ins><span class="cx"> self.assertEquals(len(client3.history), 0)
</span><span class="cx"> clock.advance(3)
</span><del>- self.assertEquals(client3.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp': 1354815999})])
</del><ins>+ self.assertEquals(
+ client3.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.high.value,
+ }
+ )
+ ]
+ )
</ins><span class="cx">
</span><span class="cx"> client1.reset()
</span><span class="cx"> client2.reset()
</span><span class="cx"> client2.unsubscribe("token2", "/CalDAV/localhost/user01/")
</span><del>- service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
</del><ins>+ service.enqueue(None, "/CalDAV/localhost/user01/",
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.low)
</ins><span class="cx"> self.assertEquals(len(client1.history), 0)
</span><span class="cx"> clock.advance(1)
</span><del>- self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
</del><ins>+ self.assertEquals(
+ client1.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.low.value,
+ }
+ )
+ ]
+ )
+
</ins><span class="cx"> self.assertEquals(len(client2.history), 0)
</span><span class="cx"> clock.advance(3)
</span><span class="cx"> self.assertEquals(len(client2.history), 0)
</span><span class="lines">@@ -87,9 +142,35 @@
</span><span class="cx"> client1.reset()
</span><span class="cx"> client2.reset()
</span><span class="cx"> client2.subscribe("token2", "/CalDAV/localhost/user01/")
</span><del>- service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
- self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
- self.assertEquals(client2.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
</del><ins>+ service.enqueue(None, "/CalDAV/localhost/user01/",
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.medium)
+ self.assertEquals(
+ client1.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.medium.value,
+ }
+ )
+ ]
+ )
+ self.assertEquals(
+ client2.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.medium.value,
+ }
+ )
+ ]
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushtesttest_applepushpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_applepush.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_applepush.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_applepush.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,14 +18,15 @@
</span><span class="cx"> import struct
</span><span class="cx"> import time
</span><span class="cx"> from calendarserver.push.applepush import (
</span><del>- ApplePushNotifierService, APNProviderProtocol
</del><ins>+ ApplePushNotifierService, APNProviderProtocol, ApplePushPriority
</ins><span class="cx"> )
</span><del>-from calendarserver.push.util import validToken, TokenHistory
</del><ins>+from calendarserver.push.util import validToken, TokenHistory, PushPriority
</ins><span class="cx"> from twistedcaldav.test.util import StoreTestCase
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, succeed
</span><span class="cx"> from twisted.internet.task import Clock
</span><span class="cx"> from txdav.common.icommondatastore import InvalidSubscriptionValues
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ApplePushNotifierServiceTests(StoreTestCase):
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -120,12 +121,14 @@
</span><span class="cx"> dataChangedTimestamp = 1354815999
</span><span class="cx"> txn = self._sqlCalendarStore.newTransaction()
</span><span class="cx"> yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/",
</span><del>- dataChangedTimestamp=dataChangedTimestamp)
</del><ins>+ dataChangedTimestamp=dataChangedTimestamp, priority=PushPriority.high)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="cx"> # The notifications should be in the queue
</span><del>- self.assertTrue(((token, key1), dataChangedTimestamp) in service.providers["CalDAV"].queue)
- self.assertTrue(((token2, key1), dataChangedTimestamp) in service.providers["CalDAV"].queue)
</del><ins>+ self.assertTrue(((token, key1), dataChangedTimestamp, PushPriority.high)
+ in service.providers["CalDAV"].queue)
+ self.assertTrue(((token2, key1), dataChangedTimestamp, PushPriority.high)
+ in service.providers["CalDAV"].queue)
</ins><span class="cx">
</span><span class="cx"> # Start the service, making the connection which should service the
</span><span class="cx"> # queue
</span><span class="lines">@@ -137,17 +140,40 @@
</span><span class="cx"> # Verify data sent to APN
</span><span class="cx"> providerConnector = service.providers["CalDAV"].testConnector
</span><span class="cx"> rawData = providerConnector.transport.data
</span><del>- self.assertEquals(len(rawData), 183)
- data = struct.unpack("!BIIH32sH", rawData[:45])
- self.assertEquals(data[0], 1) # command
- self.assertEquals(data[4].encode("hex"), token.replace(" ", "")) # token
- payloadLength = data[5]
- payload = struct.unpack("%ds" % (payloadLength,),
- rawData[45:])
</del><ins>+ self.assertEquals(len(rawData), 199)
+ data = struct.unpack("!BI", rawData[:5])
+ self.assertEquals(data[0], 2) # command
+ self.assertEquals(data[1], 194) # frame length
+ # Item 1 (device token)
+ data = struct.unpack("!BH32s", rawData[5:40])
+ self.assertEquals(data[0], 1)
+ self.assertEquals(data[1], 32)
+ self.assertEquals(data[2].encode("hex"), token.replace(" ", "")) # token
+ # Item 2 (payload)
+ data = struct.unpack("!BH", rawData[40:43])
+ self.assertEquals(data[0], 2)
+ payloadLength = data[1]
+ self.assertEquals(payloadLength, 138)
+ payload = struct.unpack("!%ds" % (payloadLength,), rawData[43:181])
</ins><span class="cx"> payload = json.loads(payload[0])
</span><span class="cx"> self.assertEquals(payload["key"], u"/CalDAV/calendars.example.com/user01/calendar/")
</span><span class="cx"> self.assertEquals(payload["dataChangedTimestamp"], dataChangedTimestamp)
</span><span class="cx"> self.assertTrue("pushRequestSubmittedTimestamp" in payload)
</span><ins>+ # Item 3 (notification id)
+ data = struct.unpack("!BHI", rawData[181:188])
+ self.assertEquals(data[0], 3)
+ self.assertEquals(data[1], 4)
+ self.assertEquals(data[2], 2)
+ # Item 4 (expiration)
+ data = struct.unpack("!BHI", rawData[188:195])
+ self.assertEquals(data[0], 4)
+ self.assertEquals(data[1], 4)
+ # Item 5 (priority)
+ data = struct.unpack("!BHB", rawData[195:199])
+ self.assertEquals(data[0], 5)
+ self.assertEquals(data[1], 1)
+ self.assertEquals(data[2], ApplePushPriority.high.value)
+
</ins><span class="cx"> # Verify token history is updated
</span><span class="cx"> self.assertTrue(token in [t for (_ignore_i, t) in providerConnector.service.protocol.history.history])
</span><span class="cx"> self.assertTrue(token2 in [t for (_ignore_i, t) in providerConnector.service.protocol.history.history])
</span><span class="lines">@@ -160,14 +186,21 @@
</span><span class="cx"> providerConnector.transport.data = None
</span><span class="cx"> # Send notification while service is connected
</span><span class="cx"> txn = self._sqlCalendarStore.newTransaction()
</span><del>- yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/")
</del><ins>+ yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/",
+ priority=PushPriority.low)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx"> clock.advance(1) # so that first push is sent
</span><del>- self.assertEquals(len(providerConnector.transport.data), 183)
</del><ins>+ self.assertEquals(len(providerConnector.transport.data), 199)
+ # Ensure that the priority is "low"
+ data = struct.unpack("!BHB", providerConnector.transport.data[195:199])
+ self.assertEquals(data[0], 5)
+ self.assertEquals(data[1], 1)
+ self.assertEquals(data[2], ApplePushPriority.low.value)
+
</ins><span class="cx"> # Reset sent data
</span><span class="cx"> providerConnector.transport.data = None
</span><span class="cx"> clock.advance(3) # so that second push is sent
</span><del>- self.assertEquals(len(providerConnector.transport.data), 183)
</del><ins>+ self.assertEquals(len(providerConnector.transport.data), 199)
</ins><span class="cx">
</span><span class="cx"> history = []
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushtesttest_notifierpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_notifier.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_notifier.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/test/test_notifier.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,12 +18,13 @@
</span><span class="cx"> from calendarserver.push.notifier import PushDistributor
</span><span class="cx"> from calendarserver.push.notifier import getPubSubAPSConfiguration
</span><span class="cx"> from calendarserver.push.notifier import PushNotificationWork
</span><del>-from twisted.internet.defer import inlineCallbacks, succeed
</del><ins>+from twisted.internet.defer import inlineCallbacks, succeed, gatherResults
</ins><span class="cx"> from twistedcaldav.config import ConfigDict
</span><span class="cx"> from txdav.common.datastore.test.util import populateCalendarsFrom
</span><span class="cx"> from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
</span><ins>+from calendarserver.push.util import PushPriority
+from txdav.idav import ChangeCategory
</ins><span class="cx">
</span><del>-
</del><span class="cx"> class StubService(object):
</span><span class="cx"> def __init__(self):
</span><span class="cx"> self.reset()
</span><span class="lines">@@ -33,8 +34,9 @@
</span><span class="cx"> self.history = []
</span><span class="cx">
</span><span class="cx">
</span><del>- def enqueue(self, transaction, id):
- self.history.append(id)
</del><ins>+ def enqueue(self, transaction, id, dataChangedTimestamp=None,
+ priority=None):
+ self.history.append((id, priority))
</ins><span class="cx"> return(succeed(None))
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -45,8 +47,8 @@
</span><span class="cx"> def test_enqueue(self):
</span><span class="cx"> stub = StubService()
</span><span class="cx"> dist = PushDistributor([stub])
</span><del>- yield dist.enqueue(None, "testing")
- self.assertEquals(stub.history, ["testing"])
</del><ins>+ yield dist.enqueue(None, "testing", PushPriority.high)
+ self.assertEquals(stub.history, [("testing", PushPriority.high)])
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_getPubSubAPSConfiguration(self):
</span><span class="lines">@@ -91,11 +93,12 @@
</span><span class="cx"> self.history = []
</span><span class="cx">
</span><span class="cx">
</span><del>- def enqueue(self, transaction, pushID):
- self.history.append(pushID)
</del><ins>+ def enqueue(self, transaction, pushID, dataChangedTimestamp=None,
+ priority=None):
+ self.history.append((pushID, priority))
+ return(succeed(None))
</ins><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> class PushNotificationWorkTests(StoreTestCase):
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -111,35 +114,71 @@
</span><span class="cx"> txn = self._sqlCalendarStore.newTransaction()
</span><span class="cx"> wp = (yield txn.enqueue(PushNotificationWork,
</span><span class="cx"> pushID="/CalDAV/localhost/foo/",
</span><ins>+ priority=PushPriority.high.value
</ins><span class="cx"> ))
</span><span class="cx"> yield txn.commit()
</span><span class="cx"> yield wp.whenExecuted()
</span><del>- self.assertEquals(pushDistributor.history, ["/CalDAV/localhost/foo/"])
</del><ins>+ self.assertEquals(pushDistributor.history,
+ [("/CalDAV/localhost/foo/", PushPriority.high)])
</ins><span class="cx">
</span><span class="cx"> pushDistributor.reset()
</span><span class="cx"> txn = self._sqlCalendarStore.newTransaction()
</span><del>- wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+ proposals = []
+ wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx"> pushID="/CalDAV/localhost/bar/",
</span><del>- ))
- wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+ priority=PushPriority.high.value
+ )
+ proposals.append(wp.whenExecuted())
+ wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx"> pushID="/CalDAV/localhost/bar/",
</span><del>- ))
- wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+ priority=PushPriority.high.value
+ )
+ proposals.append(wp.whenExecuted())
+ wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx"> pushID="/CalDAV/localhost/bar/",
</span><del>- ))
</del><ins>+ priority=PushPriority.high.value
+ )
+ proposals.append(wp.whenExecuted())
</ins><span class="cx"> # Enqueue a different pushID to ensure those are not grouped with
</span><span class="cx"> # the others:
</span><del>- wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+ wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx"> pushID="/CalDAV/localhost/baz/",
</span><del>- ))
</del><ins>+ priority=PushPriority.high.value
+ )
+ proposals.append(wp.whenExecuted())
</ins><span class="cx">
</span><span class="cx"> yield txn.commit()
</span><del>- yield wp.whenExecuted()
</del><ins>+ yield gatherResults(proposals)
+ self.assertEquals(set(pushDistributor.history),
+ set([("/CalDAV/localhost/bar/", PushPriority.high),
+ ("/CalDAV/localhost/baz/", PushPriority.high)]))
+
+ # Ensure only the high-water-mark priority push goes out, by
+ # enqueuing low, medium, and high notifications
+ pushDistributor.reset()
+ txn = self._sqlCalendarStore.newTransaction()
+ proposals = []
+ wp = txn.enqueue(PushNotificationWork,
+ pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.low.value
+ )
+ proposals.append(wp.whenExecuted())
+ wp = txn.enqueue(PushNotificationWork,
+ pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.high.value
+ )
+ proposals.append(wp.whenExecuted())
+ wp = txn.enqueue(PushNotificationWork,
+ pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.medium.value
+ )
+ proposals.append(wp.whenExecuted())
+ yield txn.commit()
+ yield gatherResults(proposals)
</ins><span class="cx"> self.assertEquals(pushDistributor.history,
</span><del>- ["/CalDAV/localhost/bar/", "/CalDAV/localhost/baz/"])
</del><ins>+ [("/CalDAV/localhost/bar/", PushPriority.high)])
</ins><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> class NotifierFactory(StoreTestCase):
</span><span class="cx">
</span><span class="cx"> requirements = {
</span><span class="lines">@@ -168,8 +207,9 @@
</span><span class="cx"> def test_homeNotifier(self):
</span><span class="cx">
</span><span class="cx"> home = yield self.homeUnderTest()
</span><del>- yield home.notifyChanged()
- self.assertEquals(self.notifierFactory.history, ["/CalDAV/example.com/home1/"])
</del><ins>+ yield home.notifyChanged(category=ChangeCategory.default)
+ self.assertEquals(self.notifierFactory.history,
+ [("/CalDAV/example.com/home1/", PushPriority.high)])
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -177,10 +217,12 @@
</span><span class="cx"> def test_calendarNotifier(self):
</span><span class="cx">
</span><span class="cx"> calendar = yield self.calendarUnderTest()
</span><del>- yield calendar.notifyChanged()
</del><ins>+ yield calendar.notifyChanged(category=ChangeCategory.default)
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><del>- set(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/calendar_1/"])
</del><ins>+ set([
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high)])
</ins><span class="cx"> )
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="lines">@@ -194,9 +236,9 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
- "/CalDAV/example.com/home2/"
</del><ins>+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
+ ("/CalDAV/example.com/home2/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx"> yield self.commit()
</span><span class="lines">@@ -207,9 +249,9 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
- "/CalDAV/example.com/home2/"
</del><ins>+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
+ ("/CalDAV/example.com/home2/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx"> yield self.commit()
</span><span class="lines">@@ -225,10 +267,12 @@
</span><span class="cx"> self.notifierFactory.reset()
</span><span class="cx">
</span><span class="cx"> shared = yield self.calendarUnderTest(home="home2", name=shareName)
</span><del>- yield shared.notifyChanged()
</del><ins>+ yield shared.notifyChanged(category=ChangeCategory.default)
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><del>- set(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/calendar_1/"])
</del><ins>+ set([
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high)])
</ins><span class="cx"> )
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="lines">@@ -237,9 +281,11 @@
</span><span class="cx"> def test_notificationNotifier(self):
</span><span class="cx">
</span><span class="cx"> notifications = yield self.transactionUnderTest().notificationsWithUID("home1")
</span><del>- yield notifications.notifyChanged()
</del><ins>+ yield notifications.notifyChanged(category=ChangeCategory.default)
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><del>- set(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/notification/"])
</del><ins>+ set([
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/notification/", PushPriority.high)])
</ins><span class="cx"> )
</span><span class="cx"> yield self.commit()
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarserverpushutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/util.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/util.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/push/util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,7 +16,20 @@
</span><span class="cx">
</span><span class="cx"> from OpenSSL import crypto
</span><span class="cx"> from twext.python.log import Logger
</span><ins>+from twisted.python.constants import Values, ValueConstant
</ins><span class="cx">
</span><ins>+
+
+class PushPriority(Values):
+ """
+ Constants to use for push priorities
+ """
+ low = ValueConstant(1)
+ medium = ValueConstant(5)
+ high = ValueConstant(10)
+
+
+
</ins><span class="cx"> def getAPNTopicFromCertificate(certPath):
</span><span class="cx"> """
</span><span class="cx"> Given the path to a certificate, extract the UID value portion of the
</span><span class="lines">@@ -128,7 +141,7 @@
</span><span class="cx"> self.staggerSeconds = staggerSeconds
</span><span class="cx">
</span><span class="cx">
</span><del>- def schedule(self, tokens, key, dataChangedTimestamp):
</del><ins>+ def schedule(self, tokens, key, dataChangedTimestamp, priority):
</ins><span class="cx"> """
</span><span class="cx"> Schedules a batch of notifications for the given tokens, staggered
</span><span class="cx"> with self.staggerSeconds between each one. Duplicates are ignored,
</span><span class="lines">@@ -151,13 +164,14 @@
</span><span class="cx"> (internalKey,))
</span><span class="cx"> else:
</span><span class="cx"> self.outstanding[internalKey] = self.reactor.callLater(
</span><del>- scheduleTime, self.send, token, key, dataChangedTimestamp)
</del><ins>+ scheduleTime, self.send, token, key, dataChangedTimestamp,
+ priority)
</ins><span class="cx"> self.log.debug("PushScheduler scheduled: %s in %.0f sec" %
</span><span class="cx"> (internalKey, scheduleTime))
</span><span class="cx"> scheduleTime += self.staggerSeconds
</span><span class="cx">
</span><span class="cx">
</span><del>- def send(self, token, key, dataChangedTimestamp):
</del><ins>+ def send(self, token, key, dataChangedTimestamp, priority):
</ins><span class="cx"> """
</span><span class="cx"> This method is what actually gets scheduled. Its job is to remove
</span><span class="cx"> its corresponding entry from the outstanding dict and call the
</span><span class="lines">@@ -173,7 +187,7 @@
</span><span class="cx"> """
</span><span class="cx"> self.log.debug("PushScheduler fired for %s %s %d" % (token, key, dataChangedTimestamp))
</span><span class="cx"> del self.outstanding[(token, key)]
</span><del>- return self.callback(token, key, dataChangedTimestamp)
</del><ins>+ return self.callback(token, key, dataChangedTimestamp, priority)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def stop(self):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertapcaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/caldav.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/caldav.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/caldav.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -401,6 +401,14 @@
</span><span class="cx"> print("Reading configuration from file: %s" % (self["config"],))
</span><span class="cx">
</span><span class="cx"> config.load(self["config"])
</span><ins>+
+ for path in config.getProvider().importedFiles:
+ print("Imported configuration from file: '%s'" % (path,))
+ for path in config.getProvider().includedFiles:
+ print("Adding configuration from file: '%s'" % (path,))
+ for path in config.getProvider().missingFiles:
+ print("Missing configuration file: '%s'" % (path,))
+
</ins><span class="cx"> config.updateDefaults(self.overrides)
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertaputilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/util.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/util.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tap/util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -636,11 +636,23 @@
</span><span class="cx"> addSystemEventTrigger("after", "startup", timezoneStdService.onStartup)
</span><span class="cx">
</span><span class="cx"> #
</span><del>- # iSchedule service
</del><ins>+ # iSchedule service for podding
</ins><span class="cx"> #
</span><ins>+ if config.Servers.Enabled:
+ log.info("Setting up iSchedule podding inbox resource: {cls}", cls=iScheduleResourceClass)
+
+ ischedule = iScheduleResourceClass(
+ root,
+ newStore,
+ podding=True
+ )
+ root.putChild(config.Servers.InboxName, ischedule)
+
+ #
+ # iSchedule service (not used for podding)
+ #
</ins><span class="cx"> if config.Scheduling.iSchedule.Enabled:
</span><del>- log.info("Setting up iSchedule inbox resource: {cls}",
- cls=iScheduleResourceClass)
</del><ins>+ log.info("Setting up iSchedule inbox resource: {cls}", cls=iScheduleResourceClass)
</ins><span class="cx">
</span><span class="cx"> ischedule = iScheduleResourceClass(
</span><span class="cx"> root,
</span><span class="lines">@@ -651,8 +663,7 @@
</span><span class="cx"> # Do DomainKey resources
</span><span class="cx"> DKIMUtils.validConfiguration(config)
</span><span class="cx"> if config.Scheduling.iSchedule.DKIM.Enabled:
</span><del>- log.info("Setting up domainkey resource: {res}",
- res=DomainKeyResource)
</del><ins>+ log.info("Setting up domainkey resource: {res}", res=DomainKeyResource)
</ins><span class="cx"> domain = config.Scheduling.iSchedule.DKIM.Domain if config.Scheduling.iSchedule.DKIM.Domain else config.ServerHostName
</span><span class="cx"> dk = DomainKeyResource(
</span><span class="cx"> domain,
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsampnotificationspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/ampnotifications.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/ampnotifications.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/ampnotifications.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -141,8 +141,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-def notificationCallback(id, dataChangedTimestamp):
- print("Received notification for:", id)
</del><ins>+def notificationCallback(id, dataChangedTimestamp, priority):
+ print("Received notification for:", id, "Priority", priority)
</ins><span class="cx"> return succeed(True)
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsanonymizepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/anonymize.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/anonymize.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/anonymize.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -33,8 +33,8 @@
</span><span class="cx">
</span><span class="cx"> from twext.python.plistlib import readPlistFromString
</span><span class="cx">
</span><del>-from pycalendar.calendar import PyCalendar
-from pycalendar.attribute import PyCalendarAttribute
</del><ins>+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.parameter import Parameter
</ins><span class="cx">
</span><span class="cx"> COPY_CAL_XATTRS = (
</span><span class="cx"> 'WebDAV:{DAV:}resourcetype',
</span><span class="lines">@@ -67,6 +67,8 @@
</span><span class="cx"> else:
</span><span class="cx"> sys.exit(0)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def main():
</span><span class="cx">
</span><span class="cx"> try:
</span><span class="lines">@@ -83,8 +85,6 @@
</span><span class="cx"> # Get configuration
</span><span class="cx"> #
</span><span class="cx"> directoryNode = "/Search"
</span><del>- sourceDirectory = None
- destDirectory = None
</del><span class="cx">
</span><span class="cx"> for opt, arg in optargs:
</span><span class="cx"> if opt in ("-h", "--help"):
</span><span class="lines">@@ -107,6 +107,7 @@
</span><span class="cx"> directoryMap.dumpDsImports(os.path.join(destDirectory, "dsimports"))
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def anonymizeRoot(directoryMap, sourceDirectory, destDirectory):
</span><span class="cx"> # sourceDirectory and destDirectory are DocumentRoots
</span><span class="cx">
</span><span class="lines">@@ -244,12 +245,10 @@
</span><span class="cx">
</span><span class="cx"> resources += 1
</span><span class="cx">
</span><del>-
</del><span class="cx"> # Store new ctag on calendar
</span><span class="cx"> xml = "<?xml version='1.0' encoding='UTF-8'?>\r\n<getctag xmlns='http://calendarserver.org/ns/'>%s</getctag>\r\n" % (str(datetime.datetime.now()),)
</span><span class="cx"> xattr.setxattr(destCal, "WebDAV:{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag", zlib.compress(xml))
</span><span class="cx">
</span><del>-
</del><span class="cx"> # Calendar home quota
</span><span class="cx"> xml = "<?xml version='1.0' encoding='UTF-8'?>\r\n<quota-used xmlns='http://twistedmatrix.com/xml_namespace/dav/private/'>%d</quota-used>\r\n" % (quotaUsed,)
</span><span class="cx"> xattr.setxattr(destHome, "WebDAV:{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2Fprivate%2F}quota-used", zlib.compress(xml))
</span><span class="lines">@@ -280,9 +279,10 @@
</span><span class="cx"> print("")
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def anonymizeData(directoryMap, data):
</span><span class="cx"> try:
</span><del>- pyobj = PyCalendar.parseText(data)
</del><ins>+ pyobj = Calendar.parseText(data)
</ins><span class="cx"> except Exception, e:
</span><span class="cx"> print("Failed to parse (%s): %s" % (e, data))
</span><span class="cx"> return None
</span><span class="lines">@@ -309,13 +309,13 @@
</span><span class="cx"> comp.removeProperty(prop)
</span><span class="cx"> continue
</span><span class="cx"> prop.setValue("urn:uuid:%s" % (record['guid'],))
</span><del>- if prop.hasAttribute('X-CALENDARSERVER-EMAIL'):
- prop.replaceAttribute(PyCalendarAttribute('X-CALENDARSERVER-EMAIL', record['email']))
</del><ins>+ if prop.hasParameter('X-CALENDARSERVER-EMAIL'):
+ prop.replaceParameter(Parameter('X-CALENDARSERVER-EMAIL', record['email']))
</ins><span class="cx"> else:
</span><del>- prop.removeAttributes('EMAIL')
- prop.addAttribute(PyCalendarAttribute('EMAIL', record['email']))
- prop.removeAttributes('CN')
- prop.addAttribute(PyCalendarAttribute('CN', record['name']))
</del><ins>+ prop.removeParameters('EMAIL')
+ prop.addParameter(Parameter('EMAIL', record['email']))
+ prop.removeParameters('CN')
+ prop.addParameter(Parameter('CN', record['name']))
</ins><span class="cx"> except KeyError:
</span><span class="cx"> pass
</span><span class="cx">
</span><span class="lines">@@ -345,11 +345,12 @@
</span><span class="cx"> return pyobj.getText(includeTimezones=True)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class DirectoryMap(object):
</span><span class="cx">
</span><span class="cx"> def __init__(self, node):
</span><span class="cx">
</span><del>- self.map = { }
</del><ins>+ self.map = {}
</ins><span class="cx"> self.byType = {
</span><span class="cx"> 'users' : [],
</span><span class="cx"> 'groups' : [],
</span><span class="lines">@@ -373,10 +374,10 @@
</span><span class="cx">
</span><span class="cx"> print("Fetching records from directory: %s" % (node,))
</span><span class="cx">
</span><del>- for internalType, (recordType, friendlyType) in self.strings.iteritems():
</del><ins>+ for internalType, (recordType, _ignore_friendlyType) in self.strings.iteritems():
</ins><span class="cx"> print(" %s..." % (internalType,))
</span><span class="cx"> child = Popen(
</span><del>- args = [
</del><ins>+ args=[
</ins><span class="cx"> "/usr/bin/dscl", "-plist", node, "-readall",
</span><span class="cx"> "/%s" % (recordType,),
</span><span class="cx"> "GeneratedUID", "RecordName", "EMailAddress", "GroupMembers"
</span><span class="lines">@@ -405,6 +406,7 @@
</span><span class="cx"> print("Done.")
</span><span class="cx"> print("")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def addRecord(self, internalType="users", guid=None, names=None,
</span><span class="cx"> emails=None, members=None, cua=None):
</span><span class="cx">
</span><span class="lines">@@ -458,6 +460,7 @@
</span><span class="cx"> keys.append(email)
</span><span class="cx"> return keys
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def cua2key(self, cua):
</span><span class="cx"> key = cua.lower()
</span><span class="cx">
</span><span class="lines">@@ -473,10 +476,11 @@
</span><span class="cx">
</span><span class="cx"> return key
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lookupCUA(self, cua):
</span><span class="cx"> key = self.cua2key(cua)
</span><span class="cx">
</span><del>- if key and self.map.has_key(key):
</del><ins>+ if key and key in self.map:
</ins><span class="cx"> return self.map[key]
</span><span class="cx"> else:
</span><span class="cx"> return None
</span><span class="lines">@@ -491,6 +495,7 @@
</span><span class="cx"> if unknown:
</span><span class="cx"> print(" Principals not found in directory: %d" % (unknown,))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def dumpDsImports(self, dirPath):
</span><span class="cx"> if not os.path.exists(dirPath):
</span><span class="cx"> os.makedirs(dirPath)
</span><span class="lines">@@ -567,6 +572,8 @@
</span><span class="cx"> Error trying to access dscl
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class DatabaseError(Exception):
</span><span class="cx"> """
</span><span class="cx"> Error trying to access sqlite3
</span><span class="lines">@@ -574,8 +581,6 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-
-
</del><span class="cx"> def anonymize(text):
</span><span class="cx"> """
</span><span class="cx"> Return a string whose value is the hex digest of text, repeated as needed
</span><span class="lines">@@ -592,19 +597,18 @@
</span><span class="cx"> h = hashlib.md5(text)
</span><span class="cx"> h = h.hexdigest()
</span><span class="cx"> l = len(text)
</span><del>- return (h*((l/32)+1))[:-(32-(l%32))]
</del><ins>+ return (h * ((l / 32) + 1))[:-(32 - (l % 32))]
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> nameChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
</span><span class="cx"> def randomName(length):
</span><span class="cx"> l = []
</span><del>- for i in xrange(length):
</del><ins>+ for _ignore in xrange(length):
</ins><span class="cx"> l.append(random.choice(nameChars))
</span><span class="cx"> return "".join(l)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> if __name__ == "__main__":
</span><span class="cx"> main()
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolscalverifypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/calverify.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/calverify.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/calverify.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -47,12 +47,12 @@
</span><span class="cx"> import traceback
</span><span class="cx"> import uuid
</span><span class="cx">
</span><del>-from pycalendar import definitions
-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.exceptions import PyCalendarError
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.datetime import DateTime
+from pycalendar.exceptions import ErrorBase
+from pycalendar.period import Period
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twisted.application.service import Service
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="lines">@@ -543,7 +543,7 @@
</span><span class="cx"> tr = schema.TIME_RANGE
</span><span class="cx"> kwds = {
</span><span class="cx"> "Start" : pyCalendarTodatetime(start),
</span><del>- "Max" : pyCalendarTodatetime(PyCalendarDateTime(1900, 1, 1, 0, 0, 0))
</del><ins>+ "Max" : pyCalendarTodatetime(DateTime(1900, 1, 1, 0, 0, 0))
</ins><span class="cx"> }
</span><span class="cx"> rows = (yield Select(
</span><span class="cx"> [ch.OWNER_UID, co.RESOURCE_ID, co.ICALENDAR_UID, cb.CALENDAR_RESOURCE_NAME, co.MD5, co.ORGANIZER, co.CREATED, co.MODIFIED],
</span><span class="lines">@@ -596,7 +596,7 @@
</span><span class="cx"> tr = schema.TIME_RANGE
</span><span class="cx"> kwds = {
</span><span class="cx"> "Start" : pyCalendarTodatetime(start),
</span><del>- "Max" : pyCalendarTodatetime(PyCalendarDateTime(1900, 1, 1, 0, 0, 0)),
</del><ins>+ "Max" : pyCalendarTodatetime(DateTime(1900, 1, 1, 0, 0, 0)),
</ins><span class="cx"> "UUID" : uuid,
</span><span class="cx"> }
</span><span class="cx"> rows = (yield Select(
</span><span class="lines">@@ -626,7 +626,7 @@
</span><span class="cx">
</span><span class="cx"> kwds = {
</span><span class="cx"> "Start" : pyCalendarTodatetime(start),
</span><del>- "Max" : pyCalendarTodatetime(PyCalendarDateTime(1900, 1, 1, 0, 0, 0)),
</del><ins>+ "Max" : pyCalendarTodatetime(DateTime(1900, 1, 1, 0, 0, 0)),
</ins><span class="cx"> "UUID" : uuid,
</span><span class="cx"> }
</span><span class="cx"> rows = (yield Select(
</span><span class="lines">@@ -704,8 +704,8 @@
</span><span class="cx"> ),
</span><span class="cx"> ).on(self.txn, **kwds))
</span><span class="cx"> try:
</span><del>- caldata = PyCalendar.parseText(rows[0][0]) if rows else None
- except PyCalendarError:
</del><ins>+ caldata = Calendar.parseText(rows[0][0]) if rows else None
+ except ErrorBase:
</ins><span class="cx"> caltxt = rows[0][0] if rows else None
</span><span class="cx"> if caltxt:
</span><span class="cx"> caltxt = caltxt.replace("\r\n ", "")
</span><span class="lines">@@ -713,8 +713,8 @@
</span><span class="cx"> if doFix:
</span><span class="cx"> caltxt = (yield self.fixBadOldCua(resid, caltxt))
</span><span class="cx"> try:
</span><del>- caldata = PyCalendar.parseText(caltxt) if rows else None
- except PyCalendarError:
</del><ins>+ caldata = Calendar.parseText(caltxt) if rows else None
+ except ErrorBase:
</ins><span class="cx"> self.parseError = "No fix bad CALENDARSERVER-OLD-CUA"
</span><span class="cx"> returnValue(None)
</span><span class="cx"> else:
</span><span class="lines">@@ -746,8 +746,8 @@
</span><span class="cx"> ).on(self.txn, **kwds))
</span><span class="cx">
</span><span class="cx"> try:
</span><del>- caldata = PyCalendar.parseText(rows[0][0]) if rows else None
- except PyCalendarError:
</del><ins>+ caldata = Calendar.parseText(rows[0][0]) if rows else None
+ except ErrorBase:
</ins><span class="cx"> returnValue((None, None, None, None,))
</span><span class="cx">
</span><span class="cx"> returnValue((caldata, rows[0][1], rows[0][2], rows[0][3],) if rows else (None, None, None, None,))
</span><span class="lines">@@ -1056,14 +1056,14 @@
</span><span class="cx">
</span><span class="cx"> self.output.write("\n---- Scanning calendar data ----\n")
</span><span class="cx">
</span><del>- self.now = PyCalendarDateTime.getNowUTC()
- self.start = PyCalendarDateTime.getToday()
</del><ins>+ self.now = DateTime.getNowUTC()
+ self.start = DateTime.getToday()
</ins><span class="cx"> self.start.setDateOnly(False)
</span><span class="cx"> self.end = self.start.duplicate()
</span><span class="cx"> self.end.offsetYear(1)
</span><span class="cx"> self.fix = self.options["fix"]
</span><span class="cx">
</span><del>- self.tzid = PyCalendarTimezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
</del><ins>+ self.tzid = Timezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
</ins><span class="cx">
</span><span class="cx"> self.txn = self.store.newTransaction()
</span><span class="cx">
</span><span class="lines">@@ -1426,14 +1426,14 @@
</span><span class="cx">
</span><span class="cx"> self.output.write("\n---- Scanning calendar data ----\n")
</span><span class="cx">
</span><del>- self.now = PyCalendarDateTime.getNowUTC()
- self.start = self.options["start"] if "start" in self.options else PyCalendarDateTime.getToday()
</del><ins>+ self.now = DateTime.getNowUTC()
+ self.start = self.options["start"] if "start" in self.options else DateTime.getToday()
</ins><span class="cx"> self.start.setDateOnly(False)
</span><span class="cx"> self.end = self.start.duplicate()
</span><span class="cx"> self.end.offsetYear(1)
</span><span class="cx"> self.fix = self.options["fix"]
</span><span class="cx">
</span><del>- self.tzid = PyCalendarTimezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
</del><ins>+ self.tzid = Timezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
</ins><span class="cx">
</span><span class="cx"> self.txn = self.store.newTransaction()
</span><span class="cx">
</span><span class="lines">@@ -2028,7 +2028,7 @@
</span><span class="cx"> """
</span><span class="cx"> Return the master iCal component in this calendar.
</span><span class="cx">
</span><del>- @return: the L{PyCalendarComponent} for the master component,
</del><ins>+ @return: the L{Component} for the master component,
</ins><span class="cx"> or C{None} if there isn't one.
</span><span class="cx"> """
</span><span class="cx"> for component in calendar.getComponents(definitions.cICalComponent_VEVENT):
</span><span class="lines">@@ -2042,7 +2042,7 @@
</span><span class="cx"> # Expand events into instances in the start/end range
</span><span class="cx"> results = []
</span><span class="cx"> calendar.getVEvents(
</span><del>- PyCalendarPeriod(
</del><ins>+ Period(
</ins><span class="cx"> start=start,
</span><span class="cx"> end=end,
</span><span class="cx"> ),
</span><span class="lines">@@ -2082,10 +2082,10 @@
</span><span class="cx"> if cancelled:
</span><span class="cx"> partstat = "CANCELLED"
</span><span class="cx"> else:
</span><del>- if not prop.hasAttribute(definitions.cICalAttribute_PARTSTAT):
- partstat = definitions.cICalAttribute_PARTSTAT_NEEDSACTION
</del><ins>+ if not prop.hasParameter(definitions.cICalParameter_PARTSTAT):
+ partstat = definitions.cICalParameter_PARTSTAT_NEEDSACTION
</ins><span class="cx"> else:
</span><del>- partstat = prop.getAttributeValue(definitions.cICalAttribute_PARTSTAT)
</del><ins>+ partstat = prop.getParameterValue(definitions.cICalParameter_PARTSTAT)
</ins><span class="cx">
</span><span class="cx"> attendees.setdefault(caladdr, set()).add((instance_id, partstat))
</span><span class="cx">
</span><span class="lines">@@ -2140,9 +2140,9 @@
</span><span class="cx">
</span><span class="cx"> self.output.write("\n---- Scanning calendar data ----\n")
</span><span class="cx">
</span><del>- self.tzid = PyCalendarTimezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
- self.now = PyCalendarDateTime.getNowUTC()
- self.start = PyCalendarDateTime.getToday()
</del><ins>+ self.tzid = Timezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
+ self.now = DateTime.getNowUTC()
+ self.start = DateTime.getToday()
</ins><span class="cx"> self.start.setDateOnly(False)
</span><span class="cx"> self.start.setTimezone(self.tzid)
</span><span class="cx"> self.end = self.start.duplicate()
</span><span class="lines">@@ -2328,7 +2328,7 @@
</span><span class="cx"> continue
</span><span class="cx"> dtstart = instance.component.propertyValue("DTSTART")
</span><span class="cx"> if tzid is None and dtstart.getTimezoneID():
</span><del>- tzid = PyCalendarTimezone(tzid=dtstart.getTimezoneID())
</del><ins>+ tzid = Timezone(tzid=dtstart.getTimezoneID())
</ins><span class="cx"> hasFloating |= dtstart.isDateOnly() or dtstart.floating()
</span><span class="cx">
</span><span class="cx"> details.append(InstanceDetails(resid, uid, instance.start, instance.end, instance.component.getOrganizer(), instance.component.propertyValue("SUMMARY")))
</span><span class="lines">@@ -2369,7 +2369,7 @@
</span><span class="cx">
</span><span class="cx"> # Adjust floating and sort
</span><span class="cx"> if hasFloating and tzid is not None:
</span><del>- utc = PyCalendarTimezone(utc=True)
</del><ins>+ utc = Timezone(utc=True)
</ins><span class="cx"> for item in details:
</span><span class="cx"> if item.start.floating():
</span><span class="cx"> item.start.setTimezone(tzid)
</span><span class="lines">@@ -2449,9 +2449,9 @@
</span><span class="cx">
</span><span class="cx"> self.output.write("\n---- Scanning calendar data ----\n")
</span><span class="cx">
</span><del>- self.tzid = PyCalendarTimezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
- self.now = PyCalendarDateTime.getNowUTC()
- self.start = self.options["start"] if "start" in self.options else PyCalendarDateTime.getToday()
</del><ins>+ self.tzid = Timezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
+ self.now = DateTime.getNowUTC()
+ self.start = self.options["start"] if "start" in self.options else DateTime.getToday()
</ins><span class="cx"> self.start.setDateOnly(False)
</span><span class="cx"> self.start.setTimezone(self.tzid)
</span><span class="cx"> self.fix = self.options["fix"]
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsdbinspectpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/dbinspect.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/dbinspect.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/dbinspect.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -24,7 +24,7 @@
</span><span class="cx">
</span><span class="cx"> from calendarserver.tools import tables
</span><span class="cx"> from calendarserver.tools.cmdline import utilityMain
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx"> from twext.enterprise.dal.syntax import Select, Parameter, Count, Delete, \
</span><span class="cx"> Constant
</span><span class="cx"> from twisted.application.service import Service
</span><span class="lines">@@ -715,12 +715,12 @@
</span><span class="cx"> end += "T000000Z"
</span><span class="cx">
</span><span class="cx"> try:
</span><del>- start = PyCalendarDateTime.parseText(start)
</del><ins>+ start = DateTime.parseText(start)
</ins><span class="cx"> except ValueError:
</span><span class="cx"> print("Invalid start value")
</span><span class="cx"> returnValue(None)
</span><span class="cx"> try:
</span><del>- end = PyCalendarDateTime.parseText(end)
</del><ins>+ end = DateTime.parseText(end)
</ins><span class="cx"> except ValueError:
</span><span class="cx"> print("Invalid end value")
</span><span class="cx"> returnValue(None)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsgatewaypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/gateway.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/gateway.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/gateway.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -36,9 +36,9 @@
</span><span class="cx"> from calendarserver.tools.purge import WorkerService, PurgeOldEventsService, DEFAULT_BATCH_SIZE, DEFAULT_RETAIN_DAYS
</span><span class="cx"> from calendarserver.tools.cmdline import utilityMain
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><del>-from twistedcaldav.config import config, ConfigDict
</del><ins>+from twistedcaldav.config import config, ConfigDict
</ins><span class="cx">
</span><span class="cx"> from calendarserver.tools.config import WRITABLE_CONFIG_KEYS, setKeyPath, getKeyPath, flattenDictionary, WritableConfig
</span><span class="cx">
</span><span class="lines">@@ -156,6 +156,7 @@
</span><span class="cx"> 'ZIP' : { 'extras' : True, 'attr' : 'zip', },
</span><span class="cx"> 'Country' : { 'extras' : True, 'attr' : 'country', },
</span><span class="cx"> 'Phone' : { 'extras' : True, 'attr' : 'phone', },
</span><ins>+ 'Geo' : { 'extras' : True, 'attr' : 'geo', },
</ins><span class="cx"> 'AutoSchedule' : { 'attr' : 'autoSchedule', },
</span><span class="cx"> 'AutoAcceptGroup' : { 'attr' : 'autoAcceptGroup', },
</span><span class="cx"> }
</span><span class="lines">@@ -373,7 +374,7 @@
</span><span class="cx"> return
</span><span class="cx"> self.respondWithRecordsOfTypes(self.dir, command, ["resources"])
</span><span class="cx">
</span><del>-
</del><ins>+
</ins><span class="cx"> def command_getLocationAndResourceList(self, command):
</span><span class="cx"> self.respondWithRecordsOfTypes(self.dir, command, ["locations", "resources"])
</span><span class="cx">
</span><span class="lines">@@ -424,10 +425,8 @@
</span><span class="cx"> self.command_readConfig(command)
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> # Proxies
</span><span class="cx">
</span><del>-
</del><span class="cx"> @inlineCallbacks
</span><span class="cx"> def command_listWriteProxies(self, command):
</span><span class="cx"> principal = principalForPrincipalID(command['Principal'], directory=self.dir)
</span><span class="lines">@@ -538,14 +537,13 @@
</span><span class="cx"> @type command: C{dict}
</span><span class="cx"> """
</span><span class="cx"> retainDays = command.get("RetainDays", DEFAULT_RETAIN_DAYS)
</span><del>- cutoff = PyCalendarDateTime.getToday()
</del><ins>+ cutoff = DateTime.getToday()
</ins><span class="cx"> cutoff.setDateOnly(False)
</span><span class="cx"> cutoff.offsetDay(-retainDays)
</span><span class="cx"> eventCount = (yield PurgeOldEventsService.purgeOldEvents(self.store, cutoff, DEFAULT_BATCH_SIZE))
</span><span class="cx"> self.respond(command, {'EventsRemoved' : eventCount, "RetainDays" : retainDays})
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> @inlineCallbacks
</span><span class="cx"> def respondWithProxies(self, directory, command, principal, proxyType):
</span><span class="cx"> proxies = []
</span><span class="lines">@@ -562,7 +560,6 @@
</span><span class="cx"> })
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> def respondWithRecordsOfTypes(self, directory, command, recordTypes):
</span><span class="cx"> result = []
</span><span class="cx"> for recordType in recordTypes:
</span><span class="lines">@@ -572,7 +569,6 @@
</span><span class="cx"> self.respond(command, result)
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> def respond(self, command, result):
</span><span class="cx"> self.output.write(writePlistToString({'command' : command['command'], 'result' : result}))
</span><span class="cx">
</span><span class="lines">@@ -581,6 +577,7 @@
</span><span class="cx"> self.output.write(writePlistToString({'error' : msg, }))
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordToDict(record):
</span><span class="cx"> recordDict = {}
</span><span class="cx"> for key, info in attrMap.iteritems():
</span><span class="lines">@@ -596,6 +593,8 @@
</span><span class="cx"> pass
</span><span class="cx"> return recordDict
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def respondWithError(msg, status=1):
</span><span class="cx"> sys.stdout.write(writePlistToString({'error' : msg, }))
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsmanagetimezonespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/managetimezones.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/managetimezones.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/managetimezones.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,8 +16,8 @@
</span><span class="cx"> ##
</span><span class="cx"> from __future__ import print_function
</span><span class="cx">
</span><del>-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> from twext.python.log import StandardIOObserver
</span><span class="lines">@@ -95,11 +95,11 @@
</span><span class="cx"> pass
</span><span class="cx">
</span><span class="cx"> if not tzvers:
</span><del>- tzvers = PyCalendarDateTime.getToday().getText()
</del><ins>+ tzvers = DateTime.getToday().getText()
</ins><span class="cx"> print("Converting data (version: %s) at: %s" % (tzvers, zonedir,))
</span><span class="cx"> startYear = 1800
</span><del>- endYear = PyCalendarDateTime.getToday().getYear() + 10
- PyCalendar.sProdID = "-//calendarserver.org//Zonal//EN"
</del><ins>+ endYear = DateTime.getToday().getYear() + 10
+ Calendar.sProdID = "-//calendarserver.org//Zonal//EN"
</ins><span class="cx"> zonefiles = "northamerica", "southamerica", "europe", "africa", "asia", "australasia", "antarctica", "etcetera", "backward"
</span><span class="cx"> parser = tzconvert()
</span><span class="cx"> for file in zonefiles:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolspurgepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/purge.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/purge.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/purge.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -23,7 +23,7 @@
</span><span class="cx">
</span><span class="cx"> from getopt import getopt, GetoptError
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx">
</span><span class="lines">@@ -147,7 +147,7 @@
</span><span class="cx"> if dryrun:
</span><span class="cx"> verbose = True
</span><span class="cx">
</span><del>- cutoff = PyCalendarDateTime.getToday()
</del><ins>+ cutoff = DateTime.getToday()
</ins><span class="cx"> cutoff.setDateOnly(False)
</span><span class="cx"> cutoff.offsetDay(-days)
</span><span class="cx"> cls.cutoff = cutoff
</span><span class="lines">@@ -328,7 +328,7 @@
</span><span class="cx">
</span><span class="cx"> cls.uuid = uuid
</span><span class="cx"> if days > 0:
</span><del>- cutoff = PyCalendarDateTime.getToday()
</del><ins>+ cutoff = DateTime.getToday()
</ins><span class="cx"> cutoff.setDateOnly(False)
</span><span class="cx"> cutoff.offsetDay(-days)
</span><span class="cx"> cls.cutoff = cutoff
</span><span class="lines">@@ -352,7 +352,7 @@
</span><span class="cx"> service = cls(store)
</span><span class="cx"> service.uuid = uuid
</span><span class="cx"> if days > 0:
</span><del>- cutoff = PyCalendarDateTime.getToday()
</del><ins>+ cutoff = DateTime.getToday()
</ins><span class="cx"> cutoff.setDateOnly(False)
</span><span class="cx"> cutoff.offsetDay(-days)
</span><span class="cx"> service.cutoff = cutoff
</span><span class="lines">@@ -732,7 +732,7 @@
</span><span class="cx"> def _purgeUID(self, uid):
</span><span class="cx">
</span><span class="cx"> if self.when is None:
</span><del>- self.when = PyCalendarDateTime.getNowUTC()
</del><ins>+ self.when = DateTime.getNowUTC()
</ins><span class="cx">
</span><span class="cx"> # Does the record exist?
</span><span class="cx"> record = self.directory.recordWithUID(uid)
</span><span class="lines">@@ -1020,7 +1020,7 @@
</span><span class="cx"> @type event: L{twistedcaldav.ical.Component}
</span><span class="cx">
</span><span class="cx"> @param when: the cutoff date (anything after which is removed)
</span><del>- @type when: PyCalendarDateTime
</del><ins>+ @type when: DateTime
</ins><span class="cx">
</span><span class="cx"> @param cua: Calendar User Address of principal being purged, to compare
</span><span class="cx"> to see if it's the organizer of the event or just an attendee
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolsshelldirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/shell/directory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/shell/directory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/shell/directory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -41,6 +41,7 @@
</span><span class="cx"> returnValue(sorted(records, key=operator.attrgetter("fullName")))
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def recordInfo(directory, record):
</span><span class="cx"> """
</span><span class="lines">@@ -58,8 +59,9 @@
</span><span class="cx"> add("Proxy access" , (yield recordProxyAccessInfo(directory, record)))
</span><span class="cx">
</span><span class="cx"> returnValue("\n".join(info))
</span><del>-
</del><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def recordBasicInfo(directory, record):
</span><span class="cx"> """
</span><span class="cx"> Basic information for a record.
</span><span class="lines">@@ -87,15 +89,15 @@
</span><span class="cx"> for cua in record.calendarUserAddresses:
</span><span class="cx"> add("Calendar User Address", cua)
</span><span class="cx">
</span><del>- add("Server ID" , record.serverID )
- add("Partition ID" , record.partitionID )
- add("Enabled" , record.enabled )
- add("Enabled for Calendar", record.enabledForCalendaring )
</del><ins>+ add("Server ID" , record.serverID)
+ add("Enabled" , record.enabled)
+ add("Enabled for Calendar", record.enabledForCalendaring)
</ins><span class="cx"> add("Enabled for Contacts", record.enabledForAddressBooks)
</span><span class="cx">
</span><span class="cx"> return succeed(table.toString())
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordGroupMembershipInfo(directory, record):
</span><span class="cx"> """
</span><span class="cx"> Group membership info for a record.
</span><span class="lines">@@ -109,7 +111,7 @@
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><span class="cx"> rows = sorted(rows,
</span><del>- key = lambda row: (row[1], row[2])
</del><ins>+ key=lambda row: (row[1], row[2])
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> table = Table()
</span><span class="lines">@@ -120,6 +122,7 @@
</span><span class="cx"> return succeed(table.toString())
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def recordProxyAccessInfo(directory, record):
</span><span class="cx"> """
</span><span class="lines">@@ -154,7 +157,7 @@
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="cx"> rows = sorted(rows,
</span><del>- key = lambda row: (row[1], row[2], row[4])
</del><ins>+ key=lambda row: (row[1], row[2], row[4])
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> table = Table()
</span><span class="lines">@@ -165,6 +168,7 @@
</span><span class="cx"> returnValue(table.toString())
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def summarizeRecords(directory, records):
</span><span class="cx"> table = Table()
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstestdeprovisioncaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/deprovision/caldavd.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/deprovision/caldavd.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/deprovision/caldavd.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -191,7 +191,6 @@
</span><span class="cx"> Augments for the directory service records to add calendar specific attributes.
</span><span class="cx">
</span><span class="cx"> A variety of augment services are available for use.
</span><del>- When using a partitioned server, a service that can be accessed from each host will be needed.
</del><span class="cx"> -->
</span><span class="cx">
</span><span class="cx"> <!-- XML File Augment Service -->
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstestgatewaycaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/gateway/caldavd.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/gateway/caldavd.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/gateway/caldavd.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -203,7 +203,6 @@
</span><span class="cx"> Augments for the directory service records to add calendar specific attributes.
</span><span class="cx">
</span><span class="cx"> A variety of augment services are available for use.
</span><del>- When using a partitioned server, a service that can be accessed from each host will be needed.
</del><span class="cx"> -->
</span><span class="cx">
</span><span class="cx"> <!-- XML File Augment Service -->
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstestprincipalscaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/principals/caldavd.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/principals/caldavd.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/principals/caldavd.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -195,7 +195,6 @@
</span><span class="cx"> Augments for the directory service records to add calendar specific attributes.
</span><span class="cx">
</span><span class="cx"> A variety of augment services are available for use.
</span><del>- When using a partitioned server, a service that can be accessed from each host will be needed.
</del><span class="cx"> -->
</span><span class="cx">
</span><span class="cx"> <!-- XML File Augment Service -->
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstesttest_calverifypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_calverify.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_calverify.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_calverify.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -22,7 +22,7 @@
</span><span class="cx"> from calendarserver.tools.calverify import BadDataService, \
</span><span class="cx"> SchedulingMismatchService, DoubleBookingService, DarkPurgeService
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="lines">@@ -511,7 +511,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": True,
</span><span class="lines">@@ -555,7 +555,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": True,
</span><span class="lines">@@ -627,7 +627,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -667,7 +667,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -969,7 +969,7 @@
</span><span class="cx"> self.notifierFactory.reset()
</span><span class="cx">
</span><span class="cx">
</span><del>-now = PyCalendarDateTime.getToday()
</del><ins>+now = DateTime.getToday()
</ins><span class="cx"> now.setDay(1)
</span><span class="cx"> now.offsetMonth(2)
</span><span class="cx"> nowYear = now.getYear()
</span><span class="lines">@@ -1404,7 +1404,7 @@
</span><span class="cx">
</span><span class="cx"> home = (yield self.homeUnderTest(name=self.uuid3))
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar2", home=self.uuid3))
</span><del>- yield home.setDefaultCalendar(calendar)
</del><ins>+ yield home.setDefaultCalendar(calendar, "VEVENT")
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1418,7 +1418,7 @@
</span><span class="cx"> sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
</span><span class="cx"> sync_token_old2 = (yield (yield self.calendarUnderTest(home=self.uuid2, name="calendar")).syncToken())
</span><span class="cx"> sync_token_old3 = (yield (yield self.calendarUnderTest(home=self.uuid3, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -1431,7 +1431,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": "",
</span><span class="cx"> "tzid": "",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> }
</span><span class="cx"> output = StringIO()
</span><span class="cx"> calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -1485,7 +1485,7 @@
</span><span class="cx"> sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
</span><span class="cx"> sync_token_old2 = (yield (yield self.calendarUnderTest(home=self.uuid2, name="calendar")).syncToken())
</span><span class="cx"> sync_token_old3 = (yield (yield self.calendarUnderTest(home=self.uuid3, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -1498,7 +1498,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": "",
</span><span class="cx"> "tzid": "",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> }
</span><span class="cx"> output = StringIO()
</span><span class="cx"> calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -1576,7 +1576,7 @@
</span><span class="cx"> self.assertNotEqual(sync_token_old3, sync_token_new3)
</span><span class="cx">
</span><span class="cx"> # Re-scan after changes to make sure there are no errors
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx"> options["fix"] = False
</span><span class="cx"> calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="cx"> yield calverify.doAction()
</span><span class="lines">@@ -1694,7 +1694,7 @@
</span><span class="cx">
</span><span class="cx"> sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -1707,7 +1707,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": "",
</span><span class="cx"> "tzid": "",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> }
</span><span class="cx"> output = StringIO()
</span><span class="cx"> calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -1745,7 +1745,7 @@
</span><span class="cx">
</span><span class="cx"> sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -1758,7 +1758,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": "",
</span><span class="cx"> "tzid": "",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> }
</span><span class="cx"> output = StringIO()
</span><span class="cx"> calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -1803,7 +1803,7 @@
</span><span class="cx"> self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
</span><span class="cx">
</span><span class="cx"> # Re-scan after changes to make sure there are no errors
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx"> options["fix"] = False
</span><span class="cx"> calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="cx"> yield calverify.doAction()
</span><span class="lines">@@ -1921,7 +1921,7 @@
</span><span class="cx">
</span><span class="cx"> sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -1934,7 +1934,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": CalVerifyMismatchTestsBase.uuidl1,
</span><span class="cx"> "tzid": "",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> }
</span><span class="cx"> output = StringIO()
</span><span class="cx"> calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -1970,7 +1970,7 @@
</span><span class="cx">
</span><span class="cx"> sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -1983,7 +1983,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": CalVerifyMismatchTestsBase.uuidl1,
</span><span class="cx"> "tzid": "",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> }
</span><span class="cx"> output = StringIO()
</span><span class="cx"> calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -2021,7 +2021,7 @@
</span><span class="cx"> self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
</span><span class="cx">
</span><span class="cx"> # Re-scan after changes to make sure there are no errors
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx"> options["fix"] = False
</span><span class="cx"> options["uuid"] = CalVerifyMismatchTestsBase.uuidl1
</span><span class="cx"> calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -2430,7 +2430,7 @@
</span><span class="cx">
</span><span class="cx"> sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -2446,7 +2446,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": self.uuidl1,
</span><span class="cx"> "tzid": "utc",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> }
</span><span class="cx"> output = StringIO()
</span><span class="cx"> calverify = DoubleBookingService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -2592,7 +2592,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -2609,7 +2609,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": self.uuidl1,
</span><span class="cx"> "tzid": "utc",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> "no-organizer": False,
</span><span class="cx"> "invalid-organizer": False,
</span><span class="cx"> "disabled-organizer": False,
</span><span class="lines">@@ -2639,7 +2639,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -2656,7 +2656,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": self.uuidl1,
</span><span class="cx"> "tzid": "utc",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> "no-organizer": False,
</span><span class="cx"> "invalid-organizer": False,
</span><span class="cx"> "disabled-organizer": False,
</span><span class="lines">@@ -2678,7 +2678,7 @@
</span><span class="cx"> self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
</span><span class="cx">
</span><span class="cx"> # Re-scan after changes to make sure there are no errors
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx"> options["fix"] = False
</span><span class="cx"> options["uuid"] = self.uuidl1
</span><span class="cx"> calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -2698,7 +2698,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -2715,7 +2715,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": self.uuidl1,
</span><span class="cx"> "tzid": "utc",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> "no-organizer": True,
</span><span class="cx"> "invalid-organizer": False,
</span><span class="cx"> "disabled-organizer": False,
</span><span class="lines">@@ -2737,7 +2737,7 @@
</span><span class="cx"> self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
</span><span class="cx">
</span><span class="cx"> # Re-scan after changes to make sure there are no errors
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx"> options["fix"] = False
</span><span class="cx"> options["uuid"] = self.uuidl1
</span><span class="cx"> calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -2757,7 +2757,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -2774,7 +2774,7 @@
</span><span class="cx"> "uid": "",
</span><span class="cx"> "uuid": self.uuidl1,
</span><span class="cx"> "tzid": "utc",
</span><del>- "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
</del><ins>+ "start": DateTime(nowYear, 1, 1, 0, 0, 0),
</ins><span class="cx"> "no-organizer": True,
</span><span class="cx"> "invalid-organizer": True,
</span><span class="cx"> "disabled-organizer": True,
</span><span class="lines">@@ -2796,7 +2796,7 @@
</span><span class="cx"> self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
</span><span class="cx">
</span><span class="cx"> # Re-scan after changes to make sure there are no errors
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx"> options["fix"] = False
</span><span class="cx"> options["uuid"] = self.uuidl1
</span><span class="cx"> calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstesttest_gatewaypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_gateway.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_gateway.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_gateway.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -193,6 +193,7 @@
</span><span class="cx"> self.assertEquals(record.extras["zip"], "95014")
</span><span class="cx"> self.assertEquals(record.extras["country"], "USA")
</span><span class="cx"> self.assertEquals(record.extras["phone"], "(408) 555-1212")
</span><ins>+ self.assertEquals(record.extras["geo"], "geo:37.331,-122.030")
</ins><span class="cx">
</span><span class="cx"> results = yield self.runCommand(command_getLocationAttributes)
</span><span class="cx"> self.assertEquals(set(results["result"]["ReadProxies"]), set(['user03', 'user04']))
</span><span class="lines">@@ -424,6 +425,8 @@
</span><span class="cx"> <string>USA</string>
</span><span class="cx"> <key>Phone</key>
</span><span class="cx"> <string>(408) 555-1212</string>
</span><ins>+ <key>Geo</key>
+ <string>geo:37.331,-122.030</string>
</ins><span class="cx"> <key>ReadProxies</key>
</span><span class="cx"> <array>
</span><span class="cx"> <string>users:user03</string>
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstesttest_purgepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -21,8 +21,8 @@
</span><span class="cx"> from twistedcaldav.ical import Component
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx"> from txdav.common.datastore.test.util import populateCalendarsFrom
</span><span class="lines">@@ -33,11 +33,11 @@
</span><span class="cx"> import os
</span><span class="cx">
</span><span class="cx">
</span><del>-future = PyCalendarDateTime.getNowUTC()
</del><ins>+future = DateTime.getNowUTC()
</ins><span class="cx"> future.offsetDay(1)
</span><span class="cx"> future = future.getText()
</span><span class="cx">
</span><del>-past = PyCalendarDateTime.getNowUTC()
</del><ins>+past = DateTime.getNowUTC()
</ins><span class="cx"> past.offsetDay(-1)
</span><span class="cx"> past = past.getText()
</span><span class="cx">
</span><span class="lines">@@ -230,7 +230,7 @@
</span><span class="cx"> def test_cancelRepeating(self):
</span><span class="cx"> # A repeating event where purged CUA is organizer
</span><span class="cx"> event = Component.fromString(REPEATING_1_ICS_BEFORE)
</span><del>- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> "urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
</span><span class="cx"> self.assertEquals(action, PurgePrincipalService.CANCELEVENT_MODIFIED)
</span><span class="cx"> self.assertEquals(str(event), REPEATING_1_ICS_AFTER)
</span><span class="lines">@@ -239,7 +239,7 @@
</span><span class="cx"> def test_cancelAllDayRepeating(self):
</span><span class="cx"> # A repeating All Day event where purged CUA is organizer
</span><span class="cx"> event = Component.fromString(REPEATING_2_ICS_BEFORE)
</span><del>- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> "urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
</span><span class="cx"> self.assertEquals(action, PurgePrincipalService.CANCELEVENT_MODIFIED)
</span><span class="cx"> self.assertEquals(str(event), REPEATING_2_ICS_AFTER)
</span><span class="lines">@@ -248,7 +248,7 @@
</span><span class="cx"> def test_cancelFutureEvent(self):
</span><span class="cx"> # A future event
</span><span class="cx"> event = Component.fromString(FUTURE_EVENT_ICS)
</span><del>- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> "urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
</span><span class="cx"> self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
</span><span class="cx">
</span><span class="lines">@@ -256,7 +256,7 @@
</span><span class="cx"> def test_cancelNonMeeting(self):
</span><span class="cx"> # A repeating non-meeting event
</span><span class="cx"> event = Component.fromString(REPEATING_NON_MEETING_ICS)
</span><del>- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> "urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
</span><span class="cx"> self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
</span><span class="cx">
</span><span class="lines">@@ -264,7 +264,7 @@
</span><span class="cx"> def test_cancelAsAttendee(self):
</span><span class="cx"> # A repeating meeting event where purged CUA is an attendee
</span><span class="cx"> event = Component.fromString(REPEATING_ATTENDEE_MEETING_ICS)
</span><del>- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> "urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
</span><span class="cx"> self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
</span><span class="cx">
</span><span class="lines">@@ -273,7 +273,7 @@
</span><span class="cx"> # A repeating meeting occurrence with no master, where purged CUA is
</span><span class="cx"> # an attendee
</span><span class="cx"> event = Component.fromString(INVITED_TO_OCCURRENCE_ICS)
</span><del>- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> "urn:uuid:9DC04A71-E6DD-11DF-9492-0800200C9A66")
</span><span class="cx"> self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
</span><span class="cx">
</span><span class="lines">@@ -282,7 +282,7 @@
</span><span class="cx"> # Multiple meeting occurrences with no master, where purged CUA is
</span><span class="cx"> # an attendee
</span><span class="cx"> event = Component.fromString(INVITED_TO_MULTIPLE_OCCURRENCES_ICS)
</span><del>- action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ action = PurgePrincipalService._cancelEvent(event, DateTime(2010, 12, 6, 12, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> "urn:uuid:9DC04A71-E6DD-11DF-9492-0800200C9A66")
</span><span class="cx"> self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescalendarservertoolstesttest_purge_old_eventspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge_old_events.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge_old_events.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/calendarserver/tools/test/test_purge_old_events.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -21,8 +21,8 @@
</span><span class="cx"> from calendarserver.tools.purge import PurgeOldEventsService, PurgeAttachmentsService, \
</span><span class="cx"> PurgePrincipalService
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twext.enterprise.dal.syntax import Update, Delete
</span><span class="cx"> from twext.web2.http_headers import MimeType
</span><span class="lines">@@ -39,7 +39,7 @@
</span><span class="cx"> import os
</span><span class="cx">
</span><span class="cx">
</span><del>-now = PyCalendarDateTime.getToday().getYear()
</del><ins>+now = DateTime.getToday().getYear()
</ins><span class="cx">
</span><span class="cx"> OLD_ICS = """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="lines">@@ -443,7 +443,7 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_eventsOlderThan(self):
</span><del>- cutoff = PyCalendarDateTime(now, 4, 1, 0, 0, 0)
</del><ins>+ cutoff = DateTime(now, 4, 1, 0, 0, 0)
</ins><span class="cx"> txn = self._sqlCalendarStore.newTransaction()
</span><span class="cx">
</span><span class="cx"> # Query for all old events
</span><span class="lines">@@ -475,7 +475,7 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_removeOldEvents(self):
</span><del>- cutoff = PyCalendarDateTime(now, 4, 1, 0, 0, 0)
</del><ins>+ cutoff = DateTime(now, 4, 1, 0, 0, 0)
</ins><span class="cx"> txn = self._sqlCalendarStore.newTransaction()
</span><span class="cx">
</span><span class="cx"> # Remove oldest event - except we don't know what that is because of the dummy timestamps
</span><span class="lines">@@ -598,7 +598,7 @@
</span><span class="cx"> self.assertTrue(os.path.exists(mattachmentPath2))
</span><span class="cx">
</span><span class="cx"> # Delete all old events (including the event containing the attachment)
</span><del>- cutoff = PyCalendarDateTime(now, 4, 1, 0, 0, 0)
</del><ins>+ cutoff = DateTime(now, 4, 1, 0, 0, 0)
</ins><span class="cx"> count = (yield self.transactionUnderTest().removeOldEvents(cutoff))
</span><span class="cx">
</span><span class="cx"> # See which events have gone and which exist
</span><span class="lines">@@ -633,7 +633,7 @@
</span><span class="cx"> # Dry run
</span><span class="cx"> total = (yield PurgeOldEventsService.purgeOldEvents(
</span><span class="cx"> self._sqlCalendarStore,
</span><del>- PyCalendarDateTime(now, 4, 1, 0, 0, 0),
</del><ins>+ DateTime(now, 4, 1, 0, 0, 0),
</ins><span class="cx"> 2,
</span><span class="cx"> dryrun=True,
</span><span class="cx"> verbose=False
</span><span class="lines">@@ -643,7 +643,7 @@
</span><span class="cx"> # Actually remove
</span><span class="cx"> total = (yield PurgeOldEventsService.purgeOldEvents(
</span><span class="cx"> self._sqlCalendarStore,
</span><del>- PyCalendarDateTime(now, 4, 1, 0, 0, 0),
</del><ins>+ DateTime(now, 4, 1, 0, 0, 0),
</ins><span class="cx"> 2,
</span><span class="cx"> verbose=False
</span><span class="cx"> ))
</span><span class="lines">@@ -652,7 +652,7 @@
</span><span class="cx"> # There should be no more left
</span><span class="cx"> total = (yield PurgeOldEventsService.purgeOldEvents(
</span><span class="cx"> self._sqlCalendarStore,
</span><del>- PyCalendarDateTime(now, 4, 1, 0, 0, 0),
</del><ins>+ DateTime(now, 4, 1, 0, 0, 0),
</ins><span class="cx"> 2,
</span><span class="cx"> verbose=False
</span><span class="cx"> ))
</span><span class="lines">@@ -681,7 +681,7 @@
</span><span class="cx"> # Purge home1
</span><span class="cx"> total, ignored = (yield PurgePrincipalService.purgeUIDs(self._sqlCalendarStore, self.directory,
</span><span class="cx"> self.rootResource, ("home1",), verbose=False, proxies=False,
</span><del>- when=PyCalendarDateTime(now, 4, 1, 12, 0, 0, 0, PyCalendarTimezone(utc=True))))
</del><ins>+ when=DateTime(now, 4, 1, 12, 0, 0, 0, Timezone(utc=True))))
</ins><span class="cx">
</span><span class="cx"> # 4 items deleted: 3 events and 1 vcard
</span><span class="cx"> self.assertEquals(total, 4)
</span><span class="lines">@@ -768,7 +768,7 @@
</span><span class="cx"> # Remove old events first
</span><span class="cx"> total = (yield PurgeOldEventsService.purgeOldEvents(
</span><span class="cx"> self._sqlCalendarStore,
</span><del>- PyCalendarDateTime(now, 4, 1, 0, 0, 0),
</del><ins>+ DateTime(now, 4, 1, 0, 0, 0),
</ins><span class="cx"> 2,
</span><span class="cx"> verbose=False
</span><span class="cx"> ))
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfauthaccountstestpodxmlfromrev12016CalendarServertrunkconfauthaccountstestpodxml"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/accounts-test-pod.xml (from rev 12016, CalendarServer/trunk/conf/auth/accounts-test-pod.xml) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/accounts-test-pod.xml         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/accounts-test-pod.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+<?xml version="1.0" encoding="utf-8"?>
+
+<accounts realm="Test Realm">
+ <user>
+ <uid>admin</uid>
+ <guid>admin</guid>
+ <password>admin</password>
+ <name>Super User</name>
+ <first-name>Super</first-name>
+ <last-name>User</last-name>
+ </user>
+ <user repeat="101">
+ <uid>user%02d</uid>
+ <uid>User %02d</uid>
+ <guid>user%02d</guid>
+ <password>user%02d</password>
+ <name>User %02d</name>
+ <first-name>User</first-name>
+ <last-name>%02d</last-name>
+ <email-address>user%02d@example.com</email-address>
+ </user>
+ <user repeat="101">
+ <uid>puser%02d</uid>
+ <uid>Puser %02d</uid>
+ <guid>puser%02d</guid>
+ <password>puser%02d</password>
+ <name>Puser %02d</name>
+ <first-name>Puser</first-name>
+ <last-name>%02d</last-name>
+ <email-address>puser%02d@example.com</email-address>
+ </user>
+</accounts>
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfauthaugmentstestpodxmlfromrev12016CalendarServertrunkconfauthaugmentstestpodxml"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments-test-pod.xml (from rev 12016, CalendarServer/trunk/conf/auth/augments-test-pod.xml) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments-test-pod.xml         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments-test-pod.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,19 @@
</span><ins>+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE augments SYSTEM "augments.dtd">
+
+<augments>
+ <record>
+ <uid>Default</uid>
+ <enable>true</enable>
+ <server-id>A</server-id>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ </record>
+ <record repeat="101">
+ <uid>puser%02d</uid>
+ <enable>true</enable>
+ <server-id>B</server-id>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ </record>
+</augments>
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfauthaugmentsdtd"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments.dtd (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments.dtd        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/augments.dtd        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -19,7 +19,7 @@
</span><span class="cx"> <!ELEMENT record (
</span><span class="cx">                 uid,
</span><span class="cx">                 enable,
</span><del>-                 (server-id, partition-id?)?,
</del><ins>+                 server-id?,
</ins><span class="cx">                 enable-calendar?,
</span><span class="cx">                 enable-addressbook?,
</span><span class="cx">                 enable-login?,
</span><span class="lines">@@ -32,7 +32,6 @@
</span><span class="cx"> <!ELEMENT uid (#PCDATA)>
</span><span class="cx"> <!ELEMENT enable (#PCDATA)>
</span><span class="cx"> <!ELEMENT server-id (#PCDATA)>
</span><del>- <!ELEMENT partition-id (#PCDATA)>
</del><span class="cx"> <!ELEMENT enable-calendar (#PCDATA)>
</span><span class="cx"> <!ELEMENT enable-addressbook (#PCDATA)>
</span><span class="cx"> <!ELEMENT enable-login (#PCDATA)>
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfauthproxiestestpodxmlfromrev12016CalendarServertrunkconfauthproxiestestpodxml"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/proxies-test-pod.xml (from rev 12016, CalendarServer/trunk/conf/auth/proxies-test-pod.xml) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/proxies-test-pod.xml         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/proxies-test-pod.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,22 @@
</span><ins>+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2009-2013 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.
+ -->
+
+<!DOCTYPE proxies SYSTEM "proxies.dtd">
+
+<proxies>
+</proxies>
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfauthresourcestestpodxmlfromrev12016CalendarServertrunkconfauthresourcestestpodxml"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/resources-test-pod.xml (from rev 12016, CalendarServer/trunk/conf/auth/resources-test-pod.xml) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/resources-test-pod.xml         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/auth/resources-test-pod.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,4 @@
</span><ins>+<?xml version="1.0" encoding="utf-8"?>
+
+<accounts realm="Test Realm">
+</accounts>
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfcaldavdpartitioningprimaryplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-primary.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-primary.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-primary.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,85 +0,0 @@
</span><del>-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
- Copyright (c) 2006-2013 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.
- -->
-
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
- <dict>
-
- <!-- Servers -->
- <key>Servers</key>
- <dict>
-         <key>Enabled</key>
-         <true/>
-         <key>ConfigFile</key>
-         <string>localservers.xml</string>
-         <key>MaxClients</key>
-         <integer>5</integer>
-        </dict>
- <key>ServerPartitionID</key>
- <string>00001</string>
-
- <!-- PostgreSQL ProxyDB Service -->
- <key>ProxyDBService</key>
- <dict>
- <key>type</key>
- <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
-
- <key>params</key>
- <dict>
- <key>host</key>
- <string>localhost</string>
- <key>database</key>
- <string>proxies</string>
- </dict>
- </dict>
-
- <!-- Support for Memcached -->
- <key>Memcached</key>
- <dict>
-         <key>Pools</key>
-                <dict>
-                 <key>CommonToAllNodes</key>
-                 <dict>
-                 <key>ClientEnabled</key>
-                 <true/>
-                 <key>ServerEnabled</key>
-                 <true/>
-                 <key>BindAddress</key>
-                 <string>localhost</string>
-                 <key>Port</key>
-                 <integer>11311</integer>
-                 <key>HandleCacheTypes</key>
-                 <array>
-                 <string>ProxyDB</string>
-                 <string>PrincipalToken</string>
-                 <string>DIGESTCREDENTIALS</string>
-                 </array>
-                 </dict>
-                </dict>
- <key>MaxClients</key>
- <integer>5</integer>
- <key>memcached</key>
- <string>../memcached/_root/bin/memcached</string> <!-- Find in PATH -->
- <key>Options</key>
- <array>
- <!--<string>-vv</string>-->
- </array>
- </dict>
-
- </dict>
-</plist>
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfcaldavdpartitioningsecondaryplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-secondary.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-secondary.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-partitioning-secondary.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,85 +0,0 @@
</span><del>-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
- Copyright (c) 2006-2013 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.
- -->
-
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
- <dict>
-
- <!-- Servers -->
- <key>Servers</key>
- <dict>
-         <key>Enabled</key>
-         <true/>
-         <key>ConfigFile</key>
-         <string>localservers.xml</string>
-         <key>MaxClients</key>
-         <integer>5</integer>
-        </dict>
- <key>ServerPartitionID</key>
- <string>00002</string>
-
- <!-- PostgreSQL ProxyDB Service -->
- <key>ProxyDBService</key>
- <dict>
- <key>type</key>
- <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
-
- <key>params</key>
- <dict>
- <key>host</key>
- <string>localhost</string>
- <key>database</key>
- <string>proxies</string>
- </dict>
- </dict>
-
- <!-- Support for Memcached -->
- <key>Memcached</key>
- <dict>
-         <key>Pools</key>
-                <dict>
-                 <key>CommonToAllNodes</key>
-                 <dict>
-                 <key>ClientEnabled</key>
-                 <true/>
-                 <key>ServerEnabled</key>
-                 <false/>
-                 <key>BindAddress</key>
-                 <string>localhost</string>
-                 <key>Port</key>
-                 <integer>11311</integer>
-                 <key>HandleCacheTypes</key>
-                 <array>
-                 <string>ProxyDB</string>
-                 <string>PrincipalToken</string>
-                 <string>DIGESTCREDENTIALS</string>
-                 </array>
-                 </dict>
-                </dict>
- <key>MaxClients</key>
- <integer>5</integer>
- <key>memcached</key>
- <string>../memcached/_root/bin/memcached</string> <!-- Find in PATH -->
- <key>Options</key>
- <array>
- <!--<string>-vv</string>-->
- </array>
- </dict>
-
- </dict>
-</plist>
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfcaldavdtestpodAplistfromrev12016CalendarServertrunkconfcaldavdtestpodAplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podA.plist (from rev 12016, CalendarServer/trunk/conf/caldavd-test-podA.plist) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podA.plist         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podA.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,158 @@
</span><ins>+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2006-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.
+ -->
+
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+
+ <!-- Import a parent config before this one -->
+ <key>ImportConfig</key>
+ <string>./conf/caldavd-test.plist</string>
+
+ <!-- HTTP port [0 = disable HTTP] -->
+ <key>HTTPPort</key>
+ <integer>8008</integer>
+
+ <!-- SSL port [0 = disable HTTPS] -->
+ <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
+ <key>SSLPort</key>
+ <integer>8443</integer>
+
+ <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
+ <key>BindHTTPPorts</key>
+ <array>
+ </array>
+
+ <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
+ <key>BindSSLPorts</key>
+ <array>
+ </array>
+
+ <!-- Server root -->
+ <key>ServerRoot</key>
+ <string>./data/podA</string>
+
+ <!-- Configuration root -->
+ <key>ConfigRoot</key>
+ <string>./conf</string>
+
+ <!-- XML File Directory Service -->
+ <key>DirectoryService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>./conf/auth/accounts-test-pod.xml</string>
+ </dict>
+ </dict>
+
+ <!-- Resource and Location Service -->
+ <key>ResourceService</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>./conf/auth/resources-test-pod.xml</string>
+ </dict>
+ </dict>
+
+ <!-- XML File Augment Service -->
+ <key>AugmentService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFiles</key>
+ <array>
+         <string>./conf/auth/augments-test-pod.xml</string>
+ </array>
+ </dict>
+ </dict>
+
+ <key>ProxyLoadFromFile</key>
+ <string>./conf/auth/proxies-test-pod.xml</string>
+
+ <!-- Servers -->
+ <key>Servers</key>
+ <dict>
+         <key>Enabled</key>
+         <true/>
+         <key>ConfigFile</key>
+         <string>./conf/localservers-test.xml</string>
+         <key>MaxClients</key>
+         <integer>5</integer>
+         <key>InboxName</key>
+         <string>podding</string>
+        </dict>
+
+ <!-- Support for Memcached -->
+ <key>Memcached</key>
+ <dict>
+         <key>Pools</key>
+                <dict>
+                 <key>Default</key>
+                 <dict>
+                 <key>ClientEnabled</key>
+                 <true/>
+                 <key>ServerEnabled</key>
+                 <true/>
+                 <key>BindAddress</key>
+                 <string>localhost</string>
+                 <key>Port</key>
+                 <integer>11211</integer>
+                 </dict>
+                 <key>ProxyDB</key>
+                 <dict>
+                 <key>ClientEnabled</key>
+                 <true/>
+                 <key>ServerEnabled</key>
+                 <true/>
+                 <key>BindAddress</key>
+                 <string>localhost</string>
+                 <key>Port</key>
+                 <integer>11311</integer>
+                 <key>HandleCacheTypes</key>
+                 <array>
+                 <string>ProxyDB</string>
+                 <string>PrincipalToken</string>
+                 <string>DIGESTCREDENTIALS</string>
+                 </array>
+                 </dict>
+                </dict>
+ <key>MaxClients</key>
+ <integer>5</integer>
+ <key>memcached</key>
+ <string>../memcached/_root/bin/memcached</string> <!-- Find in PATH -->
+ <key>Options</key>
+ <array>
+ <!--<string>-vv</string>-->
+ </array>
+ </dict>
+
+ </dict>
+</plist>
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfcaldavdtestpodBplistfromrev12016CalendarServertrunkconfcaldavdtestpodBplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podB.plist (from rev 12016, CalendarServer/trunk/conf/caldavd-test-podB.plist) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podB.plist         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test-podB.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,158 @@
</span><ins>+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2006-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.
+ -->
+
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+
+ <!-- Import a parent config before this one -->
+ <key>ImportConfig</key>
+ <string>./conf/caldavd-test.plist</string>
+
+ <!-- HTTP port [0 = disable HTTP] -->
+ <key>HTTPPort</key>
+ <integer>8108</integer>
+
+ <!-- SSL port [0 = disable HTTPS] -->
+ <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
+ <key>SSLPort</key>
+ <integer>8543</integer>
+
+ <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
+ <key>BindHTTPPorts</key>
+ <array>
+ </array>
+
+ <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
+ <key>BindSSLPorts</key>
+ <array>
+ </array>
+
+ <!-- Server root -->
+ <key>ServerRoot</key>
+ <string>./data/podB</string>
+
+ <!-- Configuration root -->
+ <key>ConfigRoot</key>
+ <string>./conf</string>
+
+ <!-- XML File Directory Service -->
+ <key>DirectoryService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>./conf/auth/accounts-test-pod.xml</string>
+ </dict>
+ </dict>
+
+ <!-- Resource and Location Service -->
+ <key>ResourceService</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>./conf/auth/resources-test-pod.xml</string>
+ </dict>
+ </dict>
+
+ <!-- XML File Augment Service -->
+ <key>AugmentService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFiles</key>
+ <array>
+         <string>./conf/auth/augments-test-pod.xml</string>
+ </array>
+ </dict>
+ </dict>
+
+ <key>ProxyLoadFromFile</key>
+ <string>./conf/auth/proxies-test-pod.xml</string>
+
+ <!-- Servers -->
+ <key>Servers</key>
+ <dict>
+         <key>Enabled</key>
+         <true/>
+         <key>ConfigFile</key>
+         <string>./conf/localservers-test.xml</string>
+         <key>MaxClients</key>
+         <integer>5</integer>
+         <key>InboxName</key>
+         <string>podding</string>
+        </dict>
+
+ <!-- Support for Memcached -->
+ <key>Memcached</key>
+ <dict>
+         <key>Pools</key>
+                <dict>
+                 <key>Default</key>
+                 <dict>
+                 <key>ClientEnabled</key>
+                 <true/>
+                 <key>ServerEnabled</key>
+                 <true/>
+                 <key>BindAddress</key>
+                 <string>localhost</string>
+                 <key>Port</key>
+                 <integer>11411</integer>
+                 </dict>
+                 <key>ProxyDB</key>
+                 <dict>
+                 <key>ClientEnabled</key>
+                 <true/>
+                 <key>ServerEnabled</key>
+                 <true/>
+                 <key>BindAddress</key>
+                 <string>localhost</string>
+                 <key>Port</key>
+                 <integer>11311</integer>
+                 <key>HandleCacheTypes</key>
+                 <array>
+                 <string>ProxyDB</string>
+                 <string>PrincipalToken</string>
+                 <string>DIGESTCREDENTIALS</string>
+                 </array>
+                 </dict>
+                </dict>
+ <key>MaxClients</key>
+ <integer>5</integer>
+ <key>memcached</key>
+ <string>../memcached/_root/bin/memcached</string> <!-- Find in PATH -->
+ <key>Options</key>
+ <array>
+ <!--<string>-vv</string>-->
+ </array>
+ </dict>
+
+ </dict>
+</plist>
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfcaldavdtestplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/caldavd-test.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -895,9 +895,10 @@
</span><span class="cx"> <key>EnableWebAdmin</key>
</span><span class="cx"> <true/>
</span><span class="cx">
</span><del>- <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
</del><ins>+ <!-- Support for Content-Encoding compression -->
</ins><span class="cx"> <key>ResponseCompression</key>
</span><del>- <false/>
</del><ins>+ <false/> <!-- Off for testing, as debugging is easier that way. -->
+
</ins><span class="cx">
</span><span class="cx"> <!-- The retry-after value (in seconds) to return with a 503 error. -->
</span><span class="cx"> <key>HTTPRetryAfter</key>
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconflocalserverstestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers-test.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers-test.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers-test.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,21 +16,19 @@
</span><span class="cx"> limitations under the License.
</span><span class="cx"> -->
</span><span class="cx">
</span><del>-<!DOCTYPE servers SYSTEM "servers.dtd">
</del><ins>+<!DOCTYPE servers SYSTEM "localservers.dtd">
</ins><span class="cx">
</span><span class="cx"> <servers>
</span><span class="cx"> <server>
</span><del>- <id>00001</id>
</del><ins>+ <id>A</id>
</ins><span class="cx"> <uri>http://localhost:8008</uri>
</span><del>- <partitions>
-         <partition>
-                 <id>00001</id>
-                 <uri>http://localhost:8008</uri>
-         </partition>
-         <partition>
-                 <id>00002</id>
-                 <uri>http://localhost:8108</uri>
-         </partition>
- </partitions>
</del><ins>+ <allowed-from>localhost</allowed-from>
+ <shared-secret>A</shared-secret>
</ins><span class="cx"> </server>
</span><ins>+ <server>
+ <id>B</id>
+ <uri>http://localhost:8108</uri>
+ <allowed-from>localhost</allowed-from>
+ <shared-secret>B</shared-secret>
+ </server>
</ins><span class="cx"> </servers>
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconflocalserversdtdfromrev12016CalendarServertrunkconflocalserversdtd"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.dtd (from rev 12016, CalendarServer/trunk/conf/localservers.dtd) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.dtd         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.dtd        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+<!--
+Copyright (c) 2011-2013 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.
+-->
+
+<!ELEMENT servers (server*) >
+
+        <!ELEMENT server (id, uri, allowed-from*, shared-secret?) >
+                <!ATTLIST server implicit (yes|no) "yes">
+
+                <!ELEMENT id (#PCDATA) >
+                <!ELEMENT uri (#PCDATA) >
+                <!ELEMENT allowed-from (#PCDATA) >
+                <!ELEMENT shared-secret (#PCDATA) >
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconflocalserversxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/localservers.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx"> limitations under the License.
</span><span class="cx"> -->
</span><span class="cx">
</span><del>-<!DOCTYPE servers SYSTEM "servers.dtd">
</del><ins>+<!DOCTYPE servers SYSTEM "localservers.dtd">
</ins><span class="cx">
</span><span class="cx"> <servers>
</span><span class="cx"> <!--
</span><span class="lines">@@ -30,16 +30,6 @@
</span><span class="cx"> <allowed-from>127.0.0.1</allowed-from>
</span><span class="cx"> <allowed-from>example.local</allowed-from>
</span><span class="cx"> <shared-secret>ABC</shared-secret>
</span><del>- <partitions>
-         <partition>
-                 <id>00001</id>
-                 <url>https://machine1.example.com:8443</url>
-         </partition>
-         <partition>
-                 <id>00002</id>
-                 <url>https://machine2.example.com:8443</url>
-         </partition>
- </partitions>
</del><span class="cx"> </server>
</span><span class="cx"> -->
</span><span class="cx"> </servers>
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfremoteserverstestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers-test.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers-test.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers-test.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx"> limitations under the License.
</span><span class="cx"> -->
</span><span class="cx">
</span><del>-<!DOCTYPE servers SYSTEM "servertoserver.dtd">
</del><ins>+<!DOCTYPE servers SYSTEM "remoteservers.dtd">
</ins><span class="cx">
</span><span class="cx"> <servers>
</span><span class="cx"> <server>
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfremoteserversdtdfromrev12016CalendarServertrunkconfremoteserversdtd"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.dtd (from rev 12016, CalendarServer/trunk/conf/remoteservers.dtd) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.dtd         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.dtd        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+<!--
+Copyright (c) 2006-2013 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.
+-->
+
+<!ELEMENT servers (server*) >
+
+        <!ELEMENT server (uri, authentication?, allow-requests-from, allow-requests-to, domains?, hosts?) >
+
+                <!ELEMENT uri (#PCDATA) >
+                <!ELEMENT authentication (user, password) >
+                 <!ATTLIST authentication type (basic) "">
+                 <!ELEMENT user (#PCDATA) >
+                 <!ELEMENT password (#PCDATA) >
+
+                <!ELEMENT allow-requests-from EMPTY >
+                <!ELEMENT allow-requests-to EMPTY >
+                <!ELEMENT domains (domain*) >
+                        <!ELEMENT domain (#PCDATA) >
+                <!ELEMENT hosts (host*) >
+                        <!ELEMENT host (#PCDATA) >
+                        
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfremoteserversxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/remoteservers.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx"> limitations under the License.
</span><span class="cx"> -->
</span><span class="cx">
</span><del>-<!DOCTYPE servers SYSTEM "servertoserver.dtd">
</del><ins>+<!DOCTYPE servers SYSTEM "remoteservers.dtd">
</ins><span class="cx">
</span><span class="cx"> <servers>
</span><span class="cx"> <!--
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfresourcescaldavdresourcesplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/resources/caldavd-resources.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/resources/caldavd-resources.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/resources/caldavd-resources.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -174,7 +174,6 @@
</span><span class="cx"> Augments for the directory service records to add calendar specific attributes.
</span><span class="cx">
</span><span class="cx"> A variety of augment services are available for use.
</span><del>- When using a partitioned server, a service that can be accessed from each host will be needed.
</del><span class="cx"> -->
</span><span class="cx">
</span><span class="cx"> <!-- XML File Augment Service -->
</span><span class="lines">@@ -670,7 +669,7 @@
</span><span class="cx"> <key>EnableWebAdmin</key>
</span><span class="cx"> <true/>
</span><span class="cx">
</span><del>- <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
</del><ins>+ <!-- Support for Content-Encoding compression -->
</ins><span class="cx"> <key>ResponseCompression</key>
</span><span class="cx"> <false/>
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfserversdtd"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servers.dtd (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servers.dtd        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servers.dtd        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,28 +0,0 @@
</span><del>-<!--
-Copyright (c) 2011-2013 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.
--->
-
-<!ELEMENT servers (server*) >
-
-        <!ELEMENT server (id, uri, allowed-from*, shared-secret?, partitions?) >
-                <!ATTLIST server implicit (yes|no) "yes">
-
-                <!ELEMENT id (#PCDATA) >
-                <!ELEMENT uri (#PCDATA) >
-                <!ELEMENT allowed-from (#PCDATA) >
-                <!ELEMENT shared-secret (#PCDATA) >
-
-                <!ELEMENT partitions (partition*) >
-                        <!ELEMENT partition (id, uri) >
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfservertoserverdtd"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servertoserver.dtd (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servertoserver.dtd        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/servertoserver.dtd        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,33 +0,0 @@
</span><del>-<!--
-Copyright (c) 2006-2013 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.
--->
-
-<!ELEMENT servers (server*) >
-
-        <!ELEMENT server (uri, authentication?, allow-requests-from, allow-requests-to, domains?, hosts?) >
-
-                <!ELEMENT uri (#PCDATA) >
-                <!ELEMENT authentication (user, password) >
-                 <!ATTLIST authentication type (basic) "">
-                 <!ELEMENT user (#PCDATA) >
-                 <!ELEMENT password (#PCDATA) >
-
-                <!ELEMENT allow-requests-from EMPTY >
-                <!ELEMENT allow-requests-to EMPTY >
-                <!ELEMENT domains (domain*) >
-                        <!ELEMENT domain (#PCDATA) >
-                <!ELEMENT hosts (host*) >
-                        <!ELEMENT host (#PCDATA) >
-                        
</del><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixesconfsudoersplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/conf/sudoers.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/conf/sudoers.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/conf/sudoers.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,42 +0,0 @@
</span><del>-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-<key>users</key>
-<array>
-<!-- Sudo user definitions -->
-<!-- With the exception of username and password none of the following
- elements are used in the current implementation. -->
-<!--
- <dict>
- <key>authorize-as</key>
- <dict>
- <key>allow</key>
- <true/>
- <key>principals</key>
- <array>
-        <string>all</string>
- <string>/principals/user/wsanchez</string>
- </array>
- </dict>
- <key>authorize-from</key>
- <array>
- <string>127.0.0.1</string>
- </array>
-
- <key>username</key>
- <string></string>
-
- <key>password</key>
- <string></string>
- </dict>
--->
- <dict>
- <key>username</key>
- <string>superuser</string>
- <key>password</key>
- <string>superuser</string>
- </dict>
-</array>
-</dict>
-</plist>
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformanceloadtesticalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/ical.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/ical.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/ical.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -29,9 +29,9 @@
</span><span class="cx"> from contrib.performance.httpclient import StringProducer, readBody
</span><span class="cx"> from contrib.performance.loadtest.subscribe import Periodical
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twext.internet.adaptendpoint import connect
</span><span class="cx"> from twext.internet.gaiendpoint import GAIEndpoint
</span><span class="lines">@@ -1199,7 +1199,7 @@
</span><span class="cx"> connect(GAIEndpoint(self.reactor, host, port), factory)
</span><span class="cx">
</span><span class="cx">
</span><del>- def _receivedPush(self, inboundID, dataChangedTimestamp):
</del><ins>+ def _receivedPush(self, inboundID, dataChangedTimestamp, priority=5):
</ins><span class="cx"> for href, id in self.ampPushKeys.iteritems():
</span><span class="cx"> if inboundID == id:
</span><span class="cx"> self._checkCalendarsForEvents(href, push=True)
</span><span class="lines">@@ -1609,13 +1609,13 @@
</span><span class="cx"> msg("Availability request spanning multiple days (%r to %r), "
</span><span class="cx"> "dropping the end date." % (start, end))
</span><span class="cx">
</span><del>- start.setTimezone(PyCalendarTimezone(utc=True))
</del><ins>+ start.setTimezone(Timezone(utc=True))
</ins><span class="cx"> start.setHHMMSS(0, 0, 0)
</span><del>- end = start + PyCalendarDuration(hours=24)
</del><ins>+ end = start + Duration(hours=24)
</ins><span class="cx">
</span><span class="cx"> start = start.getText()
</span><span class="cx"> end = end.getText()
</span><del>- now = PyCalendarDateTime.getNowUTC().getText()
</del><ins>+ now = DateTime.getNowUTC().getText()
</ins><span class="cx">
</span><span class="cx"> label_suffix = "small"
</span><span class="cx"> if len(users) > 5:
</span><span class="lines">@@ -1919,7 +1919,7 @@
</span><span class="cx"> # the sim can fire a PUT between the PROPFIND and when process the removals.
</span><span class="cx"> old_hrefs = set([calendar.url + child for child in calendar.events.keys()])
</span><span class="cx">
</span><del>- now = PyCalendarDateTime.getNowUTC()
</del><ins>+ now = DateTime.getNowUTC()
</ins><span class="cx"> now.setDateOnly(True)
</span><span class="cx"> now.offsetMonth(-1) # 1 month back default
</span><span class="cx"> result = yield self._report(
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformanceloadtestprofilespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/profiles.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/profiles.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/profiles.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -42,8 +42,8 @@
</span><span class="cx"> from contrib.performance.loadtest.logger import SummarizingMixin
</span><span class="cx"> from contrib.performance.loadtest.ical import IncorrectResponseCode
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
</ins><span class="cx">
</span><span class="cx"> class ProfileBase(object):
</span><span class="cx"> """
</span><span class="lines">@@ -407,9 +407,9 @@
</span><span class="cx"> vevent = vcalendar.mainComponent()
</span><span class="cx"> uid = str(uuid4())
</span><span class="cx"> dtstart = self._eventStartDistribution.sample()
</span><del>- dtend = dtstart + PyCalendarDuration(seconds=self._eventDurationDistribution.sample())
- vevent.replaceProperty(Property("CREATED", PyCalendarDateTime.getNowUTC()))
- vevent.replaceProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
</del><ins>+ dtend = dtstart + Duration(seconds=self._eventDurationDistribution.sample())
+ vevent.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
+ vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
</ins><span class="cx"> vevent.replaceProperty(Property("DTSTART", dtstart))
</span><span class="cx"> vevent.replaceProperty(Property("DTEND", dtend))
</span><span class="cx"> vevent.replaceProperty(Property("UID", uid))
</span><span class="lines">@@ -650,9 +650,9 @@
</span><span class="cx"> vevent = vcalendar.mainComponent()
</span><span class="cx"> uid = str(uuid4())
</span><span class="cx"> dtstart = self._eventStartDistribution.sample()
</span><del>- dtend = dtstart + PyCalendarDuration(seconds=self._eventDurationDistribution.sample())
- vevent.replaceProperty(Property("CREATED", PyCalendarDateTime.getNowUTC()))
- vevent.replaceProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
</del><ins>+ dtend = dtstart + Duration(seconds=self._eventDurationDistribution.sample())
+ vevent.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
+ vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
</ins><span class="cx"> vevent.replaceProperty(Property("DTSTART", dtstart))
</span><span class="cx"> vevent.replaceProperty(Property("DTEND", dtend))
</span><span class="cx"> vevent.replaceProperty(Property("UID", uid))
</span><span class="lines">@@ -719,8 +719,8 @@
</span><span class="cx"> vtodo = vcalendar.mainComponent()
</span><span class="cx"> uid = str(uuid4())
</span><span class="cx"> due = self._taskStartDistribution.sample()
</span><del>- vtodo.replaceProperty(Property("CREATED", PyCalendarDateTime.getNowUTC()))
- vtodo.replaceProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
</del><ins>+ vtodo.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
+ vtodo.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
</ins><span class="cx"> vtodo.replaceProperty(Property("DUE", due))
</span><span class="cx"> vtodo.replaceProperty(Property("UID", uid))
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformanceloadtestsimpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/sim.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/sim.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/sim.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -346,6 +346,7 @@
</span><span class="cx"> if 'serverStats' in config:
</span><span class="cx"> if config['serverStats']['enabled']:
</span><span class="cx"> serverStats = config['serverStats']
</span><ins>+ serverStats['server'] = config['server'] if 'server' in config else ''
</ins><span class="cx">
</span><span class="cx"> observers = []
</span><span class="cx"> if 'observers' in config:
</span><span class="lines">@@ -483,7 +484,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> if self.serverStats is not None:
</span><del>- _ignore_scheme, hostname, _ignore_path, _ignore_query, _ignore_fragment = urlsplit(self.server)
</del><ins>+ _ignore_scheme, hostname, _ignore_path, _ignore_query, _ignore_fragment = urlsplit(self.serverStats["server"])
</ins><span class="cx"> data = self.readStatsSock((hostname.split(":")[0], self.serverStats["Port"],), True)
</span><span class="cx"> if "Failed" not in data:
</span><span class="cx"> data = data["5 Minutes"]
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformanceloadtesttest_icalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/test_ical.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/test_ical.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/loadtest/test_ical.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -24,8 +24,8 @@
</span><span class="cx"> from contrib.performance.loadtest.ical import XMPPPush, Event, Calendar, OS_X_10_6
</span><span class="cx"> from contrib.performance.loadtest.sim import _DirectoryRecord
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twisted.internet.defer import Deferred, inlineCallbacks, returnValue
</span><span class="cx"> from twisted.internet.protocol import ProtocolToConsumerAdapter
</span><span class="lines">@@ -1957,8 +1957,8 @@
</span><span class="cx"> self.client.outbox = "/calendars/__uids__/%s/outbox/" % (self.record.uid,)
</span><span class="cx"> requests = self.interceptRequests()
</span><span class="cx">
</span><del>- start = PyCalendarDateTime(2011, 6, 10, 10, 45, 0, tzid=PyCalendarTimezone(utc=True))
- end = PyCalendarDateTime(2011, 6, 10, 11, 15, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ start = DateTime(2011, 6, 10, 10, 45, 0, tzid=Timezone(utc=True))
+ end = DateTime(2011, 6, 10, 11, 15, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> d = self.client.requestAvailability(
</span><span class="cx"> start, end, [u"urn:uuid:user05", u"urn:uuid:user10"])
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformancesqlusagerequestsinvitepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/invite.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/invite.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/invite.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx">
</span><span class="cx"> from caldavclientlibrary.protocol.url import URL
</span><span class="cx"> from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx"> from twext.web2.dav.util import joinURL
</span><span class="cx"> from caldavclientlibrary.protocol.webdav.definitions import davxml
</span><span class="cx">
</span><span class="lines">@@ -67,7 +67,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Invite as user02
</span><del>- now = PyCalendarDateTime.getNowUTC()
</del><ins>+ now = DateTime.getNowUTC()
</ins><span class="cx"> href = joinURL(self.sessions[1].calendarHref, "organizer.ics")
</span><span class="cx"> self.sessions[1].writeData(URL(path=href), ICAL % (now.getYear() + 1,), "text/calendar")
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformancesqlusagerequestsputpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/put.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/put.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/put.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx">
</span><span class="cx"> from caldavclientlibrary.protocol.url import URL
</span><span class="cx"> from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx"> from twext.web2.dav.util import joinURL
</span><span class="cx">
</span><span class="cx"> ICAL = """BEGIN:VCALENDAR
</span><span class="lines">@@ -62,7 +62,7 @@
</span><span class="cx"> Execute the actual HTTP request.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- now = PyCalendarDateTime.getNowUTC()
</del><ins>+ now = DateTime.getNowUTC()
</ins><span class="cx"> href = joinURL(self.sessions[0].calendarHref, "put.ics")
</span><span class="cx"> self.sessions[0].writeData(URL(path=href), ICAL % (now.getYear() + 1,), "text/calendar")
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformancesqlusagerequestsquerypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/query.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/query.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/query.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,7 +18,7 @@
</span><span class="cx"> from caldavclientlibrary.protocol.webdav.definitions import davxml, statuscodes
</span><span class="cx"> from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
</span><span class="cx"> from twext.web2.dav.util import joinURL
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx"> from caldavclientlibrary.protocol.caldav.query import QueryVEVENTTimeRange
</span><span class="cx"> from caldavclientlibrary.protocol.http.data.string import ResponseDataString
</span><span class="cx">
</span><span class="lines">@@ -70,7 +70,7 @@
</span><span class="cx"> Do some setup prior to the real request.
</span><span class="cx"> """
</span><span class="cx"> # Add resources to create required number of changes
</span><del>- self.start = PyCalendarDateTime.getNowUTC()
</del><ins>+ self.start = DateTime.getNowUTC()
</ins><span class="cx"> self.start.setHHMMSS(12, 0, 0)
</span><span class="cx"> self.end = self.start.duplicate()
</span><span class="cx"> self.end.offsetHours(1)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformancesqlusagerequestssyncpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/sync.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/sync.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/requests/sync.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,7 +18,7 @@
</span><span class="cx"> from caldavclientlibrary.protocol.webdav.definitions import davxml
</span><span class="cx"> from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
</span><span class="cx"> from twext.web2.dav.util import joinURL
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> ICAL = """BEGIN:VCALENDAR
</span><span class="cx"> CALSCALE:GREGORIAN
</span><span class="lines">@@ -75,7 +75,7 @@
</span><span class="cx"> self.synctoken = results[davxml.sync_token]
</span><span class="cx">
</span><span class="cx"> # Add resources to create required number of changes
</span><del>- now = PyCalendarDateTime.getNowUTC()
</del><ins>+ now = DateTime.getNowUTC()
</ins><span class="cx"> for i in range(self.count):
</span><span class="cx"> href = joinURL(self.sessions[0].calendarHref, "sync-collection-%d.ics" % (i + 1,))
</span><span class="cx"> self.sessions[0].writeData(URL(path=href), ICAL % (now.getYear() + 1, i + 1,), "text/calendar")
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformancesqlusagesqlusagepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/sqlusage.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/sqlusage.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/sqlusage/sqlusage.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -27,7 +27,7 @@
</span><span class="cx"> from contrib.performance.sqlusage.requests.put import PutTest
</span><span class="cx"> from contrib.performance.sqlusage.requests.query import QueryTest
</span><span class="cx"> from contrib.performance.sqlusage.requests.sync import SyncTest
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx"> from twext.web2.dav.util import joinURL
</span><span class="cx"> import getopt
</span><span class="cx"> import itertools
</span><span class="lines">@@ -183,7 +183,7 @@
</span><span class="cx"> @param n: number of events
</span><span class="cx"> @type n: C{int}
</span><span class="cx"> """
</span><del>- now = PyCalendarDateTime.getNowUTC()
</del><ins>+ now = DateTime.getNowUTC()
</ins><span class="cx"> for i in range(n - self.currentCount):
</span><span class="cx"> index = self.currentCount + i + 1
</span><span class="cx"> href = joinURL(calendarhref, "%d.ics" % (index,))
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformancestatspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/stats.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/stats.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/stats.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,10 +16,10 @@
</span><span class="cx">
</span><span class="cx"> from __future__ import print_function
</span><span class="cx"> from math import log, sqrt
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.property import PyCalendarProperty
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration as PyDuration
+from pycalendar.icalendar.property import Property
+from pycalendar.timezone import Timezone
</ins><span class="cx"> from twisted.python.util import FancyEqMixin
</span><span class="cx"> from zope.interface import Interface, implements
</span><span class="cx"> import random
</span><span class="lines">@@ -338,7 +338,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def sample(self):
</span><del>- now = PyCalendarDateTime.getNowUTC()
</del><ins>+ now = DateTime.getNowUTC()
</ins><span class="cx"> now.offsetSeconds(int(self._offset.sample()))
</span><span class="cx"> return now
</span><span class="cx">
</span><span class="lines">@@ -390,7 +390,7 @@
</span><span class="cx"> 60 * 60 * 8 * 6,
</span><span class="cx"> # Standard deviation of 4 workdays
</span><span class="cx"> 60 * 60 * 8 * 4)
</span><del>- self.now = PyCalendarDateTime.getNow
</del><ins>+ self.now = DateTime.getNow
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def astimestamp(self, dt):
</span><span class="lines">@@ -406,7 +406,7 @@
</span><span class="cx"> # Find a workday that follows the timestamp
</span><span class="cx"> weekday = when.getDayOfWeek()
</span><span class="cx"> for i in range(NUM_WEEKDAYS):
</span><del>- day = when + PyCalendarDuration(days=i)
</del><ins>+ day = when + PyDuration(days=i)
</ins><span class="cx"> if (weekday + i) % NUM_WEEKDAYS in self._daysOfWeek:
</span><span class="cx"> # Joy, a day on which work might occur. Find the first hour on
</span><span class="cx"> # this day when work may start.
</span><span class="lines">@@ -419,8 +419,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def sample(self):
</span><del>- offset = PyCalendarDuration(seconds=int(self._helperDistribution.sample()))
- beginning = self.now(PyCalendarTimezone(tzid=self._tzname))
</del><ins>+ offset = PyDuration(seconds=int(self._helperDistribution.sample()))
+ beginning = self.now(Timezone(tzid=self._tzname))
</ins><span class="cx"> while offset:
</span><span class="cx"> start, end = self._findWorkAfter(beginning)
</span><span class="cx"> if end - start > offset:
</span><span class="lines">@@ -463,8 +463,7 @@
</span><span class="cx"> index = self._helperDistribution.sample()
</span><span class="cx"> rrule = self._rrules[index]
</span><span class="cx"> if rrule:
</span><del>- prop = PyCalendarProperty()
- prop.parse(rrule)
</del><ins>+ prop = Property.parseText(rrule)
</ins><span class="cx"> return prop
</span><span class="cx">
</span><span class="cx"> return None
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribperformancetest_statspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/test_stats.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/test_stats.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/performance/test_stats.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -20,8 +20,8 @@
</span><span class="cx"> SQLDuration, LogNormalDistribution, UniformDiscreteDistribution,
</span><span class="cx"> UniformIntegerDistribution, WorkDistribution, quantize,
</span><span class="cx"> RecurrenceDistribution)
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> class SQLDurationTests(TestCase):
</span><span class="cx"> def setUp(self):
</span><span class="lines">@@ -90,21 +90,21 @@
</span><span class="cx"> tzname = "US/Eastern"
</span><span class="cx"> dist = WorkDistribution(["mon", "wed", "thu", "sat"], 10, 20, tzname)
</span><span class="cx"> dist._helperDistribution = UniformDiscreteDistribution([35 * 60 * 60 + 30 * 60])
</span><del>- dist.now = lambda tzname = None: PyCalendarDateTime(2011, 5, 29, 18, 5, 36, tzid=tzname)
</del><ins>+ dist.now = lambda tzname = None: DateTime(2011, 5, 29, 18, 5, 36, tzid=tzname)
</ins><span class="cx"> value = dist.sample()
</span><span class="cx"> self.assertEqual(
</span><span class="cx"> # Move past three workdays - monday, wednesday, thursday - using 30
</span><span class="cx"> # of the hours, and then five and a half hours into the fourth
</span><span class="cx"> # workday, saturday. Workday starts at 10am, so the sample value
</span><span class="cx"> # is 3:30pm, ie 1530 hours.
</span><del>- PyCalendarDateTime(2011, 6, 4, 15, 30, 0, tzid=PyCalendarTimezone(tzid=tzname)),
</del><ins>+ DateTime(2011, 6, 4, 15, 30, 0, tzid=Timezone(tzid=tzname)),
</ins><span class="cx"> value
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> dist = WorkDistribution(["mon", "tue", "wed", "thu", "fri"], 10, 20, tzname)
</span><span class="cx"> dist._helperDistribution = UniformDiscreteDistribution([35 * 60 * 60 + 30 * 60])
</span><span class="cx"> value = dist.sample()
</span><del>- self.assertTrue(isinstance(value, PyCalendarDateTime))
</del><ins>+ self.assertTrue(isinstance(value, DateTime))
</ins><span class="cx">
</span><span class="cx"> # twisted.trial.unittest.FailTest: not equal:
</span><span class="cx"> # a = datetime.datetime(2011, 6, 4, 15, 30, tzinfo=<DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribtoolsrequest_monitorpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/request_monitor.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/request_monitor.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/request_monitor.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -303,8 +303,6 @@
</span><span class="cx"> print("--procs N specifies how many python processes are expected in the log file (default: 80)")
</span><span class="cx"> print("--top N how many long requests to print (default: 10)")
</span><span class="cx"> print("--users N how many top users to print (default: 5)")
</span><del>- print("--router analyze a partition server router node")
- print("--worker analyze a partition server worker node")
</del><span class="cx"> print("")
</span><span class="cx"> print("Version: 5")
</span><span class="cx">
</span><span class="lines">@@ -313,19 +311,13 @@
</span><span class="cx"> numTop = 10
</span><span class="cx"> numUsers = 5
</span><span class="cx"> lineRange = None
</span><del>-router = False
-worker = False
-options, args = getopt.getopt(sys.argv[1:], "h", ["debug", "router", "worker", "lines=", "range=", "procs=", "top=", "users="])
</del><ins>+options, args = getopt.getopt(sys.argv[1:], "h", ["debug", "lines=", "range=", "procs=", "top=", "users="])
</ins><span class="cx"> for option, value in options:
</span><span class="cx"> if option == "-h":
</span><span class="cx"> usage()
</span><span class="cx"> sys.exit(0)
</span><span class="cx"> elif option == "--debug":
</span><span class="cx"> debug = True
</span><del>- elif option == "--router":
- router = True
- elif option == "--worker":
- worker = True
</del><span class="cx"> elif option == "--lines":
</span><span class="cx"> numLines = int(value)
</span><span class="cx"> elif option == "--range":
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixescontribtoolssortrecurrencespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/sortrecurrences.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/sortrecurrences.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/contrib/tools/sortrecurrences.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -20,7 +20,7 @@
</span><span class="cx"> import os
</span><span class="cx"> import sys
</span><span class="cx"> import traceback
</span><del>-from pycalendar.calendar import PyCalendar
</del><ins>+from pycalendar.icalendar.calendar import Calendar
</ins><span class="cx">
</span><span class="cx"> def usage(error_msg=None):
</span><span class="cx"> if error_msg:
</span><span class="lines">@@ -72,7 +72,7 @@
</span><span class="cx"> print("Path does not exist: '%s'. Ignoring." % (arg,))
</span><span class="cx"> continue
</span><span class="cx">
</span><del>- cal = PyCalendar()
</del><ins>+ cal = Calendar()
</ins><span class="cx"> cal.parse(open(arg))
</span><span class="cx"> print(str(cal.serialize()))
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixessetuppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/setup.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/setup.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/setup.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -145,7 +145,6 @@
</span><span class="cx"> "bin/calendarserver_export",
</span><span class="cx"> #"bin/calendarserver_icalendar_validate",
</span><span class="cx"> #"bin/calendarserver_load_augmentdb",
</span><del>- #"bin/calendarserver_make_partition",
</del><span class="cx"> #"bin/calendarserver_manage_postgres",
</span><span class="cx"> "bin/calendarserver_manage_principals",
</span><span class="cx"> "bin/calendarserver_manage_push",
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixessupportbuildsh"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/support/build.sh (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/support/build.sh        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/support/build.sh        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -40,11 +40,67 @@
</span><span class="cx"> fi;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+# Checks for presence of a C header, optionally with a version comparison.
+# With only a header file name, try to include it, returning nonzero if absent.
+# With 3 params, also attempt a version check, returning nonzero if too old.
+# Param 2 is a minimum acceptable version number
+# Param 3 is a #define from the source that holds the installed version number
+# Examples:
+# Assert that ldap.h is present
+# find_header "ldap.h"
+# Assert that ldap.h is present with a version >= 20344
+# find_header "ldap.h" 20344 "LDAP_VENDOR_VERSION"
</ins><span class="cx"> find_header () {
</span><del>- local sysheader="$1"; shift;
- echo "#include <${sysheader}>" | cc -x c -c - -o /dev/null 2> /dev/null;
- return "$?";
-}
</del><ins>+ ARGS="$@";
+ ret=1; # default to a failed check, forcing a fetch of the depencency
+ i=0;
+ for a in $ARGS; do
+ [ $i -eq 0 ] && local sysheader="$1";
+ [ $i -eq 1 ] && local minver="$2";
+ [ $i -eq 2 ] && local def="$3";
+ i=$(($i+1));
+ done;
+ [ ! $sysheader ] && return 1;
+ # Check for presence of a header. We use the "-c" cc option because we don't
+ # need to emit a file; cc exits nonzero if it can't find the header
+ if [ $# -lt 2 ]; then
+ echo "#include <${sysheader}>" | cc -x c -c - -o /dev/null 2> /dev/null;
+ return "$?";
+ # Check for presence of a header of specified version
+ else
+ found='';
+ local aout=$(mktemp -t ccXXXXXX); # compiled executable file path
+ local prog=$(mktemp -t ccXXXXXX); # C source file path
+ cat <<DOC > ${prog}
+#include <${sysheader}>
+#include <stdio.h>
+#define STR(x) #x
+#define SHOW_DEFINE(x) printf("%s", STR(x))
+int main()
+{
+ if (${def})
+ {
+ SHOW_DEFINE(${def});
+ return 0;
+ };
+ return 1;
+};
+DOC
+ cc -x c -o ${aout} ${prog} &> /dev/null;
+ if [ $? -eq 0 ] && [ -e ${aout} ] ; then
+ found=$(${aout});
+ fi;
+ if [ $? -eq 0 ] && [ ! -z ${found} ] ; then
+ cmp_version $minver $found;
+ ret=$?;
+ else
+ ret=1; #cc exited nonzero or didn't emit a file
+ fi;
+ rm -f "${aout}";
+ rm -f "${prog}";
+ fi;
+ return $ret;
+};
</ins><span class="cx">
</span><span class="cx"> # Initialize all the global state required to use this library.
</span><span class="cx"> init_build () {
</span><span class="lines">@@ -213,10 +269,13 @@
</span><span class="cx"> if "${force_setup}" || [ ! -d "${path}" ]; then
</span><span class="cx"> local ext="$(echo "${url}" | sed 's|^.*\.\([^.]*\)$|\1|')";
</span><span class="cx">
</span><ins>+ untar () { tar -xvf -; }
+ unzipstream () { tmp="$(mktemp -t ccsXXXXX)"; cat > "${tmp}"; unzip "${tmp}"; rm "${tmp}"; }
</ins><span class="cx"> case "${ext}" in
</span><del>- gz|tgz) decompress="gzip -d -c"; ;;
- bz2) decompress="bzip2 -d -c"; ;;
- tar) decompress="cat"; ;;
</del><ins>+ gz|tgz) decompress="gzip -d -c"; unpack="untar"; ;;
+ bz2) decompress="bzip2 -d -c"; unpack="untar"; ;;
+ tar) decompress="untar"; unpack="untar"; ;;
+ zip) decompress="cat"; unpack="unzipstream"; ;;
</ins><span class="cx"> *)
</span><span class="cx"> echo "Error in www_get of URL ${url}: Unknown extension ${ext}";
</span><span class="cx"> exit 1;
</span><span class="lines">@@ -228,7 +287,7 @@
</span><span class="cx"> if [ -n "${cache_deps}" ] && [ -n "${hash}" ]; then
</span><span class="cx"> mkdir -p "${cache_deps}";
</span><span class="cx">
</span><del>- local cache_basename="${name}-$(echo "${url}" | hash)-$(basename "${url}")";
</del><ins>+ local cache_basename="$(echo ${name} | tr '[ ]' '_')-$(echo "${url}" | hash)-$(basename "${url}")";
</ins><span class="cx"> local cache_file="${cache_deps}/${cache_basename}";
</span><span class="cx">
</span><span class="cx"> check_hash () {
</span><span class="lines">@@ -327,7 +386,7 @@
</span><span class="cx">
</span><span class="cx"> rm -rf "${path}";
</span><span class="cx"> cd "$(dirname "${path}")";
</span><del>- get | ${decompress} | tar -xvf -;
</del><ins>+ get | ${decompress} | ${unpack};
</ins><span class="cx"> apply_patches "${name}" "${path}";
</span><span class="cx"> cd /;
</span><span class="cx"> fi;
</span><span class="lines">@@ -670,12 +729,12 @@
</span><span class="cx"> if type -P memcached > /dev/null; then
</span><span class="cx"> using_system "memcached";
</span><span class="cx"> else
</span><del>- local le="libevent-2.0.17-stable";
- local mc="memcached-1.4.13";
- c_dependency -m "dad64aaaaff16b5fbec25160c06fee9a" \
</del><ins>+ local le="libevent-2.0.21-stable";
+ local mc="memcached-1.4.15";
+ c_dependency -m "b2405cc9ebf264aa47ff615d9de527a2" \
</ins><span class="cx"> "libevent" "${le}" \
</span><del>- "https://github.com/downloads/libevent/libevent/${le}.tar.gz";
- c_dependency -m "6d18c6d25da945442fcc1187b3b63b7f" \
</del><ins>+ "http://github.com/downloads/libevent/libevent/${le}.tar.gz";
+ c_dependency -m "36ea966f5a29655be1746bf4949f7f69" \
</ins><span class="cx"> "memcached" "${mc}" \
</span><span class="cx"> "http://memcached.googlecode.com/files/${mc}.tar.gz";
</span><span class="cx"> fi;
</span><span class="lines">@@ -683,8 +742,9 @@
</span><span class="cx"> if type -P postgres > /dev/null; then
</span><span class="cx"> using_system "Postgres";
</span><span class="cx"> else
</span><del>- local pgv="9.2.4";
- local pg="postgresql-${pgv}";
</del><ins>+ local v="9.3.1";
+ local n="postgresql";
+ local p="${n}-${v}";
</ins><span class="cx">
</span><span class="cx"> if type -P dtrace > /dev/null; then
</span><span class="cx"> local enable_dtrace="--enable-dtrace";
</span><span class="lines">@@ -692,19 +752,22 @@
</span><span class="cx"> local enable_dtrace="";
</span><span class="cx"> fi;
</span><span class="cx">
</span><del>- c_dependency -m "52df0a9e288f02d7e6e0af89ed4dcfc6" \
- "PostgreSQL" "${pg}" \
- "ftp://ftp5.us.postgresql.org/pub/PostgreSQL/source/v${pgv}/${pg}.tar.gz" \
</del><ins>+ c_dependency -m "c003d871f712d4d3895956b028a96e74" \
+ "PostgreSQL" "${p}" \
+ "http://ftp.postgresql.org/pub/source/v${v}/${p}.tar.bz2" \
</ins><span class="cx"> --with-python ${enable_dtrace};
</span><span class="cx"> :;
</span><span class="cx"> fi;
</span><span class="cx">
</span><del>- if find_header ldap.h; then
</del><ins>+ if find_header ldap.h 20428 LDAP_VENDOR_VERSION; then
</ins><span class="cx"> using_system "OpenLDAP";
</span><span class="cx"> else
</span><del>- c_dependency -m "ec63f9c2add59f323a0459128846905b" \
- "OpenLDAP" "openldap-2.4.25" \
- "http://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-2.4.25.tgz" \
</del><ins>+ local v="2.4.38";
+ local n="openldap";
+ local p="${n}-${v}";
+ c_dependency -m "39831848c731bcaef235a04e0d14412f" \
+ "OpenLDAP" "${p}" \
+ "http://www.openldap.org/software/download/OpenLDAP/${n}-release/${p}.tgz" \
</ins><span class="cx"> --disable-bdb --disable-hdb;
</span><span class="cx"> fi;
</span><span class="cx">
</span><span class="lines">@@ -726,24 +789,24 @@
</span><span class="cx">
</span><span class="cx"> # Sourceforge mirror hostname.
</span><span class="cx"> local sf="superb-sea2.dl.sourceforge.net";
</span><del>- local st="setuptools-0.6c11";
</del><ins>+ local st="setuptools-1.4";
</ins><span class="cx"> local pypi="http://pypi.python.org/packages/source";
</span><span class="cx">
</span><del>- py_dependency -m "7df2a529a074f613b509fb44feefe74e" \
</del><ins>+ py_dependency -v 1 -m "5710464bc5a61d75f5087f15ce63cfe0" \
</ins><span class="cx"> "setuptools" "setuptools" "${st}" \
</span><span class="cx"> "$pypi/s/setuptools/${st}.tar.gz";
</span><span class="cx">
</span><del>- local v="4.0.3";
</del><ins>+ local v="4.0.5";
</ins><span class="cx"> local n="zope.interface";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v 4 -m "1ddd308f2c83703accd1696158c300eb" \
</del><ins>+ py_dependency -v 4 -m "caf26025ae1b02da124a58340e423dfe" \
</ins><span class="cx"> "Zope Interface" "${n}" "${p}" \
</span><del>- "http://pypi.python.org/packages/source/z/${n}/${p}.tar.gz";
</del><ins>+ "http://pypi.python.org/packages/source/z/${n}/${p}.zip";
</ins><span class="cx">
</span><del>- local v="0.10";
</del><ins>+ local v="0.12";
</ins><span class="cx"> local n="pyOpenSSL";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v 0.9 -m "34db8056ec53ce80c7f5fc58bee9f093" \
</del><ins>+ py_dependency -v 0.12 -m "60a7bbb6160950823eddcbba2cbcb0d6" \
</ins><span class="cx"> "${n}" "OpenSSL" "${p}" \
</span><span class="cx"> "http://pypi.python.org/packages/source/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><span class="lines">@@ -754,18 +817,18 @@
</span><span class="cx"> "${svn_uri_base}/${n}/trunk";
</span><span class="cx"> fi;
</span><span class="cx">
</span><del>- local v="0.6.1";
</del><ins>+ local v="0.6.4";
</ins><span class="cx"> local n="xattr";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v 0.5 -r 1038 \
- "${n}" "${n}" "${n}" \
- "http://svn.red-bean.com/bob/${n}/releases/${p}/";
</del><ins>+ py_dependency -v 0.6 -m "1bef31afb7038800f8d5cfa2f4562b37" \
+ "${n}" "${n}" "${p}" \
+ "${pypi}/x/${n}/${n}-${v}.tar.gz";
</ins><span class="cx">
</span><span class="cx"> if [ -n "${ORACLE_HOME:-}" ]; then
</span><del>- local v="5.1";
</del><ins>+ local v="5.1.2";
</ins><span class="cx"> local n="cx_Oracle";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "${v}" -m "d2697493a40c9d46c9b7c1c210b61671" \
</del><ins>+ py_dependency -v "${v}" -m "462f309e00f7bff7100e2077fc43172c" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "http://${sf}/project/cx-oracle/${v}/${p}.tar.gz";
</span><span class="cx"> fi;
</span><span class="lines">@@ -779,10 +842,10 @@
</span><span class="cx">
</span><span class="cx"> # Maintenance note: next time the Twisted dependency gets updated, check out
</span><span class="cx"> # twext/patches.py.
</span><del>- local v="12.3.0";
</del><ins>+ local v="13.2.0";
</ins><span class="cx"> local n="Twisted";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v 12.2 -m "6e289825f3bf5591cfd670874cc0862d" \
</del><ins>+ py_dependency -v 13.2 -m "83fe6c0c911cc1602dbffb036be0ba79" \
</ins><span class="cx"> "${n}" "twisted" "${p}" \
</span><span class="cx"> "${pypi}/T/${n}/${p}.tar.bz2";
</span><span class="cx">
</span><span class="lines">@@ -793,22 +856,22 @@
</span><span class="cx"> "${n}" "dateutil" "${p}" \
</span><span class="cx"> "http://www.labix.org/download/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="0.6.1";
</del><ins>+ local v="1.2.0";
</ins><span class="cx"> local n="psutil";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -m "3cfcbfb8525f6e4c70110e44a85e907e" \
</del><ins>+ py_dependency -m "f8ae906249e65db21f17d873ae07e584" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><del>- "http://${n}.googlecode.com/files/${p}.tar.gz";
</del><ins>+ "${pypi}/p/${n}/${p}.tar.gz";
</ins><span class="cx">
</span><del>- local v="2.3.13";
</del><ins>+ local v="2.4.13";
</ins><span class="cx"> local n="python-ldap";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "${v}" -m "895223d32fa10bbc29aa349bfad59175" \
</del><ins>+ py_dependency -v "${v}" -m "74b7b50267761540451eade44b2049ee" \
</ins><span class="cx"> "Python-LDAP" "ldap" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><span class="cx"> # XXX actually PyCalendar should be imported in-place.
</span><del>- py_dependency -fe -i "src" -r 11458 \
</del><ins>+ py_dependency -fe -i "src" -r 11947 \
</ins><span class="cx"> "PyCalendar" "pycalendar" "pycalendar" \
</span><span class="cx"> "${svn_uri_base}/PyCalendar/trunk";
</span><span class="cx">
</span><span class="lines">@@ -840,44 +903,45 @@
</span><span class="cx"> "${svn_uri_base}/CalDAVClientLibrary/trunk";
</span><span class="cx">
</span><span class="cx"> # Can't add "-v 2011g" to args because the version check expects numbers.
</span><ins>+ local v="2013.8";
</ins><span class="cx"> local n="pytz";
</span><del>- local p="${n}-2011n";
- py_dependency -m "75ffdc113a4bcca8096ab953df746391" \
</del><ins>+ local p="${n}-${v}";
+ py_dependency -m "37750ca749ed3a52523b9682b0b7e381" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="2.5";
</del><ins>+ local v="2.6.1";
</ins><span class="cx"> local n="pycrypto";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "${v}" -m "783e45d4a1a309e03ab378b00f97b291" \
</del><ins>+ py_dependency -v "${v}" -m "55a61a054aa66812daf5161a0d5d7eda" \
</ins><span class="cx"> "PyCrypto" "${n}" "${p}" \
</span><span class="cx"> "http://ftp.dlitz.net/pub/dlitz/crypto/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="0.1.2";
</del><ins>+ local v="0.1.7";
</ins><span class="cx"> local n="pyasn1";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "${v}" -m "a7c67f5880a16a347a4d3ce445862a47" \
</del><ins>+ py_dependency -v "${v}" -m "2cbd80fcd4c7b1c82180d3d76fee18c8" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="1.1.6";
</del><ins>+ local v="1.1.8";
</ins><span class="cx"> local n="setproctitle";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "1.0" -m "1e42e43b440214b971f4b33c21eac369" \
</del><ins>+ py_dependency -v "1.0" -m "728f4c8c6031bbe56083a48594027edd" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/s/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="0.6";
</del><ins>+ local v="0.8";
</ins><span class="cx"> local n="cffi";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "0.6" -m "5be33b1ab0247a984d42b27344519337" \
</del><ins>+ py_dependency -v "0.6" -m "e61deb0515311bb42d5d58b9403bc923" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/c/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="2.09.1";
</del><ins>+ local v="2.10";
</ins><span class="cx"> local n="pycparser";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "0.6" -m "74aa075fc28b7c24a4426574d1ac91e0" \
</del><ins>+ py_dependency -v "0.6" -m "d87aed98c8a9f386aa56d365fe4d515f" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><span class="lines">@@ -889,21 +953,21 @@
</span><span class="cx"> local p="${n}-${v}";
</span><span class="cx"> py_dependency -o -m "36407974bd5da2af00bf90ca27feeb44" \
</span><span class="cx"> "Epydoc" "${n}" "${p}" \
</span><del>- "https://pypi.python.org/packages/source/e/${n}/${p}.tar.gz";
</del><ins>+ "${pypi}/e/${n}/${p}.tar.gz";
</ins><span class="cx">
</span><span class="cx"> local v="0.10.0";
</span><span class="cx"> local n="Nevow";
</span><span class="cx"> local p="${n}-${v}";
</span><span class="cx"> py_dependency -o -m "66dda2ad88f42dea05911add15f4d1b2" \
</span><span class="cx"> "${n}" "${n}" "${p}" \
</span><del>- "https://pypi.python.org/packages/source/N/${n}/${p}.tar.gz";
</del><ins>+ "${pypi}/N/${n}/${p}.tar.gz";
</ins><span class="cx">
</span><del>- local v="0.4";
</del><ins>+ local v="0.5b1";
</ins><span class="cx"> local n="pydoctor";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -o -m "b7564e12b5d35d4cb529a2c220b25d3a" \
</del><ins>+ py_dependency -o -m "c4fb33672f37624116cc7a0606f74f28" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><del>- "https://pypi.python.org/packages/source/p/${n}/${p}.tar.gz";
</del><ins>+ "{$pypi}/p/${n}/${p}.tar.gz";
</ins><span class="cx">
</span><span class="cx"> if "${do_setup}"; then
</span><span class="cx"> cd "${caldav}";
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixessupportversionpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/support/version.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/support/version.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/support/version.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -26,7 +26,7 @@
</span><span class="cx"> # Compute the version number.
</span><span class="cx"> #
</span><span class="cx">
</span><del>- base_version = "5.2"
</del><ins>+ base_version = "6.0"
</ins><span class="cx">
</span><span class="cx"> branches = tuple(
</span><span class="cx"> branch.format(version=base_version)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestest"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/test (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/test        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/test        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -21,7 +21,7 @@
</span><span class="cx">
</span><span class="cx"> wd="$(cd "$(dirname "$0")" && pwd -L)";
</span><span class="cx">
</span><del>-. "${wd}/support/build.sh";
</del><ins>+#. "${wd}/support/build.sh";
</ins><span class="cx">
</span><span class="cx"> do_setup="false";
</span><span class="cx"> do_get="false";
</span><span class="lines">@@ -74,9 +74,6 @@
</span><span class="cx">
</span><span class="cx"> export PYTHONPATH="${wd}:${PYTHONPATH:-}";
</span><span class="cx">
</span><del>-dependencies;
-trial="$(type -p trial)";
-
</del><span class="cx"> if [ $# -gt 0 ]; then
</span><span class="cx"> test_modules="$@";
</span><span class="cx"> flaky=true;
</span><span class="lines">@@ -88,7 +85,7 @@
</span><span class="cx"> find "${wd}" -name \*.pyc -print0 | xargs -0 rm;
</span><span class="cx">
</span><span class="cx"> mkdir -p "${wd}/data";
</span><del>-cd "${wd}" && "${python}" "${trial}" --temp-directory="${wd}/data/trial" --rterrors ${reactor} ${random} ${until_fail} ${no_colour} ${coverage} ${numjobs} ${test_modules};
</del><ins>+cd "${wd}" && "${wd}/bin/trial" --temp-directory="${wd}/data/trial" --rterrors ${reactor} ${random} ${until_fail} ${no_colour} ${coverage} ${numjobs} ${test_modules};
</ins><span class="cx">
</span><span class="cx"> if ${flaky}; then
</span><span class="cx"> echo "";
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextenterprisedalsyntaxpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/dal/syntax.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/dal/syntax.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/dal/syntax.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1686,6 +1686,7 @@
</span><span class="cx"> SQLFragment(' in %s mode' % (self.mode,)))
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class DatabaseLock(_LockingStatement):
</span><span class="cx"> """
</span><span class="cx"> An SQL exclusive session level advisory lock
</span><span class="lines">@@ -1706,6 +1707,7 @@
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class DatabaseUnlock(_LockingStatement):
</span><span class="cx"> """
</span><span class="cx"> An SQL exclusive session level advisory lock
</span><span class="lines">@@ -1726,6 +1728,7 @@
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class Savepoint(_LockingStatement):
</span><span class="cx"> """
</span><span class="cx"> An SQL 'savepoint' statement.
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextenterprisefixturespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/fixtures.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/fixtures.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/fixtures.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -76,8 +76,8 @@
</span><span class="cx">
</span><span class="cx"> def resultOf(deferred, propagate=False):
</span><span class="cx"> """
</span><del>- Add a callback and errback which will capture the result of a L{Deferred} in
- a list, and return that list. If 'propagate' is True, pass through the
</del><ins>+ Add a callback and errback which will capture the result of a L{Deferred}
+ in a list, and return that list. If 'propagate' is True, pass through the
</ins><span class="cx"> results.
</span><span class="cx"> """
</span><span class="cx"> results = []
</span><span class="lines">@@ -194,7 +194,6 @@
</span><span class="cx"> No implementation.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-
</del><span class="cx"> def callFromThread(self, thunk, *a, **kw):
</span><span class="cx"> """
</span><span class="cx"> No implementation.
</span><span class="lines">@@ -223,14 +222,16 @@
</span><span class="cx"> self.factory = ConnectionFactory()
</span><span class="cx"> connect = self.factory.connect
</span><span class="cx"> self.connect = connect
</span><del>- self.paused = False
- self.holders = []
- self.pool = ConnectionPool(connect,
- maxConnections=2,
- dialect=self.dialect,
- paramstyle=self.paramstyle)
</del><ins>+ self.paused = False
+ self.holders = []
+ self.pool = ConnectionPool(
+ connect,
+ maxConnections=2,
+ dialect=self.dialect,
+ paramstyle=self.paramstyle
+ )
</ins><span class="cx"> self.pool._createHolder = self.makeAHolder
</span><del>- self.clock = self.pool.reactor = ClockWithThreads()
</del><ins>+ self.clock = self.pool.reactor = ClockWithThreads()
</ins><span class="cx"> self.pool.startService()
</span><span class="cx"> test.addCleanup(self.flushHolders)
</span><span class="cx">
</span><span class="lines">@@ -239,7 +240,7 @@
</span><span class="cx"> """
</span><span class="cx"> Flush all pending C{submit}s since C{pauseHolders} was called. This
</span><span class="cx"> makes sure the service is stopped and the fake ThreadHolders are all
</span><del>- executing their queues so failed tsets can exit cleanly.
</del><ins>+ executing their queues so failed tests can exit cleanly.
</ins><span class="cx"> """
</span><span class="cx"> self.paused = False
</span><span class="cx"> for holder in self.holders:
</span><span class="lines">@@ -551,6 +552,21 @@
</span><span class="cx"> self._connectResultQueue.append(thunk)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def willConnectTo(self):
+ """
+ Queue a successful result for connect() and immediately add it as a
+ child to this L{ConnectionFactory}.
+
+ @return: a connection object
+ @rtype: L{FakeConnection}
+ """
+ aConnection = FakeConnection(self)
+ def thunk():
+ return aConnection
+ self._connectResultQueue.append(thunk)
+ return aConnection
+
+
</ins><span class="cx"> def willFail(self):
</span><span class="cx"> """
</span><span class="cx"> Used by tests to queue a successful result for connect().
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextenterprisequeuepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/queue.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/queue.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/queue.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -115,7 +115,7 @@
</span><span class="cx"> (in the worst case) pass from worker->controller->controller->worker.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def performWork(table, workID):
</del><ins>+ def performWork(table, workID): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @param table: The table where work is waiting.
</span><span class="cx"> @type table: L{TableSyntax}
</span><span class="lines">@@ -149,7 +149,8 @@
</span><span class="cx"> NodeTable.addColumn("PORT", SQLType("integer", None))
</span><span class="cx"> NodeTable.addColumn("TIME", SQLType("timestamp", None)).setDefaultValue(
</span><span class="cx"> # Note: in the real data structure, this is actually a not-cleaned-up
</span><del>- # sqlparse internal data structure, but it *should* look closer to this.
</del><ins>+ # sqlparse internal data structure, but it *should* look closer to
+ # this.
</ins><span class="cx"> ProcedureCall("timezone", ["UTC", NamedValue('CURRENT_TIMESTAMP')])
</span><span class="cx"> )
</span><span class="cx"> for column in NodeTable.columns:
</span><span class="lines">@@ -370,7 +371,6 @@
</span><span class="cx"> will be taken care of by the job queueing machinery.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-
</del><span class="cx"> @classmethod
</span><span class="cx"> def forTable(cls, table):
</span><span class="cx"> """
</span><span class="lines">@@ -677,8 +677,8 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def __init__(self, peerPool, boxReceiver=None, locator=None):
</span><del>- super(ConnectionFromWorker, self).__init__(peerPool.schema, boxReceiver,
- locator)
</del><ins>+ super(ConnectionFromWorker, self).__init__(peerPool.schema,
+ boxReceiver, locator)
</ins><span class="cx"> self.peerPool = peerPool
</span><span class="cx"> self._load = 0
</span><span class="cx">
</span><span class="lines">@@ -830,9 +830,9 @@
</span><span class="cx"> workItem = yield workItemClass.load(txn, workID)
</span><span class="cx"> if workItem.group is not None:
</span><span class="cx"> yield NamedLock.acquire(txn, workItem.group)
</span><del>- # TODO: what if we fail? error-handling should be recorded someplace,
- # the row should probably be marked, re-tries should be triggerable
- # administratively.
</del><ins>+ # TODO: what if we fail? error-handling should be recorded
+ # someplace, the row should probably be marked, re-tries should be
+ # triggerable administratively.
</ins><span class="cx"> yield workItem.delete()
</span><span class="cx"> # TODO: verify that workID is the primary key someplace.
</span><span class="cx"> yield workItem.doWork()
</span><span class="lines">@@ -865,9 +865,6 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-
-
-
</del><span class="cx"> class WorkerFactory(Factory, object):
</span><span class="cx"> """
</span><span class="cx"> Factory, to be used as the client to connect from the worker to the
</span><span class="lines">@@ -950,7 +947,7 @@
</span><span class="cx"> waiting for the transaction where that addition was completed to
</span><span class="cx"> commit, and asking the local node controller process to do the work.
</span><span class="cx"> """
</span><del>- @passthru(self.workItemType.create(self.txn, **self.kw).addCallback)
</del><ins>+ created = self.workItemType.create(self.txn, **self.kw)
</ins><span class="cx"> def whenCreated(item):
</span><span class="cx"> self._whenProposed.callback(self)
</span><span class="cx"> @self.txn.postCommit
</span><span class="lines">@@ -967,12 +964,15 @@
</span><span class="cx"> self._whenExecuted.errback(why)
</span><span class="cx"> reactor = self._chooser.reactor
</span><span class="cx"> when = max(0, astimestamp(item.notBefore) - reactor.seconds())
</span><del>- # TODO: Track the returned DelayedCall so it can be stopped when
- # the service stops.
</del><ins>+ # TODO: Track the returned DelayedCall so it can be stopped
+ # when the service stops.
</ins><span class="cx"> self._chooser.reactor.callLater(when, maybeLater)
</span><span class="cx"> @self.txn.postAbort
</span><span class="cx"> def whenFailed():
</span><span class="cx"> self._whenCommitted.errback(TransactionFailed)
</span><ins>+ def whenNotCreated(failure):
+ self._whenProposed.errback(failure)
+ created.addCallbacks(whenCreated, whenNotCreated)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def whenExecuted(self):
</span><span class="lines">@@ -1023,6 +1023,8 @@
</span><span class="cx"> """
</span><span class="cx"> return _cloneDeferred(self._whenCommitted)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class _BaseQueuer(object):
</span><span class="cx"> implements(IQueuer)
</span><span class="cx">
</span><span class="lines">@@ -1030,13 +1032,16 @@
</span><span class="cx"> super(_BaseQueuer, self).__init__()
</span><span class="cx"> self.proposalCallbacks = set()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def callWithNewProposals(self, callback):
</span><del>- self.proposalCallbacks.add(callback);
</del><ins>+ self.proposalCallbacks.add(callback)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def transferProposalCallbacks(self, newQueuer):
</span><span class="cx"> newQueuer.proposalCallbacks = self.proposalCallbacks
</span><span class="cx"> return newQueuer
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def enqueueWork(self, txn, workItemType, **kw):
</span><span class="cx"> """
</span><span class="cx"> There is some work to do. Do it, someplace else, ideally in parallel.
</span><span class="lines">@@ -1061,6 +1066,7 @@
</span><span class="cx"> return wp
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class PeerConnectionPool(_BaseQueuer, MultiService, object):
</span><span class="cx"> """
</span><span class="cx"> Each node has a L{PeerConnectionPool} connecting it to all the other nodes
</span><span class="lines">@@ -1140,7 +1146,7 @@
</span><span class="cx"> self.mappedPeers = {}
</span><span class="cx"> self.schema = schema
</span><span class="cx"> self._startingUp = None
</span><del>- self._listeningPortObject = None
</del><ins>+ self._listeningPort = None
</ins><span class="cx"> self._lastSeenTotalNodes = 1
</span><span class="cx"> self._lastSeenNodeIndex = 1
</span><span class="cx">
</span><span class="lines">@@ -1197,7 +1203,8 @@
</span><span class="cx"> A peer has requested us to perform some work; choose a work performer
</span><span class="cx"> local to this node, and then execute it.
</span><span class="cx"> """
</span><del>- return self.choosePerformer(onlyLocally=True).performWork(table, workID)
</del><ins>+ performer = self.choosePerformer(onlyLocally=True)
+ return performer.performWork(table, workID)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def allWorkItemTypes(self):
</span><span class="lines">@@ -1225,8 +1232,8 @@
</span><span class="cx">
</span><span class="cx"> @return: the maximum number of other L{PeerConnectionPool} instances
</span><span class="cx"> that may be connected to the database described by
</span><del>- C{self.transactionFactory}. Note that this is not the current count
- by connectivity, but the count according to the database.
</del><ins>+ C{self.transactionFactory}. Note that this is not the current
+ count by connectivity, but the count according to the database.
</ins><span class="cx"> @rtype: L{int}
</span><span class="cx"> """
</span><span class="cx"> # TODO
</span><span class="lines">@@ -1277,7 +1284,6 @@
</span><span class="cx"> overdueItem.workID)
</span><span class="cx"> return inTransaction(self.transactionFactory, workCheck)
</span><span class="cx">
</span><del>-
</del><span class="cx"> _currentWorkDeferred = None
</span><span class="cx"> _lostWorkCheckCall = None
</span><span class="cx">
</span><span class="lines">@@ -1315,10 +1321,10 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def startup(txn):
</span><span class="cx"> endpoint = TCP4ServerEndpoint(self.reactor, self.ampPort)
</span><del>- # If this fails, the failure mode is going to be ugly, just like all
- # conflicted-port failures. But, at least it won't proceed.
- self._listeningPortObject = yield endpoint.listen(self.peerFactory())
- self.ampPort = self._listeningPortObject.getHost().port
</del><ins>+ # If this fails, the failure mode is going to be ugly, just like
+ # all conflicted-port failures. But, at least it won't proceed.
+ self._listeningPort = yield endpoint.listen(self.peerFactory())
+ self.ampPort = self._listeningPort.getHost().port
</ins><span class="cx"> yield Lock.exclusive(NodeInfo.table).on(txn)
</span><span class="cx"> nodes = yield self.activeNodes(txn)
</span><span class="cx"> selves = [node for node in nodes
</span><span class="lines">@@ -1354,8 +1360,8 @@
</span><span class="cx"> yield super(PeerConnectionPool, self).stopService()
</span><span class="cx"> if self._startingUp is not None:
</span><span class="cx"> yield self._startingUp
</span><del>- if self._listeningPortObject is not None:
- yield self._listeningPortObject.stopListening()
</del><ins>+ if self._listeningPort is not None:
+ yield self._listeningPort.stopListening()
</ins><span class="cx"> if self._lostWorkCheckCall is not None:
</span><span class="cx"> self._lostWorkCheckCall.cancel()
</span><span class="cx"> if self._currentWorkDeferred is not None:
</span><span class="lines">@@ -1430,8 +1436,6 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-
-
</del><span class="cx"> class LocalQueuer(_BaseQueuer):
</span><span class="cx"> """
</span><span class="cx"> When work is enqueued with this queuer, it is just executed locally.
</span><span class="lines">@@ -1458,7 +1462,8 @@
</span><span class="cx"> """
</span><span class="cx"> Implementor of C{performWork} that doesn't actual perform any work. This
</span><span class="cx"> is used in the case where you want to be able to enqueue work for someone
</span><del>- else to do, but not take on any work yourself (such as a command line tool).
</del><ins>+ else to do, but not take on any work yourself (such as a command line
+ tool).
</ins><span class="cx"> """
</span><span class="cx"> implements(_IWorkPerformer)
</span><span class="cx">
</span><span class="lines">@@ -1469,6 +1474,7 @@
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class NonPerformingQueuer(_BaseQueuer):
</span><span class="cx"> """
</span><span class="cx"> When work is enqueued with this queuer, it is never executed locally.
</span><span class="lines">@@ -1487,4 +1493,4 @@
</span><span class="cx"> """
</span><span class="cx"> Choose to perform the work locally.
</span><span class="cx"> """
</span><del>- return NonPerformer()
</del><span class="cx">\ No newline at end of file
</span><ins>+ return NonPerformer()
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextenterprisetesttest_queuepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/test/test_queue.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/test/test_queue.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/enterprise/test/test_queue.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -36,6 +36,7 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> from twisted.trial.unittest import TestCase
</span><ins>+from twisted.python.failure import Failure
</ins><span class="cx"> from twisted.internet.defer import (
</span><span class="cx"> Deferred, inlineCallbacks, gatherResults, passthru#, returnValue
</span><span class="cx"> )
</span><span class="lines">@@ -55,6 +56,8 @@
</span><span class="cx"> from twisted.test.proto_helpers import StringTransport, MemoryReactor
</span><span class="cx"> from twext.enterprise.fixtures import SteppablePoolHelper
</span><span class="cx"> from twisted.internet.defer import returnValue
</span><ins>+from twext.enterprise.queue import LocalQueuer
+from twext.enterprise.fixtures import ConnectionPoolHelper
</ins><span class="cx">
</span><span class="cx"> from twext.enterprise.queue import _BaseQueuer, NonPerformingQueuer
</span><span class="cx"> import twext.enterprise.queue
</span><span class="lines">@@ -67,7 +70,7 @@
</span><span class="cx">
</span><span class="cx"> def callLater(self, _seconds, _f, *args, **kw):
</span><span class="cx"> if _seconds < 0:
</span><del>- raise ValueError("%s<0: "%(_seconds,))
</del><ins>+ raise ValueError("%s<0: " % (_seconds,))
</ins><span class="cx"> return super(Clock, self).callLater(_seconds, _f, *args, **kw)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -267,6 +270,56 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><ins>+class WorkProposalTests(TestCase):
+ """
+ Tests for L{WorkProposal}.
+ """
+
+ def test_whenProposedSuccess(self):
+ """
+ The L{Deferred} returned by L{WorkProposal.whenProposed} fires when the
+ SQL sent to the database has completed.
+ """
+ cph = ConnectionPoolHelper()
+ cph.setUp(test=self)
+ cph.pauseHolders()
+ lq = LocalQueuer(cph.createTransaction)
+ enqTxn = cph.createTransaction()
+ wp = lq.enqueueWork(enqTxn, DummyWorkItem, a=3, b=4)
+ d = wp.whenProposed()
+ r = cph.resultOf(d)
+ self.assertEquals(r, [])
+ cph.flushHolders()
+ self.assertEquals(len(r), 1)
+
+
+ def test_whenProposedFailure(self):
+ """
+ The L{Deferred} returned by L{WorkProposal.whenProposed} fails with an
+ errback when the SQL executed to create the WorkItem row fails.
+ """
+ cph = ConnectionPoolHelper()
+ cph.setUp(self)
+ cph.pauseHolders()
+ firstConnection = cph.factory.willConnectTo()
+ enqTxn = cph.createTransaction()
+ # Execute some SQL on the connection before enqueueing the work-item so
+ # that we don't get the initial-statement.
+ enqTxn.execSQL("some sql")
+ lq = LocalQueuer(cph.createTransaction)
+ cph.flushHolders()
+ cph.pauseHolders()
+ wp = lq.enqueueWork(enqTxn, DummyWorkItem, a=3, b=4)
+ firstConnection.executeWillFail(lambda: RuntimeError("foo"))
+ d = wp.whenProposed()
+ r = cph.resultOf(d)
+ self.assertEquals(r, [])
+ cph.flushHolders()
+ self.assertEquals(len(r), 1)
+ self.assertIsInstance(r[0], Failure)
+
+
+
</ins><span class="cx"> class PeerConnectionPoolUnitTests(TestCase):
</span><span class="cx"> """
</span><span class="cx"> L{PeerConnectionPool} has many internal components.
</span><span class="lines">@@ -393,7 +446,8 @@
</span><span class="cx"> # Next, create one that's actually far enough into the past to run.
</span><span class="cx"> yield DummyWorkItem.create(
</span><span class="cx"> txn, a=3, b=4, notBefore=(
</span><del>- # Schedule it in the past so that it should have already run.
</del><ins>+ # Schedule it in the past so that it should have already
+ # run.
</ins><span class="cx"> fakeNow - datetime.timedelta(
</span><span class="cx"> seconds=qpool.queueProcessTimeout + 20
</span><span class="cx"> )
</span><span class="lines">@@ -509,8 +563,8 @@
</span><span class="cx"> t = StringTransport()
</span><span class="cx"> p.makeConnection(t)
</span><span class="cx"> return p, t
</span><del>- worker1, trans1 = peer()
- worker2, trans2 = peer()
</del><ins>+ worker1, _ignore_trans1 = peer()
+ worker2, _ignore_trans2 = peer()
</ins><span class="cx"> # Ask the worker to do something.
</span><span class="cx"> worker1.performWork(schema.DUMMY_WORK_ITEM, 1)
</span><span class="cx"> self.assertEquals(worker1.currentLoad, 1)
</span><span class="lines">@@ -619,11 +673,12 @@
</span><span class="cx"> self.receiver, self.sender = self.sender, self.receiver
</span><span class="cx"> return result
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def flush(self, turns=10):
</span><span class="cx"> """
</span><span class="cx"> Keep relaying data until there's no more.
</span><span class="cx"> """
</span><del>- for x in range(turns):
</del><ins>+ for _ignore_x in range(turns):
</ins><span class="cx"> if not (self.pump() or self.pump()):
</span><span class="cx"> return
</span><span class="cx">
</span><span class="lines">@@ -718,7 +773,7 @@
</span><span class="cx"> def op2(txn):
</span><span class="cx"> return Select([schema.DUMMY_WORK_DONE.WORK_ID,
</span><span class="cx"> schema.DUMMY_WORK_DONE.A_PLUS_B],
</span><del>- From=schema.DUMMY_WORK_DONE).on(txn)
</del><ins>+ From=schema.DUMMY_WORK_DONE).on(txn)
</ins><span class="cx"> rows = yield inTransaction(self.store.newTransaction, op2)
</span><span class="cx"> self.assertEquals(rows, [[4321, 7]])
</span><span class="cx">
</span><span class="lines">@@ -729,7 +784,7 @@
</span><span class="cx"> When a L{WorkItem} is concurrently deleted by another transaction, it
</span><span class="cx"> should I{not} perform its work.
</span><span class="cx"> """
</span><del>- # Provide access to a method called 'concurrently' everything using
</del><ins>+ # Provide access to a method called 'concurrently' everything using
</ins><span class="cx"> original = self.store.newTransaction
</span><span class="cx"> def decorate(*a, **k):
</span><span class="cx"> result = original(*a, **k)
</span><span class="lines">@@ -746,13 +801,13 @@
</span><span class="cx"> # Sanity check on the concurrent deletion.
</span><span class="cx"> def op2(txn):
</span><span class="cx"> return Select([schema.DUMMY_WORK_ITEM.WORK_ID],
</span><del>- From=schema.DUMMY_WORK_ITEM).on(txn)
</del><ins>+ From=schema.DUMMY_WORK_ITEM).on(txn)
</ins><span class="cx"> rows = yield inTransaction(self.store.newTransaction, op2)
</span><span class="cx"> self.assertEquals(rows, [])
</span><span class="cx"> def op3(txn):
</span><span class="cx"> return Select([schema.DUMMY_WORK_DONE.WORK_ID,
</span><span class="cx"> schema.DUMMY_WORK_DONE.A_PLUS_B],
</span><del>- From=schema.DUMMY_WORK_DONE).on(txn)
</del><ins>+ From=schema.DUMMY_WORK_DONE).on(txn)
</ins><span class="cx"> rows = yield inTransaction(self.store.newTransaction, op3)
</span><span class="cx"> self.assertEquals(rows, [])
</span><span class="cx">
</span><span class="lines">@@ -763,18 +818,23 @@
</span><span class="cx"> def __init__(self, *ignored):
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _start(self):
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class BaseQueuerTests(TestCase):
</span><span class="cx">
</span><span class="cx"> def setUp(self):
</span><span class="cx"> self.proposal = None
</span><span class="cx"> self.patch(twext.enterprise.queue, "WorkProposal", DummyProposal)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _proposalCallback(self, proposal):
</span><span class="cx"> self.proposal = proposal
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_proposalCallbacks(self):
</span><span class="cx"> queuer = _BaseQueuer()
</span><span class="cx"> queuer.callWithNewProposals(self._proposalCallback)
</span><span class="lines">@@ -783,6 +843,7 @@
</span><span class="cx"> self.assertNotEqual(self.proposal, None)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class NonPerformingQueuerTests(TestCase):
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -791,5 +852,3 @@
</span><span class="cx"> performer = queuer.choosePerformer()
</span><span class="cx"> result = (yield performer.performWork(None, None))
</span><span class="cx"> self.assertEquals(result, None)
</span><del>-
-
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextinternetsendfdportpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/internet/sendfdport.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/internet/sendfdport.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/internet/sendfdport.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -94,8 +94,8 @@
</span><span class="cx"> A socket in the master process pointing at a file descriptor that can be
</span><span class="cx"> used to transmit sockets to a subprocess.
</span><span class="cx">
</span><del>- @ivar skt: the UNIX socket used as the sendmsg() transport.
- @type skt: L{socket.socket}
</del><ins>+ @ivar outSocket: the UNIX socket used as the sendmsg() transport.
+ @type outSocket: L{socket.socket}
</ins><span class="cx">
</span><span class="cx"> @ivar outgoingSocketQueue: an outgoing queue of sockets to send to the
</span><span class="cx"> subprocess, along with their descriptions (strings describing their
</span><span class="lines">@@ -115,12 +115,13 @@
</span><span class="cx"> @type dispatcher: L{InheritedSocketDispatcher}
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def __init__(self, dispatcher, skt, status):
</del><ins>+ def __init__(self, dispatcher, inSocket, outSocket, status):
</ins><span class="cx"> FileDescriptor.__init__(self, dispatcher.reactor)
</span><span class="cx"> self.status = status
</span><span class="cx"> self.dispatcher = dispatcher
</span><del>- self.skt = skt # XXX needs to be set non-blocking by somebody
- self.fileno = skt.fileno
</del><ins>+ self.inSocket = inSocket
+ self.outSocket = outSocket # XXX needs to be set non-blocking by somebody
+ self.fileno = outSocket.fileno
</ins><span class="cx"> self.outgoingSocketQueue = []
</span><span class="cx"> self.pendingCloseSocketQueue = []
</span><span class="cx">
</span><span class="lines">@@ -138,7 +139,7 @@
</span><span class="cx"> Receive a status / health message and record it.
</span><span class="cx"> """
</span><span class="cx"> try:
</span><del>- data, _ignore_flags, _ignore_ancillary = recvmsg(self.skt.fileno())
</del><ins>+ data, _ignore_flags, _ignore_ancillary = recvmsg(self.outSocket.fileno())
</ins><span class="cx"> except SocketError, se:
</span><span class="cx"> if se.errno not in (EAGAIN, ENOBUFS):
</span><span class="cx"> raise
</span><span class="lines">@@ -155,7 +156,7 @@
</span><span class="cx"> while self.outgoingSocketQueue:
</span><span class="cx"> skt, desc = self.outgoingSocketQueue.pop(0)
</span><span class="cx"> try:
</span><del>- sendfd(self.skt.fileno(), skt.fileno(), desc)
</del><ins>+ sendfd(self.outSocket.fileno(), skt.fileno(), desc)
</ins><span class="cx"> except SocketError, se:
</span><span class="cx"> if se.errno in (EAGAIN, ENOBUFS):
</span><span class="cx"> self.outgoingSocketQueue.insert(0, (skt, desc))
</span><span class="lines">@@ -341,14 +342,27 @@
</span><span class="cx"> i, o = socketpair()
</span><span class="cx"> i.setblocking(False)
</span><span class="cx"> o.setblocking(False)
</span><del>- a = _SubprocessSocket(self, o, self.statusWatcher.initialStatus())
</del><ins>+ a = _SubprocessSocket(self, i, o, self.statusWatcher.initialStatus())
</ins><span class="cx"> self._subprocessSockets.append(a)
</span><span class="cx"> if self._isDispatching:
</span><span class="cx"> a.startReading()
</span><span class="cx"> return i
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def removeSocket(self, skt):
+ """
+ Removes a previously added socket from the pool of sockets being used
+ for transmitting file descriptors to child processes.
+ """
+ for a in self._subprocessSockets:
+ if a.inSocket == skt:
+ self._subprocessSockets.remove(a)
+ break
+ else:
+ raise ValueError("Unknown socket: {0}".format(skt))
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class InheritedPort(FileDescriptor, object):
</span><span class="cx"> """
</span><span class="cx"> An L{InheritedPort} is an L{IReadDescriptor}/L{IWriteDescriptor} created in
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextprotocolsechopyfromrev12016CalendarServertrunktwextprotocolsechopy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/echo.py (from rev 12016, CalendarServer/trunk/twext/protocols/echo.py) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/echo.py         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/echo.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,35 @@
</span><ins>+##
+# Copyright (c) 2013 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.
+##
+
+"""
+Echo protocol.
+"""
+
+__all__ = ["EchoProtocol"]
+
+from twisted.internet.protocol import Protocol
+
+
+class EchoProtocol(Protocol):
+ """
+ Say what you hear.
+ """
+
+ def dataReceived(self, data):
+ """
+ As soon as any data is received, write it back.
+ """
+ self.transport.write(data)
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextprotocolstesttest_memcachepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/test/test_memcache.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/test/test_memcache.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/protocols/test/test_memcache.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,7 +14,17 @@
</span><span class="cx"> from twisted.internet.defer import Deferred, gatherResults, TimeoutError
</span><span class="cx">
</span><span class="cx">
</span><ins>+def onConnectionLossFire(protocol, deferred):
+ """
+ When the given L{MemCacheProtocol} is disconnected, fire the given
+ L{Deferred} with L{None}.
+ """
+ def cl(reason):
+ deferred.callback(None)
+ protocol.connectionLost = cl
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class MemCacheTestCase(TestCase):
</span><span class="cx"> """
</span><span class="cx"> Test client protocol class L{MemCacheProtocol}.
</span><span class="lines">@@ -244,7 +254,7 @@
</span><span class="cx"> d1 = self.proto.get("foo")
</span><span class="cx"> d2 = self.proto.get("bar")
</span><span class="cx"> d3 = Deferred()
</span><del>- self.proto.connectionLost = d3.callback
</del><ins>+ onConnectionLossFire(self.proto, d3)
</ins><span class="cx">
</span><span class="cx"> self.clock.advance(self.proto.persistentTimeOut)
</span><span class="cx"> self.assertFailure(d1, TimeoutError)
</span><span class="lines">@@ -280,7 +290,7 @@
</span><span class="cx"> """
</span><span class="cx"> d1 = self.proto.get("foo")
</span><span class="cx"> d2 = Deferred()
</span><del>- self.proto.connectionLost = d2.callback
</del><ins>+ onConnectionLossFire(self.proto, d2)
</ins><span class="cx">
</span><span class="cx"> self.proto.dataReceived("VALUE foo 0 10\r\n12345")
</span><span class="cx"> self.clock.advance(self.proto.persistentTimeOut)
</span><span class="lines">@@ -295,7 +305,7 @@
</span><span class="cx"> """
</span><span class="cx"> d1 = self.proto.stats()
</span><span class="cx"> d2 = Deferred()
</span><del>- self.proto.connectionLost = d2.callback
</del><ins>+ onConnectionLossFire(self.proto, d2)
</ins><span class="cx">
</span><span class="cx"> self.proto.dataReceived("STAT foo bar\r\n")
</span><span class="cx"> self.clock.advance(self.proto.persistentTimeOut)
</span><span class="lines">@@ -311,7 +321,7 @@
</span><span class="cx"> d1 = self.proto.get("foo")
</span><span class="cx"> d2 = self.proto.get("bar")
</span><span class="cx"> d3 = Deferred()
</span><del>- self.proto.connectionLost = d3.callback
</del><ins>+ onConnectionLossFire(self.proto, d3)
</ins><span class="cx">
</span><span class="cx"> self.clock.advance(self.proto.persistentTimeOut - 1)
</span><span class="cx"> self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n")
</span><span class="lines">@@ -319,7 +329,7 @@
</span><span class="cx"> def check(result):
</span><span class="cx"> self.assertEquals(result, (0, "bar"))
</span><span class="cx"> self.assertEquals(len(self.clock.calls), 1)
</span><del>- for i in range(self.proto.persistentTimeOut):
</del><ins>+ for _ignore_i in range(self.proto.persistentTimeOut):
</ins><span class="cx"> self.clock.advance(1)
</span><span class="cx"> return self.assertFailure(d2, TimeoutError).addCallback(checkTime)
</span><span class="cx"> def checkTime(ignored):
</span><span class="lines">@@ -338,7 +348,7 @@
</span><span class="cx"> """
</span><span class="cx"> d1 = self.proto.get("foo")
</span><span class="cx"> d3 = Deferred()
</span><del>- self.proto.connectionLost = d3.callback
</del><ins>+ onConnectionLossFire(self.proto, d3)
</ins><span class="cx">
</span><span class="cx"> self.clock.advance(self.proto.persistentTimeOut - 1)
</span><span class="cx"> d2 = self.proto.get("bar")
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhoaggregatepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/aggregate.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/aggregate.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/aggregate.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -30,8 +30,8 @@
</span><span class="cx">
</span><span class="cx"> from twext.who.idirectory import DirectoryConfigurationError
</span><span class="cx"> from twext.who.idirectory import IDirectoryService
</span><del>-from twext.who.index import DirectoryService as BaseDirectoryService
-from twext.who.index import DirectoryRecord
</del><ins>+from twext.who.directory import DirectoryService as BaseDirectoryService
+from twext.who.directory import DirectoryRecord
</ins><span class="cx"> from twext.who.util import ConstantsContainer
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -78,10 +78,10 @@
</span><span class="cx"> return self._recordType
</span><span class="cx">
</span><span class="cx">
</span><del>- def recordsFromExpression(self, expression, records=None):
</del><ins>+ def recordsFromExpression(self, expression):
</ins><span class="cx"> ds = []
</span><span class="cx"> for service in self.services:
</span><del>- d = service.recordsFromExpression(expression, records)
</del><ins>+ d = service.recordsFromExpression(expression)
</ins><span class="cx"> ds.append(d)
</span><span class="cx">
</span><span class="cx"> def unwrapFirstError(f):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhodirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/directory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/directory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/directory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -24,35 +24,81 @@
</span><span class="cx"> "DirectoryRecord",
</span><span class="cx"> ]
</span><span class="cx">
</span><del>-from uuid import UUID
</del><ins>+from zope.interface import implementer
</ins><span class="cx">
</span><del>-from zope.interface import implements
-
</del><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx"> from twisted.internet.defer import succeed, fail
</span><span class="cx">
</span><span class="cx"> from twext.who.idirectory import QueryNotSupportedError, NotAllowedError
</span><span class="cx"> from twext.who.idirectory import FieldName, RecordType
</span><del>-from twext.who.idirectory import Operand
</del><span class="cx"> from twext.who.idirectory import IDirectoryService, IDirectoryRecord
</span><ins>+from twext.who.expression import CompoundExpression, Operand
</ins><span class="cx"> from twext.who.expression import MatchExpression
</span><span class="cx"> from twext.who.util import uniqueResult, describe
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><ins>+@implementer(IDirectoryService)
</ins><span class="cx"> class DirectoryService(object):
</span><del>- implements(IDirectoryService)
</del><ins>+ """
+ Generic (and abstract) implementation of L{IDirectoryService}.
</ins><span class="cx">
</span><ins>+ Most of the C{recordsWith*} methods call L{recordsWithFieldValue}, which in
+ turn calls L{recordsFromExpression} with a corresponding
+ L{MatchExpression}.
+
+ L{recordsFromExpression} relies on L{recordsFromNonCompoundExpression} for
+ all expression types other than L{CompoundExpression}, which it handles
+ directly.
+
+ L{recordsFromNonCompoundExpression} (and therefore most uses of the other
+ methods) will always fail with a L{QueryNotSupportedError}.
+
+ A subclass should therefore override L{recordsFromNonCompoundExpression}
+ with an implementation that handles any queries that it can support (which
+ should include L{MatchExpression}) and calls its superclass' implementation
+ with any query it cannot support.
+
+ A subclass may override L{recordsFromExpression} if it is to support
+ L{CompoundExpression}s with operands other than L{Operand.AND} and
+ L{Operand.OR}.
+
+ A subclass may override L{recordsFromExpression} if it is built on top
+ of a directory service that supports compound expressions, as that may be
+ more effient than relying on L{DirectoryService}'s implementation.
+
+ L{updateRecords} and L{removeRecords} will fail with L{NotAllowedError}
+ when asked to modify data.
+ A subclass should override these methods if is to allow editing of
+ directory information.
+
+ @cvar recordType: a L{Names} class or compatible object (eg.
+ L{ConstantsContainer}) which contains the L{NamedConstant}s denoting
+ the record types that are supported by this directory service.
+
+ @cvar fieldName: a L{Names} class or compatible object (eg.
+ L{ConstantsContainer}) which contains the L{NamedConstant}s denoting
+ the record field names that are supported by this directory service.
+
+ @cvar normalizedFields: a L{dict} mapping of (ie. L{NamedConstant}s
+ contained in the C{fieldName} class variable) to callables that take
+ a field value (a L{unicode}) and return a normalized field value (also
+ a L{unicode}).
+ """
+
</ins><span class="cx"> recordType = RecordType
</span><span class="cx"> fieldName = FieldName
</span><span class="cx">
</span><span class="cx"> normalizedFields = {
</span><del>- FieldName.guid: lambda g: UUID(g).hex,
- FieldName.emailAddresses: lambda e: e.lower(),
</del><ins>+ FieldName.emailAddresses: lambda e: bytes(e).lower(),
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def __init__(self, realmName):
</span><ins>+ """
+ @param realmName: a realm name
+ @type realmName: L{unicode}
+ """
</ins><span class="cx"> self.realmName = realmName
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -67,59 +113,98 @@
</span><span class="cx"> return self.recordType.iterconstants()
</span><span class="cx">
</span><span class="cx">
</span><del>- def recordsFromExpression(self, expression, records=None):
</del><ins>+ def recordsFromNonCompoundExpression(self, expression, records=None):
</ins><span class="cx"> """
</span><del>- Finds records matching a single expression.
- @param expression: an expression
</del><ins>+ Finds records matching a expression.
+
+ @note: This method is called by L{recordsFromExpression} to handle
+ all expressions other than L{CompoundExpression}.
+ This implementation always fails with L{QueryNotSupportedError}.
+ Subclasses should override this in order to handle additional
+ expression types, and call on the superclass' implementation
+ for other expression types.
+
+ @note: This interface is the same as L{recordsFromExpression}, except
+ for the additional C{records} argument.
+
+ @param expression: an expression to apply
</ins><span class="cx"> @type expression: L{object}
</span><del>- @param records: a set of records to search within. C{None} if
</del><ins>+
+ @param records: a set of records to limit the search to. C{None} if
</ins><span class="cx"> the whole directory should be searched.
</span><ins>+ This is provided by L{recordsFromExpression} when it has already
+ narrowed down results to a set of records.
+ That is, it's a performance optimization; ignoring this and
+ searching the entire directory will also work.
</ins><span class="cx"> @type records: L{set} or L{frozenset}
</span><ins>+
+ @return: The matching records.
+ @rtype: deferred iterable of L{IDirectoryRecord}s
+
+ @raises: L{QueryNotSupportedError} if the expression is not
+ supported by this directory service.
</ins><span class="cx"> """
</span><ins>+ if records is not None:
+ for record in records:
+ break
+ else:
+ return succeed(())
+
</ins><span class="cx"> return fail(QueryNotSupportedError(
</span><span class="cx"> "Unknown expression: {0}".format(expression)
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def recordsFromQuery(self, expressions, operand=Operand.AND):
- expressionIterator = iter(expressions)
</del><ins>+ def recordsFromExpression(self, expression):
+ if isinstance(expression, CompoundExpression):
+ operand = expression.operand
+ subExpressions = iter(expression.expressions)
</ins><span class="cx">
</span><del>- try:
- expression = expressionIterator.next()
- except StopIteration:
- returnValue(())
</del><ins>+ try:
+ subExpression = subExpressions.next()
+ except StopIteration:
+ returnValue(())
</ins><span class="cx">
</span><del>- results = set((yield self.recordsFromExpression(expression)))
</del><ins>+ results = set((
+ yield self.recordsFromNonCompoundExpression(subExpression)
+ ))
</ins><span class="cx">
</span><del>- for expression in expressions:
- if operand == Operand.AND:
- if not results:
- # No need to bother continuing here
- returnValue(())
</del><ins>+ for subExpression in subExpressions:
+ if operand == Operand.AND:
+ if not results:
+ # No need to bother continuing here
+ returnValue(())
</ins><span class="cx">
</span><del>- records = results
- else:
- records = None
</del><ins>+ records = results
+ else:
+ records = None
</ins><span class="cx">
</span><del>- recordsMatchingExpression = frozenset((
- yield self.recordsFromExpression(expression, records=records)
- ))
</del><ins>+ recordsMatchingExpression = frozenset((
+ yield self.recordsFromNonCompoundExpression(
+ subExpression,
+ records=records
+ )
+ ))
</ins><span class="cx">
</span><del>- if operand == Operand.AND:
- results &= recordsMatchingExpression
- elif operand == Operand.OR:
- results |= recordsMatchingExpression
- else:
- raise QueryNotSupportedError(
- "Unknown operand: {0}".format(operand)
- )
</del><ins>+ if operand == Operand.AND:
+ results &= recordsMatchingExpression
+ elif operand == Operand.OR:
+ results |= recordsMatchingExpression
+ else:
+ raise QueryNotSupportedError(
+ "Unknown operand: {0}".format(operand)
+ )
+ else:
+ results = yield self.recordsFromNonCompoundExpression(expression)
</ins><span class="cx">
</span><span class="cx"> returnValue(results)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def recordsWithFieldValue(self, fieldName, value):
</span><del>- return self.recordsFromExpression(MatchExpression(fieldName, value))
</del><ins>+ return self.recordsFromExpression(
+ MatchExpression(fieldName, value)
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -142,10 +227,17 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def recordWithShortName(self, recordType, shortName):
</span><del>- returnValue(uniqueResult((yield self.recordsFromQuery((
- MatchExpression(FieldName.recordType, recordType),
- MatchExpression(FieldName.shortNames, shortName),
- )))))
</del><ins>+ returnValue(uniqueResult((
+ yield self.recordsFromExpression(
+ CompoundExpression(
+ (
+ MatchExpression(FieldName.recordType, recordType),
+ MatchExpression(FieldName.shortNames, shortName),
+ ),
+ operand=Operand.AND
+ )
+ )
+ )))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def recordsWithEmailAddress(self, emailAddress):
</span><span class="lines">@@ -158,17 +250,32 @@
</span><span class="cx"> def updateRecords(self, records, create=False):
</span><span class="cx"> for record in records:
</span><span class="cx"> return fail(NotAllowedError("Record updates not allowed."))
</span><ins>+ return succeed(None)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def removeRecords(self, uids):
</span><span class="cx"> for uid in uids:
</span><span class="cx"> return fail(NotAllowedError("Record removal not allowed."))
</span><ins>+ return succeed(None)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><ins>+@implementer(IDirectoryRecord)
</ins><span class="cx"> class DirectoryRecord(object):
</span><del>- implements(IDirectoryRecord)
</del><ins>+ """
+ Generic implementation of L{IDirectoryService}.
</ins><span class="cx">
</span><ins>+ This is an incomplete implementation of L{IDirectoryRecord}.
+
+ L{groups} will always fail with L{NotImplementedError} and L{members} will
+ do so if this is a group record.
+ A subclass should override these methods to support group membership and
+ complete this implementation.
+
+ @cvar requiredFields: an iterable of field names that must be present in
+ all directory records.
+ """
+
</ins><span class="cx"> requiredFields = (
</span><span class="cx"> FieldName.uid,
</span><span class="cx"> FieldName.recordType,
</span><span class="lines">@@ -183,11 +290,6 @@
</span><span class="cx">
</span><span class="cx"> if FieldName.isMultiValue(fieldName):
</span><span class="cx"> values = fields[fieldName]
</span><del>- if len(values) == 0:
- raise ValueError(
- "{0} field must have at least one value."
- .format(fieldName)
- )
</del><span class="cx"> for value in values:
</span><span class="cx"> if not value:
</span><span class="cx"> raise ValueError(
</span><span class="lines">@@ -262,32 +364,47 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def description(self):
</span><del>- description = [self.__class__.__name__, ":"]
</del><ins>+ """
+ Generate a string description of this directory record.
</ins><span class="cx">
</span><del>- for name, value in self.fields.items():
</del><ins>+ @return: A description.
+ @rtype: L{unicode}
+ """
+ description = [self.__class__.__name__, u":"]
+
+ for name in sorted(self.service.fieldName.iterconstants()):
+ if name not in self.fields:
+ continue
+
+ value = self.fields[name]
+
</ins><span class="cx"> if hasattr(name, "description"):
</span><span class="cx"> name = name.description
</span><span class="cx"> else:
</span><del>- name = str(name)
</del><ins>+ name = unicode(name)
</ins><span class="cx">
</span><span class="cx"> if hasattr(value, "description"):
</span><span class="cx"> value = value.description
</span><span class="cx"> else:
</span><del>- value = str(value)
</del><ins>+ value = unicode(value)
</ins><span class="cx">
</span><del>- description.append("\n ")
</del><ins>+ description.append(u"\n ")
</ins><span class="cx"> description.append(name)
</span><del>- description.append(" = ")
</del><ins>+ description.append(u" = ")
</ins><span class="cx"> description.append(value)
</span><span class="cx">
</span><del>- return "".join(description)
</del><ins>+ description.append(u"\n")
</ins><span class="cx">
</span><ins>+ return u"".join(description)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def members(self):
</span><span class="cx"> if self.recordType == RecordType.group:
</span><del>- raise NotImplementedError("Subclasses must implement members()")
</del><ins>+ return fail(
+ NotImplementedError("Subclasses must implement members()")
+ )
</ins><span class="cx"> return succeed(())
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def groups(self):
</span><del>- raise NotImplementedError("Subclasses must implement groups()")
</del><ins>+ return fail(NotImplementedError("Subclasses must implement groups()"))
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhoexpressionpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/expression.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/expression.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/expression.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -20,6 +20,9 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> __all__ = [
</span><ins>+ "Operand",
+ "CompoundExpression",
+
</ins><span class="cx"> "MatchType",
</span><span class="cx"> "MatchFlags",
</span><span class="cx"> "MatchExpression",
</span><span class="lines">@@ -28,25 +31,55 @@
</span><span class="cx"> from twisted.python.constants import Names, NamedConstant
</span><span class="cx"> from twisted.python.constants import Flags, FlagConstant
</span><span class="cx">
</span><ins>+from twext.who.util import iterFlags, describe
</ins><span class="cx">
</span><span class="cx">
</span><del>-##
-# Match expression
-##
</del><ins>+#
+# Compound expression
+#
</ins><span class="cx">
</span><ins>+class Operand(Names):
+ """
+ Contants for common operands.
+ """
+ OR = NamedConstant()
+ AND = NamedConstant()
</ins><span class="cx">
</span><ins>+ OR.description = u"or"
+ AND.description = u"and"
</ins><span class="cx">
</span><ins>+
+
+class CompoundExpression(object):
+ """
+ An expression that groups multiple expressions with an operand.
+
+ @ivar expressions: An iterable of expressions.
+
+ @ivar operand: A L{NamedConstant} specifying an operand.
+ """
+
+ def __init__(self, expressions, operand):
+ self.expressions = expressions
+ self.operand = operand
+
+
+#
+# Match expression
+#
+
</ins><span class="cx"> class MatchType(Names):
</span><span class="cx"> """
</span><span class="cx"> Query match types.
</span><span class="cx"> """
</span><del>- equals = NamedConstant()
</del><ins>+ equals = NamedConstant()
+ equals.description = u"equals"
+
</ins><span class="cx"> startsWith = NamedConstant()
</span><del>- contains = NamedConstant()
</del><ins>+ startsWith.description = u"starts with"
</ins><span class="cx">
</span><del>- equals.description = "equals"
- startsWith.description = "starts with"
- contains.description = "contains"
</del><ins>+ contains = NamedConstant()
+ contains.description = u"contains"
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -55,21 +88,95 @@
</span><span class="cx"> Match expression flags.
</span><span class="cx"> """
</span><span class="cx"> NOT = FlagConstant()
</span><del>- NOT.description = "not"
</del><ins>+ NOT.description = u"not"
</ins><span class="cx">
</span><span class="cx"> caseInsensitive = FlagConstant()
</span><del>- caseInsensitive.description = "case insensitive"
</del><ins>+ caseInsensitive.description = u"case insensitive"
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ @staticmethod
+ def _setMatchFunctions(flags):
+ """
+ Compute a predicate and normalize functions for the given match
+ expression flags.
</ins><span class="cx">
</span><ins>+ @param flags: Match expression flags.
+ @type flags: L{MatchFlags}
+
+ @return: Predicate and normalize functions.
+ @rtype: L{tuple} of callables.
+ """
+ predicate = lambda x: x
+ normalize = lambda x: x
+
+ if flags is None:
+ flags = FlagConstant()
+ else:
+ for flag in iterFlags(flags):
+ if flag == MatchFlags.NOT:
+ predicate = lambda x: not x
+ elif flag == MatchFlags.caseInsensitive:
+ normalize = lambda x: x.lower()
+ else:
+ raise NotImplementedError(
+ "Unknown query flag: {0}".format(describe(flag))
+ )
+
+ flags._predicate = predicate
+ flags._normalize = normalize
+
+ return flags
+
+
+ @staticmethod
+ def predicator(flags):
+ """
+ Determine a predicate function for the given flags.
+
+ @param flags: Match expression flags.
+ @type flags: L{MatchFlags}
+
+ @return: a L{callable} that accepts an L{object} argument and returns a
+ L{object} that has the opposite or same truth value as the argument,
+ depending on whether L{MatchFlags.NOT} is or is not in C{flags}.
+ @rtype: callable
+ """
+ if not hasattr(flags, "_predicate"):
+ flags = MatchFlags._setMatchFunctions(flags)
+ return flags._predicate
+
+
+ @staticmethod
+ def normalizer(flags):
+ """
+ Determine a predicate function for the given flags.
+
+ @param flags: Match expression flags.
+ @type flags: L{MatchFlags}
+
+ @return: a L{callable} that accepts a L{unicode} and returns the same
+ L{unicode} or a normalized L{unicode} that can be compared with other
+ normalized L{unicode}s in a case-insensitive fashion, depending on
+ whether L{MatchFlags.caseInsensitive} is not or is in C{flags}.
+ @rtype: callable
+ """
+ if not hasattr(flags, "_normalize"):
+ flags = MatchFlags._setMatchFunctions(flags)
+ return flags._normalize
+
+
+
</ins><span class="cx"> class MatchExpression(object):
</span><span class="cx"> """
</span><span class="cx"> Query for a matching value in a given field.
</span><span class="cx">
</span><del>- @ivar fieldName: a L{NamedConstant} specifying the field
- @ivar fieldValue: a text value to match
- @ivar matchType: a L{NamedConstant} specifying the match algorythm
- @ivar flags: L{NamedConstant} specifying additional options
</del><ins>+ @ivar fieldName: A L{NamedConstant} specifying the field.
+
+ @ivar fieldValue: A value to match.
+
+ @ivar matchType: A L{NamedConstant} specifying the match algorithm.
+
+ @ivar flags: A L{NamedConstant} specifying additional options.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def __init__(
</span><span class="lines">@@ -77,14 +184,15 @@
</span><span class="cx"> fieldName, fieldValue,
</span><span class="cx"> matchType=MatchType.equals, flags=None
</span><span class="cx"> ):
</span><del>- self.fieldName = fieldName
</del><ins>+ self.fieldName = fieldName
</ins><span class="cx"> self.fieldValue = fieldValue
</span><del>- self.matchType = matchType
- self.flags = flags
</del><ins>+ self.matchType = matchType
+ self.flags = flags
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def __repr__(self):
</span><span class="cx"> def describe(constant):
</span><del>- return getattr(constant, "description", str(constant))
</del><ins>+ return getattr(constant, "description", unicode(constant))
</ins><span class="cx">
</span><span class="cx"> if self.flags is None:
</span><span class="cx"> flags = ""
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhoidirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/idirectory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/idirectory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/idirectory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx"> ##
</span><span class="cx">
</span><span class="cx"> """
</span><del>-Directory service interface.
</del><ins>+Directory service interfaces.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> __all__ = [
</span><span class="lines">@@ -30,21 +30,22 @@
</span><span class="cx">
</span><span class="cx"> "RecordType",
</span><span class="cx"> "FieldName",
</span><del>- "Operand",
</del><span class="cx">
</span><span class="cx"> "IDirectoryService",
</span><span class="cx"> "IDirectoryRecord",
</span><span class="cx"> ]
</span><span class="cx">
</span><ins>+from uuid import UUID
+
</ins><span class="cx"> from zope.interface import Attribute, Interface
</span><span class="cx">
</span><span class="cx"> from twisted.python.constants import Names, NamedConstant
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-##
</del><ins>+#
</ins><span class="cx"> # Exceptions
</span><del>-##
</del><ins>+#
</ins><span class="cx">
</span><span class="cx"> class DirectoryServiceError(Exception):
</span><span class="cx"> """
</span><span class="lines">@@ -55,7 +56,7 @@
</span><span class="cx">
</span><span class="cx"> class DirectoryConfigurationError(DirectoryServiceError):
</span><span class="cx"> """
</span><del>- Directory configurtion error.
</del><ins>+ Directory configuration error.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -93,27 +94,54 @@
</span><span class="cx">
</span><span class="cx"> class NotAllowedError(DirectoryServiceError):
</span><span class="cx"> """
</span><del>- Apparently, you can't do that.
</del><ins>+ It seems you aren't permitted to do that.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-##
</del><ins>+#
</ins><span class="cx"> # Data Types
</span><del>-##
</del><ins>+#
</ins><span class="cx">
</span><span class="cx"> class RecordType(Names):
</span><ins>+ """
+ Constants for common directory record types.
+ """
</ins><span class="cx"> user = NamedConstant()
</span><span class="cx"> group = NamedConstant()
</span><span class="cx">
</span><del>- user.description = "user"
- group.description = "group"
</del><ins>+ user.description = u"user"
+ group.description = u"group"
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class FieldName(Names):
</span><span class="cx"> """
</span><del>- Constants for common field names.
</del><ins>+ Constants for common directory record field names.
+
+ Fields as assciated with either a single value or an iterable of values.
+
+ @cvar uid: The primary unique identifier for a directory record.
+ The associated value must be a L{unicode}.
+
+ @cvar guid: The globally unique identifier for a directory record.
+ The associated value must be a L{UUID} or C{None}.
+
+ @cvar recordType: The type of a directory record.
+ The associated value must be a L{NamedConstant}.
+
+ @cvar shortNames: The short names for a directory record.
+ The associated values must L{unicode}s and there must be at least
+ one associated value.
+
+ @cvar fullNames: The full names for a directory record.
+ The associated values must be L{unicode}s.
+
+ @cvar emailAddresses: The email addresses for a directory record.
+ The associated values must be L{unicodes}.
+
+ @cvar password: The clear text password for a directory record.
+ The associated value must be a L{unicode} or C{None}.
</ins><span class="cx"> """
</span><span class="cx"> uid = NamedConstant()
</span><span class="cx"> guid = NamedConstant()
</span><span class="lines">@@ -123,14 +151,16 @@
</span><span class="cx"> emailAddresses = NamedConstant()
</span><span class="cx"> password = NamedConstant()
</span><span class="cx">
</span><del>- uid.description = "UID"
- guid.description = "GUID"
- recordType.description = "record type"
- shortNames.description = "short names"
- fullNames.description = "full names"
- emailAddresses.description = "email addresses"
- password.description = "password"
</del><ins>+ uid.description = u"UID"
+ guid.description = u"GUID"
+ recordType.description = u"record type"
+ shortNames.description = u"short names"
+ fullNames.description = u"full names"
+ emailAddresses.description = u"email addresses"
+ password.description = u"password"
</ins><span class="cx">
</span><ins>+ guid.valueType = UUID
+
</ins><span class="cx"> shortNames.multiValue = True
</span><span class="cx"> fullNames.multiValue = True
</span><span class="cx"> emailAddresses.multiValue = True
</span><span class="lines">@@ -138,22 +168,36 @@
</span><span class="cx">
</span><span class="cx"> @staticmethod
</span><span class="cx"> def isMultiValue(name):
</span><ins>+ """
+ Check for whether a field is multi-value (as opposed to single-value).
+
+ @param name: The name of the field.
+ @type name: L{NamedConstant}
+
+ @return: C{True} if the field is multi-value, C{False} otherwise.
+ @rtype: L{BOOL}
+ """
</ins><span class="cx"> return getattr(name, "multiValue", False)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ @staticmethod
+ def valueType(name):
+ """
+ Check for the expected type of values for a field.
</ins><span class="cx">
</span><del>-class Operand(Names):
- OR = NamedConstant()
- AND = NamedConstant()
</del><ins>+ @param name: The name of the field.
+ @type name: L{NamedConstant}
</ins><span class="cx">
</span><del>- OR.description = "or"
- AND.description = "and"
</del><ins>+ @return: The expected type.
+ @rtype: L{type}
+ """
+ return getattr(name, "valueType", unicode)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-##
</del><ins>+#
</ins><span class="cx"> # Interfaces
</span><del>-##
</del><ins>+#
</ins><span class="cx">
</span><span class="cx"> class IDirectoryService(Interface):
</span><span class="cx"> """
</span><span class="lines">@@ -169,7 +213,15 @@
</span><span class="cx">
</span><span class="cx"> A directory service may allow support the editing, removal and
</span><span class="cx"> addition of records.
</span><ins>+ Services are read-only should fail with L{NotAllowedError} in editing
+ methods.
+
+ The L{FieldName.uid} field, the L{FieldName.guid} field (if not C{None}),
+ and the combination of the L{FieldName.recordType} and
+ L{FieldName.shortName} fields must be unique to each directory record
+ vended by a directory service.
</ins><span class="cx"> """
</span><ins>+
</ins><span class="cx"> realmName = Attribute(
</span><span class="cx"> "The name of the authentication realm this service represents."
</span><span class="cx"> )
</span><span class="lines">@@ -177,32 +229,24 @@
</span><span class="cx">
</span><span class="cx"> def recordTypes():
</span><span class="cx"> """
</span><del>- @return: an iterable of L{NamedConstant}s denoting the record
- types that are kept in this directory.
</del><ins>+ Get the record types supported by this directory service.
+
+ @return: The record types that are supported by this directory service.
+ @rtype: iterable of L{NamedConstant}s
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><del>- def recordsFromExpression(self, expression):
</del><ins>+ def recordsFromExpression(expression):
</ins><span class="cx"> """
</span><span class="cx"> Find records matching an expression.
</span><ins>+
</ins><span class="cx"> @param expression: an expression to apply
</span><span class="cx"> @type expression: L{object}
</span><del>- @return: a deferred iterable of matching L{IDirectoryRecord}s.
- @raises: L{QueryNotSupportedError} if the expression is not
- supported by this directory service.
- """
</del><span class="cx">
</span><ins>+ @return: The matching records.
+ @rtype: deferred iterable of L{IDirectoryRecord}s
</ins><span class="cx">
</span><del>- def recordsFromQuery(expressions, operand=Operand.AND):
- """
- Find records by composing a query consisting of an iterable of
- expressions and an operand.
- @param expressions: expressions to query against
- @type expressions: iterable of L{object}s
- @param operand: an operand
- @type operand: a L{NamedConstant}
- @return: a deferred iterable of matching L{IDirectoryRecord}s.
- @raises: L{QueryNotSupportedError} if the query is not
</del><ins>+ @raises: L{QueryNotSupportedError} if the expression is not
</ins><span class="cx"> supported by this directory service.
</span><span class="cx"> """
</span><span class="cx">
</span><span class="lines">@@ -211,80 +255,111 @@
</span><span class="cx"> """
</span><span class="cx"> Find records that have the given field name with the given
</span><span class="cx"> value.
</span><ins>+
</ins><span class="cx"> @param fieldName: a field name
</span><span class="cx"> @type fieldName: L{NamedConstant}
</span><ins>+
</ins><span class="cx"> @param value: a value to match
</span><span class="cx"> @type value: L{bytes}
</span><del>- @return: a deferred iterable of L{IDirectoryRecord}s.
</del><ins>+
+ @return: The matching records.
+ @rtype: deferred iterable of L{IDirectoryRecord}s
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def recordWithUID(uid):
</span><span class="cx"> """
</span><span class="cx"> Find the record that has the given UID.
</span><ins>+
</ins><span class="cx"> @param uid: a UID
</span><span class="cx"> @type uid: L{bytes}
</span><del>- @return: a deferred iterable of L{IDirectoryRecord}s, or
- C{None} if there is no such record.
</del><ins>+
+ @return: The matching record or C{None} if there is no match.
+ @rtype: deferred L{IDirectoryRecord}s or C{None}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def recordWithGUID(guid):
</span><span class="cx"> """
</span><span class="cx"> Find the record that has the given GUID.
</span><ins>+
</ins><span class="cx"> @param guid: a GUID
</span><del>- @type guid: L{bytes}
- @return: a deferred iterable of L{IDirectoryRecord}s, or
- C{None} if there is no such record.
</del><ins>+ @type guid: L{UUID}
+
+ @return: The matching record or C{None} if there is no match.
+ @rtype: deferred L{IDirectoryRecord}s or C{None}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def recordsWithRecordType(recordType):
</span><span class="cx"> """
</span><span class="cx"> Find the records that have the given record type.
</span><ins>+
</ins><span class="cx"> @param recordType: a record type
</span><span class="cx"> @type recordType: L{NamedConstant}
</span><del>- @return: a deferred iterable of L{IDirectoryRecord}s.
</del><ins>+
+ @return: The matching records.
+ @rtype: deferred iterable of L{IDirectoryRecord}s
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def recordWithShortName(recordType, shortName):
</span><span class="cx"> """
</span><span class="cx"> Find the record that has the given record type and short name.
</span><ins>+
</ins><span class="cx"> @param recordType: a record type
</span><span class="cx"> @type recordType: L{NamedConstant}
</span><ins>+
</ins><span class="cx"> @param shortName: a short name
</span><span class="cx"> @type shortName: L{bytes}
</span><del>- @return: a deferred iterable of L{IDirectoryRecord}s, or
- C{None} if there is no such record.
</del><ins>+
+ @return: The matching record or C{None} if there is no match.
+ @rtype: deferred L{IDirectoryRecord}s or C{None}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def recordsWithEmailAddress(emailAddress):
</span><span class="cx"> """
</span><span class="cx"> Find the records that have the given email address.
</span><ins>+
</ins><span class="cx"> @param emailAddress: an email address
</span><span class="cx"> @type emailAddress: L{bytes}
</span><del>- @return: a deferred iterable of L{IDirectoryRecord}s, or
- C{None} if there is no such record.
</del><ins>+
+ @return: The matching records.
+ @rtype: deferred iterable of L{IDirectoryRecord}s
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def updateRecords(records, create=False):
</span><span class="cx"> """
</span><span class="cx"> Updates existing directory records.
</span><ins>+
</ins><span class="cx"> @param records: the records to update
</span><span class="cx"> @type records: iterable of L{IDirectoryRecord}s
</span><ins>+
</ins><span class="cx"> @param create: if true, create records if necessary
</span><span class="cx"> @type create: boolean
</span><ins>+
+ @return: unspecifiied
+ @rtype: deferred object
+
+ @raises L{NotAllowedError}: if the update is not allowed by the
+ directory service.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def removeRecords(uids):
</span><span class="cx"> """
</span><span class="cx"> Removes the records with the given UIDs.
</span><ins>+
</ins><span class="cx"> @param uids: the UIDs of the records to remove
</span><span class="cx"> @type uids: iterable of L{bytes}
</span><ins>+
+ @return: unspecifiied
+ @rtype: deferred object
+
+ @raises L{NotAllowedError}: if the removal is not allowed by the
+ directory service.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -325,6 +400,7 @@
</span><span class="cx"> """
</span><span class="cx"> Find the records that are members of this group. Only direct
</span><span class="cx"> members are included; members of members are not expanded.
</span><ins>+
</ins><span class="cx"> @return: a deferred iterable of L{IDirectoryRecord}s which are
</span><span class="cx"> direct members of this group.
</span><span class="cx"> """
</span><span class="lines">@@ -335,6 +411,7 @@
</span><span class="cx"> Find the group records that this record is a member of. Only
</span><span class="cx"> groups for which this record is a direct member is are
</span><span class="cx"> included; membership is not expanded.
</span><ins>+
</ins><span class="cx"> @return: a deferred iterable of L{IDirectoryRecord}s which are
</span><span class="cx"> groups that this record is a member of.
</span><span class="cx"> """
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhoindexpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/index.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/index.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/index.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -30,7 +30,7 @@
</span><span class="cx"> from twisted.internet.defer import succeed, inlineCallbacks, returnValue
</span><span class="cx">
</span><span class="cx"> from twext.who.util import ConstantsContainer
</span><del>-from twext.who.util import describe, uniqueResult, iterFlags
</del><ins>+from twext.who.util import uniqueResult
</ins><span class="cx"> from twext.who.idirectory import FieldName as BaseFieldName
</span><span class="cx"> from twext.who.expression import MatchExpression, MatchType, MatchFlags
</span><span class="cx"> from twext.who.directory import DirectoryService as BaseDirectoryService
</span><span class="lines">@@ -44,7 +44,7 @@
</span><span class="cx">
</span><span class="cx"> class FieldName(Names):
</span><span class="cx"> memberUIDs = NamedConstant()
</span><del>- memberUIDs.description = "member UIDs"
</del><ins>+ memberUIDs.description = u"member UIDs"
</ins><span class="cx"> memberUIDs.multiValue = True
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -55,7 +55,91 @@
</span><span class="cx">
</span><span class="cx"> class DirectoryService(BaseDirectoryService):
</span><span class="cx"> """
</span><del>- XML directory service.
</del><ins>+ Generic (and abstract) in-memory-indexed directory service.
+
+ This class implements the record access API in L{BaseDirectoryService} by
+ caching all records in an in-memory dictionary.
+
+ Each indexed field has a top-level key in the index and in turn contains
+ a dictionary in which keys are field values, and values are directory
+ records which have a matching field value for the cooresponding key::
+
+ {
+ <FieldName1>: {
+ <value1a>: set([<record1a1>, ...]),
+ ...
+ },
+ ...
+ }
+
+ Here is an example index for a service with a three user records and one
+ group record::
+
+ {
+ <FieldName=uid>: {
+ u'__calendar-dev__': set([
+ <DirectoryRecord (group)calendar-dev>
+ ]),
+ u'__dre__': set([
+ <DirectoryRecord (user)dre>
+ ]),
+ u'__sagen__': set([
+ <DirectoryRecord (user)sagen>
+ ]),
+ u'__wsanchez__': set([
+ <DirectoryRecord (user)wsanchez>
+ ])
+ },
+ <FieldName=recordType>: {
+ <RecordType=group>: set([
+ <DirectoryRecord (group)calendar-dev>,
+ ]),
+ <RecordType=user>: set([
+ <DirectoryRecord (user)sagen>,
+ <DirectoryRecord (user)wsanchez>
+ ])
+ },
+ <FieldName=shortNames>: {
+ u'calendar-dev': set([<DirectoryRecord (group)calendar-dev>]),
+ u'dre': set([<DirectoryRecord (user)dre>]),
+ u'sagen': set([<DirectoryRecord (user)sagen>]),
+ u'wilfredo_sanchez': set([<DirectoryRecord (user)wsanchez>]),
+ u'wsanchez': set([<DirectoryRecord (user)wsanchez>])
+ },
+ <FieldName=emailAddresses>: {
+ 'dev@bitbucket.calendarserver.org': set([
+ <DirectoryRecord (group)calendar-dev>
+ ]),
+ 'dre@bitbucket.calendarserver.org': set([
+ <DirectoryRecord (user)dre>
+ ]),
+ 'sagen@bitbucket.calendarserver.org': set([
+ <DirectoryRecord (user)sagen>
+ ]),
+ 'shared@example.com': set([
+ <DirectoryRecord (user)sagen>,
+ <DirectoryRecord (user)dre>
+ ]),
+ 'wsanchez@bitbucket.calendarserver.org': set([
+ <DirectoryRecord (user)wsanchez>
+ ]),
+ 'wsanchez@devnull.twistedmatrix.com': set([
+ <DirectoryRecord (user)wsanchez>
+ ])
+ },
+ <FieldName=memberUIDs>: {
+ u'__sagen__': set([<DirectoryRecord (group)calendar-dev>]),
+ u'__wsanchez__': set([<DirectoryRecord (group)calendar-dev>])
+ }
+ }
+
+ The field names that are indexed are defined by the C{indexedFields}
+ attribute of the service.
+
+ A subclass must override L{loadRecords}, which populates the index.
+
+ @cvar indexedFields: an iterable of field names (C{NamedConstant})
+ which are indexed.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> fieldName = ConstantsContainer(chain(
</span><span class="lines">@@ -81,58 +165,95 @@
</span><span class="cx">
</span><span class="cx"> @property
</span><span class="cx"> def index(self):
</span><ins>+ """
+ Call L{loadRecords} and return the index.
+ """
</ins><span class="cx"> self.loadRecords()
</span><span class="cx"> return self._index
</span><span class="cx">
</span><span class="cx">
</span><del>- @index.setter
- def index(self, value):
- self._index = value
</del><ins>+ def loadRecords(self):
+ """
+ Load records. This method is called by the L{index} property and
+ provides a hook into which the index can be updated.
</ins><span class="cx">
</span><ins>+ This method must be implemented by subclasses.
</ins><span class="cx">
</span><del>- def loadRecords(self):
</del><ins>+ An example implementation::
+
+ def loadRecords(self):
+ self.flush()
+ while True:
+ records = readSomeRecordsFromMyBackEnd()
+ if not records:
+ break
+ self.indexRecords(records)
</ins><span class="cx"> """
</span><del>- Load records.
- """
</del><span class="cx"> raise NotImplementedError("Subclasses must implement loadRecords().")
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def indexRecords(self, records):
+ """
+ Add some records to the index.
+
+ @param records: The records to index.
+ @type records: iterable of L{DirectoryRecord}
+ """
+ index = self._index
+
+ for fieldName in self.indexedFields:
+ index.setdefault(fieldName, {})
+
+ for record in records:
+ for fieldName in self.indexedFields:
+ values = record.fields.get(fieldName, None)
+
+ if values is not None:
+ if not BaseFieldName.isMultiValue(fieldName):
+ values = (values,)
+
+ for value in values:
+ index[fieldName].setdefault(value, set()).add(record)
+
+
</ins><span class="cx"> def flush(self):
</span><span class="cx"> """
</span><span class="cx"> Flush the index.
</span><span class="cx"> """
</span><del>- self._index = None
</del><ins>+ index = {}
</ins><span class="cx">
</span><ins>+ for fieldName in self.indexedFields:
+ index.setdefault(fieldName, {})
</ins><span class="cx">
</span><del>- @staticmethod
- def _queryFlags(flags):
- predicate = lambda x: x
- normalize = lambda x: x
</del><ins>+ self._index = index
</ins><span class="cx">
</span><del>- if flags is not None:
- for flag in iterFlags(flags):
- if flag == MatchFlags.NOT:
- predicate = lambda x: not x
- elif flag == MatchFlags.caseInsensitive:
- normalize = lambda x: x.lower()
- else:
- raise NotImplementedError(
- "Unknown query flag: {0}".format(describe(flag))
- )
</del><span class="cx">
</span><del>- return predicate, normalize
</del><ins>+ def indexedRecordsFromMatchExpression(self, expression, records=None):
+ """
+ Finds records in the internal indexes matching a single expression.
</ins><span class="cx">
</span><ins>+ @param expression: An expression.
+ @type expression: L{MatchExpression}
</ins><span class="cx">
</span><del>- def indexedRecordsFromMatchExpression(self, expression, records=None):
</del><ins>+ @param records: a set of records to limit the search to. C{None} if
+ the whole directory should be searched.
+ @type records: L{set} or L{frozenset}
+
+ @return: The matching records.
+ @rtype: deferred iterable of L{DirectoryRecord}s
</ins><span class="cx"> """
</span><del>- Finds records in the internal indexes matching a single
- expression.
- @param expression: an expression
- @type expression: L{object}
- """
- predicate, normalize = self._queryFlags(expression.flags)
</del><ins>+ predicate = MatchFlags.predicator(expression.flags)
+ normalize = MatchFlags.normalizer(expression.flags)
</ins><span class="cx">
</span><del>- fieldIndex = self.index[expression.fieldName]
</del><ins>+ try:
+ fieldIndex = self.index[expression.fieldName]
+ except KeyError:
+ raise TypeError(
+ "indexedRecordsFromMatchExpression() was passed an "
+ "expression with an unindexed field: {0!r}"
+ .format(expression.fieldName)
+ )
+
</ins><span class="cx"> matchValue = normalize(expression.fieldValue)
</span><span class="cx"> matchType = expression.matchType
</span><span class="cx">
</span><span class="lines">@@ -156,27 +277,36 @@
</span><span class="cx"> )
</span><span class="cx"> else:
</span><span class="cx"> raise NotImplementedError(
</span><del>- "Unknown match type: {0}".format(describe(matchType))
</del><ins>+ "Unknown match type: {0!r}".format(matchType)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> matchingRecords = set()
</span><span class="cx"> for key in indexKeys:
</span><span class="cx"> matchingRecords |= fieldIndex.get(key, frozenset())
</span><span class="cx">
</span><del>- if records is not None:
- matchingRecords &= records
</del><ins>+ # Not necessary, so don't unless we know it's a performance win:
+ # if records is not None:
+ # matchingRecords &= records
</ins><span class="cx">
</span><span class="cx"> return succeed(matchingRecords)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def unIndexedRecordsFromMatchExpression(self, expression, records=None):
</span><span class="cx"> """
</span><del>- Finds records not in the internal indexes matching a single
- expression.
- @param expression: an expression
- @type expression: L{object}
</del><ins>+ Finds records not in the internal indexes matching a single expression.
+
+ @param expression: An expression.
+ @type expression: L{MatchExpression}
+
+ @param records: a set of records to limit the search to. C{None} if
+ the whole directory should be searched.
+ @type records: L{set} or L{frozenset}
+
+ @return: The matching records.
+ @rtype: deferred iterable of L{DirectoryRecord}s
</ins><span class="cx"> """
</span><del>- predicate, normalize = self._queryFlags(expression.flags)
</del><ins>+ predicate = MatchFlags.predicator(expression.flags)
+ normalize = MatchFlags.normalizer(expression.flags)
</ins><span class="cx">
</span><span class="cx"> matchValue = normalize(expression.fieldValue)
</span><span class="cx"> matchType = expression.matchType
</span><span class="lines">@@ -191,7 +321,7 @@
</span><span class="cx"> match = lambda fieldValue: predicate(fieldValue == matchValue)
</span><span class="cx"> else:
</span><span class="cx"> raise NotImplementedError(
</span><del>- "Unknown match type: {0}".format(describe(matchType))
</del><ins>+ "Unknown match type: {0!r}".format(matchType)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> result = set()
</span><span class="lines">@@ -215,7 +345,11 @@
</span><span class="cx"> return succeed(result)
</span><span class="cx">
</span><span class="cx">
</span><del>- def recordsFromExpression(self, expression, records=None):
</del><ins>+ def recordsFromNonCompoundExpression(self, expression, records=None):
+ """
+ This implementation can handle L{MatchExpression} expressions; other
+ expressions are passed up to the superclass.
+ """
</ins><span class="cx"> if isinstance(expression, MatchExpression):
</span><span class="cx"> if expression.fieldName in self.indexedFields:
</span><span class="cx"> return self.indexedRecordsFromMatchExpression(
</span><span class="lines">@@ -226,7 +360,7 @@
</span><span class="cx"> expression, records=records
</span><span class="cx"> )
</span><span class="cx"> else:
</span><del>- return BaseDirectoryService.recordsFromExpression(
</del><ins>+ return BaseDirectoryService.recordsFromNonCompoundExpression(
</ins><span class="cx"> self, expression, records=records
</span><span class="cx"> )
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_aggregatepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_aggregate.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_aggregate.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_aggregate.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -22,14 +22,16 @@
</span><span class="cx"> from twisted.trial import unittest
</span><span class="cx">
</span><span class="cx"> from twext.who.idirectory import IDirectoryService, DirectoryConfigurationError
</span><del>-from twext.who.aggregate import DirectoryService
</del><ins>+from twext.who.aggregate import DirectoryService, DirectoryRecord
</ins><span class="cx"> from twext.who.util import ConstantsContainer
</span><del>-
</del><span class="cx"> from twext.who.test import test_directory, test_xml
</span><del>-from twext.who.test.test_xml import QueryMixIn, xmlService, TestService as XMLTestService
</del><ins>+from twext.who.test.test_xml import (
+ QueryMixIn, xmlService,
+ TestService as XMLTestService,
+ DirectoryServiceConvenienceTestMixIn
+)
</ins><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> class BaseTest(object):
</span><span class="cx"> def service(self, services=None):
</span><span class="cx"> if services is None:
</span><span class="lines">@@ -48,18 +50,30 @@
</span><span class="cx"> class TestService(DirectoryService, QueryMixIn):
</span><span class="cx"> pass
</span><span class="cx">
</span><del>- return TestService("xyzzy", services)
</del><ins>+ return TestService(u"xyzzy", services)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def xmlService(self, xmlData=None, serviceClass=None):
</span><del>- return xmlService(self.mktemp(), xmlData, serviceClass)
</del><ins>+ return xmlService(
+ self.mktemp(),
+ xmlData=xmlData,
+ serviceClass=serviceClass
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceBaseTest(BaseTest, test_xml.DirectoryServiceBaseTest):
</del><ins>+class DirectoryServiceTest(
+ unittest.TestCase,
+ BaseTest,
+ DirectoryServiceConvenienceTestMixIn,
+ test_directory.BaseDirectoryServiceTest
+):
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
</ins><span class="cx"> def test_repr(self):
</span><span class="cx"> service = self.service()
</span><del>- self.assertEquals(repr(service), "<TestService 'xyzzy'>")
</del><ins>+ self.assertEquals(repr(service), "<TestService u'xyzzy'>")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -68,8 +82,12 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceImmutableTest(BaseTest, test_directory.DirectoryServiceImmutableTest):
- pass
</del><ins>+class DirectoryServiceImmutableTest(
+ BaseTest,
+ test_directory.BaseDirectoryServiceImmutableTest,
+):
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -81,24 +99,42 @@
</span><span class="cx"> class GroupsDirectoryService(XMLTestService):
</span><span class="cx"> recordType = ConstantsContainer((XMLTestService.recordType.group,))
</span><span class="cx">
</span><del>- usersService = self.xmlService(testXMLConfigUsers, UsersDirectoryService)
- groupsService = self.xmlService(testXMLConfigGroups, GroupsDirectoryService)
</del><ins>+ usersService = self.xmlService(
+ xmlData=testXMLConfigUsers,
+ serviceClass=UsersDirectoryService
+ )
+ groupsService = self.xmlService(
+ xmlData=testXMLConfigGroups,
+ serviceClass=GroupsDirectoryService
+ )
</ins><span class="cx">
</span><del>- return BaseTest.service(self, (usersService, groupsService))
</del><ins>+ return BaseTest.service(
+ self,
+ services=(usersService, groupsService)
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceAggregatedBaseTest(AggregatedBaseTest, DirectoryServiceBaseTest):
</del><ins>+class DirectoryServiceAggregatedBaseTest(
+ AggregatedBaseTest,
+ DirectoryServiceTest,
+):
</ins><span class="cx"> pass
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceAggregatedQueryTest(AggregatedBaseTest, test_xml.DirectoryServiceQueryTest):
</del><ins>+class DirectoryServiceAggregatedQueryTest(
+ AggregatedBaseTest,
+ test_xml.DirectoryServiceQueryTest,
+):
</ins><span class="cx"> pass
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceAggregatedImmutableTest(AggregatedBaseTest, test_directory.DirectoryServiceImmutableTest):
</del><ins>+class DirectoryServiceAggregatedImmutableTest(
+ AggregatedBaseTest,
+ test_directory.BaseDirectoryServiceImmutableTest,
+):
</ins><span class="cx"> pass
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -107,8 +143,8 @@
</span><span class="cx"> def test_conflictingRecordTypes(self):
</span><span class="cx"> self.assertRaises(
</span><span class="cx"> DirectoryConfigurationError,
</span><del>- BaseTest.service, self,
- (self.xmlService(), self.xmlService(testXMLConfigUsers)),
</del><ins>+ self.service,
+ services=(self.xmlService(), self.xmlService(testXMLConfigUsers)),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_directorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_directory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_directory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_directory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,32 +18,96 @@
</span><span class="cx"> Generic directory service base implementation tests.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+from uuid import UUID
+from textwrap import dedent
+
</ins><span class="cx"> from zope.interface.verify import verifyObject, BrokenMethodImplementation
</span><span class="cx">
</span><ins>+from twisted.python.constants import Names, NamedConstant
</ins><span class="cx"> from twisted.trial import unittest
</span><del>-from twisted.trial.unittest import SkipTest
</del><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><ins>+from twisted.internet.defer import succeed
</ins><span class="cx">
</span><span class="cx"> from twext.who.idirectory import QueryNotSupportedError, NotAllowedError
</span><span class="cx"> from twext.who.idirectory import RecordType, FieldName
</span><span class="cx"> from twext.who.idirectory import IDirectoryService, IDirectoryRecord
</span><ins>+from twext.who.expression import CompoundExpression, Operand
</ins><span class="cx"> from twext.who.directory import DirectoryService, DirectoryRecord
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class BaseTest(unittest.TestCase):
- realmName = "xyzzy"
</del><ins>+class StubDirectoryService(DirectoryService):
+ """
+ Stub directory service with some built-in records and an implementation
+ of C{recordsFromNonCompoundExpression}.
+ """
</ins><span class="cx">
</span><ins>+ def __init__(self, realmName):
+ DirectoryService.__init__(self, realmName)
</ins><span class="cx">
</span><del>- def service(self):
- if not hasattr(self, "_service"):
- self._service = DirectoryService(self.realmName)
- return self._service
</del><ins>+ self.records = RecordStorage(self, DirectoryRecord)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ def recordsFromExpression(self, expression):
+ self.seenExpressions = []
</ins><span class="cx">
</span><del>-class DirectoryServiceTest(BaseTest):
</del><ins>+ return DirectoryService.recordsFromExpression(self, expression)
+
+
+ def recordsFromNonCompoundExpression(self, expression, records=None):
+ """
+ This implementation handles three expressions:
+
+ The expression C{u"None"} will match no records.
+
+ The expressions C{u"twistedmatrix.com"} and C{u"calendarserver.org"}
+ will match records that have an email address ending with the
+ given expression.
+ """
+ self.seenExpressions.append(expression)
+
+ if expression == u"None":
+ return succeed([])
+
+ if expression in (u"twistedmatrix.com", u"calendarserver.org"):
+ result = []
+ for record in self.records:
+ for email in record.emailAddresses:
+ if email.endswith(expression):
+ result.append(record)
+ break
+ return succeed(result)
+
+ return DirectoryService.recordsFromNonCompoundExpression(
+ self, expression, records=records
+ )
+
+
+
+class ServiceMixIn(object):
+ """
+ MixIn that sets up a service appropriate for testing.
+ """
+
+ realmName = u"xyzzy"
+
+
+ def service(self, subClass=None):
+ if subClass is None:
+ subClass = self.serviceClass
+ return subClass(self.realmName)
+
+
+
+class BaseDirectoryServiceTest(ServiceMixIn):
+ """
+ Tests for directory services.
+ """
+
</ins><span class="cx"> def test_interface(self):
</span><ins>+ """
+ Service instance conforms to L{IDirectoryService}.
+ """
</ins><span class="cx"> service = self.service()
</span><span class="cx"> try:
</span><span class="cx"> verifyObject(IDirectoryService, service)
</span><span class="lines">@@ -52,16 +116,30 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_init(self):
</span><ins>+ """
+ Test initialization.
+ """
</ins><span class="cx"> service = self.service()
</span><span class="cx"> self.assertEquals(service.realmName, self.realmName)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_repr(self):
</span><ins>+ """
+ L{DirectoryService.repr} returns the expected string.
+ """
</ins><span class="cx"> service = self.service()
</span><del>- self.assertEquals(repr(service), "<DirectoryService 'xyzzy'>")
</del><ins>+ self.assertEquals(
+ repr(service),
+ "<{0} u'xyzzy'>".format(self.serviceClass.__name__)
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_recordTypes(self):
</span><ins>+ """
+ L{DirectoryService.recordTypes} returns the supported set of record
+ types. For L{DirectoryService}, that's the set of constants in the
+ L{DirectoryService.recordType} attribute.
+ """
</ins><span class="cx"> service = self.service()
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(service.recordTypes()),
</span><span class="lines">@@ -69,102 +147,451 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def test_recordsFromNonCompoundExpression_unknownExpression(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with an unknown
+ expression type fails with L{QueryNotSupportedError}.
+ """
+ service = self.service()
+ self.assertFailure(
+ service.recordsFromNonCompoundExpression(object()),
+ QueryNotSupportedError
+ )
+
+
</ins><span class="cx"> @inlineCallbacks
</span><del>- def test_recordsFromQueryNone(self):
</del><ins>+ def test_recordsFromNonCompoundExpression_emptyRecords(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with an unknown
+ expression type and an empty C{records} set returns an empty result.
+ """
</ins><span class="cx"> service = self.service()
</span><del>- records = (yield service.recordsFromQuery(()))
- for record in records:
- self.failTest("No records expected")
</del><ins>+ result = (
+ yield service.recordsFromNonCompoundExpression(
+ object(), records=()
+ )
+ )
+ self.assertEquals(set(result), set(()))
</ins><span class="cx">
</span><span class="cx">
</span><del>- def test_recordsFromQueryBogus(self):
</del><ins>+ def test_recordsFromNonCompoundExpression_nonEmptyRecords(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with an unknown
+ expression type and a non-empty C{records} fails with
+ L{QueryNotSupportedError}.
+ """
</ins><span class="cx"> service = self.service()
</span><del>- self.assertFailure(service.recordsFromQuery((object(),)), QueryNotSupportedError)
</del><span class="cx">
</span><ins>+ wsanchez = self.directoryRecordClass(
+ service,
+ {
+ service.fieldName.recordType: service.recordType.user,
+ service.fieldName.uid: u"__wsanchez__",
+ service.fieldName.shortNames: [u"wsanchez"],
+ }
+ )
</ins><span class="cx">
</span><ins>+ self.assertFailure(
+ service.recordsFromNonCompoundExpression(
+ object(), records=((wsanchez,))
+ ),
+ QueryNotSupportedError
+ )
+
+
+ def test_recordsFromExpression_unknownExpression(self):
+ """
+ L{DirectoryService.recordsFromExpression} with an unknown expression
+ type fails with L{QueryNotSupportedError}.
+ """
+ service = self.service()
+ result = yield(service.recordsFromExpression(object()))
+ self.assertFailure(result, QueryNotSupportedError)
+
+
+ @inlineCallbacks
+ def test_recordsFromExpression_emptyExpression(self):
+ """
+ L{DirectoryService.recordsFromExpression} with an unknown expression
+ type and an empty L{CompoundExpression} returns an empty result.
+ """
+ service = self.service()
+
+ for operand in Operand.iterconstants():
+ result = yield(service.recordsFromExpression(
+ CompoundExpression((), operand)
+ ))
+ self.assertEquals(set(result), set(()))
+
+
+ def _unimplemented(self):
+ """
+ Unimplemented test.
+ """
+ raise NotImplementedError("Subclasses should implement this test.")
+
+
+ test_recordWithUID = _unimplemented
+ test_recordWithGUID = _unimplemented
+ test_recordsWithRecordType = _unimplemented
+ test_recordWithShortName = _unimplemented
+ test_recordsWithEmailAddress = _unimplemented
+
+
+ def test_updateRecordsEmpty(self):
+ """
+ Updating no records is not an error.
+ """
+ service = self.service()
+ for create in (True, False):
+ service.updateRecords((), create=create),
+
+
+ def test_removeRecordsEmpty(self):
+ """
+ Removing no records is allowed.
+ """
+ service = self.service()
+
+ service.removeRecords(())
+
+
+
+class DirectoryServiceRecordsFromExpressionTest(
+ unittest.TestCase,
+ ServiceMixIn
+):
+ """
+ Tests for L{DirectoryService.recordsFromExpression}.
+ """
+ serviceClass = StubDirectoryService
+ directoryRecordClass = DirectoryRecord
+
+ @inlineCallbacks
+ def test_recordsFromExpression_single(self):
+ """
+ L{DirectoryService.recordsFromExpression} handles a single expression.
+ """
+ service = self.service()
+
+ result = yield service.recordsFromExpression("twistedmatrix.com")
+
+ self.assertEquals(
+ set((
+ u"__wsanchez__",
+ u"__glyph__",
+ u"__exarkun__",
+ u"__dreid__",
+ )),
+ set((record.uid for record in result))
+ )
+
+
+ @inlineCallbacks
+ def test_recordsFromExpression_OR(self):
+ """
+ L{DirectoryService.recordsFromExpression} handles a
+ L{CompoundExpression} with L{Operand.OR}.
+ """
+ service = self.service()
+
+ result = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ u"twistedmatrix.com",
+ u"calendarserver.org",
+ ),
+ Operand.OR
+ )
+ )
+
+ self.assertEquals(
+ set((
+ u"__wsanchez__",
+ u"__glyph__",
+ u"__sagen__",
+ u"__cdaboo__",
+ u"__dre__",
+ u"__exarkun__",
+ u"__dreid__",
+ )),
+ set((record.uid for record in result))
+ )
+
+
+ @inlineCallbacks
+ def test_recordsFromExpression_AND(self):
+ """
+ L{DirectoryService.recordsFromExpression} handles a
+ L{CompoundExpression} with L{Operand.AND}.
+ """
+ service = self.service()
+
+ result = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ u"twistedmatrix.com",
+ u"calendarserver.org",
+ ),
+ Operand.AND
+ )
+ )
+
+ self.assertEquals(
+ set((
+ u"__wsanchez__",
+ u"__glyph__",
+ )),
+ set((record.uid for record in result))
+ )
+
+
+ @inlineCallbacks
+ def test_recordsFromExpression_AND_optimized(self):
+ """
+ L{DirectoryService.recordsFromExpression} handles a
+ L{CompoundExpression} with L{Operand.AND}, and when one of the
+ expression matches no records, the subsequent expressions are skipped.
+ """
+ service = self.service()
+
+ result = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ u"twistedmatrix.com",
+ u"None",
+ u"calendarserver.org",
+ ),
+ Operand.AND
+ )
+ )
+
+ self.assertEquals(
+ set(()),
+ set((record.uid for record in result))
+ )
+
+ self.assertEquals(
+ [u"twistedmatrix.com", u"None"],
+ service.seenExpressions
+ )
+
+
+ def test_recordsFromExpression_unknownOperand(self):
+ """
+ L{DirectoryService.recordsFromExpression} fails with
+ L{QueryNotSupportedError} when given a L{CompoundExpression} with an
+ unknown operand.
+ """
+ service = self.service()
+
+ results = service.recordsFromExpression(
+ CompoundExpression(
+ (
+ u"twistedmatrix.com",
+ u"calendarserver.org",
+ ),
+ WackyOperand.WHUH
+ )
+ )
+
+ self.assertFailure(results, QueryNotSupportedError)
+
+
+
+class DirectoryServiceConvenienceTest(
+ unittest.TestCase,
+ BaseDirectoryServiceTest
+):
+ """
+ Tests for L{DirectoryService} convenience methods.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
</ins><span class="cx"> def test_recordWithUID(self):
</span><del>- raise SkipTest("Subclasses should implement this test.")
</del><ins>+ """
+ L{DirectoryService.recordWithUID} fails with L{QueryNotSupportedError}.
+ """
+ service = self.service()
</ins><span class="cx">
</span><ins>+ self.assertFailure(
+ service.recordWithUID(u""),
+ QueryNotSupportedError
+ )
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordWithGUID(self):
</span><del>- raise SkipTest("Subclasses should implement this test.")
</del><ins>+ """
+ L{DirectoryService.recordWithGUID} fails with
+ L{QueryNotSupportedError}.
+ """
+ service = self.service()
</ins><span class="cx">
</span><ins>+ self.assertFailure(
+ service.recordWithGUID(UUID(int=0)),
+ QueryNotSupportedError
+ )
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordsWithRecordType(self):
</span><del>- raise SkipTest("Subclasses should implement this test.")
</del><ins>+ """
+ L{DirectoryService.recordsWithRecordType} fails with
+ L{QueryNotSupportedError}.
+ """
+ service = self.service()
</ins><span class="cx">
</span><ins>+ for recordType in RecordType.iterconstants():
+ self.assertFailure(
+ service.recordsWithRecordType(recordType),
+ QueryNotSupportedError
+ )
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordWithShortName(self):
</span><del>- raise SkipTest("Subclasses should implement this test.")
</del><ins>+ """
+ L{DirectoryService.recordWithShortName} fails with
+ L{QueryNotSupportedError}.
+ """
+ service = self.service()
</ins><span class="cx">
</span><ins>+ for recordType in RecordType.iterconstants():
+ self.assertFailure(
+ service.recordWithShortName(recordType, u""),
+ QueryNotSupportedError
+ )
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordsWithEmailAddress(self):
</span><del>- raise SkipTest("Subclasses should implement this test.")
</del><ins>+ """
+ L{DirectoryService.recordsWithEmailAddress} fails with
+ L{QueryNotSupportedError}.
+ """
+ service = self.service()
</ins><span class="cx">
</span><ins>+ self.assertFailure(
+ service.recordsWithEmailAddress("a@b"),
+ QueryNotSupportedError
+ )
</ins><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceImmutableTest(BaseTest):
</del><ins>+
+class BaseDirectoryServiceImmutableTest(ServiceMixIn):
+ """
+ Tests for immutable directory services.
+ """
+
</ins><span class="cx"> def test_updateRecordsNotAllowed(self):
</span><ins>+ """
+ Updating records is not allowed.
+ """
</ins><span class="cx"> service = self.service()
</span><span class="cx">
</span><del>- newRecord = DirectoryRecord(
</del><ins>+ newRecord = self.directoryRecordClass(
</ins><span class="cx"> service,
</span><del>- fields = {
- service.fieldName.uid: "__plugh__",
</del><ins>+ fields={
+ service.fieldName.uid: u"__plugh__",
</ins><span class="cx"> service.fieldName.recordType: service.recordType.user,
</span><del>- service.fieldName.shortNames: ("plugh",),
</del><ins>+ service.fieldName.shortNames: (u"plugh",),
</ins><span class="cx"> }
</span><span class="cx"> )
</span><span class="cx">
</span><del>- self.assertFailure(
- service.updateRecords((newRecord,), create=True),
- NotAllowedError,
- )
</del><ins>+ for create in (True, False):
+ self.assertFailure(
+ service.updateRecords((newRecord,), create=create),
+ NotAllowedError,
+ )
</ins><span class="cx">
</span><del>- self.assertFailure(
- service.updateRecords((newRecord,), create=False),
- NotAllowedError,
- )
</del><span class="cx">
</span><del>-
</del><span class="cx"> def test_removeRecordsNotAllowed(self):
</span><ins>+ """
+ Removing records is not allowed.
+ """
</ins><span class="cx"> service = self.service()
</span><span class="cx">
</span><del>- service.removeRecords(())
</del><span class="cx"> self.assertFailure(
</span><del>- service.removeRecords(("foo",)),
</del><ins>+ service.removeRecords((u"foo",)),
</ins><span class="cx"> NotAllowedError,
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryRecordTest(BaseTest):
</del><ins>+class DirectoryServiceImmutableTest(
+ unittest.TestCase,
+ BaseDirectoryServiceImmutableTest,
+):
+ """
+ Tests for immutable L{DirectoryService}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+
+class BaseDirectoryRecordTest(ServiceMixIn):
+ """
+ Tests for directory records.
+ """
+
</ins><span class="cx"> fields_wsanchez = {
</span><del>- FieldName.uid: "UID:wsanchez",
- FieldName.recordType: RecordType.user,
- FieldName.shortNames: ("wsanchez", "wilfredo_sanchez"),
- FieldName.fullNames: ("Wilfredo Sanchez", "Wilfredo Sanchez Vega"),
- FieldName.emailAddresses: ("wsanchez@calendarserver.org", "wsanchez@example.com")
</del><ins>+ FieldName.uid: u"UID:wsanchez",
+ FieldName.recordType: RecordType.user,
+ FieldName.shortNames: (u"wsanchez", u"wilfredo_sanchez"),
+ FieldName.fullNames: (
+ u"Wilfredo Sanchez",
+ u"Wilfredo Sanchez Vega",
+ ),
+ FieldName.emailAddresses: (
+ u"wsanchez@calendarserver.org",
+ u"wsanchez@example.com",
+ )
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> fields_glyph = {
</span><del>- FieldName.uid: "UID:glyph",
- FieldName.recordType: RecordType.user,
- FieldName.shortNames: ("glyph",),
- FieldName.fullNames: ("Glyph Lefkowitz",),
- FieldName.emailAddresses: ("glyph@calendarserver.org",)
</del><ins>+ FieldName.uid: u"UID:glyph",
+ FieldName.recordType: RecordType.user,
+ FieldName.shortNames: (u"glyph",),
+ FieldName.fullNames: (u"Glyph Lefkowitz",),
+ FieldName.emailAddresses: (u"glyph@calendarserver.org",)
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> fields_sagen = {
</span><del>- FieldName.uid: "UID:sagen",
- FieldName.recordType: RecordType.user,
- FieldName.shortNames: ("sagen",),
- FieldName.fullNames: ("Morgen Sagen",),
- FieldName.emailAddresses: ("sagen@CalendarServer.org",)
</del><ins>+ FieldName.uid: u"UID:sagen",
+ FieldName.recordType: RecordType.user,
+ FieldName.shortNames: (u"sagen",),
+ FieldName.fullNames: (u"Morgen Sagen",),
+ FieldName.emailAddresses: (u"sagen@CalendarServer.org",)
</ins><span class="cx"> }
</span><span class="cx">
</span><ins>+ fields_nobody = {
+ FieldName.uid: u"UID:nobody",
+ FieldName.recordType: RecordType.user,
+ FieldName.shortNames: (u"nobody",),
+ }
</ins><span class="cx">
</span><del>- def _testRecord(self, fields=None, service=None):
</del><ins>+ fields_staff = {
+ FieldName.uid: u"UID:staff",
+ FieldName.recordType: RecordType.group,
+ FieldName.shortNames: (u"staff",),
+ FieldName.fullNames: (u"Staff",),
+ FieldName.emailAddresses: (u"staff@CalendarServer.org",)
+ }
+
+
+ def makeRecord(self, fields=None, service=None):
+ """
+ Create a directory record from fields and a service.
+
+ @param fields: Record fields.
+ @type fields: L{dict} with L{FieldName} keys
+
+ @param service: Directory service.
+ @type service: L{DirectoryService}
+
+ @return: A directory record.
+ @rtype: L{DirectoryRecord}
+ """
</ins><span class="cx"> if fields is None:
</span><span class="cx"> fields = self.fields_wsanchez
</span><span class="cx"> if service is None:
</span><span class="lines">@@ -173,7 +600,10 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_interface(self):
</span><del>- record = self._testRecord()
</del><ins>+ """
+ L{DirectoryRecord} complies with L{IDirectoryRecord}.
+ """
+ record = self.makeRecord()
</ins><span class="cx"> try:
</span><span class="cx"> verifyObject(IDirectoryRecord, record)
</span><span class="cx"> except BrokenMethodImplementation as e:
</span><span class="lines">@@ -181,100 +611,348 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_init(self):
</span><ins>+ """
+ L{DirectoryRecord} initialization sets service and fields.
+ """
</ins><span class="cx"> service = self.service()
</span><del>- wsanchez = self._testRecord(self.fields_wsanchez, service=service)
</del><ins>+ wsanchez = self.makeRecord(self.fields_wsanchez, service=service)
</ins><span class="cx">
</span><span class="cx"> self.assertEquals(wsanchez.service, service)
</span><del>- self.assertEquals(wsanchez.fields , self.fields_wsanchez)
</del><ins>+ self.assertEquals(wsanchez.fields, self.fields_wsanchez)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_initWithNoUID(self):
</span><ins>+ """
+ Directory records must have a UID.
+ """
</ins><span class="cx"> fields = self.fields_wsanchez.copy()
</span><span class="cx"> del fields[FieldName.uid]
</span><del>- self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+ self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx">
</span><span class="cx"> fields = self.fields_wsanchez.copy()
</span><del>- fields[FieldName.uid] = ""
- self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+ fields[FieldName.uid] = u""
+ self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_initWithNoRecordType(self):
</span><ins>+ """
+ Directory records must have a record type.
+ """
</ins><span class="cx"> fields = self.fields_wsanchez.copy()
</span><span class="cx"> del fields[FieldName.recordType]
</span><del>- self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+ self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx">
</span><span class="cx"> fields = self.fields_wsanchez.copy()
</span><del>- fields[FieldName.recordType] = ""
- self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+ fields[FieldName.recordType] = None
+ self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ def test_initWithBogusRecordType(self):
+ """
+ Directory records must have a known record type.
+ """
+ fields = self.fields_wsanchez.copy()
+ fields[FieldName.recordType] = object()
+ self.assertRaises(ValueError, self.makeRecord, fields)
+
+
</ins><span class="cx"> def test_initWithNoShortNames(self):
</span><ins>+ """
+ Directory records must have a short name.
+ """
</ins><span class="cx"> fields = self.fields_wsanchez.copy()
</span><span class="cx"> del fields[FieldName.shortNames]
</span><del>- self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+ self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx">
</span><span class="cx"> fields = self.fields_wsanchez.copy()
</span><span class="cx"> fields[FieldName.shortNames] = ()
</span><del>- self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+ self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx">
</span><span class="cx"> fields = self.fields_wsanchez.copy()
</span><del>- fields[FieldName.shortNames] = ("",)
- self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+ fields[FieldName.shortNames] = (u"",)
+ self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx">
</span><span class="cx"> fields = self.fields_wsanchez.copy()
</span><del>- fields[FieldName.shortNames] = ("wsanchez", "")
- self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+ fields[FieldName.shortNames] = (u"wsanchez", u"")
+ self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx">
</span><span class="cx">
</span><del>- def test_initWithBogusRecordType(self):
- fields = self.fields_wsanchez.copy()
- fields[FieldName.recordType] = object()
- self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+ def test_initNormalizeEmailLowercase(self):
+ """
+ Email addresses are normalized to lowercase.
+ """
+ sagen = self.makeRecord(self.fields_sagen)
</ins><span class="cx">
</span><ins>+ self.assertEquals(
+ sagen.fields[FieldName.emailAddresses],
+ (u"sagen@calendarserver.org",)
+ )
</ins><span class="cx">
</span><del>- def test_initNormalize(self):
- sagen = self._testRecord(self.fields_sagen)
</del><span class="cx">
</span><ins>+ def test_repr(self):
+ """
+ L{DirectoryRecord.repr} returns the expected string.
+ """
+ wsanchez = self.makeRecord(self.fields_wsanchez)
+
</ins><span class="cx"> self.assertEquals(
</span><del>- sagen.fields[FieldName.emailAddresses],
- ("sagen@calendarserver.org",)
</del><ins>+ "<DirectoryRecord (user)wsanchez>",
+ repr(wsanchez)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_compare(self):
</span><ins>+ """
+ Comparison of records.
+ """
</ins><span class="cx"> fields_glyphmod = self.fields_glyph.copy()
</span><span class="cx"> del fields_glyphmod[FieldName.emailAddresses]
</span><span class="cx">
</span><del>- wsanchez = self._testRecord(self.fields_wsanchez)
- wsanchezmod = self._testRecord(self.fields_wsanchez, DirectoryService("plugh"))
- glyph = self._testRecord(self.fields_glyph)
- glyphmod = self._testRecord(fields_glyphmod)
</del><ins>+ plugh = DirectoryService(u"plugh")
</ins><span class="cx">
</span><ins>+ wsanchez = self.makeRecord(self.fields_wsanchez)
+ wsanchezmod = self.makeRecord(self.fields_wsanchez, plugh)
+ glyph = self.makeRecord(self.fields_glyph)
+ glyphmod = self.makeRecord(fields_glyphmod)
+
</ins><span class="cx"> self.assertEquals(wsanchez, wsanchez)
</span><span class="cx"> self.assertNotEqual(wsanchez, glyph)
</span><del>- self.assertNotEqual(glyph, glyphmod) # UID matches, other fields do not
</del><ins>+ self.assertNotEqual(glyph, glyphmod) # UID matches, other fields don't
</ins><span class="cx"> self.assertNotEqual(glyphmod, wsanchez)
</span><del>- self.assertNotEqual(wsanchez, wsanchezmod) # Different service
</del><ins>+ self.assertNotEqual(wsanchez, wsanchezmod) # Different service
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_attributeAccess(self):
</span><del>- wsanchez = self._testRecord(self.fields_wsanchez)
</del><ins>+ """
+ Fields can be accessed as attributes.
+ """
+ wsanchez = self.makeRecord(self.fields_wsanchez)
</ins><span class="cx">
</span><del>- self.assertEquals(wsanchez.recordType , wsanchez.fields[FieldName.recordType ])
- self.assertEquals(wsanchez.uid , wsanchez.fields[FieldName.uid ])
- self.assertEquals(wsanchez.shortNames , wsanchez.fields[FieldName.shortNames ])
- self.assertEquals(wsanchez.emailAddresses, wsanchez.fields[FieldName.emailAddresses])
</del><ins>+ self.assertEquals(
+ wsanchez.recordType,
+ wsanchez.fields[FieldName.recordType]
+ )
+ self.assertEquals(
+ wsanchez.uid,
+ wsanchez.fields[FieldName.uid]
+ )
+ self.assertEquals(
+ wsanchez.shortNames,
+ wsanchez.fields[FieldName.shortNames]
+ )
+ self.assertEquals(
+ wsanchez.emailAddresses,
+ wsanchez.fields[FieldName.emailAddresses]
+ )
</ins><span class="cx">
</span><ins>+ self.assertRaises(AttributeError, lambda: wsanchez.fooBarBaz)
+
+ nobody = self.makeRecord(self.fields_nobody)
+
+ self.assertRaises(AttributeError, lambda: nobody.emailAddresses)
+
+
+ def test_description(self):
+ """
+ L{DirectoryRecord.description} returns the expected string.
+ """
+ sagen = self.makeRecord(self.fields_sagen)
+
+ self.assertEquals(
+ dedent(
+ u"""
+ DirectoryRecord:
+ UID = UID:sagen
+ record type = user
+ short names = (u'sagen',)
+ full names = (u'Morgen Sagen',)
+ email addresses = ('sagen@calendarserver.org',)
+ """[1:]
+ ),
+ sagen.description()
+ )
+
+ test_description.todo = "Intermittent order issues"
+
+
+ def test_members_group(self):
+ """
+ Group members for group records.
+ """
+ raise NotImplementedError("Subclasses should implement this test.")
+
+
</ins><span class="cx"> @inlineCallbacks
</span><del>- def test_members(self):
- wsanchez = self._testRecord(self.fields_wsanchez)
</del><ins>+ def test_members_nonGroup(self):
+ """
+ Group members for non-group records. Non-groups have no members.
+ """
+ wsanchez = self.makeRecord(self.fields_wsanchez)
</ins><span class="cx">
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set((yield wsanchez.members())),
</span><span class="cx"> set()
</span><span class="cx"> )
</span><span class="cx">
</span><del>- raise SkipTest("Subclasses should implement this test.")
</del><span class="cx">
</span><del>- def test_groups(self):
- raise SkipTest("Subclasses should implement this test.")
</del><ins>+ def test_memberships(self):
+ """
+ Group memberships.
+ """
+ raise NotImplementedError("Subclasses should implement this test.")
+
+
+
+class DirectoryRecordTest(unittest.TestCase, BaseDirectoryRecordTest):
+ """
+ Tests for L{DirectoryRecord}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+ def test_members_group(self):
+ staff = self.makeRecord(self.fields_staff)
+
+ self.assertFailure(staff.members(), NotImplementedError)
+
+
+ def test_memberships(self):
+ wsanchez = self.makeRecord(self.fields_wsanchez)
+
+ self.assertFailure(wsanchez.groups(), NotImplementedError)
+
+
+
+class RecordStorage(object):
+ """
+ Container for directory records.
+ """
+ def __init__(self, service, recordClass):
+ self.service = service
+ self.recordClass = recordClass
+ self.records = []
+
+ self.addDefaultRecords()
+
+
+ def addDefaultRecords(self):
+ """
+ Add a known set of records to this service.
+ """
+ self.addUser(
+ shortNames=[u"wsanchez", u"wilfredo_sanchez"],
+ fullNames=[
+ u"Wilfredo S\xe1nchez Vega",
+ u"Wilfredo Sanchez Vega",
+ u"Wilfredo Sanchez",
+ ],
+ emailAddresses=[
+ u"wsanchez@bitbucket.calendarserver.org",
+ u"wsanchez@devnull.twistedmatrix.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"glyph"],
+ fullNames=[u"Glyph Lefkowitz"],
+ emailAddresses=[
+ u"glyph@bitbucket.calendarserver.org",
+ u"glyph@devnull.twistedmatrix.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"sagen"],
+ fullNames=[u"Morgen Sagen"],
+ emailAddresses=[
+ u"sagen@bitbucket.calendarserver.org",
+ u"shared@example.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"cdaboo"],
+ fullNames=[u"Cyrus Daboo"],
+ emailAddresses=[
+ u"cdaboo@bitbucket.calendarserver.org",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"dre"],
+ fullNames=[u"Andre LaBranche"],
+ emailAddresses=[
+ u"dre@bitbucket.calendarserver.org",
+ u"shared@example.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"exarkun"],
+ fullNames=[u"Jean-Paul Calderone"],
+ emailAddresses=[
+ u"exarkun@devnull.twistedmatrix.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"dreid"],
+ fullNames=[u"David Reid"],
+ emailAddresses=[
+ u"dreid@devnull.twistedmatrix.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"joe"],
+ fullNames=[u"Joe Schmoe"],
+ emailAddresses=[
+ u"joe@example.com",
+ ],
+ )
+
+ self.addUser(
+ shortNames=[u"alyssa"],
+ fullNames=[u"Alyssa P. Hacker"],
+ emailAddresses=[
+ u"alyssa@example.com",
+ ],
+ )
+
+
+ def addUser(self, shortNames, fullNames, emailAddresses=[]):
+ """
+ Add a user record with the given field information.
+
+ @param shortNames: Record short names.
+ @type shortNames: L{list} of L{unicode}s
+
+ @param fullNames: Record full names.
+ @type fullNames: L{list} of L{unicode}s
+
+ @param emailAddresses: Record email addresses.
+ @type emailAddresses: L{list} of L{unicode}s
+ """
+ service = self.service
+ fieldName = service.fieldName
+ recordType = service.recordType
+ self.records.append(self.recordClass(self.service, {
+ fieldName.recordType: recordType.user,
+ fieldName.uid: u"__{0}__".format(shortNames[0]),
+ fieldName.shortNames: shortNames,
+ fieldName.fullNames: fullNames,
+ fieldName.password: u"".join(reversed(shortNames[0])),
+ fieldName.emailAddresses: emailAddresses,
+ }))
+
+
+ def __iter__(self):
+ return iter(self.records)
+
+
+
+class WackyOperand(Names):
+ """
+ Wacky operands.
+ """
+ WHUH = NamedConstant()
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_expressionpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_expression.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_expression.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_expression.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -26,29 +26,31 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class MatchExpressionTest(unittest.TestCase):
</span><del>- def test_repr(self):
</del><ins>+ def test_repr_name(self):
</ins><span class="cx"> self.assertEquals(
</span><del>- "<MatchExpression: 'full names' equals 'Wilfredo Sanchez'>",
</del><ins>+ "<MatchExpression: u'full names' equals u'Wilfredo Sanchez'>",
</ins><span class="cx"> repr(MatchExpression(
</span><span class="cx"> FieldName.fullNames,
</span><del>- "Wilfredo Sanchez",
</del><ins>+ u"Wilfredo Sanchez",
</ins><span class="cx"> )),
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+ def test_repr_type(self):
</ins><span class="cx"> self.assertEquals(
</span><del>- "<MatchExpression: 'full names' contains 'Sanchez'>",
</del><ins>+ "<MatchExpression: u'full names' contains u'Sanchez'>",
</ins><span class="cx"> repr(MatchExpression(
</span><span class="cx"> FieldName.fullNames,
</span><del>- "Sanchez",
</del><ins>+ u"Sanchez",
</ins><span class="cx"> matchType=MatchType.contains,
</span><span class="cx"> )),
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+ def test_repr_flags(self):
</ins><span class="cx"> self.assertEquals(
</span><del>- "<MatchExpression: 'full names' starts with 'Wilfredo' (not)>",
</del><ins>+ "<MatchExpression: u'full names' starts with u'Wilfredo' (not)>",
</ins><span class="cx"> repr(MatchExpression(
</span><span class="cx"> FieldName.fullNames,
</span><del>- "Wilfredo",
</del><ins>+ u"Wilfredo",
</ins><span class="cx"> matchType=MatchType.startsWith,
</span><span class="cx"> flags=MatchFlags.NOT,
</span><span class="cx"> )),
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_indexpyfromrev12016CalendarServertrunktwextwhotesttest_indexpy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_index.py (from rev 12016, CalendarServer/trunk/twext/who/test/test_index.py) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_index.py         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_index.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,486 @@
</span><ins>+##
+# Copyright (c) 2013 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.
+##
+
+"""
+Indexed directory service base implementation tests.
+"""
+
+from twisted.trial import unittest
+from twisted.internet.defer import inlineCallbacks, returnValue
+
+from twext.who.idirectory import FieldName as BaseFieldName
+from twext.who.idirectory import QueryNotSupportedError
+from twext.who.expression import MatchExpression, MatchType
+from twext.who.index import DirectoryService, DirectoryRecord
+from twext.who.test import test_directory
+from twext.who.test.test_directory import RecordStorage
+
+
+
+def noLoadDirectoryService(superClass):
+ """
+ Creates an indexed directory service that has a no-op implementation of
+ L{DirectoryService.loadRecords}.
+
+ @param superClass: The superclass of the new service.
+ @type superClass: subclass of L{DirectoryService}
+
+ @return: A new directory service class.
+ @rtype: subclass of C{superClass}
+ """
+ assert issubclass(superClass, DirectoryService)
+
+ class NoLoadDirectoryService(superClass):
+ def loadRecords(self):
+ pass
+
+ def indexedRecordsFromMatchExpression(self, *args, **kwargs):
+ self._calledIndexed = True
+ return superClass.indexedRecordsFromMatchExpression(
+ self, *args, **kwargs
+ )
+
+ def unIndexedRecordsFromMatchExpression(self, *args, **kwargs):
+ self._calledUnindexed = True
+ return superClass.unIndexedRecordsFromMatchExpression(
+ self, *args, **kwargs
+ )
+
+ return NoLoadDirectoryService
+
+
+class BaseDirectoryServiceTest(test_directory.BaseDirectoryServiceTest):
+ """
+ Tests for indexed directory services.
+ """
+
+ def noLoadServicePopulated(self):
+ service = self.service(
+ subClass=noLoadDirectoryService(self.serviceClass)
+ )
+
+ records = RecordStorage(service, DirectoryRecord)
+ service.indexRecords(records)
+ service.records = records
+
+ return service
+
+ def test_indexRecords_positive(self):
+ """
+ L{DirectoryService.indexRecords} ensures all record data is in the
+ index.
+ """
+ service = self.noLoadServicePopulated()
+ index = service.index
+
+ # Verify that the fields that should be indexed are, in fact, indexed
+ # for each record.
+ for record in service.records:
+ for fieldName in service.indexedFields:
+ values = record.fields.get(fieldName, None)
+
+ if values is None:
+ continue
+
+ if not BaseFieldName.isMultiValue(fieldName):
+ values = (values,)
+
+ for value in values:
+ self.assertIn(fieldName, index)
+ self.assertIn(value, index[fieldName])
+
+ indexedRecords = index[fieldName][value]
+ self.assertIn(record, indexedRecords)
+
+
+ def test_indexRecords_negative(self):
+ """
+ L{DirectoryService.indexRecords} does not have extra data in the index.
+ """
+ service = self.noLoadServicePopulated()
+ index = service.index
+
+ # Verify that all data in the index cooresponds to the records passed
+ # in.
+ for fieldName, fieldIndex in index.iteritems():
+ for fieldValue, records in fieldIndex.iteritems():
+ for record in records:
+ self.assertIn(fieldName, record.fields)
+ values = record.fields[fieldName]
+
+ if not BaseFieldName.isMultiValue(fieldName):
+ values = (values,)
+
+ self.assertIn(fieldValue, values)
+
+
+ def test_flush(self):
+ """
+ C{flush} empties the index.
+ """
+ service = self.noLoadServicePopulated()
+
+ self.assertFalse(emptyIndex(service.index)) # Test the test
+ service.flush()
+ self.assertTrue(emptyIndex(service.index))
+
+
+ @inlineCallbacks
+ def _test_indexedRecordsFromMatchExpression(
+ self, inOut, matchType, fieldName=BaseFieldName.shortNames,
+ ):
+ service = self.noLoadServicePopulated()
+
+ for subString, uids in (inOut):
+ records = yield service.indexedRecordsFromMatchExpression(
+ MatchExpression(
+ fieldName, subString,
+ matchType
+ )
+ )
+ self.assertEquals(
+ set((record.uid for record in records)),
+ set(uids)
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_startsWith(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with a startsWith
+ expression.
+ """
+ return self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"w", (u"__wsanchez__",)), # Duplicates
+ (u"dr", (u"__dre__", u"__dreid__")), # Multiple
+ (u"sage", (u"__sagen__",)), # Single
+ ),
+ MatchType.startsWith
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_contains(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with a contains
+ expression.
+ """
+ return self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"sanch", (u"__wsanchez__",)), # Duplicates
+ (u"dr", (u"__dre__", u"__dreid__")), # Multiple
+ (u"agen", (u"__sagen__",)), # Single
+ ),
+ MatchType.contains
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_equals(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with an equals
+ expression.
+ """
+ return self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"wsanchez", (u"__wsanchez__",)), # MultiValue
+ (u"dre", (u"__dre__",)), # Single value
+ ),
+ MatchType.equals
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_notIndexed(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with an
+ unindexed field name.
+ """
+ result = self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"zehcnasw", (u"__wsanchez__",)),
+ ),
+ MatchType.equals,
+ fieldName=BaseFieldName.password
+ )
+ self.assertFailure(result, TypeError)
+
+
+ def test_indexedRecordsFromMatchExpression_notMatchExpression(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with a
+ non-match expression.
+ """
+ result = self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"zehcnasw", (u"__wsanchez__",)),
+ ),
+ "Not a match type we know about"
+ )
+ self.assertFailure(result, NotImplementedError)
+
+
+ @inlineCallbacks
+ def _test_unIndexedRecordsFromMatchExpression(
+ self, inOut, matchType, fieldName=BaseFieldName.fullNames,
+ ):
+ service = self.noLoadServicePopulated()
+
+ for subString, uids in (inOut):
+ records = yield service.unIndexedRecordsFromMatchExpression(
+ MatchExpression(
+ fieldName, subString,
+ matchType
+ )
+ )
+ self.assertEquals(
+ set((record.uid for record in records)),
+ set(uids)
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_startsWith(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with a
+ startsWith expression.
+ """
+ return self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"Wilfredo", (u"__wsanchez__",)), # Duplicates
+ (u"A", (u"__alyssa__", u"__dre__")), # Multiple
+ (u"Andre", (u"__dre__",)), # Single
+ ),
+ MatchType.startsWith
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_contains(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with a contains
+ expression.
+ """
+ return self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"Sanchez", (u"__wsanchez__",)), # Duplicates
+ (u"A", (u"__alyssa__", u"__dre__")), # Multiple
+ (u"LaBra", (u"__dre__",)), # Single
+ ),
+ MatchType.contains
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_equals(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with an equals
+ expression.
+ """
+ return self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"Wilfredo Sanchez", (u"__wsanchez__",)), # MultiValue
+ (u"Andre LaBranche", (u"__dre__",)), # Single value
+ ),
+ MatchType.equals
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_indexed(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with an
+ indexed field name.
+ """
+ self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"wsanchez", (u"__wsanchez__",)),
+ ),
+ MatchType.equals,
+ fieldName=BaseFieldName.shortNames
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_notMatchExpression(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with a
+ non-match expression.
+ """
+ result = self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"zehcnasw", (u"__wsanchez__",)),
+ ),
+ "Not a match type we know about"
+ )
+ self.assertFailure(result, NotImplementedError)
+
+
+ @inlineCallbacks
+ def _test_recordsFromNonCompoundExpression(self, expression):
+ service = self.noLoadServicePopulated()
+ yield service.recordsFromNonCompoundExpression(expression)
+ returnValue(service)
+
+
+ @inlineCallbacks
+ def test_recordsFromNonCompoundExpression_match_indexed(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with a
+ L{MatchExpression} for an indexed field calls
+ L{DirectoryRecord.indexedRecordsFromMatchExpression}.
+ """
+ service = yield self._test_recordsFromNonCompoundExpression(
+ MatchExpression(BaseFieldName.shortNames, u"...")
+ )
+ self.assertTrue(getattr(service, "_calledIndexed", False))
+ self.assertFalse(getattr(service, "_calledUnindexed", False))
+
+
+ @inlineCallbacks
+ def test_recordsFromNonCompoundExpression_match_unindexed(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with a
+ L{MatchExpression} for an unindexed field calls
+ L{DirectoryRecord.unIndexedRecordsFromMatchExpression}.
+ """
+ service = yield self._test_recordsFromNonCompoundExpression(
+ MatchExpression(BaseFieldName.password, u"...")
+ )
+ self.assertFalse(getattr(service, "_calledIndexed", False))
+ self.assertTrue(getattr(service, "_calledUnindexed", False))
+
+
+ def test_recordsFromNonCompoundExpression_unknown(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with a
+ an unknown expression calls superclass, which will result in a
+ L{QueryNotSupportedError}.
+ """
+ result = self._test_recordsFromNonCompoundExpression(object())
+ self.assertFailure(result, QueryNotSupportedError)
+
+
+
+class DirectoryServiceTest(unittest.TestCase, BaseDirectoryServiceTest):
+ """
+ Tests for L{DirectoryService}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+ def test_init_noIndex(self):
+ """
+ Index starts as C{None}.
+ """
+ service = self.service()
+ self.assertTrue(emptyIndex(service._index))
+
+
+ def test_index_get(self):
+ """
+ Getting the C{index} property calls C{loadRecords}.
+ """
+ class TestService(DirectoryService):
+ loaded = False
+
+ def loadRecords(self):
+ self.loaded = True
+
+ service = TestService(u"")
+ service.index
+ self.assertTrue(service.loaded)
+
+
+ def test_loadRecords(self):
+ """
+ L{DirectoryService.loadRecords} raises C{NotImplementedError}.
+ """
+ service = self.service()
+ self.assertRaises(NotImplementedError, service.loadRecords)
+
+
+ def _noop(self):
+ """
+ Does nothing.
+ """
+
+
+ test_recordWithUID = _noop
+ test_recordWithGUID = _noop
+ test_recordsWithRecordType = _noop
+ test_recordWithShortName = _noop
+ test_recordsWithEmailAddress = _noop
+
+
+
+class BaseDirectoryServiceImmutableTest(
+ test_directory.BaseDirectoryServiceImmutableTest
+):
+ """
+ Tests for immutable indexed directory services.
+ """
+
+
+
+class DirectoryServiceImmutableTest(
+ unittest.TestCase, BaseDirectoryServiceImmutableTest
+):
+ """
+ Tests for immutable L{DirectoryService}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+
+class BaseDirectoryRecordTest(test_directory.BaseDirectoryRecordTest):
+ """
+ Tests for indexed directory records.
+ """
+
+
+
+class DirectoryRecordTest(unittest.TestCase, BaseDirectoryRecordTest):
+ """
+ Tests for L{DirectoryRecord}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+ def _noop(self):
+ """
+ Does nothing.
+ """
+
+
+ test_members_group = _noop
+ test_memberships = _noop
+
+
+
+def emptyIndex(index):
+ """
+ Determine whether an index is empty.
+
+ @param index: An index.
+ @type index: L{dict}
+
+ @return: true if C{index} is empty, otherwise false.
+ """
+ if not index:
+ return True
+
+ for fieldName, fieldIndex in index.iteritems():
+ for fieldValue, records in fieldIndex.iteritems():
+ for record in records:
+ return False
+
+ return True
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_utilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_util.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_util.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -29,11 +29,11 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class Tools(Names):
</span><del>- hammer = NamedConstant()
</del><ins>+ hammer = NamedConstant()
</ins><span class="cx"> screwdriver = NamedConstant()
</span><span class="cx">
</span><del>- hammer.description = "nail pounder"
- screwdriver.description = "screw twister"
</del><ins>+ hammer.description = u"nail pounder"
+ screwdriver.description = u"screw twister"
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -48,9 +48,9 @@
</span><span class="cx"> g = FlagConstant()
</span><span class="cx"> b = FlagConstant()
</span><span class="cx">
</span><del>- r.description = "red"
- g.description = "green"
- b.description = "blue"
</del><ins>+ r.description = u"red"
+ g.description = u"green"
+ b.description = u"blue"
</ins><span class="cx">
</span><span class="cx"> black = FlagConstant()
</span><span class="cx">
</span><span class="lines">@@ -82,7 +82,11 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> def test_lookupByName(self):
</span><del>- constants = set((Instruments.hammer, Tools.screwdriver, Instruments.chisel))
</del><ins>+ constants = set((
+ Instruments.hammer,
+ Tools.screwdriver,
+ Instruments.chisel,
+ ))
</ins><span class="cx"> container = ConstantsContainer(constants)
</span><span class="cx">
</span><span class="cx"> self.assertEquals(
</span><span class="lines">@@ -108,13 +112,13 @@
</span><span class="cx"> class UtilTest(unittest.TestCase):
</span><span class="cx"> def test_uniqueResult(self):
</span><span class="cx"> self.assertEquals(1, uniqueResult((1,)))
</span><del>- self.assertRaises(DirectoryServiceError, uniqueResult, (1,2,3))
</del><ins>+ self.assertRaises(DirectoryServiceError, uniqueResult, (1, 2, 3))
</ins><span class="cx">
</span><span class="cx"> def test_describe(self):
</span><del>- self.assertEquals("nail pounder", describe(Tools.hammer))
- self.assertEquals("hammer", describe(Instruments.hammer))
</del><ins>+ self.assertEquals(u"nail pounder", describe(Tools.hammer))
+ self.assertEquals(u"hammer", describe(Instruments.hammer))
</ins><span class="cx">
</span><span class="cx"> def test_describeFlags(self):
</span><del>- self.assertEquals("blue", describe(Switches.b))
- self.assertEquals("red|green", describe(Switches.r|Switches.g))
- self.assertEquals("blue|black", describe(Switches.b|Switches.black))
</del><ins>+ self.assertEquals(u"blue", describe(Switches.b))
+ self.assertEquals(u"red|green", describe(Switches.r | Switches.g))
+ self.assertEquals(u"blue|black", describe(Switches.b | Switches.black))
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhotesttest_xmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_xml.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_xml.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/test/test_xml.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -19,26 +19,31 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> from time import sleep
</span><ins>+from uuid import UUID
+from textwrap import dedent
</ins><span class="cx">
</span><span class="cx"> from twisted.trial import unittest
</span><span class="cx"> from twisted.python.filepath import FilePath
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx">
</span><span class="cx"> from twext.who.idirectory import NoSuchRecordError
</span><del>-from twext.who.idirectory import Operand
</del><ins>+from twext.who.expression import CompoundExpression, Operand
</ins><span class="cx"> from twext.who.expression import MatchExpression, MatchType, MatchFlags
</span><span class="cx"> from twext.who.xml import ParseError
</span><span class="cx"> from twext.who.xml import DirectoryService, DirectoryRecord
</span><ins>+from twext.who.test import test_index
</ins><span class="cx">
</span><del>-from twext.who.test import test_directory
</del><span class="cx">
</span><span class="cx">
</span><ins>+class BaseTest(object):
+ def service(self, subClass=None, xmlData=None):
+ return xmlService(
+ self.mktemp(),
+ xmlData=xmlData,
+ serviceClass=subClass
+ )
</ins><span class="cx">
</span><del>-class BaseTest(unittest.TestCase):
- def service(self, xmlData=None):
- return xmlService(self.mktemp(), xmlData)
</del><span class="cx">
</span><del>-
</del><span class="cx"> def assertRecords(self, records, uids):
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> frozenset((record.uid for record in records)),
</span><span class="lines">@@ -47,30 +52,26 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceBaseTest(BaseTest, test_directory.DirectoryServiceTest):
- def test_repr(self):
- service = self.service()
-
- self.assertEquals(repr(service), "<TestService (not loaded)>")
- service.loadRecords()
- self.assertEquals(repr(service), "<TestService 'xyzzy'>")
-
-
</del><ins>+class DirectoryServiceConvenienceTestMixIn(BaseTest):
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_recordWithUID(self):
</span><span class="cx"> service = self.service()
</span><span class="cx">
</span><del>- record = (yield service.recordWithUID("__null__"))
</del><ins>+ record = (yield service.recordWithUID(u"__null__"))
</ins><span class="cx"> self.assertEquals(record, None)
</span><span class="cx">
</span><del>- record = (yield service.recordWithUID("__wsanchez__"))
- self.assertEquals(record.uid, "__wsanchez__")
</del><ins>+ record = (yield service.recordWithUID(u"__wsanchez__"))
+ self.assertEquals(record.uid, u"__wsanchez__")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_recordWithGUID(self):
</span><span class="cx"> service = self.service()
</span><del>- record = (yield service.recordWithGUID("6C495FCD-7E78-4D5C-AA66-BC890AD04C9D"))
</del><ins>+ record = (
+ yield service.recordWithGUID(
+ UUID("6C495FCD-7E78-4D5C-AA66-BC890AD04C9D")
+ )
+ )
</ins><span class="cx"> self.assertEquals(record, None)
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -80,27 +81,33 @@
</span><span class="cx"> records = (yield service.recordsWithRecordType(object()))
</span><span class="cx"> self.assertEquals(set(records), set())
</span><span class="cx">
</span><del>- records = (yield service.recordsWithRecordType(service.recordType.user))
- self.assertRecords(records,
</del><ins>+ records = (
+ yield service.recordsWithRecordType(service.recordType.user)
+ )
+ self.assertRecords(
+ records,
</ins><span class="cx"> (
</span><del>- "__wsanchez__",
- "__glyph__",
- "__sagen__",
- "__cdaboo__",
- "__dre__",
- "__exarkun__",
- "__dreid__",
- "__alyssa__",
- "__joe__",
</del><ins>+ u"__wsanchez__",
+ u"__glyph__",
+ u"__sagen__",
+ u"__cdaboo__",
+ u"__dre__",
+ u"__exarkun__",
+ u"__dreid__",
+ u"__alyssa__",
+ u"__joe__",
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx">
</span><del>- records = (yield service.recordsWithRecordType(service.recordType.group))
- self.assertRecords(records,
</del><ins>+ records = (
+ yield service.recordsWithRecordType(service.recordType.group)
+ )
+ self.assertRecords(
+ records,
</ins><span class="cx"> (
</span><del>- "__calendar-dev__",
- "__twisted__",
- "__developers__",
</del><ins>+ u"__calendar-dev__",
+ u"__twisted__",
+ u"__developers__",
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -109,42 +116,86 @@
</span><span class="cx"> def test_recordWithShortName(self):
</span><span class="cx"> service = self.service()
</span><span class="cx">
</span><del>- record = (yield service.recordWithShortName(service.recordType.user, "null"))
</del><ins>+ record = (
+ yield service.recordWithShortName(
+ service.recordType.user,
+ u"null",
+ )
+ )
</ins><span class="cx"> self.assertEquals(record, None)
</span><span class="cx">
</span><del>- record = (yield service.recordWithShortName(service.recordType.user, "wsanchez"))
- self.assertEquals(record.uid, "__wsanchez__")
</del><ins>+ record = (
+ yield service.recordWithShortName(
+ service.recordType.user,
+ u"wsanchez",
+ )
+ )
+ self.assertEquals(record.uid, u"__wsanchez__")
</ins><span class="cx">
</span><del>- record = (yield service.recordWithShortName(service.recordType.user, "wilfredo_sanchez"))
- self.assertEquals(record.uid, "__wsanchez__")
</del><ins>+ record = (
+ yield service.recordWithShortName(
+ service.recordType.user,
+ u"wilfredo_sanchez",
+ )
+ )
+ self.assertEquals(record.uid, u"__wsanchez__")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_recordsWithEmailAddress(self):
</span><span class="cx"> service = self.service()
</span><span class="cx">
</span><del>- records = (yield service.recordsWithEmailAddress("wsanchez@bitbucket.calendarserver.org"))
- self.assertRecords(records, ("__wsanchez__",))
</del><ins>+ records = (
+ yield service.recordsWithEmailAddress(
+ u"wsanchez@bitbucket.calendarserver.org"
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
</ins><span class="cx">
</span><del>- records = (yield service.recordsWithEmailAddress("wsanchez@devnull.twistedmatrix.com"))
- self.assertRecords(records, ("__wsanchez__",))
</del><ins>+ records = (
+ yield service.recordsWithEmailAddress(
+ u"wsanchez@devnull.twistedmatrix.com"
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
</ins><span class="cx">
</span><del>- records = (yield service.recordsWithEmailAddress("shared@example.com"))
- self.assertRecords(records, ("__sagen__", "__dre__"))
</del><ins>+ records = (
+ yield service.recordsWithEmailAddress(
+ u"shared@example.com"
+ )
+ )
+ self.assertRecords(records, (u"__sagen__", u"__dre__"))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceRealmTest(BaseTest):
</del><ins>+class DirectoryServiceTest(
+ unittest.TestCase,
+ DirectoryServiceConvenienceTestMixIn,
+ test_index.BaseDirectoryServiceTest,
+):
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+ def test_repr(self):
+ service = self.service()
+
+ self.assertEquals(repr(service), "<TestService (not loaded)>")
+ service.loadRecords()
+ self.assertEquals(repr(service), "<TestService u'xyzzy'>")
+
+
+
+class DirectoryServiceRealmTest(unittest.TestCase, BaseTest):
</ins><span class="cx"> def test_realmNameImmutable(self):
</span><span class="cx"> def setRealmName():
</span><span class="cx"> service = self.service()
</span><del>- service.realmName = "foo"
</del><ins>+ service.realmName = u"foo"
</ins><span class="cx">
</span><span class="cx"> self.assertRaises(AssertionError, setRealmName)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceParsingTest(BaseTest):
</del><ins>+class DirectoryServiceParsingTest(unittest.TestCase, BaseTest):
</ins><span class="cx"> def test_reloadInterval(self):
</span><span class="cx"> service = self.service()
</span><span class="cx">
</span><span class="lines">@@ -176,12 +227,13 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_badRootElement(self):
</span><del>- service = self.service(xmlData=
-"""<?xml version="1.0" encoding="utf-8"?>
</del><ins>+ service = self.service(xmlData=(dedent(
+ b"""
+ <?xml version="1.0" encoding="utf-8"?>
</ins><span class="cx">
</span><del>-<frobnitz />
-"""
- )
</del><ins>+ <frobnitz />
+ """[1:]
+ )))
</ins><span class="cx">
</span><span class="cx"> self.assertRaises(ParseError, service.loadRecords)
</span><span class="cx"> try:
</span><span class="lines">@@ -189,16 +241,17 @@
</span><span class="cx"> except ParseError as e:
</span><span class="cx"> self.assertTrue(str(e).startswith("Incorrect root element"), e)
</span><span class="cx"> else:
</span><del>- raise AssertionError
</del><ins>+ raise AssertionError("Expected ParseError")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_noRealmName(self):
</span><del>- service = self.service(xmlData=
-"""<?xml version="1.0" encoding="utf-8"?>
</del><ins>+ service = self.service(xmlData=(dedent(
+ b"""
+ <?xml version="1.0" encoding="utf-8"?>
</ins><span class="cx">
</span><del>-<directory />
-"""
- )
</del><ins>+ <directory />
+ """[1:]
+ )))
</ins><span class="cx">
</span><span class="cx"> self.assertRaises(ParseError, service.loadRecords)
</span><span class="cx"> try:
</span><span class="lines">@@ -206,7 +259,7 @@
</span><span class="cx"> except ParseError as e:
</span><span class="cx"> self.assertTrue(str(e).startswith("No realm name"), e)
</span><span class="cx"> else:
</span><del>- raise AssertionError
</del><ins>+ raise AssertionError("Expected ParseError")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_unknownFieldElementsClean(self):
</span><span class="lines">@@ -215,19 +268,23 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_unknownFieldElementsDirty(self):
</span><del>- service = self.service(xmlData=
-"""<?xml version="1.0" encoding="utf-8"?>
</del><ins>+ service = self.service(xmlData=(dedent(
+ b"""
+ <?xml version="1.0" encoding="utf-8"?>
</ins><span class="cx">
</span><del>-<directory realm="Unknown Record Types">
- <record type="user">
- <uid>__wsanchez__</uid>
- <short-name>wsanchez</short-name>
- <political-affiliation>Community and Freedom Party</political-affiliation>
- </record>
-</directory>
-"""
</del><ins>+ <directory realm="Unknown Record Types">
+ <record type="user">
+ <uid>__wsanchez__</uid>
+ <short-name>wsanchez</short-name>
+ <political-affiliation>Community and Freedom Party</political-affiliation>
+ </record>
+ </directory>
+ """[1:]
+ )))
+ self.assertEquals(
+ set(service.unknownFieldElements),
+ set((u"political-affiliation",))
</ins><span class="cx"> )
</span><del>- self.assertEquals(set(service.unknownFieldElements), set(("political-affiliation",)))
</del><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_unknownRecordTypesClean(self):
</span><span class="lines">@@ -236,34 +293,37 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_unknownRecordTypesDirty(self):
</span><del>- service = self.service(xmlData=
-"""<?xml version="1.0" encoding="utf-8"?>
</del><ins>+ service = self.service(xmlData=(dedent(
+ b"""
+ <?xml version="1.0" encoding="utf-8"?>
</ins><span class="cx">
</span><del>-<directory realm="Unknown Record Types">
- <record type="camera">
- <uid>__d600__</uid>
- <short-name>d600</short-name>
- <full-name>Nikon D600</full-name>
- </record>
-</directory>
-"""
- )
- self.assertEquals(set(service.unknownRecordTypes), set(("camera",)))
</del><ins>+ <directory realm="Unknown Record Types">
+ <record type="camera">
+ <uid>__d600__</uid>
+ <short-name>d600</short-name>
+ <full-name>Nikon D600</full-name>
+ </record>
+ </directory>
+ """[1:]
+ )))
+ self.assertEquals(set(service.unknownRecordTypes), set((u"camera",)))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceQueryTest(BaseTest):
</del><ins>+class DirectoryServiceQueryTest(unittest.TestCase, BaseTest):
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryAnd(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery(
- (
- service.query("emailAddresses", "shared@example.com"),
- service.query("shortNames", "sagen"),
- ),
- operand=Operand.AND
</del><ins>+ records = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ service.query(u"emailAddresses", u"shared@example.com"),
+ service.query(u"shortNames", u"sagen"),
+ ),
+ operand=Operand.AND
+ )
</ins><span class="cx"> )
</span><del>- self.assertRecords(records, ("__sagen__",))
</del><ins>+ self.assertRecords(records, (u"__sagen__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -272,12 +332,14 @@
</span><span class="cx"> Test optimized case, where first expression yields no results.
</span><span class="cx"> """
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery(
- (
- service.query("emailAddresses", "nobody@example.com"),
- service.query("shortNames", "sagen"),
- ),
- operand=Operand.AND
</del><ins>+ records = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ service.query(u"emailAddresses", u"nobody@example.com"),
+ service.query(u"shortNames", u"sagen"),
+ ),
+ operand=Operand.AND
+ )
</ins><span class="cx"> )
</span><span class="cx"> self.assertRecords(records, ())
</span><span class="cx">
</span><span class="lines">@@ -285,102 +347,131 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryOr(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery(
- (
- service.query("emailAddresses", "shared@example.com"),
- service.query("shortNames", "wsanchez"),
- ),
- operand=Operand.OR
</del><ins>+ records = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ service.query(u"emailAddresses", u"shared@example.com"),
+ service.query(u"shortNames", u"wsanchez"),
+ ),
+ operand=Operand.OR
+ )
</ins><span class="cx"> )
</span><del>- self.assertRecords(records, ("__sagen__", "__dre__", "__wsanchez__"))
</del><ins>+ self.assertRecords(
+ records,
+ (u"__sagen__", u"__dre__", u"__wsanchez__")
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryNot(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery(
- (
- service.query("emailAddresses", "shared@example.com"),
- service.query("shortNames", "sagen", flags=MatchFlags.NOT),
- ),
- operand=Operand.AND
</del><ins>+ records = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ service.query(
+ u"emailAddresses", u"shared@example.com"
+ ),
+ service.query(
+ u"shortNames", u"sagen",
+ flags=MatchFlags.NOT
+ ),
+ ),
+ operand=Operand.AND
+ )
</ins><span class="cx"> )
</span><del>- self.assertRecords(records, ("__dre__",))
</del><ins>+ self.assertRecords(records, (u"__dre__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryNotNoIndex(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery(
- (
- service.query("emailAddresses", "shared@example.com"),
- service.query("fullNames", "Andre LaBranche", flags=MatchFlags.NOT),
- ),
- operand=Operand.AND
</del><ins>+ records = yield service.recordsFromExpression(
+ CompoundExpression(
+ (
+ service.query(u"emailAddresses", u"shared@example.com"),
+ service.query(
+ u"fullNames", u"Andre LaBranche",
+ flags=MatchFlags.NOT
+ ),
+ ),
+ operand=Operand.AND
+ )
</ins><span class="cx"> )
</span><del>- self.assertRecords(records, ("__sagen__",))
</del><ins>+ self.assertRecords(records, (u"__sagen__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryCaseInsensitive(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
- service.query("shortNames", "SagEn", flags=MatchFlags.caseInsensitive),
- ))
- self.assertRecords(records, ("__sagen__",))
</del><ins>+ records = yield service.recordsFromExpression(
+ service.query(
+ u"shortNames", u"SagEn",
+ flags=MatchFlags.caseInsensitive
+ )
+ )
+ self.assertRecords(records, (u"__sagen__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryCaseInsensitiveNoIndex(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
- service.query("fullNames", "moRGen SAGen", flags=MatchFlags.caseInsensitive),
- ))
- self.assertRecords(records, ("__sagen__",))
</del><ins>+ records = yield service.recordsFromExpression(
+ service.query(
+ u"fullNames", u"moRGen SAGen",
+ flags=MatchFlags.caseInsensitive
+ )
+ )
+ self.assertRecords(records, (u"__sagen__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryStartsWith(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
- service.query("shortNames", "wil", matchType=MatchType.startsWith),
- ))
- self.assertRecords(records, ("__wsanchez__",))
</del><ins>+ records = yield service.recordsFromExpression(
+ service.query(
+ u"shortNames", u"wil",
+ matchType=MatchType.startsWith
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryStartsWithNoIndex(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
- service.query("fullNames", "Wilfredo", matchType=MatchType.startsWith),
- ))
- self.assertRecords(records, ("__wsanchez__",))
</del><ins>+ records = yield service.recordsFromExpression(
+ service.query(
+ u"fullNames", u"Wilfredo",
+ matchType=MatchType.startsWith
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryStartsWithNot(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
</del><ins>+ records = yield service.recordsFromExpression(
</ins><span class="cx"> service.query(
</span><del>- "shortNames", "w",
- matchType = MatchType.startsWith,
- flags = MatchFlags.NOT,
- ),
- ))
</del><ins>+ u"shortNames", u"w",
+ matchType=MatchType.startsWith,
+ flags=MatchFlags.NOT,
+ )
+ )
</ins><span class="cx"> self.assertRecords(
</span><span class="cx"> records,
</span><span class="cx"> (
</span><del>- '__alyssa__',
- '__calendar-dev__',
- '__cdaboo__',
- '__developers__',
- '__dre__',
- '__dreid__',
- '__exarkun__',
- '__glyph__',
- '__joe__',
- '__sagen__',
- '__twisted__',
</del><ins>+ u"__alyssa__",
+ u"__calendar-dev__",
+ u"__cdaboo__",
+ u"__developers__",
+ u"__dre__",
+ u"__dreid__",
+ u"__exarkun__",
+ u"__glyph__",
+ u"__joe__",
+ u"__sagen__",
+ u"__twisted__",
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -394,28 +485,28 @@
</span><span class="cx"> should NOT require that all match?
</span><span class="cx"> """
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
</del><ins>+ records = yield service.recordsFromExpression(
</ins><span class="cx"> service.query(
</span><del>- "shortNames", "wil",
- matchType = MatchType.startsWith,
- flags = MatchFlags.NOT,
- ),
- ))
</del><ins>+ u"shortNames", u"wil",
+ matchType=MatchType.startsWith,
+ flags=MatchFlags.NOT,
+ )
+ )
</ins><span class="cx"> self.assertRecords(
</span><span class="cx"> records,
</span><span class="cx"> (
</span><del>- '__alyssa__',
- '__calendar-dev__',
- '__cdaboo__',
- '__developers__',
- '__dre__',
- '__dreid__',
- '__exarkun__',
- '__glyph__',
- '__joe__',
- '__sagen__',
- '__twisted__',
- '__wsanchez__',
</del><ins>+ u"__alyssa__",
+ u"__calendar-dev__",
+ u"__cdaboo__",
+ u"__developers__",
+ u"__dre__",
+ u"__dreid__",
+ u"__exarkun__",
+ u"__glyph__",
+ u"__joe__",
+ u"__sagen__",
+ u"__twisted__",
+ u"__wsanchez__",
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -423,27 +514,27 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryStartsWithNotNoIndex(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
</del><ins>+ records = yield service.recordsFromExpression(
</ins><span class="cx"> service.query(
</span><del>- "fullNames", "Wilfredo",
- matchType = MatchType.startsWith,
- flags = MatchFlags.NOT,
- ),
- ))
</del><ins>+ u"fullNames", u"Wilfredo",
+ matchType=MatchType.startsWith,
+ flags=MatchFlags.NOT,
+ )
+ )
</ins><span class="cx"> self.assertRecords(
</span><span class="cx"> records,
</span><span class="cx"> (
</span><del>- '__alyssa__',
- '__calendar-dev__',
- '__cdaboo__',
- '__developers__',
- '__dre__',
- '__dreid__',
- '__exarkun__',
- '__glyph__',
- '__joe__',
- '__sagen__',
- '__twisted__',
</del><ins>+ u"__alyssa__",
+ u"__calendar-dev__",
+ u"__cdaboo__",
+ u"__developers__",
+ u"__dre__",
+ u"__dreid__",
+ u"__exarkun__",
+ u"__glyph__",
+ u"__joe__",
+ u"__sagen__",
+ u"__twisted__",
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -451,71 +542,77 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryStartsWithCaseInsensitive(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
</del><ins>+ records = yield service.recordsFromExpression(
</ins><span class="cx"> service.query(
</span><del>- "shortNames", "WIL",
- matchType = MatchType.startsWith,
- flags = MatchFlags.caseInsensitive,
- ),
- ))
- self.assertRecords(records, ("__wsanchez__",))
</del><ins>+ u"shortNames", u"WIL",
+ matchType=MatchType.startsWith,
+ flags=MatchFlags.caseInsensitive,
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryStartsWithCaseInsensitiveNoIndex(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
</del><ins>+ records = yield service.recordsFromExpression(
</ins><span class="cx"> service.query(
</span><del>- "fullNames", "wilfrEdo",
- matchType = MatchType.startsWith,
- flags = MatchFlags.caseInsensitive,
- ),
- ))
- self.assertRecords(records, ("__wsanchez__",))
</del><ins>+ u"fullNames", u"wilfrEdo",
+ matchType=MatchType.startsWith,
+ flags=MatchFlags.caseInsensitive,
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryContains(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
- service.query("shortNames", "sanchez", matchType=MatchType.contains),
- ))
- self.assertRecords(records, ("__wsanchez__",))
</del><ins>+ records = yield service.recordsFromExpression(
+ service.query(
+ u"shortNames", u"sanchez",
+ matchType=MatchType.contains,
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryContainsNoIndex(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
- service.query("fullNames", "fred", matchType=MatchType.contains),
- ))
- self.assertRecords(records, ("__wsanchez__",))
</del><ins>+ records = yield service.recordsFromExpression(
+ service.query(
+ u"fullNames", u"fred",
+ matchType=MatchType.contains,
+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryContainsNot(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
</del><ins>+ records = yield service.recordsFromExpression(
</ins><span class="cx"> service.query(
</span><del>- "shortNames", "sanchez",
- matchType = MatchType.contains,
- flags = MatchFlags.NOT,
- ),
- ))
</del><ins>+ u"shortNames", u"sanchez",
+ matchType=MatchType.contains,
+ flags=MatchFlags.NOT,
+ )
+ )
</ins><span class="cx"> self.assertRecords(
</span><span class="cx"> records,
</span><span class="cx"> (
</span><del>- '__alyssa__',
- '__calendar-dev__',
- '__cdaboo__',
- '__developers__',
- '__dre__',
- '__dreid__',
- '__exarkun__',
- '__glyph__',
- '__joe__',
- '__sagen__',
- '__twisted__',
</del><ins>+ u"__alyssa__",
+ u"__calendar-dev__",
+ u"__cdaboo__",
+ u"__developers__",
+ u"__dre__",
+ u"__dreid__",
+ u"__exarkun__",
+ u"__glyph__",
+ u"__joe__",
+ u"__sagen__",
+ u"__twisted__",
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -523,27 +620,27 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryContainsNotNoIndex(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
</del><ins>+ records = yield service.recordsFromExpression(
</ins><span class="cx"> service.query(
</span><del>- "fullNames", "fred",
- matchType = MatchType.contains,
- flags = MatchFlags.NOT,
- ),
- ))
</del><ins>+ u"fullNames", u"fred",
+ matchType=MatchType.contains,
+ flags=MatchFlags.NOT,
+ )
+ )
</ins><span class="cx"> self.assertRecords(
</span><span class="cx"> records,
</span><span class="cx"> (
</span><del>- '__alyssa__',
- '__calendar-dev__',
- '__cdaboo__',
- '__developers__',
- '__dre__',
- '__dreid__',
- '__exarkun__',
- '__glyph__',
- '__joe__',
- '__sagen__',
- '__twisted__',
</del><ins>+ u"__alyssa__",
+ u"__calendar-dev__",
+ u"__cdaboo__",
+ u"__developers__",
+ u"__dre__",
+ u"__dreid__",
+ u"__exarkun__",
+ u"__glyph__",
+ u"__joe__",
+ u"__sagen__",
+ u"__twisted__",
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -551,51 +648,57 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryContainsCaseInsensitive(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
</del><ins>+ records = yield service.recordsFromExpression(
</ins><span class="cx"> service.query(
</span><del>- "shortNames", "Sanchez",
</del><ins>+ u"shortNames", u"Sanchez",
</ins><span class="cx"> matchType=MatchType.contains,
</span><span class="cx"> flags=MatchFlags.caseInsensitive,
</span><del>- ),
- ))
- self.assertRecords(records, ("__wsanchez__",))
</del><ins>+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_queryContainsCaseInsensitiveNoIndex(self):
</span><span class="cx"> service = self.service()
</span><del>- records = yield service.recordsFromQuery((
</del><ins>+ records = yield service.recordsFromExpression(
</ins><span class="cx"> service.query(
</span><del>- "fullNames", "frEdo",
</del><ins>+ u"fullNames", u"frEdo",
</ins><span class="cx"> matchType=MatchType.contains,
</span><span class="cx"> flags=MatchFlags.caseInsensitive,
</span><del>- ),
- ))
- self.assertRecords(records, ("__wsanchez__",))
</del><ins>+ )
+ )
+ self.assertRecords(records, (u"__wsanchez__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryServiceMutableTest(BaseTest):
</del><ins>+class DirectoryServiceMutableTest(unittest.TestCase, BaseTest):
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_updateRecord(self):
</span><span class="cx"> service = self.service()
</span><span class="cx">
</span><del>- record = (yield service.recordWithUID("__wsanchez__"))
</del><ins>+ record = (yield service.recordWithUID(u"__wsanchez__"))
</ins><span class="cx">
</span><span class="cx"> fields = record.fields.copy()
</span><del>- fields[service.fieldName.fullNames] = ["Wilfredo Sanchez Vega"]
</del><ins>+ fields[service.fieldName.fullNames] = [u"Wilfredo Sanchez Vega"]
</ins><span class="cx">
</span><span class="cx"> updatedRecord = DirectoryRecord(service, fields)
</span><span class="cx"> yield service.updateRecords((updatedRecord,))
</span><span class="cx">
</span><span class="cx"> # Verify change is present immediately
</span><del>- record = (yield service.recordWithUID("__wsanchez__"))
- self.assertEquals(set(record.fullNames), set(("Wilfredo Sanchez Vega",)))
</del><ins>+ record = (yield service.recordWithUID(u"__wsanchez__"))
+ self.assertEquals(
+ set(record.fullNames),
+ set((u"Wilfredo Sanchez Vega",))
+ )
</ins><span class="cx">
</span><span class="cx"> # Verify change is persisted
</span><span class="cx"> service.flush()
</span><del>- record = (yield service.recordWithUID("__wsanchez__"))
- self.assertEquals(set(record.fullNames), set(("Wilfredo Sanchez Vega",)))
</del><ins>+ record = (yield service.recordWithUID(u"__wsanchez__"))
+ self.assertEquals(
+ set(record.fullNames),
+ set((u"Wilfredo Sanchez Vega",))
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -604,23 +707,23 @@
</span><span class="cx">
</span><span class="cx"> newRecord = DirectoryRecord(
</span><span class="cx"> service,
</span><del>- fields = {
- service.fieldName.uid: "__plugh__",
</del><ins>+ fields={
+ service.fieldName.uid: u"__plugh__",
</ins><span class="cx"> service.fieldName.recordType: service.recordType.user,
</span><del>- service.fieldName.shortNames: ("plugh",),
</del><ins>+ service.fieldName.shortNames: (u"plugh",),
</ins><span class="cx"> }
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> yield service.updateRecords((newRecord,), create=True)
</span><span class="cx">
</span><span class="cx"> # Verify change is present immediately
</span><del>- record = (yield service.recordWithUID("__plugh__"))
- self.assertEquals(set(record.shortNames), set(("plugh",)))
</del><ins>+ record = (yield service.recordWithUID(u"__plugh__"))
+ self.assertEquals(set(record.shortNames), set((u"plugh",)))
</ins><span class="cx">
</span><span class="cx"> # Verify change is persisted
</span><span class="cx"> service.flush()
</span><del>- record = (yield service.recordWithUID("__plugh__"))
- self.assertEquals(set(record.shortNames), set(("plugh",)))
</del><ins>+ record = (yield service.recordWithUID(u"__plugh__"))
+ self.assertEquals(set(record.shortNames), set((u"plugh",)))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_addRecordNoCreate(self):
</span><span class="lines">@@ -628,81 +731,91 @@
</span><span class="cx">
</span><span class="cx"> newRecord = DirectoryRecord(
</span><span class="cx"> service,
</span><del>- fields = {
- service.fieldName.uid: "__plugh__",
</del><ins>+ fields={
+ service.fieldName.uid: u"__plugh__",
</ins><span class="cx"> service.fieldName.recordType: service.recordType.user,
</span><del>- service.fieldName.shortNames: ("plugh",),
</del><ins>+ service.fieldName.shortNames: (u"plugh",),
</ins><span class="cx"> }
</span><span class="cx"> )
</span><span class="cx">
</span><del>- self.assertFailure(service.updateRecords((newRecord,)), NoSuchRecordError)
</del><ins>+ self.assertFailure(
+ service.updateRecords((newRecord,)),
+ NoSuchRecordError
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_removeRecord(self):
</span><span class="cx"> service = self.service()
</span><span class="cx">
</span><del>- yield service.removeRecords(("__wsanchez__",))
</del><ins>+ yield service.removeRecords((u"__wsanchez__",))
</ins><span class="cx">
</span><span class="cx"> # Verify change is present immediately
</span><del>- self.assertEquals((yield service.recordWithUID("__wsanchez__")), None)
</del><ins>+ self.assertEquals((yield service.recordWithUID(u"__wsanchez__")), None)
</ins><span class="cx">
</span><span class="cx"> # Verify change is persisted
</span><span class="cx"> service.flush()
</span><del>- self.assertEquals((yield service.recordWithUID("__wsanchez__")), None)
</del><ins>+ self.assertEquals((yield service.recordWithUID(u"__wsanchez__")), None)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_removeRecordNoExist(self):
</span><span class="cx"> service = self.service()
</span><span class="cx">
</span><del>- return service.removeRecords(("__plugh__",))
</del><ins>+ return service.removeRecords((u"__plugh__",))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class DirectoryRecordTest(BaseTest, test_directory.DirectoryRecordTest):
</del><ins>+class DirectoryRecordTest(
+ unittest.TestCase,
+ BaseTest,
+ test_index.BaseDirectoryRecordTest
+):
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
</ins><span class="cx"> @inlineCallbacks
</span><del>- def test_members(self):
</del><ins>+ def test_members_group(self):
</ins><span class="cx"> service = self.service()
</span><span class="cx">
</span><del>- record = (yield service.recordWithUID("__wsanchez__"))
</del><ins>+ record = (yield service.recordWithUID(u"__wsanchez__"))
</ins><span class="cx"> members = (yield record.members())
</span><span class="cx"> self.assertEquals(set(members), set())
</span><span class="cx">
</span><del>- record = (yield service.recordWithUID("__twisted__"))
</del><ins>+ record = (yield service.recordWithUID(u"__twisted__"))
</ins><span class="cx"> members = (yield record.members())
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set((member.uid for member in members)),
</span><span class="cx"> set((
</span><del>- "__wsanchez__",
- "__glyph__",
- "__exarkun__",
- "__dreid__",
- "__dre__",
</del><ins>+ u"__wsanchez__",
+ u"__glyph__",
+ u"__exarkun__",
+ u"__dreid__",
+ u"__dre__",
</ins><span class="cx"> ))
</span><span class="cx"> )
</span><span class="cx">
</span><del>- record = (yield service.recordWithUID("__developers__"))
</del><ins>+ record = (yield service.recordWithUID(u"__developers__"))
</ins><span class="cx"> members = (yield record.members())
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set((member.uid for member in members)),
</span><span class="cx"> set((
</span><del>- "__calendar-dev__",
- "__twisted__",
- "__alyssa__",
</del><ins>+ u"__calendar-dev__",
+ u"__twisted__",
+ u"__alyssa__",
</ins><span class="cx"> ))
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def test_groups(self):
</del><ins>+ def test_memberships(self):
</ins><span class="cx"> service = self.service()
</span><span class="cx">
</span><del>- record = (yield service.recordWithUID("__wsanchez__"))
</del><ins>+ record = (yield service.recordWithUID(u"__wsanchez__"))
</ins><span class="cx"> groups = (yield record.groups())
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(group.uid for group in groups),
</span><span class="cx"> set((
</span><del>- "__calendar-dev__",
- "__twisted__",
</del><ins>+ u"__calendar-dev__",
+ u"__twisted__",
</ins><span class="cx"> ))
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -714,8 +827,8 @@
</span><span class="cx"> assert name is not None
</span><span class="cx"> return MatchExpression(
</span><span class="cx"> name, value,
</span><del>- matchType = matchType,
- flags = flags,
</del><ins>+ matchType=matchType,
+ flags=flags,
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -735,11 +848,17 @@
</span><span class="cx"> filePath = FilePath(tmp)
</span><span class="cx"> filePath.setContent(xmlData)
</span><span class="cx">
</span><del>- return serviceClass(filePath)
</del><ins>+ try:
+ return serviceClass(filePath)
+ except Exception as e:
+ raise AssertionError(
+ "Unable to instantiate XML service {0}: {1}"
+ .format(serviceClass, e)
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-testXMLConfig = """<?xml version="1.0" encoding="utf-8"?>
</del><ins>+testXMLConfig = b"""<?xml version="1.0" encoding="utf-8"?>
</ins><span class="cx">
</span><span class="cx"> <directory realm="xyzzy">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhoutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/util.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/util.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -45,15 +45,18 @@
</span><span class="cx">
</span><span class="cx"> self._constants = myConstants
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __getattr__(self, name):
</span><span class="cx"> try:
</span><span class="cx"> return self._constants[name]
</span><span class="cx"> except KeyError:
</span><span class="cx"> raise AttributeError(name)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def iterconstants(self):
</span><span class="cx"> return self._constants.itervalues()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lookupByName(self, name):
</span><span class="cx"> try:
</span><span class="cx"> return self._constants[name]
</span><span class="lines">@@ -61,6 +64,7 @@
</span><span class="cx"> raise ValueError(name)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def uniqueResult(values):
</span><span class="cx"> result = None
</span><span class="cx"> for value in values:
</span><span class="lines">@@ -73,6 +77,7 @@
</span><span class="cx"> return result
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def describe(constant):
</span><span class="cx"> if isinstance(constant, FlagConstant):
</span><span class="cx"> parts = []
</span><span class="lines">@@ -83,6 +88,7 @@
</span><span class="cx"> return getattr(constant, "description", constant.name)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def iterFlags(flags):
</span><span class="cx"> if hasattr(flags, "__iter__"):
</span><span class="cx"> return flags
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwextwhoxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/xml.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/xml.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twext/who/xml.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> ]
</span><span class="cx">
</span><span class="cx"> from time import time
</span><ins>+from uuid import UUID
</ins><span class="cx">
</span><span class="cx"> from xml.etree.ElementTree import parse as parseXML
</span><span class="cx"> from xml.etree.ElementTree import ParseError as XMLParseError
</span><span class="lines">@@ -62,38 +63,38 @@
</span><span class="cx"> ##
</span><span class="cx">
</span><span class="cx"> class Element(Values):
</span><del>- directory = ValueConstant("directory")
- record = ValueConstant("record")
</del><ins>+ directory = ValueConstant(u"directory")
+ record = ValueConstant(u"record")
</ins><span class="cx">
</span><span class="cx"> #
</span><span class="cx"> # Field names
</span><span class="cx"> #
</span><del>- uid = ValueConstant("uid")
</del><ins>+ uid = ValueConstant(u"uid")
</ins><span class="cx"> uid.fieldName = BaseFieldName.uid
</span><span class="cx">
</span><del>- guid = ValueConstant("guid")
</del><ins>+ guid = ValueConstant(u"guid")
</ins><span class="cx"> guid.fieldName = BaseFieldName.guid
</span><span class="cx">
</span><del>- shortName = ValueConstant("short-name")
</del><ins>+ shortName = ValueConstant(u"short-name")
</ins><span class="cx"> shortName.fieldName = BaseFieldName.shortNames
</span><span class="cx">
</span><del>- fullName = ValueConstant("full-name")
</del><ins>+ fullName = ValueConstant(u"full-name")
</ins><span class="cx"> fullName.fieldName = BaseFieldName.fullNames
</span><span class="cx">
</span><del>- emailAddress = ValueConstant("email")
</del><ins>+ emailAddress = ValueConstant(u"email")
</ins><span class="cx"> emailAddress.fieldName = BaseFieldName.emailAddresses
</span><span class="cx">
</span><del>- password = ValueConstant("password")
</del><ins>+ password = ValueConstant(u"password")
</ins><span class="cx"> password.fieldName = BaseFieldName.password
</span><span class="cx">
</span><del>- memberUID = ValueConstant("member-uid")
</del><ins>+ memberUID = ValueConstant(u"member-uid")
</ins><span class="cx"> memberUID.fieldName = IndexFieldName.memberUIDs
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class Attribute(Values):
</span><del>- realm = ValueConstant("realm")
- recordType = ValueConstant("type")
</del><ins>+ realm = ValueConstant(u"realm")
+ recordType = ValueConstant(u"type")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -101,16 +102,16 @@
</span><span class="cx"> #
</span><span class="cx"> # Booleans
</span><span class="cx"> #
</span><del>- true = ValueConstant("true")
- false = ValueConstant("false")
</del><ins>+ true = ValueConstant(u"true")
+ false = ValueConstant(u"false")
</ins><span class="cx">
</span><span class="cx"> #
</span><span class="cx"> # Record types
</span><span class="cx"> #
</span><del>- user = ValueConstant("user")
</del><ins>+ user = ValueConstant(u"user")
</ins><span class="cx"> user.recordType = RecordType.user
</span><span class="cx">
</span><del>- group = ValueConstant("group")
</del><ins>+ group = ValueConstant(u"group")
</ins><span class="cx"> group.recordType = RecordType.group
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -234,14 +235,14 @@
</span><span class="cx"> "Incorrect root element: {0}".format(directoryNode.tag)
</span><span class="cx"> )
</span><span class="cx">
</span><del>- realmName = directoryNode.get(
- self.attribute.realm.value, ""
- ).encode("utf-8")
</del><ins>+ realmName = unicode(directoryNode.get(
+ self.attribute.realm.value, u""
+ ))
</ins><span class="cx">
</span><span class="cx"> if not realmName:
</span><span class="cx"> raise ParseError("No realm name.")
</span><span class="cx">
</span><del>- unknownRecordTypes = set()
</del><ins>+ unknownRecordTypes = set()
</ins><span class="cx"> unknownFieldElements = set()
</span><span class="cx">
</span><span class="cx"> records = set()
</span><span class="lines">@@ -258,39 +259,24 @@
</span><span class="cx"> # Store results
</span><span class="cx"> #
</span><span class="cx">
</span><del>- index = {}
</del><ins>+ self.flush()
+ self.indexRecords(records)
</ins><span class="cx">
</span><del>- for fieldName in self.indexedFields:
- index[fieldName] = {}
-
- for record in records:
- for fieldName in self.indexedFields:
- values = record.fields.get(fieldName, None)
-
- if values is not None:
- if not BaseFieldName.isMultiValue(fieldName):
- values = (values,)
-
- for value in values:
- index[fieldName].setdefault(value, set()).add(record)
-
</del><span class="cx"> self._realmName = realmName
</span><span class="cx">
</span><del>- self._unknownRecordTypes = unknownRecordTypes
</del><ins>+ self._unknownRecordTypes = unknownRecordTypes
</ins><span class="cx"> self._unknownFieldElements = unknownFieldElements
</span><span class="cx">
</span><span class="cx"> self._cacheTag = cacheTag
</span><span class="cx"> self._lastRefresh = now
</span><span class="cx">
</span><del>- self.index = index
-
</del><span class="cx"> return etree
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def parseRecordNode(self, recordNode, unknownFieldElements=None):
</span><span class="cx"> recordTypeAttribute = recordNode.get(
</span><del>- self.attribute.recordType.value, ""
- ).encode("utf-8")
</del><ins>+ self.attribute.recordType.value, u""
+ )
</ins><span class="cx"> if recordTypeAttribute:
</span><span class="cx"> try:
</span><span class="cx"> recordType = (
</span><span class="lines">@@ -317,8 +303,16 @@
</span><span class="cx"> if unknownFieldElements is not None:
</span><span class="cx"> unknownFieldElements.add(fieldNode.tag)
</span><span class="cx">
</span><del>- value = fieldNode.text.encode("utf-8")
</del><ins>+ vType = BaseFieldName.valueType(fieldName)
</ins><span class="cx">
</span><ins>+ if vType in (unicode, UUID):
+ value = vType(fieldNode.text)
+ else:
+ raise AssertionError(
+ "Unknown value type {0} for field {1}",
+ vType, fieldName
+ )
+
</ins><span class="cx"> if BaseFieldName.isMultiValue(fieldName):
</span><span class="cx"> values = fields.setdefault(fieldName, [])
</span><span class="cx"> values.append(value)
</span><span class="lines">@@ -339,11 +333,11 @@
</span><span class="cx"> def flush(self):
</span><span class="cx"> BaseDirectoryService.flush(self)
</span><span class="cx">
</span><del>- self._realmName = None
- self._unknownRecordTypes = None
</del><ins>+ self._realmName = None
+ self._unknownRecordTypes = None
</ins><span class="cx"> self._unknownFieldElements = None
</span><del>- self._cacheTag = None
- self._lastRefresh = 0
</del><ins>+ self._cacheTag = None
+ self._lastRefresh = 0
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def updateRecords(self, records, create=False):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedpluginsmasterchildpyfromrev12016CalendarServertrunktwistedpluginsmasterchildpy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/twisted/plugins/masterchild.py (from rev 12016, CalendarServer/trunk/twisted/plugins/masterchild.py) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twisted/plugins/masterchild.py         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twisted/plugins/masterchild.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,58 @@
</span><ins>+##
+# Copyright (c) 2010-2013 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 zope.interface import implementer
+
+from twisted.python.reflect import namedClass
+from twisted.plugin import IPlugin
+from twisted.application.service import IServiceMaker
+
+from twext.application.masterchild import MasterOptions, ChildOptions
+
+
+@implementer(IPlugin, IServiceMaker)
+class ServiceMaker(object):
+ def __init__(self, name, description, options, serviceMakerClass):
+ self.tapname = name
+ self.description = description
+ self.options = options
+ self.serviceMakerClass = serviceMakerClass
+ self._serviceMaker = None
+
+
+ def makeService(self, options):
+ if self._serviceMaker is None:
+ self._serviceMaker = namedClass(self.serviceMakerClass)()
+
+ return self._serviceMaker.makeService(options)
+
+
+
+masterServiceMaker = ServiceMaker(
+ "master",
+ "Master process application container",
+ MasterOptions,
+ "twext.application.masterchild.MasterServiceMaker"
+)
+
+
+
+childServiceMaker = ServiceMaker(
+ "child",
+ "Child process application container",
+ ChildOptions,
+ "twext.application.masterchild.ChildServiceMaker"
+)
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldav__init__py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/__init__.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/__init__.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/__init__.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -59,12 +59,14 @@
</span><span class="cx"> })
</span><span class="cx">
</span><span class="cx"> # Do some PyCalendar init
</span><del>-from pycalendar.calendar import PyCalendar
-from pycalendar.property import PyCalendarProperty
-from pycalendar.value import PyCalendarValue
</del><ins>+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.icalendar.property import Property
+from pycalendar.vcard.card import Card
+from pycalendar.value import Value
</ins><span class="cx">
</span><del>-PyCalendar.setPRODID("-//CALENDARSERVER.ORG//NONSGML Version 1//EN")
</del><ins>+Calendar.setPRODID("-//CALENDARSERVER.ORG//NONSGML Version 1//EN")
+Card.setPRODID("-//CALENDARSERVER.ORG//NONSGML Version 1//EN")
</ins><span class="cx">
</span><span class="cx"> # These are properties we use directly and we want the default value type set for TEXT
</span><del>-PyCalendarProperty.registerDefaultValue("X-CALENDARSERVER-PRIVATE-COMMENT", PyCalendarValue.VALUETYPE_TEXT)
-PyCalendarProperty.registerDefaultValue("X-CALENDARSERVER-ATTENDEE-COMMENT", PyCalendarValue.VALUETYPE_TEXT)
</del><ins>+Property.registerDefaultValue("X-CALENDARSERVER-PRIVATE-COMMENT", Value.VALUETYPE_TEXT)
+Property.registerDefaultValue("X-CALENDARSERVER-ATTENDEE-COMMENT", Value.VALUETYPE_TEXT)
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavaccountingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/accounting.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/accounting.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/accounting.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -42,6 +42,8 @@
</span><span class="cx"> accountingEnabledForPrincipal(principal)
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def accountingEnabledForCategory(category):
</span><span class="cx"> """
</span><span class="cx"> Determine if accounting is enabled for the given category.
</span><span class="lines">@@ -51,6 +53,8 @@
</span><span class="cx"> return False
</span><span class="cx"> return AccountingCategories.get(category, False)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def accountingEnabledForPrincipal(principal):
</span><span class="cx"> """
</span><span class="cx"> Determine if accounting is enabled for the given principal.
</span><span class="lines">@@ -69,6 +73,8 @@
</span><span class="cx">
</span><span class="cx"> return False
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def emitAccounting(category, principal, data, tag=None):
</span><span class="cx"> """
</span><span class="cx"> Write the supplied data to the appropriate location for the given
</span><span class="lines">@@ -80,7 +86,7 @@
</span><span class="cx"> @type category: C{tuple}
</span><span class="cx"> @param data: data to write.
</span><span class="cx"> @type data: C{str}
</span><del>- """
</del><ins>+ """
</ins><span class="cx"> if isinstance(principal, str):
</span><span class="cx"> principalLogPath = principal
</span><span class="cx"> elif accountingEnabled(category, principal):
</span><span class="lines">@@ -107,7 +113,7 @@
</span><span class="cx"> logDirectory,
</span><span class="cx"> datetime.datetime.now().isoformat()
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> if not os.path.isdir(os.path.join(logRoot, logDirectory)):
</span><span class="cx"> os.makedirs(os.path.join(logRoot, logDirectory))
</span><span class="cx"> logFilename = "%s-01" % (logFilename,)
</span><span class="lines">@@ -128,7 +134,7 @@
</span><span class="cx"> log.error("Too many %s accounting files for %s" % (category, principal))
</span><span class="cx"> return None
</span><span class="cx"> index += 1
</span><del>-
</del><ins>+
</ins><span class="cx"> #
</span><span class="cx"> # Now write out the data to the log file
</span><span class="cx"> #
</span><span class="lines">@@ -137,7 +143,7 @@
</span><span class="cx"> logFile.write(data)
</span><span class="cx"> finally:
</span><span class="cx"> logFile.close()
</span><del>-
</del><ins>+
</ins><span class="cx"> return logFilename
</span><span class="cx">
</span><span class="cx"> except OSError, e:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavauthkerbpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/authkerb.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/authkerb.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/authkerb.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -22,7 +22,7 @@
</span><span class="cx">
</span><span class="cx"> 1. An alternative to password based BASIC authentication in which the BASIC credentials are
</span><span class="cx"> verified against Kerberos.
</span><del>-
</del><ins>+
</ins><span class="cx"> 2. The NEGOTIATE mechanism (as defined in http://www.ietf.org/rfc/rfc4559.txt)
</span><span class="cx"> that implements full GSSAPI authentication.
</span><span class="cx"> """
</span><span class="lines">@@ -58,7 +58,7 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, principal=None, type=None, hostname=None):
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> @param principal: full Kerberos principal (e.g., 'HTTP/server.example.com@EXAMPLE.COM'). If C{None}
</span><span class="cx"> then the type and hostname arguments are used instead.
</span><span class="cx"> @type service: str
</span><span class="lines">@@ -82,6 +82,7 @@
</span><span class="cx">
</span><span class="cx"> self.service, self.realm = self._splitPrincipal(principal)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _splitPrincipal(self, principal):
</span><span class="cx">
</span><span class="cx"> try:
</span><span class="lines">@@ -93,12 +94,14 @@
</span><span class="cx"> except IndexError:
</span><span class="cx"> self.log.error("Invalid Kerberos principal: %s" % (principal,))
</span><span class="cx"> raise ValueError('Authentication System Failure: Invalid Kerberos principal: %s' % (principal,))
</span><del>-
</del><ins>+
</ins><span class="cx"> service = "%s@%s" % (servicetype, service,)
</span><span class="cx"> realm = realm
</span><del>-
</del><ins>+
</ins><span class="cx"> return (service, realm,)
</span><del>-
</del><ins>+
+
+
</ins><span class="cx"> class BasicKerberosCredentials(credentials.UsernamePassword):
</span><span class="cx"> """
</span><span class="cx"> A set of user/password credentials that checks itself against Kerberos.
</span><span class="lines">@@ -106,7 +109,7 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, username, password, service, realm):
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> @param username: user name of user to authenticate
</span><span class="cx"> @type username: str
</span><span class="cx"> @param password: password for user being authenticated
</span><span class="lines">@@ -117,11 +120,13 @@
</span><span class="cx"> @type hostname: str
</span><span class="cx"> """
</span><span class="cx"> credentials.UsernamePassword.__init__(self, username, password)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Convert Kerberos principal spec into service and realm
</span><span class="cx"> self.service = service
</span><span class="cx"> self.default_realm = realm
</span><del>-
</del><ins>+
+
+
</ins><span class="cx"> class BasicKerberosCredentialFactory(KerberosCredentialFactoryBase):
</span><span class="cx"> """
</span><span class="cx"> Authorizer for insecure Basic (base64-encoded plaintext) authentication.
</span><span class="lines">@@ -134,7 +139,7 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, principal=None, type=None, hostname=None):
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> @param principal: full Kerberos principal (e.g., 'HTTP/server.example.com@EXAMPLE.COM'). If C{None}
</span><span class="cx"> then the type and hostname arguments are used instead.
</span><span class="cx"> @type service: str
</span><span class="lines">@@ -146,9 +151,11 @@
</span><span class="cx">
</span><span class="cx"> super(BasicKerberosCredentialFactory, self).__init__(principal, type, hostname)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getChallenge(self, _ignore_peer):
</span><span class="cx"> return succeed({'realm': self.realm})
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def decode(self, response, request): #@UnusedVariable
</span><span class="cx"> try:
</span><span class="cx"> creds = (response + '===').decode('base64')
</span><span class="lines">@@ -161,6 +168,8 @@
</span><span class="cx"> return succeed(c)
</span><span class="cx"> raise error.LoginFailed('Invalid credentials')
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class BasicKerberosCredentialsChecker(object):
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -187,9 +196,11 @@
</span><span class="cx"> pcreds.authnPrincipal,
</span><span class="cx"> pcreds.authzPrincipal,
</span><span class="cx"> ))
</span><del>-
</del><ins>+
</ins><span class="cx"> raise error.UnauthorizedLogin("Bad credentials for: %s" % (pcreds.authnURI,))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class NegotiateCredentials(object):
</span><span class="cx"> """
</span><span class="cx"> A set of user/password credentials that checks itself against Kerberos.
</span><span class="lines">@@ -198,10 +209,12 @@
</span><span class="cx"> implements(credentials.ICredentials)
</span><span class="cx">
</span><span class="cx"> def __init__(self, principal, username):
</span><del>-
</del><ins>+
</ins><span class="cx"> self.principal = principal
</span><span class="cx"> self.username = username
</span><del>-
</del><ins>+
+
+
</ins><span class="cx"> class NegotiateCredentialFactory(KerberosCredentialFactoryBase):
</span><span class="cx"> """
</span><span class="cx"> Authorizer for Negotiate authentication (http://www.ietf.org/rfc/rfc4559.txt).
</span><span class="lines">@@ -211,7 +224,7 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, principal=None, type=None, hostname=None):
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> @param principal: full Kerberos principal (e.g., 'HTTP/server.example.com@EXAMPLE.COM'). If C{None}
</span><span class="cx"> then the type and hostname arguments are used instead.
</span><span class="cx"> @type service: str
</span><span class="lines">@@ -223,22 +236,24 @@
</span><span class="cx">
</span><span class="cx"> super(NegotiateCredentialFactory, self).__init__(principal, type, hostname)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getChallenge(self, _ignore_peer):
</span><span class="cx"> return succeed({})
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def decode(self, base64data, request):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Init GSSAPI first - we won't specify the service now as we need to accept a target
</span><span class="cx"> # name that is case-insenstive as some clients will use "http" instead of "HTTP"
</span><span class="cx"> try:
</span><del>- _ignore_result, context = kerberos.authGSSServerInit("");
</del><ins>+ _ignore_result, context = kerberos.authGSSServerInit("")
</ins><span class="cx"> except kerberos.GSSError, ex:
</span><span class="cx"> self.log.error("authGSSServerInit: %s(%s)" % (ex[0][0], ex[1][0],))
</span><span class="cx"> raise error.LoginFailed('Authentication System Failure: %s(%s)' % (ex[0][0], ex[1][0],))
</span><span class="cx">
</span><span class="cx"> # Do the GSSAPI step and get response and username
</span><span class="cx"> try:
</span><del>- kerberos.authGSSServerStep(context, base64data);
</del><ins>+ kerberos.authGSSServerStep(context, base64data)
</ins><span class="cx"> except kerberos.GSSError, ex:
</span><span class="cx"> self.log.error("authGSSServerStep: %s(%s)" % (ex[0][0], ex[1][0],))
</span><span class="cx"> kerberos.authGSSServerClean(context)
</span><span class="lines">@@ -264,13 +279,13 @@
</span><span class="cx"> principal = kerberos.authGSSServerUserName(context)
</span><span class="cx"> username = principal
</span><span class="cx"> realmname = ""
</span><del>-
</del><ins>+
</ins><span class="cx"> # Username may include realm suffix which we want to strip
</span><span class="cx"> if username.find("@") != -1:
</span><span class="cx"> splits = username.split("@", 1)
</span><span class="cx"> username = splits[0]
</span><span class="cx"> realmname = splits[1]
</span><del>-
</del><ins>+
</ins><span class="cx"> # We currently do not support cross-realm authentication, so we
</span><span class="cx"> # must verify that the realm we got exactly matches the one we expect.
</span><span class="cx"> if realmname != self.realm:
</span><span class="lines">@@ -278,11 +293,11 @@
</span><span class="cx">
</span><span class="cx"> # Close the context
</span><span class="cx"> try:
</span><del>- kerberos.authGSSServerClean(context);
</del><ins>+ kerberos.authGSSServerClean(context)
</ins><span class="cx"> except kerberos.GSSError, ex:
</span><span class="cx"> self.log.error("authGSSServerClean: %s" % (ex[0][0], ex[1][0],))
</span><span class="cx"> raise error.LoginFailed('Authentication System Failure %s(%s)' % (ex[0][0], ex[1][0],))
</span><del>-
</del><ins>+
</ins><span class="cx"> # If we successfully decoded and verified the Kerberos credentials we need to add the Kerberos
</span><span class="cx"> # response data to the outgoing request
</span><span class="cx">
</span><span class="lines">@@ -299,6 +314,8 @@
</span><span class="cx">
</span><span class="cx"> return succeed(NegotiateCredentials(principal, username))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class NegotiateCredentialsChecker(object):
</span><span class="cx">
</span><span class="cx"> implements(checkers.ICredentialsChecker)
</span><span class="lines">@@ -320,6 +337,5 @@
</span><span class="cx"> pcreds.authnPrincipal,
</span><span class="cx"> pcreds.authzPrincipal,
</span><span class="cx"> ))
</span><del>-
</del><ins>+
</ins><span class="cx"> raise error.UnauthorizedLogin("Bad credentials for: %s" % (pcreds.authnURI,))
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavbackuppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/backup.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/backup.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/backup.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -38,19 +38,22 @@
</span><span class="cx"> for x in xrange(0, len(argv)):
</span><span class="cx"> opt = argv[x]
</span><span class="cx"> if opt.startswith('-'):
</span><del>- self[opt.strip('-')] = argv[x+1]
</del><ins>+ self[opt.strip('-')] = argv[x + 1]
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def debug(string):
</span><span class="cx"> if VERBOSE:
</span><span class="cx"> print("DEBUG:", string)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def funclog(string):
</span><span class="cx"> if FUNCLOG:
</span><span class="cx"> print("FUNCLOG:", string)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def logFuncCall(func):
</span><span class="cx"> def printArgs(args):
</span><span class="cx"> a = []
</span><span class="lines">@@ -60,6 +63,7 @@
</span><span class="cx">
</span><span class="cx"> return ''.join(a).strip(', ')
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def printKwargs(kwargs):
</span><span class="cx"> a = []
</span><span class="cx"> for kwarg, value in kwargs:
</span><span class="lines">@@ -67,8 +71,9 @@
</span><span class="cx">
</span><span class="cx"> return ''.join(a).strip(', ')
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _(*args, **kwargs):
</span><del>- funclog("%s(%s)" % (func.func_name,
</del><ins>+ funclog("%s(%s)" % (func.func_name,
</ins><span class="cx"> ', '.join((printArgs(args),
</span><span class="cx"> printKwargs(kwargs))).strip(', ')))
</span><span class="cx">
</span><span class="lines">@@ -77,10 +82,11 @@
</span><span class="cx"> funclog("%s - > %s" % (func.func_name, retval))
</span><span class="cx">
</span><span class="cx"> return retval
</span><del>-
</del><ins>+
</ins><span class="cx"> return _
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @logFuncCall
</span><span class="cx"> def readConfig(configFile):
</span><span class="cx"> config = readPlist(configFile + '.default')
</span><span class="lines">@@ -89,14 +95,16 @@
</span><span class="cx"> config.update(readPlist(configFile))
</span><span class="cx">
</span><span class="cx"> return config
</span><del>-
</del><span class="cx">
</span><ins>+
+
</ins><span class="cx"> @logFuncCall
</span><span class="cx"> def mkroot(path):
</span><span class="cx"> root = '/'.join(path.rstrip('/').split('/')[:-1])
</span><span class="cx"> os.makedirs(root)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @logFuncCall
</span><span class="cx"> def serveradmin(action, service):
</span><span class="cx"> cmd = ' '.join((
</span><span class="lines">@@ -112,6 +120,7 @@
</span><span class="cx"> return status
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @logFuncCall
</span><span class="cx"> def isRunning(service):
</span><span class="cx"> cmd = ' '.join((
</span><span class="lines">@@ -134,21 +143,25 @@
</span><span class="cx"> return False
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @logFuncCall
</span><span class="cx"> def copy(src, dst):
</span><span class="cx"> shutil.copytree(src, dst)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @logFuncCall
</span><span class="cx"> def move(src, dst):
</span><span class="cx"> os.rename(src, dst)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @logFuncCall
</span><span class="cx"> def remove(dst):
</span><span class="cx"> shutil.rmtree(dst)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @logFuncCall
</span><span class="cx"> def purge(root, patterns):
</span><span class="cx"> removed = []
</span><span class="lines">@@ -175,5 +188,5 @@
</span><span class="cx"> os.remove(full)
</span><span class="cx">
</span><span class="cx"> removed.append(full)
</span><del>-
</del><ins>+
</ins><span class="cx"> return removed
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavcaldavxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/caldavxml.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/caldavxml.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/caldavxml.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -25,8 +25,8 @@
</span><span class="cx"> See draft spec: http://ietf.webdav.org/caldav/draft-dusseault-caldav.txt
</span><span class="cx"> """
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from txdav.xml.element import registerElement, dav_namespace
</span><span class="cx"> from txdav.xml.element import WebDAVElement, PCDATAElement
</span><span class="lines">@@ -110,8 +110,8 @@
</span><span class="cx"> if "start" not in attributes and "end" not in attributes:
</span><span class="cx"> raise ValueError("One of 'start' or 'end' must be present in CALDAV:time-range")
</span><span class="cx">
</span><del>- self.start = PyCalendarDateTime.parseText(attributes["start"]) if "start" in attributes else None
- self.end = PyCalendarDateTime.parseText(attributes["end"]) if "end" in attributes else None
</del><ins>+ self.start = DateTime.parseText(attributes["start"]) if "start" in attributes else None
+ self.end = DateTime.parseText(attributes["end"]) if "end" in attributes else None
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def valid(self, level=0):
</span><span class="lines">@@ -139,23 +139,102 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class CalDAVTimeZoneElement (CalDAVTextElement):
</del><ins>+class CalDAVDataMixin(object):
</ins><span class="cx"> """
</span><del>- CalDAV element containing iCalendar data with a single VTIMEZONE component.
</del><ins>+ A mixin to support accept/returning data in various formats.
</ins><span class="cx"> """
</span><ins>+
+ def __init__(self, *children, **attributes):
+
+ if "content-type" in attributes:
+ self.content_type = attributes["content-type"]
+ else:
+ self.content_type = "text/calendar"
+
+ if "version" in attributes:
+ self.version = attributes["version"]
+ else:
+ self.version = "2.0"
+
+ super(CalDAVDataMixin, self).__init__(*children, **attributes)
+
+
+ def verifyTypeVersion(self):
+ """
+ Make sure any content-type and version matches at least one supported set.
+
+ @return: C{True} if there is at least one match, C{False} otherwise.
+ """
+ allowedTypes = set()
+ allowedTypes.add(("text/calendar", "2.0",))
+ if config.EnableJSONData:
+ allowedTypes.add(("application/calendar+json", "2.0",))
+ for format, version in allowedTypes:
+ if (format == self.content_type) and (version == self.version):
+ return True
+
+ return False
+
+
+ @classmethod
+ def fromCalendar(clazz, calendar, format=None):
+ attrs = {}
+ if format is not None and format != "text/calendar":
+ attrs["content-type"] = format
+
+ if isinstance(calendar, str):
+ if not calendar:
+ raise ValueError("Missing calendar data")
+ return clazz(PCDATAElement(calendar), **attrs)
+ elif isinstance(calendar, iComponent):
+ assert calendar.name() == "VCALENDAR", "Not a calendar: %r" % (calendar,)
+ return clazz(PCDATAElement(calendar.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference, format=format)), **attrs)
+ else:
+ raise ValueError("Not a calendar: %s" % (calendar,))
+
+ fromTextData = fromCalendar
+ fromComponent = fromCalendar
+
</ins><span class="cx"> def calendar(self):
</span><span class="cx"> """
</span><del>- Returns a calendar component derived from this element, which contains
- exactly one VTIMEZONE component.
</del><ins>+ Returns a calendar component derived from this element.
</ins><span class="cx"> """
</span><del>- return iComponent.fromString(str(self))
</del><ins>+ data = self.calendarData()
+ if data:
+ return iComponent.fromString(data, format=self.content_type)
+ else:
+ return None
</ins><span class="cx">
</span><ins>+ generateComponent = calendar
</ins><span class="cx">
</span><ins>+
+ def calendarData(self):
+ """
+ Returns the calendar data derived from this element.
+ """
+ for data in self.children:
+ if not isinstance(data, PCDATAElement):
+ return None
+ else:
+ # We guaranteed in __init__() that there is only one child...
+ break
+
+ return str(data)
+
+ textData = calendarData
+
+
+
+class CalDAVTimeZoneElement (CalDAVDataMixin, CalDAVTextElement):
+ """
+ CalDAV element containing iCalendar data with a single VTIMEZONE component.
+ """
+
</ins><span class="cx"> def gettimezone(self):
</span><span class="cx"> """
</span><span class="cx"> Get the timezone to use. If none, return UTC timezone.
</span><span class="cx">
</span><del>- @return: the L{PyCalendarTimezone} derived from the VTIMEZONE or utc.
</del><ins>+ @return: the L{Timezone} derived from the VTIMEZONE or utc.
</ins><span class="cx"> """
</span><span class="cx"> calendar = self.calendar()
</span><span class="cx"> if calendar is not None:
</span><span class="lines">@@ -164,7 +243,7 @@
</span><span class="cx"> return tz
</span><span class="cx">
</span><span class="cx"> # Default to using utc tzinfo
</span><del>- return PyCalendarTimezone(utc=True)
</del><ins>+ return Timezone(utc=True)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def valid(self):
</span><span class="lines">@@ -231,8 +310,13 @@
</span><span class="cx"> name = "calendar-timezone"
</span><span class="cx"> hidden = True
</span><span class="cx">
</span><ins>+ allowed_attributes = {
+ "content-type": False,
+ "version" : False,
+ }
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class SupportedCalendarComponentSets (CalDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -428,7 +512,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @registerElement
</span><del>-class CalendarData (CalDAVElement):
</del><ins>+class CalendarData (CalDAVDataMixin, CalDAVElement):
</ins><span class="cx"> """
</span><span class="cx"> Defines which parts of a calendar component object should be returned by a
</span><span class="cx"> report.
</span><span class="lines">@@ -448,21 +532,6 @@
</span><span class="cx"> "version" : False,
</span><span class="cx"> }
</span><span class="cx">
</span><del>- @classmethod
- def fromCalendar(clazz, calendar):
- if isinstance(calendar, str):
- if not calendar:
- raise ValueError("Missing calendar data")
- return clazz(PCDATAElement(calendar))
- elif isinstance(calendar, iComponent):
- assert calendar.name() == "VCALENDAR", "Not a calendar: %r" % (calendar,)
- return clazz(PCDATAElement(calendar.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference)))
- else:
- raise ValueError("Not a calendar: %s" % (calendar,))
-
- fromTextData = fromCalendar
-
-
</del><span class="cx"> def __init__(self, *children, **attributes):
</span><span class="cx"> super(CalendarData, self).__init__(*children, **attributes)
</span><span class="cx">
</span><span class="lines">@@ -515,61 +584,8 @@
</span><span class="cx"> # optimize them originals away
</span><span class="cx"> self.children = (data,)
</span><span class="cx">
</span><del>- if "content-type" in attributes:
- self.content_type = attributes["content-type"]
- else:
- self.content_type = "text/calendar"
</del><span class="cx">
</span><del>- if "version" in attributes:
- self.version = attributes["version"]
- else:
- self.version = "2.0"
</del><span class="cx">
</span><del>-
- def verifyTypeVersion(self, types_and_versions):
- """
- Make sure any content-type and version matches at least one of the supplied set.
-
- @param types_and_versions: a list of (content-type, version) tuples to test against.
- @return: True if there is at least one match, False otherwise.
- """
- for item in types_and_versions:
- if (item[0] == self.content_type) and (item[1] == self.version):
- return True
-
- return False
-
-
- def calendar(self):
- """
- Returns a calendar component derived from this element.
- """
- data = self.calendarData()
- if data:
- return iComponent.fromString(data)
- else:
- return None
-
- generateComponent = calendar
-
-
- def calendarData(self):
- """
- Returns the calendar data derived from this element.
- """
- for data in self.children:
- if not isinstance(data, PCDATAElement):
- return None
- else:
- # We guaranteed in __init__() that there is only one child...
- break
-
- return str(data)
-
- textData = calendarData
-
-
-
</del><span class="cx"> @registerElement
</span><span class="cx"> class CalendarComponent (CalDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -882,8 +898,13 @@
</span><span class="cx"> """
</span><span class="cx"> name = "timezone"
</span><span class="cx">
</span><ins>+ allowed_attributes = {
+ "content-type": False,
+ "version" : False,
+ }
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class TimeRange (CalDAVTimeRangeElement):
</span><span class="cx"> """
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavcarddavxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/carddavxml.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/carddavxml.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/carddavxml.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -22,7 +22,7 @@
</span><span class="cx"> This API is considered private to static.py and is therefore subject to
</span><span class="cx"> change.
</span><span class="cx">
</span><del>-See draft spec:
</del><ins>+See draft spec:
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> from txdav.xml.element import registerElement, dav_namespace
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx"> from txdav.xml.element import WebDAVEmptyElement, WebDAVTextElement
</span><span class="cx"> from txdav.xml.element import ResourceType, Collection
</span><span class="cx">
</span><ins>+from twistedcaldav.config import config
</ins><span class="cx"> from twistedcaldav.vcard import Component
</span><span class="cx">
</span><span class="cx"> ##
</span><span class="lines">@@ -50,6 +51,7 @@
</span><span class="cx"> namespace = carddav_namespace
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class CardDAVEmptyElement (WebDAVEmptyElement):
</span><span class="cx"> """
</span><span class="cx"> CardDAV element with no contents.
</span><span class="lines">@@ -57,6 +59,7 @@
</span><span class="cx"> namespace = carddav_namespace
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class CardDAVTextElement (WebDAVTextElement):
</span><span class="cx"> """
</span><span class="cx"> CardDAV element containing PCDATA.
</span><span class="lines">@@ -64,6 +67,93 @@
</span><span class="cx"> namespace = carddav_namespace
</span><span class="cx">
</span><span class="cx">
</span><ins>+
+class CardDAVDataMixin(object):
+ """
+ A mixin to support accept/returning data in various formats.
+ """
+
+ def __init__(self, *children, **attributes):
+
+ if "content-type" in attributes:
+ self.content_type = attributes["content-type"]
+ else:
+ self.content_type = "text/vcard"
+
+ if "version" in attributes:
+ self.version = attributes["version"]
+ else:
+ self.version = "3.0"
+
+ super(CardDAVDataMixin, self).__init__(*children, **attributes)
+
+
+ def verifyTypeVersion(self):
+ """
+ Make sure any content-type and version matches at least one supported set.
+
+ @return: C{True} if there is at least one match, C{False} otherwise.
+ """
+ allowedTypes = set()
+ allowedTypes.add(("text/vcard", "3.0",))
+ if config.EnableJSONData:
+ allowedTypes.add(("application/vcard+json", "3.0",))
+ for format, version in allowedTypes:
+ if (format == self.content_type) and (version == self.version):
+ return True
+
+ return False
+
+
+ @classmethod
+ def fromAddress(clazz, address, format=None):
+ attrs = {}
+ if format is not None and format != "text/vcard":
+ attrs["content-type"] = format
+
+ if isinstance(address, str):
+ if not address:
+ raise ValueError("Missing address data")
+ return clazz(PCDATAElement(address), **attrs)
+ elif isinstance(address, Component):
+ assert address.name() == "VCARD", "Not a vCard: %r" % (address,)
+ return clazz(PCDATAElement(address.getText(format)), **attrs)
+ else:
+ raise ValueError("Not an address: %s" % (address,))
+
+ fromTextData = fromAddress
+ fromComponent = fromAddress
+
+ def address(self):
+ """
+ Returns an address component derived from this element.
+ """
+ data = self.addressData()
+ if data:
+ return Component.fromString(data, format=self.content_type)
+ else:
+ return None
+
+ generateComponent = address
+
+
+ def addressData(self):
+ """
+ Returns the address data derived from this element.
+ """
+ for data in self.children:
+ if not isinstance(data, PCDATAElement):
+ return None
+ else:
+ # We guaranteed in __init__() that there is only one child...
+ break
+
+ return str(data)
+
+ textData = addressData
+
+
+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class AddressBookHomeSet (CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -73,9 +163,10 @@
</span><span class="cx"> name = "addressbook-home-set"
</span><span class="cx"> hidden = True
</span><span class="cx">
</span><del>- allowed_children = { (dav_namespace, "href"): (0, None) }
</del><ins>+ allowed_children = {(dav_namespace, "href"): (0, None)}
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class AddressBookDescription (CardDAVTextElement):
</span><span class="cx"> """
</span><span class="lines">@@ -88,6 +179,7 @@
</span><span class="cx"> # May be protected; but we'll let the client set this if they like.
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class SupportedAddressData (CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -98,9 +190,10 @@
</span><span class="cx"> hidden = True
</span><span class="cx"> protected = True
</span><span class="cx">
</span><del>- allowed_children = { (carddav_namespace, "address-data-type"): (0, None) }
</del><ins>+ allowed_children = {(carddav_namespace, "address-data-type"): (0, None)}
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class MaxResourceSize (CardDAVTextElement):
</span><span class="cx"> """
</span><span class="lines">@@ -112,6 +205,7 @@
</span><span class="cx"> protected = True
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class AddressBook (CardDAVEmptyElement):
</span><span class="cx"> """
</span><span class="lines">@@ -121,6 +215,7 @@
</span><span class="cx"> name = "addressbook"
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class AddressBookQuery (CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -130,11 +225,11 @@
</span><span class="cx"> name = "addressbook-query"
</span><span class="cx">
</span><span class="cx"> allowed_children = {
</span><del>- (dav_namespace, "allprop" ): (0, None),
- (dav_namespace, "propname"): (0, None),
- (dav_namespace, "prop" ): (0, None),
- (carddav_namespace, "filter" ): (0, 1), # Actually (1, 1) unless element is empty
- (carddav_namespace, "limit" ): (0, None),
</del><ins>+ (dav_namespace, "allprop"): (0, None),
+ (dav_namespace, "propname"): (0, None),
+ (dav_namespace, "prop"): (0, None),
+ (carddav_namespace, "filter"): (0, 1), # Actually (1, 1) unless element is empty
+ (carddav_namespace, "limit"): (0, None),
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> def __init__(self, *children, **attributes):
</span><span class="lines">@@ -148,9 +243,9 @@
</span><span class="cx"> qname = child.qname()
</span><span class="cx">
</span><span class="cx"> if qname in (
</span><del>- (dav_namespace, "allprop" ),
</del><ins>+ (dav_namespace, "allprop"),
</ins><span class="cx"> (dav_namespace, "propname"),
</span><del>- (dav_namespace, "prop" ),
</del><ins>+ (dav_namespace, "prop"),
</ins><span class="cx"> ):
</span><span class="cx"> if props is not None:
</span><span class="cx"> raise ValueError("Only one of CardDAV:allprop, CardDAV:propname, CardDAV:prop allowed")
</span><span class="lines">@@ -159,7 +254,7 @@
</span><span class="cx"> elif qname == (carddav_namespace, "filter"):
</span><span class="cx"> filter = child
</span><span class="cx"> elif qname == (carddav_namespace, "limit"):
</span><del>- # type check
</del><ins>+ # type check
</ins><span class="cx"> child.childOfType(NResults)
</span><span class="cx"> limit = child
</span><span class="cx">
</span><span class="lines">@@ -170,11 +265,12 @@
</span><span class="cx"> if filter is None:
</span><span class="cx"> raise ValueError("CARDDAV:filter required")
</span><span class="cx">
</span><del>- self.props = props
</del><ins>+ self.props = props
</ins><span class="cx"> self.filter = filter
</span><span class="cx"> self.limit = limit
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class AddressDataType (CardDAVEmptyElement):
</span><span class="cx"> """
</span><span class="lines">@@ -190,8 +286,9 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><del>-class AddressData (CardDAVElement):
</del><ins>+class AddressData (CardDAVDataMixin, CardDAVElement):
</ins><span class="cx"> """
</span><span class="cx"> Defines which parts of a address component object should be returned by a
</span><span class="cx"> report.
</span><span class="lines">@@ -201,7 +298,7 @@
</span><span class="cx">
</span><span class="cx"> allowed_children = {
</span><span class="cx"> (carddav_namespace, "allprop"): (0, 1),
</span><del>- (carddav_namespace, "prop" ): (0, None),
</del><ins>+ (carddav_namespace, "prop"): (0, None),
</ins><span class="cx"> PCDATAElement : (0, None),
</span><span class="cx"> }
</span><span class="cx"> allowed_attributes = {
</span><span class="lines">@@ -209,27 +306,11 @@
</span><span class="cx"> "version" : False,
</span><span class="cx"> }
</span><span class="cx">
</span><del>- @classmethod
- def fromAddress(clazz, address):
- assert address.name() == "VCARD", "Not a vCard: %r" % (address,)
- return clazz(PCDATAElement(str(address)))
-
- @classmethod
- def fromAddressData(clazz, addressdata):
- """
- Return a AddressData element comprised of the supplied address data.
- @param addressdata: a string of valid address data.
- @return: a L{Addressata} element.
- """
- return clazz(PCDATAElement(addressdata))
-
- fromTextData = fromAddressData
-
</del><span class="cx"> def __init__(self, *children, **attributes):
</span><span class="cx"> super(AddressData, self).__init__(*children, **attributes)
</span><span class="cx">
</span><span class="cx"> properties = None
</span><del>- data = None
</del><ins>+ data = None
</ins><span class="cx">
</span><span class="cx"> for child in self.children:
</span><span class="cx"> qname = child.qname()
</span><span class="lines">@@ -256,73 +337,25 @@
</span><span class="cx"> else:
</span><span class="cx"> data += child
</span><span class="cx">
</span><del>- else: raise AssertionError("We shouldn't be here")
</del><ins>+ else:
+ raise AssertionError("We shouldn't be here")
</ins><span class="cx">
</span><del>-
</del><span class="cx"> self.properties = properties
</span><span class="cx">
</span><span class="cx"> if data is not None:
</span><span class="cx"> try:
</span><span class="cx"> if properties is not None:
</span><del>- raise ValueError("Only one of allprop, prop (%r) or PCDATA (%r) allowed"% (properties, str(data)))
</del><ins>+ raise ValueError("Only one of allprop, prop (%r) or PCDATA (%r) allowed" % (properties, str(data)))
</ins><span class="cx"> except ValueError:
</span><del>- if not data.isWhitespace(): raise
</del><ins>+ if not data.isWhitespace():
+ raise
</ins><span class="cx"> else:
</span><span class="cx"> # Since we've already combined PCDATA elements, we'd may as well
</span><span class="cx"> # optimize them originals away
</span><span class="cx"> self.children = (data,)
</span><span class="cx">
</span><del>- if "content-type" in attributes:
- self.content_type = attributes["content-type"]
- else:
- self.content_type = "text/vcard"
</del><span class="cx">
</span><del>- if "version" in attributes:
- self.version = attributes["version"]
- else:
- self.version = "3.0"
</del><span class="cx">
</span><del>- def verifyTypeVersion(self, types_and_versions):
- """
- Make sure any content-type and version matches at least one of the supplied set.
-
- @param types_and_versions: a list of (content-type, version) tuples to test against.
- @return: True if there is at least one match, False otherwise.
- """
- for item in types_and_versions:
- if (item[0] == self.content_type) and (item[1] == self.version):
- return True
-
- return False
-
- def address(self):
- """
- Returns an address component derived from this element.
- """
- data = self.addressData()
- if data:
- return Component.fromString(data)
- else:
- return None
-
- generateComponent = address
-
- def addressData(self):
- """
- Returns an address component derived from this element.
- """
- for data in self.children:
- if not isinstance(data, PCDATAElement):
- return None
- else:
- # We guaranteed in __init__() that there is only one child...
- break
-
- return str(data)
-
- textData = addressData
-
-
</del><span class="cx"> @registerElement
</span><span class="cx"> class AllProperties (CardDAVEmptyElement):
</span><span class="cx"> """
</span><span class="lines">@@ -332,6 +365,7 @@
</span><span class="cx"> name = "allprop"
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class Property (CardDAVEmptyElement):
</span><span class="cx"> """
</span><span class="lines">@@ -362,6 +396,7 @@
</span><span class="cx"> self.novalue = False
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class Filter (CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -370,10 +405,11 @@
</span><span class="cx"> """
</span><span class="cx"> name = "filter"
</span><span class="cx">
</span><del>- allowed_children = { (carddav_namespace, "prop-filter"): (0, None) }
- allowed_attributes = { "test": False }
-
</del><ins>+ allowed_children = {(carddav_namespace, "prop-filter"): (0, None)}
+ allowed_attributes = {"test": False}
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class PropertyFilter (CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -383,9 +419,9 @@
</span><span class="cx"> name = "prop-filter"
</span><span class="cx">
</span><span class="cx"> allowed_children = {
</span><del>- (carddav_namespace, "is-not-defined" ): (0, 1),
- (carddav_namespace, "text-match" ): (0, None),
- (carddav_namespace, "param-filter" ): (0, None),
</del><ins>+ (carddav_namespace, "is-not-defined"): (0, 1),
+ (carddav_namespace, "text-match"): (0, None),
+ (carddav_namespace, "param-filter"): (0, None),
</ins><span class="cx"> }
</span><span class="cx"> allowed_attributes = {
</span><span class="cx"> "name": True,
</span><span class="lines">@@ -393,6 +429,7 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class ParameterFilter (CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -402,12 +439,13 @@
</span><span class="cx"> name = "param-filter"
</span><span class="cx">
</span><span class="cx"> allowed_children = {
</span><del>- (carddav_namespace, "is-not-defined" ): (0, 1),
- (carddav_namespace, "text-match" ): (0, 1),
</del><ins>+ (carddav_namespace, "is-not-defined"): (0, 1),
+ (carddav_namespace, "text-match"): (0, 1),
</ins><span class="cx"> }
</span><del>- allowed_attributes = { "name": True }
</del><ins>+ allowed_attributes = {"name": True}
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class Limit (WebDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -416,10 +454,11 @@
</span><span class="cx"> namespace = carddav_namespace
</span><span class="cx"> name = "limit"
</span><span class="cx"> allowed_children = {
</span><del>- (carddav_namespace, "nresults" ) : (1, 1),
</del><ins>+ (carddav_namespace, "nresults") : (1, 1),
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class NResults (WebDAVTextElement):
</span><span class="cx"> """
</span><span class="lines">@@ -439,6 +478,7 @@
</span><span class="cx"> name = "is-not-defined"
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class TextMatch (CardDAVTextElement):
</span><span class="cx"> """
</span><span class="lines">@@ -464,6 +504,7 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class AddressBookMultiGet (CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -475,10 +516,10 @@
</span><span class="cx"> # To allow for an empty element in a supported-report-set property we need
</span><span class="cx"> # to relax the child restrictions
</span><span class="cx"> allowed_children = {
</span><del>- (dav_namespace, "allprop" ): (0, 1),
</del><ins>+ (dav_namespace, "allprop"): (0, 1),
</ins><span class="cx"> (dav_namespace, "propname"): (0, 1),
</span><del>- (dav_namespace, "prop" ): (0, 1),
- (dav_namespace, "href" ): (0, None), # Actually ought to be (1, None)
</del><ins>+ (dav_namespace, "prop"): (0, 1),
+ (dav_namespace, "href"): (0, None), # Actually ought to be (1, None)
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> def __init__(self, *children, **attributes):
</span><span class="lines">@@ -491,9 +532,9 @@
</span><span class="cx"> qname = child.qname()
</span><span class="cx">
</span><span class="cx"> if qname in (
</span><del>- (dav_namespace, "allprop" ),
</del><ins>+ (dav_namespace, "allprop"),
</ins><span class="cx"> (dav_namespace, "propname"),
</span><del>- (dav_namespace, "prop" ),
</del><ins>+ (dav_namespace, "prop"),
</ins><span class="cx"> ):
</span><span class="cx"> if property is not None:
</span><span class="cx"> raise ValueError("Only one of DAV:allprop, DAV:propname, DAV:prop allowed")
</span><span class="lines">@@ -502,10 +543,11 @@
</span><span class="cx"> elif qname == (dav_namespace, "href"):
</span><span class="cx"> resources.append(child)
</span><span class="cx">
</span><del>- self.property = property
</del><ins>+ self.property = property
</ins><span class="cx"> self.resources = resources
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class NoUIDConflict(CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -514,9 +556,10 @@
</span><span class="cx"> """
</span><span class="cx"> name = "no-uid-conflict"
</span><span class="cx">
</span><del>- allowed_children = { (dav_namespace, "href"): (1, 1) }
-
</del><ins>+ allowed_children = {(dav_namespace, "href"): (1, 1)}
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class SupportedFilter(CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -527,11 +570,12 @@
</span><span class="cx"> name = "supported-filter"
</span><span class="cx">
</span><span class="cx"> allowed_children = {
</span><del>- (carddav_namespace, "prop-filter" ): (0, None),
</del><ins>+ (carddav_namespace, "prop-filter"): (0, None),
</ins><span class="cx"> (carddav_namespace, "param-filter"): (0, None)
</span><span class="cx"> }
</span><del>-
</del><span class="cx">
</span><ins>+
+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class DirectoryGateway(CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -541,17 +585,19 @@
</span><span class="cx"> hidden = True
</span><span class="cx"> protected = True
</span><span class="cx">
</span><del>- allowed_children = { (dav_namespace, "href"): (0, None) }
-
</del><ins>+ allowed_children = {(dav_namespace, "href"): (0, None)}
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class Directory(CardDAVEmptyElement):
</span><span class="cx"> """
</span><span class="cx"> CardDAV property on a principal to indicate where the directory resource is.
</span><span class="cx"> """
</span><span class="cx"> name = "directory"
</span><del>-
</del><span class="cx">
</span><ins>+
+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class DefaultAddressBookURL (CardDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -559,14 +605,17 @@
</span><span class="cx"> """
</span><span class="cx"> name = "default-addressbook-URL"
</span><span class="cx">
</span><del>- allowed_children = { (dav_namespace, "href"): (0, 1) }
</del><ins>+ allowed_children = {(dav_namespace, "href"): (0, 1)}
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> ##
</span><span class="cx"> # Extensions to ResourceType
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-def _isAddressBook(self): return bool(self.childrenOfType(AddressBook))
</del><ins>+def _isAddressBook(self):
+ return bool(self.childrenOfType(AddressBook))
</ins><span class="cx"> ResourceType.isAddressBook = _isAddressBook
</span><span class="cx">
</span><span class="cx"> ResourceType.addressbook = ResourceType(Collection(), AddressBook())
</span><del>-ResourceType.directory = ResourceType(Collection(), AddressBook(), Directory())
</del><ins>+ResourceType.directory = ResourceType(Collection(), AddressBook(), Directory())
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/config.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/config.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/config.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -97,6 +97,9 @@
</span><span class="cx"> self._defaults = ConfigDict()
</span><span class="cx"> else:
</span><span class="cx"> self._defaults = ConfigDict(copy.deepcopy(defaults))
</span><ins>+ self.importedFiles = []
+ self.includedFiles = []
+ self.missingFiles = []
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def getDefaults(self):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavcustomxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/customxml.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/customxml.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/customxml.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -34,7 +34,7 @@
</span><span class="cx"> from twistedcaldav.caldavxml import caldav_namespace
</span><span class="cx"> from twistedcaldav.ical import Component as iComponent
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> calendarserver_namespace = "http://calendarserver.org/ns/"
</span><span class="lines">@@ -640,7 +640,7 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, *children):
</span><span class="cx"> super(DTStamp, self).__init__(children)
</span><del>- self.children = (PCDATAElement(PyCalendarDateTime.getNowUTC().getText()),)
</del><ins>+ self.children = (PCDATAElement(DateTime.getNowUTC().getText()),)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdatabasepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/database.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/database.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/database.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -44,14 +44,15 @@
</span><span class="cx"> """
</span><span class="cx"> A ThreadPool that closes connections for each worker thread
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def _worker(self):
</span><span class="cx"> log.debug("Starting ADBAPI thread: %s" % (thread.get_ident(),))
</span><span class="cx"> ThreadPool._worker(self)
</span><span class="cx"> self._closeConnection()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _closeConnection(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> tid = thread.get_ident()
</span><span class="cx"> log.debug("Closing ADBAPI thread: %s" % (tid,))
</span><span class="cx">
</span><span class="lines">@@ -59,6 +60,8 @@
</span><span class="cx"> self.pool._close(conn)
</span><span class="cx"> del self.pool.connections[tid]
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AbstractADBAPIDatabase(object):
</span><span class="cx"> """
</span><span class="cx"> A generic SQL database.
</span><span class="lines">@@ -66,7 +69,7 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, dbID, dbapiName, dbapiArgs, persistent, **kwargs):
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> @param persistent: C{True} if the data in the DB must be perserved during upgrades,
</span><span class="cx"> C{False} if the DB data can be re-created from an external source.
</span><span class="cx"> @type persistent: bool
</span><span class="lines">@@ -77,12 +80,14 @@
</span><span class="cx"> self.dbapikwargs = kwargs
</span><span class="cx">
</span><span class="cx"> self.persistent = persistent
</span><del>-
</del><ins>+
</ins><span class="cx"> self.initialized = False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __repr__(self):
</span><span class="cx"> return "<%s %r>" % (self.__class__.__name__, self.pool)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def open(self):
</span><span class="cx"> """
</span><span class="lines">@@ -92,7 +97,7 @@
</span><span class="cx"> if not self.initialized:
</span><span class="cx">
</span><span class="cx"> self.pool = ConnectionPool(self.dbapiName, *self.dbapiArgs, **self.dbapikwargs)
</span><del>-
</del><ins>+
</ins><span class="cx"> # sqlite3 is not thread safe which means we have to close the sqlite3 connections in the same thread that
</span><span class="cx"> # opened them. We need a special thread pool class that has a thread worker function that does a close
</span><span class="cx"> # when a thread is closed.
</span><span class="lines">@@ -126,7 +131,7 @@
</span><span class="cx"> elif version != self._db_version():
</span><span class="cx"> log.error("Database %s has different schema (v.%s vs. v.%s)"
</span><span class="cx"> % (self.dbID, version, self._db_version()))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Upgrade the DB
</span><span class="cx"> yield self._db_upgrade(version)
</span><span class="cx">
</span><span class="lines">@@ -139,8 +144,9 @@
</span><span class="cx"> self.pool = None
</span><span class="cx"> raise
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def close(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> if self.initialized:
</span><span class="cx"> try:
</span><span class="cx"> self.pool.close()
</span><span class="lines">@@ -149,9 +155,10 @@
</span><span class="cx"> self.pool = None
</span><span class="cx"> self.initialized = False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def clean(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Re-try at least once
</span><span class="cx"> for _ignore in (0, 1):
</span><span class="cx"> if not self.initialized:
</span><span class="lines">@@ -165,14 +172,15 @@
</span><span class="cx"> else:
</span><span class="cx"> break
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def execute(self, sql, *query_params):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Re-try at least once
</span><span class="cx"> for _ignore in (0, 1):
</span><span class="cx"> if not self.initialized:
</span><span class="cx"> yield self.open()
</span><del>-
</del><ins>+
</ins><span class="cx"> try:
</span><span class="cx"> yield self._db_execute(sql, *query_params)
</span><span class="cx"> except Exception, e:
</span><span class="lines">@@ -181,14 +189,15 @@
</span><span class="cx"> else:
</span><span class="cx"> break
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def executescript(self, script):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Re-try at least once
</span><span class="cx"> for _ignore in (0, 1):
</span><span class="cx"> if not self.initialized:
</span><span class="cx"> yield self.open()
</span><del>-
</del><ins>+
</ins><span class="cx"> try:
</span><span class="cx"> yield self._db_execute_script(script)
</span><span class="cx"> except Exception, e:
</span><span class="lines">@@ -197,14 +206,15 @@
</span><span class="cx"> else:
</span><span class="cx"> break
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def query(self, sql, *query_params):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Re-try at least once
</span><span class="cx"> for _ignore in (0, 1):
</span><span class="cx"> if not self.initialized:
</span><span class="cx"> yield self.open()
</span><del>-
</del><ins>+
</ins><span class="cx"> try:
</span><span class="cx"> result = (yield self._db_all_values_for_sql(sql, *query_params))
</span><span class="cx"> except Exception, e:
</span><span class="lines">@@ -215,14 +225,15 @@
</span><span class="cx">
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def queryList(self, sql, *query_params):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Re-try at least once
</span><span class="cx"> for _ignore in (0, 1):
</span><span class="cx"> if not self.initialized:
</span><span class="cx"> yield self.open()
</span><del>-
</del><ins>+
</ins><span class="cx"> try:
</span><span class="cx"> result = (yield self._db_values_for_sql(sql, *query_params))
</span><span class="cx"> except Exception, e:
</span><span class="lines">@@ -233,14 +244,15 @@
</span><span class="cx">
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def queryOne(self, sql, *query_params):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Re-try at least once
</span><span class="cx"> for _ignore in (0, 1):
</span><span class="cx"> if not self.initialized:
</span><span class="cx"> yield self.open()
</span><del>-
</del><ins>+
</ins><span class="cx"> try:
</span><span class="cx"> result = (yield self._db_value_for_sql(sql, *query_params))
</span><span class="cx"> except Exception, e:
</span><span class="lines">@@ -251,21 +263,25 @@
</span><span class="cx">
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_version(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the schema version assigned to this DB.
</span><span class="cx"> """
</span><span class="cx"> raise NotImplementedError
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _db_type(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the collection type assigned to this DB.
</span><span class="cx"> """
</span><span class="cx"> raise NotImplementedError
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _test_schema_table(self):
</span><span class="cx"> return self._test_table("CALDAV")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _db_init(self):
</span><span class="cx"> """
</span><span class="lines">@@ -275,12 +291,12 @@
</span><span class="cx">
</span><span class="cx"> # TODO we need an exclusive lock of some kind here to prevent a race condition
</span><span class="cx"> # in which multiple processes try to create the tables.
</span><del>-
</del><span class="cx">
</span><span class="cx"> yield self._db_init_schema_table()
</span><span class="cx"> yield self._db_init_data_tables()
</span><span class="cx"> yield self._db_recreate()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _db_init_schema_table(self):
</span><span class="cx"> """
</span><span class="lines">@@ -310,12 +326,14 @@
</span><span class="cx"> """, (self._db_type(),)
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_init_data_tables(self):
</span><span class="cx"> """
</span><span class="cx"> Initialise the underlying database tables.
</span><span class="cx"> """
</span><span class="cx"> raise NotImplementedError
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_empty_data_tables(self):
</span><span class="cx"> """
</span><span class="cx"> Delete the database tables.
</span><span class="lines">@@ -323,7 +341,8 @@
</span><span class="cx">
</span><span class="cx"> # Implementations can override this to re-create data
</span><span class="cx"> pass
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _db_recreate(self):
</span><span class="cx"> """
</span><span class="cx"> Recreate the database tables.
</span><span class="lines">@@ -332,12 +351,13 @@
</span><span class="cx"> # Implementations can override this to re-create data
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _db_upgrade(self, old_version):
</span><span class="cx"> """
</span><span class="cx"> Upgrade the database tables.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> if self.persistent:
</span><span class="cx"> yield self._db_upgrade_data_tables(old_version)
</span><span class="cx"> yield self._db_upgrade_schema()
</span><span class="lines">@@ -346,7 +366,8 @@
</span><span class="cx"> # DB upgrades they SHOULD override this method and handle those for better performance.
</span><span class="cx"> yield self._db_remove()
</span><span class="cx"> yield self._db_init()
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _db_upgrade_data_tables(self, old_version):
</span><span class="cx"> """
</span><span class="cx"> Upgrade the data from an older version of the DB.
</span><span class="lines">@@ -372,12 +393,14 @@
</span><span class="cx"> yield self._db_remove_data_tables()
</span><span class="cx"> yield self._db_remove_schema()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_remove_data_tables(self):
</span><span class="cx"> """
</span><span class="cx"> Remove all the data from an older version of the DB.
</span><span class="cx"> """
</span><span class="cx"> raise NotImplementedError("Each database must remove its own tables.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _db_remove_schema(self):
</span><span class="cx"> """
</span><span class="lines">@@ -385,6 +408,7 @@
</span><span class="cx"> """
</span><span class="cx"> yield self._db_execute("drop table if exists CALDAV")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _db_all_values_for_sql(self, sql, *query_params):
</span><span class="cx"> """
</span><span class="lines">@@ -395,11 +419,12 @@
</span><span class="cx"> resulting from executing C{sql} with C{query_params}.
</span><span class="cx"> @raise AssertionError: if the query yields multiple columns.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> sql = self._prepare_statement(sql)
</span><span class="cx"> results = (yield self.pool.runQuery(sql, *query_params))
</span><span class="cx"> returnValue(tuple(results))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _db_values_for_sql(self, sql, *query_params):
</span><span class="cx"> """
</span><span class="lines">@@ -411,11 +436,12 @@
</span><span class="cx"> resulting from executing C{sql} with C{query_params}.
</span><span class="cx"> @raise AssertionError: if the query yields multiple columns.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> sql = self._prepare_statement(sql)
</span><span class="cx"> results = (yield self.pool.runQuery(sql, *query_params))
</span><span class="cx"> returnValue(tuple([row[0] for row in results]))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _db_value_for_sql(self, sql, *query_params):
</span><span class="cx"> """
</span><span class="lines">@@ -433,6 +459,7 @@
</span><span class="cx"> value = row
</span><span class="cx"> returnValue(value)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_execute(self, sql, *query_params):
</span><span class="cx"> """
</span><span class="cx"> Execute an SQL operation that returns None.
</span><span class="lines">@@ -442,7 +469,7 @@
</span><span class="cx"> @return: an iterable of tuples for each row resulting from executing
</span><span class="cx"> C{sql} with C{query_params}.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> sql = self._prepare_statement(sql)
</span><span class="cx"> return self.pool.runOperation(sql, *query_params)
</span><span class="cx">
</span><span class="lines">@@ -450,37 +477,43 @@
</span><span class="cx"> Since different databases support different types of columns and modifiers on those we need to
</span><span class="cx"> have an "abstract" way of specifying columns in our code and then map the abstract specifiers to
</span><span class="cx"> the underlying DB's allowed types.
</span><del>-
</del><ins>+
</ins><span class="cx"> Types we can use are:
</span><del>-
</del><ins>+
</ins><span class="cx"> integer
</span><span class="cx"> text
</span><span class="cx"> text(n)
</span><span class="cx"> date
</span><span class="cx"> serial
</span><del>-
</del><ins>+
</ins><span class="cx"> The " unique" modifier can be appended to any of those.
</span><span class="cx"> """
</span><span class="cx"> def _map_column_types(self, type):
</span><span class="cx"> raise NotImplementedError
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _create_table(self, name, columns, ifnotexists=False):
</span><span class="cx"> raise NotImplementedError
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _test_table(self, name):
</span><span class="cx"> raise NotImplementedError
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _create_index(self, name, ontable, columns, ifnotexists=False):
</span><span class="cx"> raise NotImplementedError
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _prepare_statement(self, sql):
</span><span class="cx"> raise NotImplementedError
</span><del>-
</del><ins>+
+
+
</ins><span class="cx"> class ADBAPISqliteMixin(object):
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> def _map_column_types(self, coltype):
</span><del>-
</del><ins>+
</ins><span class="cx"> result = ""
</span><span class="cx"> splits = coltype.split()
</span><span class="cx"> if splits[0] == "integer":
</span><span class="lines">@@ -493,15 +526,16 @@
</span><span class="cx"> result = "date"
</span><span class="cx"> elif splits[0] == "serial":
</span><span class="cx"> result = "integer primary key autoincrement"
</span><del>-
</del><ins>+
</ins><span class="cx"> if len(splits) > 1 and splits[1] == "unique":
</span><span class="cx"> result += " unique"
</span><del>-
</del><ins>+
</ins><span class="cx"> return result
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _create_table(self, name, columns, ifnotexists=False):
</span><del>-
</del><ins>+
</ins><span class="cx"> colDefs = ["%s %s" % (colname, self._map_column_types(coltype)) for colname, coltype in columns]
</span><span class="cx"> statement = "create table %s%s (%s)" % (
</span><span class="cx"> "if not exists " if ifnotexists else "",
</span><span class="lines">@@ -510,6 +544,7 @@
</span><span class="cx"> )
</span><span class="cx"> yield self._db_execute(statement)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _test_table(self, name):
</span><span class="cx"> result = (yield self._db_value_for_sql("""
</span><span class="lines">@@ -518,9 +553,10 @@
</span><span class="cx"> """ % (name,)))
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _create_index(self, name, ontable, columns, ifnotexists=False):
</span><del>-
</del><ins>+
</ins><span class="cx"> statement = "create index %s%s on %s (%s)" % (
</span><span class="cx"> "if not exists " if ifnotexists else "",
</span><span class="cx"> name,
</span><span class="lines">@@ -529,6 +565,7 @@
</span><span class="cx"> )
</span><span class="cx"> yield self._db_execute(statement)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _prepare_statement(self, sql):
</span><span class="cx"> # We are going to use the sqlite syntax of :1, :2 etc for our
</span><span class="cx"> # internal statements so we do not need to remap those
</span><span class="lines">@@ -537,10 +574,10 @@
</span><span class="cx"> if pgdb:
</span><span class="cx">
</span><span class="cx"> class ADBAPIPostgreSQLMixin(object):
</span><del>-
</del><ins>+
</ins><span class="cx"> @classmethod
</span><span class="cx"> def _map_column_types(self, coltype):
</span><del>-
</del><ins>+
</ins><span class="cx"> result = ""
</span><span class="cx"> splits = coltype.split()
</span><span class="cx"> if splits[0] == "integer":
</span><span class="lines">@@ -553,32 +590,34 @@
</span><span class="cx"> result = "date"
</span><span class="cx"> elif splits[0] == "serial":
</span><span class="cx"> result = "serial"
</span><del>-
</del><ins>+
</ins><span class="cx"> if len(splits) > 1 and splits[1] == "unique":
</span><span class="cx"> result += " unique"
</span><del>-
</del><ins>+
</ins><span class="cx"> return result
</span><del>-
</del><ins>+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _create_table(self, name, columns, ifnotexists=False):
</span><del>-
</del><ins>+
</ins><span class="cx"> colDefs = ["%s %s" % (colname, self._map_column_types(coltype)) for colname, coltype in columns]
</span><span class="cx"> statement = "create table %s (%s)" % (
</span><span class="cx"> name,
</span><span class="cx"> ", ".join(colDefs),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> try:
</span><span class="cx"> yield self._db_execute(statement)
</span><span class="cx"> except pgdb.DatabaseError:
</span><del>-
</del><ins>+
</ins><span class="cx"> if not ifnotexists:
</span><span class="cx"> raise
</span><del>-
</del><ins>+
</ins><span class="cx"> result = (yield self._test_table(name))
</span><span class="cx"> if not result:
</span><del>- raise
-
</del><ins>+ raise
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _test_table(self, name):
</span><span class="cx"> result = (yield self._db_value_for_sql("""
</span><span class="lines">@@ -586,27 +625,29 @@
</span><span class="cx"> where tablename = '%s'
</span><span class="cx"> """ % (name.lower(),)))
</span><span class="cx"> returnValue(result)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _create_index(self, name, ontable, columns, ifnotexists=False):
</span><del>-
</del><ins>+
</ins><span class="cx"> statement = "create index %s on %s (%s)" % (
</span><span class="cx"> name,
</span><span class="cx"> ontable,
</span><span class="cx"> ", ".join(columns),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> try:
</span><span class="cx"> yield self._db_execute(statement)
</span><span class="cx"> except pgdb.DatabaseError:
</span><del>-
</del><ins>+
</ins><span class="cx"> if not ifnotexists:
</span><span class="cx"> raise
</span><del>-
</del><ins>+
</ins><span class="cx"> result = (yield self._test_table(name))
</span><span class="cx"> if not result:
</span><del>- raise
-
</del><ins>+ raise
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _db_init_schema_table(self):
</span><span class="cx"> """
</span><span class="lines">@@ -614,7 +655,7 @@
</span><span class="cx"> @param db_filename: the file name of the index database.
</span><span class="cx"> @param q: a database cursor to use.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> #
</span><span class="cx"> # CALDAV table keeps track of our schema version and type
</span><span class="cx"> #
</span><span class="lines">@@ -623,7 +664,7 @@
</span><span class="cx"> ("KEY", "text unique"),
</span><span class="cx"> ("VALUE", "text unique"),
</span><span class="cx"> ), True)
</span><del>-
</del><ins>+
</ins><span class="cx"> yield self._db_execute(
</span><span class="cx"> """
</span><span class="cx"> insert into CALDAV (KEY, VALUE)
</span><span class="lines">@@ -638,7 +679,8 @@
</span><span class="cx"> )
</span><span class="cx"> except pgdb.DatabaseError:
</span><span class="cx"> pass
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _prepare_statement(self, sql):
</span><span class="cx"> # Convert :1, :2 etc format into %s
</span><span class="cx"> ctr = 1
</span><span class="lines">@@ -649,6 +691,6 @@
</span><span class="cx">
</span><span class="cx"> else:
</span><span class="cx"> class ADBAPIPostgreSQLMixin(object):
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self):
</span><span class="cx"> raise ConfigurationError("PostgreSQL module not available.")
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdatafilterscalendardatapy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/datafilters/calendardata.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/datafilters/calendardata.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/datafilters/calendardata.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -19,7 +19,7 @@
</span><span class="cx"> from twistedcaldav.datafilters.filter import CalendarFilter
</span><span class="cx"> from twistedcaldav.dateops import clipPeriod
</span><span class="cx"> from twistedcaldav.ical import Component
</span><del>-from pycalendar.period import PyCalendarPeriod
</del><ins>+from pycalendar.period import Period
</ins><span class="cx">
</span><span class="cx"> __all__ = [
</span><span class="cx"> "CalendarDataFilter",
</span><span class="lines">@@ -161,7 +161,7 @@
</span><span class="cx"> for property in component.properties("FREEBUSY"):
</span><span class="cx"> newvalue = []
</span><span class="cx"> for period in property.value():
</span><del>- clipped = clipPeriod(period.getValue(), PyCalendarPeriod(self.calendardata.freebusy_set.start, self.calendardata.freebusy_set.end))
</del><ins>+ clipped = clipPeriod(period.getValue(), Period(self.calendardata.freebusy_set.start, self.calendardata.freebusy_set.end))
</ins><span class="cx"> if clipped:
</span><span class="cx"> newvalue.append(clipped)
</span><span class="cx"> if len(newvalue):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdateopspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dateops.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dateops.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dateops.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -28,9 +28,9 @@
</span><span class="cx"> "clipPeriod"
</span><span class="cx"> ]
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
-from pycalendar.period import PyCalendarPeriod
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
+from pycalendar.period import Period
</ins><span class="cx">
</span><span class="cx"> import datetime
</span><span class="cx"> import dateutil.tz
</span><span class="lines">@@ -39,14 +39,14 @@
</span><span class="cx">
</span><span class="cx"> def normalizeForIndex(dt):
</span><span class="cx"> """
</span><del>- Normalize a L{PyCalendarDateTime} object for use in the Index.
</del><ins>+ Normalize a L{DateTime} object for use in the Index.
</ins><span class="cx"> Convert to date-time in UTC.
</span><del>- @param dt: a L{PyCalendarDateTime} object to normalize
- @return: the normalized PyCalendarDateTime
</del><ins>+ @param dt: a L{DateTime} object to normalize
+ @return: the normalized DateTime
</ins><span class="cx"> """
</span><del>- if not isinstance(dt, PyCalendarDateTime):
- raise TypeError("%r is not a PyCalendarDateTime instance" % (dt,))
-
</del><ins>+ if not isinstance(dt, DateTime):
+ raise TypeError("%r is not a DateTime instance" % (dt,))
+
</ins><span class="cx"> dt = dt.duplicate()
</span><span class="cx"> if dt.isDateOnly():
</span><span class="cx"> dt.setDateOnly(False)
</span><span class="lines">@@ -59,13 +59,15 @@
</span><span class="cx"> dt.adjustToUTC()
</span><span class="cx"> return dt
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def normalizeToUTC(dt):
</span><span class="cx"> """
</span><del>- Normalize a L{PyCalendarDateTime} object to UTC.
</del><ins>+ Normalize a L{DateTime} object to UTC.
</ins><span class="cx"> """
</span><del>- if not isinstance(dt, PyCalendarDateTime):
- raise TypeError("%r is not a PyCalendarDateTime instance" % (dt,))
-
</del><ins>+ if not isinstance(dt, DateTime):
+ raise TypeError("%r is not a DateTime instance" % (dt,))
+
</ins><span class="cx"> dt = dt.duplicate()
</span><span class="cx"> if dt.isDateOnly():
</span><span class="cx"> dt.setDateOnly(False)
</span><span class="lines">@@ -79,16 +81,18 @@
</span><span class="cx"> dt.adjustToUTC()
</span><span class="cx"> return dt
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def normalizeForExpand(dt):
</span><span class="cx"> """
</span><del>- Normalize a L{PyCalendarDateTime} object for use with the CalDAV expand option.
</del><ins>+ Normalize a L{DateTime} object for use with the CalDAV expand option.
</ins><span class="cx"> Convert to date-time in UTC, leave date only and floating alone.
</span><del>- @param dt: a L{PyCalendarDateTime} object to normalize
- @return: the normalized PyCalendarDateTime
</del><ins>+ @param dt: a L{DateTime} object to normalize
+ @return: the normalized DateTime
</ins><span class="cx"> """
</span><del>- if not isinstance(dt, PyCalendarDateTime):
- raise TypeError("%r is not a PyCalendarDateTime instance" % (dt,))
-
</del><ins>+ if not isinstance(dt, DateTime):
+ raise TypeError("%r is not a DateTime instance" % (dt,))
+
</ins><span class="cx"> dt = dt.duplicate()
</span><span class="cx"> if dt.isDateOnly() or dt.floating():
</span><span class="cx"> return dt
</span><span class="lines">@@ -96,41 +100,49 @@
</span><span class="cx"> dt.adjustToUTC()
</span><span class="cx"> return dt
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def floatoffset(dt, pytz):
</span><span class="cx"> """
</span><span class="cx"> Apply the timezone offset to the supplied time, then force tz to utc. This gives the local
</span><span class="cx"> date-time as if the local tz were UTC. It can be used in floating time comparisons with UTC date-times.
</span><del>-
- @param dt: a L{PyCalendarDateTime} object to normalize
- @param pytz: a L{PyCalendarTimezone} object to apply offset from
- @return: the normalized PyCalendarDateTime
</del><ins>+
+ @param dt: a L{DateTime} object to normalize
+ @param pytz: a L{Timezone} object to apply offset from
+ @return: the normalized DateTime
</ins><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> if pytz is None:
</span><del>- pytz = PyCalendarTimezone(utc=True)
-
</del><ins>+ pytz = Timezone(utc=True)
+
</ins><span class="cx"> dt = dt.duplicate()
</span><span class="cx"> dt.adjustTimezone(pytz)
</span><span class="cx"> dt.setTimezoneUTC(True)
</span><span class="cx"> return dt
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def adjustFloatingToTimezone(dtadjust, dtcopyfrom, pytz=None):
</span><del>-
</del><ins>+
</ins><span class="cx"> dtadjust = dtadjust.duplicate()
</span><span class="cx"> dtadjust.setTimezone(pytz if pytz else dtcopyfrom.getTimezone())
</span><span class="cx"> return dtadjust
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def compareDateTime(dt1, dt2, defaulttz=None):
</span><del>-
</del><ins>+
</ins><span class="cx"> if dt1.floating() and not dt2.floating():
</span><span class="cx"> dt1 = adjustFloatingToTimezone(dt1, dt2, defaulttz)
</span><span class="cx"> elif dt2.floating() and not dt1.floating():
</span><span class="cx"> dt2 = adjustFloatingToTimezone(dt2, dt1, defaulttz)
</span><del>-
</del><ins>+
</ins><span class="cx"> return dt1.compareDateTime(dt2)
</span><span class="cx">
</span><del>-def differenceDateTime(start, end, defaulttz = None):
</del><span class="cx">
</span><ins>+
+def differenceDateTime(start, end, defaulttz=None):
+
</ins><span class="cx"> if start.floating() and not end.floating():
</span><span class="cx"> start = adjustFloatingToTimezone(start, end, defaulttz)
</span><span class="cx"> elif end.floating() and not start.floating():
</span><span class="lines">@@ -138,13 +150,19 @@
</span><span class="cx">
</span><span class="cx"> return end - start
</span><span class="cx">
</span><del>-def timeRangesOverlap(start1, end1, start2, end2, defaulttz = None):
</del><ins>+
+
+def timeRangesOverlap(start1, end1, start2, end2, defaulttz=None):
</ins><span class="cx"> # Can't compare date-time and date only, so normalize
</span><span class="cx"> # to date only if they are mixed.
</span><del>- if (start1 is not None) and not start1.isDateOnly() and (start2 is not None) and start2.isDateOnly(): start1.setDateOnly(True)
- if (start2 is not None) and not start2.isDateOnly() and (start1 is not None) and start1.isDateOnly(): start2.setDateOnly(True)
- if (end1 is not None) and not end1.isDateOnly() and (end2 is not None) and end2.isDateOnly(): end1.setDateOnly(True)
- if (end2 is not None) and not end2.isDateOnly() and (end1 is not None) and end1.isDateOnly(): end2.setDateOnly(True)
</del><ins>+ if (start1 is not None) and not start1.isDateOnly() and (start2 is not None) and start2.isDateOnly():
+ start1.setDateOnly(True)
+ if (start2 is not None) and not start2.isDateOnly() and (start1 is not None) and start1.isDateOnly():
+ start2.setDateOnly(True)
+ if (end1 is not None) and not end1.isDateOnly() and (end2 is not None) and end2.isDateOnly():
+ end1.setDateOnly(True)
+ if (end2 is not None) and not end2.isDateOnly() and (end1 is not None) and end1.isDateOnly():
+ end2.setDateOnly(True)
</ins><span class="cx">
</span><span class="cx"> # Note that start times are inclusive and end times are not.
</span><span class="cx"> if start1 is not None and start2 is not None:
</span><span class="lines">@@ -163,40 +181,41 @@
</span><span class="cx"> else:
</span><span class="cx"> return False
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def normalizePeriodList(periods):
</span><span class="cx"> """
</span><span class="cx"> Normalize the list of periods by merging overlapping or consecutive ranges
</span><span class="cx"> and sorting the list by each periods start.
</span><del>- @param list: a list of tuples of L{PyCalendarPeriod}. The list is changed in place.
</del><ins>+ @param list: a list of tuples of L{Period}. The list is changed in place.
</ins><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> # First sort the list
</span><span class="cx"> def sortPeriods(p1, p2):
</span><span class="cx"> """
</span><span class="cx"> Compare two periods. Sort by their start and then end times.
</span><del>- A period is a L{PyCalendarPeriod}.
</del><ins>+ A period is a L{Period}.
</ins><span class="cx"> @param p1: first period
</span><span class="cx"> @param p2: second period
</span><span class="cx"> @return: 1 if p1>p2, 0 if p1==p2, -1 if p1<p2
</span><span class="cx"> """
</span><span class="cx">
</span><del>- assert isinstance(p1, PyCalendarPeriod), "Period is not a PyCalendarPeriod: %r" % (p1,)
- assert isinstance(p2, PyCalendarPeriod), "Period is not a PyCalendarPeriod: %r" % (p2,)
-
-
</del><ins>+ assert isinstance(p1, Period), "Period is not a Period: %r" % (p1,)
+ assert isinstance(p2, Period), "Period is not a Period: %r" % (p2,)
+
</ins><span class="cx"> if p1.getStart() == p2.getStart():
</span><span class="cx"> cmp1 = p1.getEnd()
</span><span class="cx"> cmp2 = p2.getEnd()
</span><span class="cx"> else:
</span><span class="cx"> cmp1 = p1.getStart()
</span><span class="cx"> cmp2 = p2.getStart()
</span><del>-
</del><ins>+
</ins><span class="cx"> return compareDateTime(cmp1, cmp2)
</span><span class="cx">
</span><span class="cx"> for period in periods:
</span><span class="cx"> period.adjustToUTC()
</span><span class="cx"> periods.sort(cmp=sortPeriods)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Now merge overlaps and consecutive periods
</span><span class="cx"> index = None
</span><span class="cx"> p = None
</span><span class="lines">@@ -210,15 +229,17 @@
</span><span class="cx"> ie = periods[i].getEnd()
</span><span class="cx"> if (pe >= periods[i].getStart()):
</span><span class="cx"> if ie > pe:
</span><del>- periods[index] = PyCalendarPeriod(periods[index].getStart(), ie)
</del><ins>+ periods[index] = Period(periods[index].getStart(), ie)
</ins><span class="cx"> pe = ie
</span><span class="cx"> periods[i] = None
</span><span class="cx"> else:
</span><span class="cx"> index = i
</span><span class="cx"> p = periods[i]
</span><del>- pe =p.getEnd()
</del><ins>+ pe = p.getEnd()
</ins><span class="cx"> periods[:] = [x for x in periods if x]
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def clipPeriod(period, clipPeriod):
</span><span class="cx"> """
</span><span class="cx"> Clip the start/end period so that it lies entirely within the clip period.
</span><span class="lines">@@ -234,20 +255,22 @@
</span><span class="cx">
</span><span class="cx"> if start < clipStart:
</span><span class="cx"> start = clipStart
</span><del>-
</del><ins>+
</ins><span class="cx"> if end > clipEnd:
</span><span class="cx"> end = clipEnd
</span><del>-
</del><ins>+
</ins><span class="cx"> if start >= end:
</span><span class="cx"> return None
</span><span class="cx"> else:
</span><span class="cx"> # Try to preserve use of duration in period
</span><del>- result = PyCalendarPeriod(start, end)
</del><ins>+ result = Period(start, end)
</ins><span class="cx"> result.setUseDuration(period.getUseDuration())
</span><span class="cx"> return result
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def pyCalendarTodatetime(pydt):
</span><del>-
</del><ins>+
</ins><span class="cx"> if pydt.isDateOnly():
</span><span class="cx"> return datetime.date(year=pydt.getYear(), month=pydt.getMonth(), day=pydt.getDay())
</span><span class="cx"> else:
</span><span class="lines">@@ -261,17 +284,19 @@
</span><span class="cx"> tzinfo=dateutil.tz.tzutc()
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def parseSQLTimestampToPyCalendar(ts):
</span><span class="cx"> """
</span><del>- Parse an SQL formated timestamp into a PyCalendarDateTime
</del><ins>+ Parse an SQL formated timestamp into a DateTime
</ins><span class="cx"> @param ts: the SQL timestamp
</span><span class="cx"> @type ts: C{str}
</span><del>-
- @return: L{PyCalendarDateTime} result
</del><ins>+
+ @return: L{DateTime} result
</ins><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> # Format is "%Y-%m-%d %H:%M:%S"
</span><del>- return PyCalendarDateTime(
</del><ins>+ return DateTime(
</ins><span class="cx"> year=int(ts[0:4]),
</span><span class="cx"> month=int(ts[5:7]),
</span><span class="cx"> day=int(ts[8:10]),
</span><span class="lines">@@ -280,22 +305,26 @@
</span><span class="cx"> seconds=int(ts[17:19])
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def parseSQLDateToPyCalendar(ts):
</span><span class="cx"> """
</span><del>- Parse an SQL formated date into a PyCalendarDateTime
</del><ins>+ Parse an SQL formated date into a DateTime
</ins><span class="cx"> @param ts: the SQL date
</span><span class="cx"> @type ts: C{str}
</span><del>-
- @return: L{PyCalendarDateTime} result
</del><ins>+
+ @return: L{DateTime} result
</ins><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> # Format is "%Y-%m-%d", though Oracle may add zero time which we ignore
</span><del>- return PyCalendarDateTime(
</del><ins>+ return DateTime(
</ins><span class="cx"> year=int(ts[0:4]),
</span><span class="cx"> month=int(ts[5:7]),
</span><span class="cx"> day=int(ts[8:10])
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def datetimeMktime(dt):
</span><span class="cx">
</span><span class="cx"> assert isinstance(dt, datetime.date)
</span><span class="lines">@@ -303,4 +332,3 @@
</span><span class="cx"> if dt.tzinfo is None:
</span><span class="cx"> dt.replace(tzinfo=dateutil.tz.tzutc())
</span><span class="cx"> return calendar.timegm(dt.utctimetuple())
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryaddressbookpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/addressbook.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/addressbook.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/addressbook.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -56,6 +56,8 @@
</span><span class="cx"> + config.CalDAVComplianceClasses
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class DirectoryAddressBookProvisioningResource (
</span><span class="cx"> ReadOnlyResourceMixIn,
</span><span class="cx"> CalDAVComplianceMixIn,
</span><span class="lines">@@ -65,18 +67,21 @@
</span><span class="cx"> def defaultAccessControlList(self):
</span><span class="cx"> return config.ProvisioningResourceACL
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def etag(self):
</span><span class="cx"> return succeed(ETag(str(uuid4())))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def contentType(self):
</span><span class="cx"> return MimeType("httpd", "unix-directory")
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class DirectoryAddressBookHomeProvisioningResource (
</span><span class="cx"> DirectoryAddressBookProvisioningResource
</span><span class="cx"> ):
</span><span class="cx"> """
</span><del>- Resource which provisions address book home collections as needed.
</del><ins>+ Resource which provisions address book home collections as needed.
</ins><span class="cx"> """
</span><span class="cx"> def __init__(self, directory, url, store):
</span><span class="cx"> """
</span><span class="lines">@@ -103,22 +108,27 @@
</span><span class="cx">
</span><span class="cx"> self.putChild(uidsResourceName, DirectoryAddressBookHomeUIDProvisioningResource(self))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def url(self):
</span><span class="cx"> return self._url
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def listChildren(self):
</span><span class="cx"> return self.directory.recordTypes()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def principalCollections(self):
</span><span class="cx"> # FIXME: directory.principalCollection smells like a hack
</span><span class="cx"> # See DirectoryPrincipalProvisioningResource.__init__()
</span><span class="cx"> return self.directory.principalCollection.principalCollections()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def principalForRecord(self, record):
</span><span class="cx"> # FIXME: directory.principalCollection smells like a hack
</span><span class="cx"> # See DirectoryPrincipalProvisioningResource.__init__()
</span><span class="cx"> return self.directory.principalCollection.principalForRecord(record)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def homeForDirectoryRecord(self, record, request):
</span><span class="cx"> uidResource = self.getChild(uidsResourceName)
</span><span class="cx"> if uidResource is None:
</span><span class="lines">@@ -126,17 +136,20 @@
</span><span class="cx"> else:
</span><span class="cx"> return uidResource.homeResourceForRecord(record, request)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> ##
</span><span class="cx"> # DAV
</span><span class="cx"> ##
</span><del>-
</del><ins>+
</ins><span class="cx"> def isCollection(self):
</span><span class="cx"> return True
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def displayName(self):
</span><span class="cx"> return "addressbooks"
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class DirectoryAddressBookHomeTypeProvisioningResource (
</span><span class="cx"> CommonHomeTypeProvisioningResource,
</span><span class="cx"> DirectoryAddressBookProvisioningResource
</span><span class="lines">@@ -159,6 +172,7 @@
</span><span class="cx"> self.recordType = recordType
</span><span class="cx"> self._parent = parent
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def url(self):
</span><span class="cx"> return joinURL(self._parent.url(), self.recordType)
</span><span class="cx">
</span><span class="lines">@@ -177,16 +191,19 @@
</span><span class="cx"> # Not a listable collection
</span><span class="cx"> raise HTTPError(responsecode.FORBIDDEN)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def makeChild(self, name):
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> ##
</span><span class="cx"> # DAV
</span><span class="cx"> ##
</span><del>-
</del><ins>+
</ins><span class="cx"> def isCollection(self):
</span><span class="cx"> return True
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def displayName(self):
</span><span class="cx"> return self.recordType
</span><span class="cx">
</span><span class="lines">@@ -194,13 +211,16 @@
</span><span class="cx"> # ACL
</span><span class="cx"> ##
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def principalCollections(self):
</span><span class="cx"> return self._parent.principalCollections()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def principalForRecord(self, record):
</span><span class="cx"> return self._parent.principalForRecord(record)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class DirectoryAddressBookHomeUIDProvisioningResource (
</span><span class="cx"> CommonUIDProvisioningResource,
</span><span class="cx"> DirectoryAddressBookProvisioningResource
</span><span class="lines">@@ -210,11 +230,13 @@
</span><span class="cx">
</span><span class="cx"> enabledAttribute = 'enabledForAddressBooks'
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def homeResourceCreator(self, record, transaction):
</span><span class="cx"> return DirectoryAddressBookHomeResource.createHomeResource(
</span><span class="cx"> self, record, transaction)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class DirectoryAddressBookHomeResource (AddressBookHomeResource):
</span><span class="cx"> """
</span><span class="cx"> Address book home collection resource.
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryaugmentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/augment.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/augment.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/augment.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -25,11 +25,11 @@
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.config import fullServerPath, config
</span><del>-from twistedcaldav.database import AbstractADBAPIDatabase, ADBAPISqliteMixin,\
</del><ins>+from twistedcaldav.database import AbstractADBAPIDatabase, ADBAPISqliteMixin, \
</ins><span class="cx"> ADBAPIPostgreSQLMixin
</span><span class="cx"> from twistedcaldav.directory import xmlaugmentsparser
</span><span class="cx"> from twistedcaldav.directory.xmlaugmentsparser import XMLAugmentsParser
</span><del>-from twistedcaldav.xmlutil import newElementTreeWithRoot, addSubElement,\
</del><ins>+from twistedcaldav.xmlutil import newElementTreeWithRoot, addSubElement, \
</ins><span class="cx"> writeXML, readXML
</span><span class="cx"> from twistedcaldav.directory.util import normalizeUUID
</span><span class="cx">
</span><span class="lines">@@ -56,7 +56,6 @@
</span><span class="cx"> uid,
</span><span class="cx"> enabled=False,
</span><span class="cx"> serverID="",
</span><del>- partitionID="",
</del><span class="cx"> enabledForCalendaring=False,
</span><span class="cx"> autoSchedule=False,
</span><span class="cx"> autoScheduleMode="default",
</span><span class="lines">@@ -67,7 +66,6 @@
</span><span class="cx"> self.uid = uid
</span><span class="cx"> self.enabled = enabled
</span><span class="cx"> self.serverID = serverID
</span><del>- self.partitionID = partitionID
</del><span class="cx"> self.enabledForCalendaring = enabledForCalendaring
</span><span class="cx"> self.enabledForAddressBooks = enabledForAddressBooks
</span><span class="cx"> self.enabledForLogin = enabledForLogin
</span><span class="lines">@@ -87,9 +85,9 @@
</span><span class="cx"> """
</span><span class="cx"> Abstract base class for an augment record database.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> self.cachedRecords = {}
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -126,10 +124,10 @@
</span><span class="cx">
</span><span class="cx"> @param uid: directory UID to lookup
</span><span class="cx"> @type uid: C{str}
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> recordType = recordTypesMap[recordType]
</span><span class="cx">
</span><span class="cx"> result = (yield self._lookupAugmentRecord(uid))
</span><span class="lines">@@ -166,6 +164,7 @@
</span><span class="cx"> result.clonedFromDefault = True
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def getAllUIDs(self):
</span><span class="cx"> """
</span><span class="lines">@@ -173,21 +172,23 @@
</span><span class="cx">
</span><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> raise NotImplementedError("Child class must define this.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _lookupAugmentRecord(self, uid):
</span><span class="cx"> """
</span><span class="cx"> Get an AugmentRecord for the specified UID.
</span><span class="cx">
</span><span class="cx"> @param uid: directory UID to lookup
</span><span class="cx"> @type uid: C{str}
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> raise NotImplementedError("Child class must define this.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _cachedAugmentRecord(self, uid):
</span><span class="cx"> """
</span><span class="lines">@@ -195,59 +196,64 @@
</span><span class="cx">
</span><span class="cx"> @param uid: directory UID to lookup
</span><span class="cx"> @type uid: C{str}
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> if not uid in self.cachedRecords:
</span><span class="cx"> result = (yield self._lookupAugmentRecord(uid))
</span><span class="cx"> self.cachedRecords[uid] = result
</span><span class="cx"> returnValue(self.cachedRecords[uid])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def addAugmentRecords(self, records):
</span><span class="cx"> """
</span><span class="cx"> Add an AugmentRecord to the DB.
</span><span class="cx">
</span><span class="cx"> @param record: augment records to add
</span><span class="cx"> @type record: C{list} of L{AugmentRecord}
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> raise NotImplementedError("Child class must define this.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def removeAugmentRecords(self, uids):
</span><span class="cx"> """
</span><span class="cx"> Remove AugmentRecords with the specified UIDs.
</span><span class="cx">
</span><span class="cx"> @param uid: directory UIDs to remove
</span><span class="cx"> @type uid: C{list} of C{str}
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> raise NotImplementedError("Child class must define this.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def refresh(self):
</span><span class="cx"> """
</span><span class="cx"> Refresh any cached data.
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> self.cachedRecords.clear()
</span><span class="cx"> return succeed(None)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def clean(self):
</span><span class="cx"> """
</span><span class="cx"> Remove all records.
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> raise NotImplementedError("Child class must define this.")
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class AugmentXMLDB(AugmentDB):
</span><span class="cx"> """
</span><span class="cx"> XMLFile based augment database implementation.
</span><span class="lines">@@ -257,7 +263,7 @@
</span><span class="cx">
</span><span class="cx"> super(AugmentXMLDB, self).__init__()
</span><span class="cx"> self.xmlFiles = [fullServerPath(config.DataRoot, path) for path in xmlFiles]
</span><del>- self.xmlFileStats = { }
</del><ins>+ self.xmlFileStats = {}
</ins><span class="cx"> for path in self.xmlFiles:
</span><span class="cx"> self.xmlFileStats[path] = (0, 0) # mtime, size
</span><span class="cx">
</span><span class="lines">@@ -290,16 +296,17 @@
</span><span class="cx">
</span><span class="cx"> @param uid: directory UID to lookup
</span><span class="cx"> @type uid: C{str}
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> # May need to re-cache
</span><span class="cx"> if time.time() - self.lastCached > self.statSeconds:
</span><span class="cx"> self.refresh()
</span><del>-
</del><ins>+
</ins><span class="cx"> return succeed(self.db.get(uid))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def addAugmentRecords(self, records):
</span><span class="cx"> """
</span><span class="cx"> Add an AugmentRecord to the DB.
</span><span class="lines">@@ -308,13 +315,13 @@
</span><span class="cx"> @type records: C{list} of L{AugmentRecord}
</span><span class="cx"> @param update: C{True} if changing an existing record
</span><span class="cx"> @type update: C{bool}
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Look at each record and determine whether it is new or a modify
</span><span class="cx"> new_records = list()
</span><del>- existing_records = list()
</del><ins>+ existing_records = list()
</ins><span class="cx"> for record in records:
</span><span class="cx"> (existing_records if record.uid in self.db else new_records).append(record)
</span><span class="cx">
</span><span class="lines">@@ -332,6 +339,7 @@
</span><span class="cx">
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _doAddToFile(self, xmlfile, records):
</span><span class="cx">
</span><span class="cx"> if not os.path.exists(xmlfile):
</span><span class="lines">@@ -343,7 +351,6 @@
</span><span class="cx"> for record in self.db.itervalues():
</span><span class="cx"> self._addRecordToXMLDB(record, augments_node)
</span><span class="cx">
</span><del>-
</del><span class="cx"> writeXML(xmlfile, augments_node)
</span><span class="cx">
</span><span class="cx"> # Set permissions
</span><span class="lines">@@ -362,33 +369,33 @@
</span><span class="cx"> if uid != -1 and gid != -1:
</span><span class="cx"> os.chown(xmlfile, uid, gid)
</span><span class="cx">
</span><del>-
</del><span class="cx"> _ignore_etree, augments_node = readXML(xmlfile)
</span><span class="cx">
</span><span class="cx"> # Create new record
</span><span class="cx"> for record in records:
</span><span class="cx"> self._addRecordToXMLDB(record, augments_node)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Modify xmlfile
</span><span class="cx"> writeXML(xmlfile, augments_node)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _doModifyInFile(self, xmlfile, records):
</span><del>-
</del><ins>+
</ins><span class="cx"> if not os.path.exists(xmlfile):
</span><span class="cx"> return
</span><span class="cx">
</span><span class="cx"> _ignore_etree, augments_node = readXML(xmlfile)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Map uid->record for fast lookup
</span><span class="cx"> recordMap = dict([(record.uid, record) for record in records])
</span><span class="cx">
</span><span class="cx"> # Make sure UID is present
</span><span class="cx"> changed = False
</span><span class="cx"> for record_node in augments_node:
</span><del>-
</del><ins>+
</ins><span class="cx"> if record_node.tag != xmlaugmentsparser.ELEMENT_RECORD:
</span><span class="cx"> continue
</span><del>-
</del><ins>+
</ins><span class="cx"> uid = record_node.find(xmlaugmentsparser.ELEMENT_UID).text
</span><span class="cx"> if uid in recordMap:
</span><span class="cx"> # Modify record
</span><span class="lines">@@ -400,13 +407,14 @@
</span><span class="cx"> if changed:
</span><span class="cx"> writeXML(xmlfile, augments_node)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def removeAugmentRecords(self, uids):
</span><span class="cx"> """
</span><span class="cx"> Remove AugmentRecords with the specified UIDs.
</span><span class="cx">
</span><span class="cx"> @param uids: list of uids to remove
</span><span class="cx"> @type uids: C{list} of C{str}
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><span class="cx">
</span><span class="lines">@@ -423,10 +431,11 @@
</span><span class="cx">
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _doRemoveFromFile(self, xmlfile, uids):
</span><del>-
</del><ins>+
</ins><span class="cx"> _ignore_etree, augments_node = readXML(xmlfile)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Remove all UIDs present
</span><span class="cx"> changed = False
</span><span class="cx"> for child in augments_node:
</span><span class="lines">@@ -440,20 +449,19 @@
</span><span class="cx"> # Modify xmlfile
</span><span class="cx"> if changed:
</span><span class="cx"> writeXML(xmlfile, augments_node)
</span><del>-
-
</del><ins>+
+
</ins><span class="cx"> def _addRecordToXMLDB(self, record, parentNode):
</span><span class="cx"> record_node = addSubElement(parentNode, xmlaugmentsparser.ELEMENT_RECORD)
</span><span class="cx"> self._updateRecordInXMLDB(record, record_node)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _updateRecordInXMLDB(self, record, recordNode):
</span><span class="cx"> del recordNode[:]
</span><span class="cx"> addSubElement(recordNode, xmlaugmentsparser.ELEMENT_UID, record.uid)
</span><span class="cx"> addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLE, "true" if record.enabled else "false")
</span><span class="cx"> if record.serverID:
</span><span class="cx"> addSubElement(recordNode, xmlaugmentsparser.ELEMENT_SERVERID, record.serverID)
</span><del>- if record.partitionID:
- addSubElement(recordNode, xmlaugmentsparser.ELEMENT_PARTITIONID, record.partitionID)
</del><span class="cx"> addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLECALENDAR, "true" if record.enabledForCalendaring else "false")
</span><span class="cx"> addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLEADDRESSBOOK, "true" if record.enabledForAddressBooks else "false")
</span><span class="cx"> addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLELOGIN, "true" if record.enabledForLogin else "false")
</span><span class="lines">@@ -463,6 +471,7 @@
</span><span class="cx"> if record.autoAcceptGroup:
</span><span class="cx"> addSubElement(recordNode, xmlaugmentsparser.ELEMENT_AUTOACCEPTGROUP, record.autoAcceptGroup)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def refresh(self):
</span><span class="cx"> """
</span><span class="cx"> Refresh any cached data.
</span><span class="lines">@@ -479,6 +488,7 @@
</span><span class="cx">
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def clean(self):
</span><span class="cx"> """
</span><span class="cx"> Remove all records.
</span><span class="lines">@@ -487,6 +497,7 @@
</span><span class="cx"> self.removeAugmentRecords(self.db.keys())
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _shouldReparse(self, xmlFiles):
</span><span class="cx"> """
</span><span class="cx"> Check to see whether any of the given files have been modified since
</span><span class="lines">@@ -501,6 +512,7 @@
</span><span class="cx"> return True
</span><span class="cx"> return False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _parseXML(self):
</span><span class="cx"> """
</span><span class="cx"> Parse self.xmlFiles into AugmentRecords.
</span><span class="lines">@@ -536,19 +548,22 @@
</span><span class="cx">
</span><span class="cx"> return results
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AugmentADAPI(AugmentDB, AbstractADBAPIDatabase):
</span><span class="cx"> """
</span><span class="cx"> DBAPI based augment database implementation.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- schema_version = "2"
- schema_type = "AugmentDB"
-
</del><ins>+ schema_version = "3"
+ schema_type = "AugmentDB"
+
</ins><span class="cx"> def __init__(self, dbID, dbapiName, dbapiArgs, **kwargs):
</span><del>-
</del><ins>+
</ins><span class="cx"> AugmentDB.__init__(self)
</span><span class="cx"> AbstractADBAPIDatabase.__init__(self, dbID, dbapiName, dbapiArgs, True, **kwargs)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def getAllUIDs(self):
</span><span class="cx"> """
</span><span class="lines">@@ -556,11 +571,12 @@
</span><span class="cx">
</span><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> # Query for the record information
</span><span class="cx"> results = (yield self.queryList("select UID from AUGMENTS", ()))
</span><span class="cx"> returnValue(results)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _lookupAugmentRecord(self, uid):
</span><span class="cx"> """
</span><span class="lines">@@ -571,34 +587,34 @@
</span><span class="cx">
</span><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> # Query for the record information
</span><del>- results = (yield self.query("select UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED from AUGMENTS where UID = :1", (uid,)))
</del><ins>+ results = (yield self.query("select UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED from AUGMENTS where UID = :1", (uid,)))
</ins><span class="cx"> if not results:
</span><span class="cx"> returnValue(None)
</span><span class="cx"> else:
</span><del>- uid, enabled, serverid, partitionid, enabledForCalendaring, enabledForAddressBooks, autoSchedule, autoScheduleMode, autoAcceptGroup, enabledForLogin = results[0]
-
</del><ins>+ uid, enabled, serverid, enabledForCalendaring, enabledForAddressBooks, autoSchedule, autoScheduleMode, autoAcceptGroup, enabledForLogin = results[0]
+
</ins><span class="cx"> record = AugmentRecord(
</span><del>- uid = uid,
- enabled = enabled == "T",
- serverID = serverid,
- partitionID = partitionid,
- enabledForCalendaring = enabledForCalendaring == "T",
- enabledForAddressBooks = enabledForAddressBooks == "T",
- enabledForLogin = enabledForLogin == "T",
- autoSchedule = autoSchedule == "T",
- autoScheduleMode = autoScheduleMode,
- autoAcceptGroup = autoAcceptGroup,
</del><ins>+ uid=uid,
+ enabled=enabled == "T",
+ serverID=serverid,
+ enabledForCalendaring=enabledForCalendaring == "T",
+ enabledForAddressBooks=enabledForAddressBooks == "T",
+ enabledForLogin=enabledForLogin == "T",
+ autoSchedule=autoSchedule == "T",
+ autoScheduleMode=autoScheduleMode,
+ autoAcceptGroup=autoAcceptGroup,
</ins><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> returnValue(record)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def addAugmentRecords(self, records):
</span><span class="cx">
</span><span class="cx"> for record in records:
</span><del>-
</del><ins>+
</ins><span class="cx"> results = (yield self.query("select UID from AUGMENTS where UID = :1", (record.uid,)))
</span><span class="cx"> update = len(results) > 0
</span><span class="cx">
</span><span class="lines">@@ -607,6 +623,7 @@
</span><span class="cx"> else:
</span><span class="cx"> yield self._addRecord(record)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def removeAugmentRecords(self, uids):
</span><span class="cx"> """
</span><span class="lines">@@ -614,32 +631,36 @@
</span><span class="cx">
</span><span class="cx"> @param uids: list of uids to remove
</span><span class="cx"> @type uids: C{list} of C{str}
</span><del>-
</del><ins>+
</ins><span class="cx"> @return: L{Deferred}
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> for uid in uids:
</span><span class="cx"> yield self.execute("delete from AUGMENTS where UID = :1", (uid,))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def clean(self):
</span><span class="cx"> """
</span><span class="cx"> Remove all records.
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> return self.execute("delete from AUGMENTS", ())
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _db_version(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the schema version assigned to this index.
</span><span class="cx"> """
</span><span class="cx"> return AugmentADAPI.schema_version
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _db_type(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the collection type assigned to this index.
</span><span class="cx"> """
</span><span class="cx"> return AugmentADAPI.schema_type
</span><del>-
</del><ins>+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _db_init_data_tables(self):
</span><span class="cx"> """
</span><span class="lines">@@ -652,45 +673,47 @@
</span><span class="cx"> yield self._create_table(
</span><span class="cx"> "AUGMENTS",
</span><span class="cx"> (
</span><del>- ("UID", "text unique"),
- ("ENABLED", "text(1)"),
- ("SERVERID", "text"),
- ("PARTITIONID", "text"),
- ("CALENDARING", "text(1)"),
- ("ADDRESSBOOKS", "text(1)"),
- ("AUTOSCHEDULE", "text(1)"),
</del><ins>+ ("UID", "text unique"),
+ ("ENABLED", "text(1)"),
+ ("SERVERID", "text"),
+ ("CALENDARING", "text(1)"),
+ ("ADDRESSBOOKS", "text(1)"),
+ ("AUTOSCHEDULE", "text(1)"),
</ins><span class="cx"> ("AUTOSCHEDULEMODE", "text"),
</span><del>- ("AUTOACCEPTGROUP", "text"),
- ("LOGINENABLED", "text(1)"),
</del><ins>+ ("AUTOACCEPTGROUP", "text"),
+ ("LOGINENABLED", "text(1)"),
</ins><span class="cx"> ),
</span><span class="cx"> ifnotexists=True,
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _db_empty_data_tables(self):
</span><span class="cx"> yield self._db_execute("delete from AUGMENTS")
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AugmentSqliteDB(ADBAPISqliteMixin, AugmentADAPI):
</span><span class="cx"> """
</span><span class="cx"> Sqlite based augment database implementation.
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def __init__(self, dbpath):
</span><del>-
</del><ins>+
</ins><span class="cx"> ADBAPISqliteMixin.__init__(self)
</span><span class="cx"> AugmentADAPI.__init__(self, "Augments", "sqlite3", (fullServerPath(config.DataRoot, dbpath),))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _addRecord(self, record):
</span><span class="cx"> yield self.execute(
</span><span class="cx"> """insert or replace into AUGMENTS
</span><del>- (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
- values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)""",
</del><ins>+ (UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
+ values (:1, :2, :3, :4, :5, :6, :7, :8, :9)""",
</ins><span class="cx"> (
</span><span class="cx"> record.uid,
</span><span class="cx"> "T" if record.enabled else "F",
</span><span class="cx"> record.serverID,
</span><del>- record.partitionID,
</del><span class="cx"> "T" if record.enabledForCalendaring else "F",
</span><span class="cx"> "T" if record.enabledForAddressBooks else "F",
</span><span class="cx"> "T" if record.autoSchedule else "F",
</span><span class="lines">@@ -700,30 +723,33 @@
</span><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _modifyRecord(self, record):
</span><span class="cx"> return self._addRecord(record)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AugmentPostgreSQLDB(ADBAPIPostgreSQLMixin, AugmentADAPI):
</span><span class="cx"> """
</span><span class="cx"> PostgreSQL based augment database implementation.
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def __init__(self, host, database, user=None, password=None):
</span><del>-
</del><ins>+
</ins><span class="cx"> ADBAPIPostgreSQLMixin.__init__(self)
</span><span class="cx"> AugmentADAPI.__init__(self, "Augments", "pgdb", (), host=host, database=database, user=user, password=password,)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _addRecord(self, record):
</span><span class="cx"> yield self.execute(
</span><span class="cx"> """insert into AUGMENTS
</span><del>- (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
- values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)""",
</del><ins>+ (UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
+ values (:1, :2, :3, :4, :5, :6, :7, :8, :9)""",
</ins><span class="cx"> (
</span><span class="cx"> record.uid,
</span><span class="cx"> "T" if record.enabled else "F",
</span><span class="cx"> record.serverID,
</span><del>- record.partitionID,
</del><span class="cx"> "T" if record.enabledForCalendaring else "F",
</span><span class="cx"> "T" if record.enabledForAddressBooks else "F",
</span><span class="cx"> "T" if record.autoSchedule else "F",
</span><span class="lines">@@ -733,17 +759,17 @@
</span><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _modifyRecord(self, record):
</span><span class="cx"> yield self.execute(
</span><span class="cx"> """update AUGMENTS set
</span><del>- (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED) =
- (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10) where UID = :11""",
</del><ins>+ (UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED) =
+ (:1, :2, :3, :4, :5, :6, :7, :8, :9) where UID = :10""",
</ins><span class="cx"> (
</span><span class="cx"> record.uid,
</span><span class="cx"> "T" if record.enabled else "F",
</span><span class="cx"> record.serverID,
</span><del>- record.partitionID,
</del><span class="cx"> "T" if record.enabledForCalendaring else "F",
</span><span class="cx"> "T" if record.enabledForAddressBooks else "F",
</span><span class="cx"> "T" if record.autoSchedule else "F",
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorycommonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/common.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/common.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/common.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx"> Common ancestor for addressbook/calendar UID provisioning resources.
</span><span class="cx">
</span><span class="cx"> Must be mixed in to the hierarchy I{before} the appropriate resource type.
</span><del>-
</del><ins>+
</ins><span class="cx"> @ivar homeResourceTypeName: The name of the home resource type ('calendars'
</span><span class="cx"> or 'addressbooks').
</span><span class="cx">
</span><span class="lines">@@ -78,13 +78,11 @@
</span><span class="cx">
</span><span class="cx"> assert len(name) > 4, "Directory record has an invalid GUID: %r" % (
</span><span class="cx"> name,)
</span><del>-
- if record.locallyHosted():
</del><ins>+
+ if record.thisServer():
</ins><span class="cx"> child = yield self.homeResourceCreator(record, transaction)
</span><del>- elif record.thisServer():
</del><ins>+ else:
</ins><span class="cx"> child = DirectoryReverseProxyResource(self, record)
</span><del>- else:
- child = None # Use a redirect?
</del><span class="cx">
</span><span class="cx"> returnValue(child)
</span><span class="cx">
</span><span class="lines">@@ -108,6 +106,7 @@
</span><span class="cx"> # Not a listable collection
</span><span class="cx"> raise HTTPError(responsecode.FORBIDDEN)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> ##
</span><span class="cx"> # ACL
</span><span class="cx"> ##
</span><span class="lines">@@ -115,12 +114,15 @@
</span><span class="cx"> def principalCollections(self):
</span><span class="cx"> return self.parent.principalCollections()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def principalForRecord(self, record):
</span><span class="cx"> return self.parent.principalForRecord(record)
</span><ins>+
+
</ins><span class="cx"> ##
</span><span class="cx"> # DAV
</span><span class="cx"> ##
</span><del>-
</del><ins>+
</ins><span class="cx"> def isCollection(self):
</span><span class="cx"> return True
</span><span class="cx">
</span><span class="lines">@@ -129,9 +131,11 @@
</span><span class="cx"> raise NotImplementedError(self.__class__.__name__ +
</span><span class="cx"> ".getChild no longer exists.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def displayName(self):
</span><span class="cx"> return uidsResourceName
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def url(self):
</span><span class="cx"> return joinURL(self.parent.url(), uidsResourceName)
</span><span class="cx">
</span><span class="lines">@@ -153,4 +157,3 @@
</span><span class="cx">
</span><span class="cx"> child = yield self._parent.homeForDirectoryRecord(record, request)
</span><span class="cx"> returnValue((child, segments[1:]))
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorydirectoryprincipalresourcehtml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory-principal-resource.html (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory-principal-resource.html        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory-principal-resource.html        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -5,8 +5,7 @@
</span><span class="cx"> ---------------------
</span><span class="cx"> Directory GUID: <t:slot name="directoryGUID"/>
</span><span class="cx"> Realm: <t:slot name="realm"/>
</span><del>-<t:transparent t:render="serversEnabled">Hosted-At: <t:slot name="hostedAt"/>
-Partition: <t:slot name="partition"/></t:transparent>
</del><ins>+<t:transparent t:render="serversEnabled">Hosted-At: <t:slot name="hostedAt"/></t:transparent>
</ins><span class="cx"> Principal Information
</span><span class="cx"> ---------------------
</span><span class="cx"> GUID: <t:slot name="principalGUID"/>
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorydirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/directory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -89,8 +89,8 @@
</span><span class="cx">
</span><span class="cx"> searchContext_location = "location"
</span><span class="cx"> searchContext_resource = "resource"
</span><del>- searchContext_user = "user"
- searchContext_group = "group"
</del><ins>+ searchContext_user = "user"
+ searchContext_group = "group"
</ins><span class="cx"> searchContext_attendee = "attendee"
</span><span class="cx">
</span><span class="cx"> aggregateService = None
</span><span class="lines">@@ -628,6 +628,7 @@
</span><span class="cx"> self.expireSeconds = expireSeconds
</span><span class="cx"> self.lockSeconds = lockSeconds
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def setGroupsFor(self, guid, memberships):
</span><span class="cx"> self.log.debug("set groups-for %s : %s" % (guid, memberships))
</span><span class="cx"> return self.set("groups-for:%s" %
</span><span class="lines">@@ -675,7 +676,6 @@
</span><span class="cx"> return self.add("group-cacher-lock", "1", expireTime=self.lockSeconds)
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> def extendLock(self):
</span><span class="cx"> """
</span><span class="cx"> Update the expiration time of the memcached lock
</span><span class="lines">@@ -694,6 +694,7 @@
</span><span class="cx"> return self.delete("group-cacher-lock")
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class GroupMembershipCacheUpdater(object):
</span><span class="cx"> """
</span><span class="cx"> Responsible for updating memcached with group memberships. This will run
</span><span class="lines">@@ -1130,7 +1131,7 @@
</span><span class="cx"> implements(IDirectoryRecord, ICalendarStoreDirectoryRecord)
</span><span class="cx">
</span><span class="cx"> def __repr__(self):
</span><del>- return "<%s[%s@%s(%s)] %s(%s) %r @ %s/#%s>" % (
</del><ins>+ return "<%s[%s@%s(%s)] %s(%s) %r @ %s>" % (
</ins><span class="cx"> self.__class__.__name__,
</span><span class="cx"> self.recordType,
</span><span class="cx"> self.service.guid,
</span><span class="lines">@@ -1139,7 +1140,6 @@
</span><span class="cx"> ",".join(self.shortNames),
</span><span class="cx"> self.fullName,
</span><span class="cx"> self.serverURI(),
</span><del>- self.partitionID,
</del><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1175,7 +1175,6 @@
</span><span class="cx"> self.uid = uid
</span><span class="cx"> self.enabled = False
</span><span class="cx"> self.serverID = ""
</span><del>- self.partitionID = ""
</del><span class="cx"> self.shortNames = shortNames
</span><span class="cx"> self.authIDs = authIDs
</span><span class="cx"> self.fullName = fullName
</span><span class="lines">@@ -1257,7 +1256,6 @@
</span><span class="cx"> if augment:
</span><span class="cx"> self.enabled = augment.enabled
</span><span class="cx"> self.serverID = augment.serverID
</span><del>- self.partitionID = augment.partitionID
</del><span class="cx"> self.enabledForCalendaring = augment.enabledForCalendaring
</span><span class="cx"> self.enabledForAddressBooks = augment.enabledForAddressBooks
</span><span class="cx"> self.autoSchedule = augment.autoSchedule
</span><span class="lines">@@ -1278,7 +1276,6 @@
</span><span class="cx"> # Groups are by default always enabled
</span><span class="cx"> self.enabled = (self.recordType == self.service.recordType_groups)
</span><span class="cx"> self.serverID = ""
</span><del>- self.partitionID = ""
</del><span class="cx"> self.enabledForCalendaring = False
</span><span class="cx"> self.enabledForAddressBooks = False
</span><span class="cx"> self.enabledForLogin = False
</span><span class="lines">@@ -1496,46 +1493,9 @@
</span><span class="cx"> return None
</span><span class="cx">
</span><span class="cx">
</span><del>- def partitionURI(self):
- """
- URL of the server hosting this record. Return None if hosted on this server.
- """
- if config.Servers.Enabled and self.serverID:
- s = Servers.getServerById(self.serverID)
- if s:
- return s.getPartitionURIForId(self.partitionID)
- return None
-
-
- def locallyHosted(self):
- """
- Hosted on this server/partition instance.
- """
-
- if config.Servers.Enabled and self.serverID:
- s = Servers.getServerById(self.serverID)
- if s:
- return s.thisServer and (not s.isPartitioned() or not self.partitionID or self.partitionID == config.ServerPartitionID)
- return True
-
-
- def effectivePartitionID(self):
- """
- Record partition ID taking into account whether the server is partitioned.
- """
- if config.Servers.Enabled and self.serverID:
- s = Servers.getServerById(self.serverID)
- if s and s.isPartitioned():
- return self.partitionID
- return ""
-
-
</del><span class="cx"> def thisServer(self):
</span><del>- if config.Servers.Enabled and self.serverID:
- s = Servers.getServerById(self.serverID)
- if s:
- return s.thisServer
- return True
</del><ins>+ s = self.server()
+ return s.thisServer if s is not None else True
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def autoAcceptMembers(self):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryidirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/idirectory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/idirectory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/idirectory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -32,20 +32,20 @@
</span><span class="cx"> realmName = Attribute("The name of the authentication realm this service represents.")
</span><span class="cx"> guid = Attribute("A GUID for this service.")
</span><span class="cx">
</span><del>- def recordTypes():
</del><ins>+ def recordTypes(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @return: a sequence of strings denoting the record types that
</span><span class="cx"> are kept in the directory. For example: C{["users",
</span><span class="cx"> "groups", "resources"]}.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def listRecords(recordType):
</del><ins>+ def listRecords(recordType): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @param type: the type of records to retrieve.
</span><span class="cx"> @return: an iterable of records of the given type.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def recordWithShortName(recordType, shortName):
</del><ins>+ def recordWithShortName(recordType, shortName): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @param recordType: the type of the record to look up.
</span><span class="cx"> @param shortName: the short name of the record to look up.
</span><span class="lines">@@ -53,21 +53,21 @@
</span><span class="cx"> C{None} if no such record exists.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def recordWithUID(uid):
</del><ins>+ def recordWithUID(uid): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @param uid: the UID of the record to look up.
</span><span class="cx"> @return: an L{IDirectoryRecord} with the given UID, or C{None}
</span><span class="cx"> if no such record exists.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def recordWithGUID(guid):
</del><ins>+ def recordWithGUID(guid): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @param guid: the GUID of the record to look up.
</span><span class="cx"> @return: an L{IDirectoryRecord} with the given GUID, or
</span><span class="cx"> C{None} if no such record exists.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def recordWithCalendarUserAddress(address):
</del><ins>+ def recordWithCalendarUserAddress(address): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @param address: the calendar user address of the record to look up.
</span><span class="cx"> @type address: C{str}
</span><span class="lines">@@ -81,7 +81,7 @@
</span><span class="cx"> directory service may not be aware of these addresses.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def recordWithCachedGroupsAlias(recordType, alias):
</del><ins>+ def recordWithCachedGroupsAlias(recordType, alias): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @param recordType: the type of the record to look up.
</span><span class="cx"> @param alias: the cached-groups alias of the record to look up.
</span><span class="lines">@@ -91,14 +91,13 @@
</span><span class="cx"> alias, or C{None} if no such record is found.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-
- def recordsMatchingFields(fields):
</del><ins>+ def recordsMatchingFields(fields): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @return: a deferred sequence of L{IDirectoryRecord}s which
</span><span class="cx"> match the given fields.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def recordsMatchingTokens(tokens, context=None):
</del><ins>+ def recordsMatchingTokens(tokens, context=None): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @param tokens: The tokens to search on
</span><span class="cx"> @type tokens: C{list} of C{str} (utf-8 bytes)
</span><span class="lines">@@ -119,31 +118,31 @@
</span><span class="cx"> "attendee", only users, groups, and resources are considered.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-
- def setRealm(realmName):
</del><ins>+ def setRealm(realmName): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> Set a new realm name for this (and nested services if any)
</span><span class="cx">
</span><span class="cx"> @param realmName: the realm name this service should use.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class IDirectoryRecord(Interface):
</span><span class="cx"> """
</span><span class="cx"> Directory Record
</span><span class="cx"> """
</span><del>- service = Attribute("The L{IDirectoryService} this record exists in.")
- recordType = Attribute("The type of this record.")
- guid = Attribute("The GUID of this record.")
- uid = Attribute("The UID of this record.")
- enabled = Attribute("Determines whether this record should allow a principal to be created.")
- serverID = Attribute("Identifies the server that actually hosts data for the record.")
- partitionID = Attribute("Identifies the partition node that actually hosts data for the record.")
- shortNames = Attribute("The names for this record.")
- authIDs = Attribute("Alternative security identities for this record.")
- fullName = Attribute("The full name of this record.")
- firstName = Attribute("The first name of this record.")
- lastName = Attribute("The last name of this record.")
- emailAddresses = Attribute("The email addresses of this record.")
</del><ins>+ service = Attribute("The L{IDirectoryService} this record exists in.")
+ recordType = Attribute("The type of this record.")
+ guid = Attribute("The GUID of this record.")
+ uid = Attribute("The UID of this record.")
+ enabled = Attribute("Determines whether this record should allow a principal to be created.")
+ serverID = Attribute("Identifies the server that actually hosts data for the record.")
+ shortNames = Attribute("The names for this record.")
+ authIDs = Attribute("Alternative security identities for this record.")
+ fullName = Attribute("The full name of this record.")
+ firstName = Attribute("The first name of this record.")
+ lastName = Attribute("The last name of this record.")
+ emailAddresses = Attribute("The email addresses of this record.")
</ins><span class="cx"> enabledForCalendaring = Attribute("Determines whether this record creates a principal with a calendar home.")
</span><span class="cx"> enabledForAddressBooks = Attribute("Determines whether this record creates a principal with an address book home.")
</span><span class="cx"> calendarUserAddresses = Attribute(
</span><span class="lines">@@ -159,19 +158,19 @@
</span><span class="cx"> """
</span><span class="cx"> )
</span><span class="cx">
</span><del>- def members():
</del><ins>+ def members(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @return: an iterable of L{IDirectoryRecord}s for the members of this
</span><span class="cx"> (group) record.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def groups():
</del><ins>+ def groups(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @return: an iterable of L{IDirectoryRecord}s for the groups this
</span><span class="cx"> record is a member of.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def verifyCredentials(credentials):
</del><ins>+ def verifyCredentials(credentials): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> Verify that the given credentials can authenticate the principal
</span><span class="cx"> represented by this record.
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryldapdirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/ldapdirectory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/ldapdirectory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/ldapdirectory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -75,12 +75,13 @@
</span><span class="cx"> return "<%s %r: %r>" % (self.__class__.__name__, self.realmName,
</span><span class="cx"> self.uri)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __init__(self, params):
</span><span class="cx"> """
</span><span class="cx"> @param params: a dictionary containing the following keys:
</span><span class="cx"> cacheTimeout, realmName, uri, tls, tlsCACertFile, tlsCACertDir,
</span><span class="cx"> tlsRequireCert, credentials, rdnSchema, groupSchema, resourceSchema
</span><del>- partitionSchema
</del><ins>+ poddingSchema
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> defaults = {
</span><span class="lines">@@ -188,9 +189,8 @@
</span><span class="cx"> "readOnlyProxyAttr": None, # list of GUIDs
</span><span class="cx"> "autoAcceptGroupAttr": None, # single group GUID
</span><span class="cx"> },
</span><del>- "partitionSchema": {
</del><ins>+ "poddingSchema": {
</ins><span class="cx"> "serverIdAttr": None, # maps to augments server-id
</span><del>- "partitionIdAttr": None, # maps to augments partition-id
</del><span class="cx"> },
</span><span class="cx"> }
</span><span class="cx"> ignored = None
</span><span class="lines">@@ -222,7 +222,7 @@
</span><span class="cx"> self.rdnSchema = params["rdnSchema"]
</span><span class="cx"> self.groupSchema = params["groupSchema"]
</span><span class="cx"> self.resourceSchema = params["resourceSchema"]
</span><del>- self.partitionSchema = params["partitionSchema"]
</del><ins>+ self.poddingSchema = params["poddingSchema"]
</ins><span class="cx">
</span><span class="cx"> self.base = ldap.dn.str2dn(self.rdnSchema["base"])
</span><span class="cx">
</span><span class="lines">@@ -272,10 +272,8 @@
</span><span class="cx"> attrSet.add(self.resourceSchema["proxyAttr"])
</span><span class="cx"> if self.resourceSchema["readOnlyProxyAttr"]:
</span><span class="cx"> attrSet.add(self.resourceSchema["readOnlyProxyAttr"])
</span><del>- if self.partitionSchema["serverIdAttr"]:
- attrSet.add(self.partitionSchema["serverIdAttr"])
- if self.partitionSchema["partitionIdAttr"]:
- attrSet.add(self.partitionSchema["partitionIdAttr"])
</del><ins>+ if self.poddingSchema["serverIdAttr"]:
+ attrSet.add(self.poddingSchema["serverIdAttr"])
</ins><span class="cx"> self.attrlist = list(attrSet)
</span><span class="cx">
</span><span class="cx"> self.typeDNs = {}
</span><span class="lines">@@ -284,10 +282,8 @@
</span><span class="cx"> self.rdnSchema[recordType]["rdn"].lower()
</span><span class="cx"> ) + self.base
</span><span class="cx">
</span><del>-
</del><span class="cx"> self.ldap = None
</span><span class="cx">
</span><del>-
</del><span class="cx"> # Separate LDAP connection used solely for authenticating clients
</span><span class="cx"> self.authLDAP = None
</span><span class="cx">
</span><span class="lines">@@ -314,7 +310,7 @@
</span><span class="cx">
</span><span class="cx"> # Query the LDAP server
</span><span class="cx"> self.log.debug("Querying ldap for records matching base {base} and "
</span><del>- "filter {filter} for attributes {attrs}.",
</del><ins>+ "filter {filter} for attributes {attrs}.",
</ins><span class="cx"> base=ldap.dn.dn2str(base), filter=filterstr, attrs=self.attrlist)
</span><span class="cx">
</span><span class="cx"> # This takes a while, so if you don't want to have a "long request"
</span><span class="lines">@@ -353,6 +349,7 @@
</span><span class="cx">
</span><span class="cx"> return records
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def recordWithCachedGroupsAlias(self, recordType, alias):
</span><span class="cx"> """
</span><span class="lines">@@ -373,6 +370,7 @@
</span><span class="cx"> else:
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getExternalProxyAssignments(self):
</span><span class="cx"> """
</span><span class="cx"> Retrieve proxy assignments for locations and resources from the
</span><span class="lines">@@ -425,6 +423,7 @@
</span><span class="cx">
</span><span class="cx"> return assignments
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getLDAPConnection(self):
</span><span class="cx"> if self.ldap is None:
</span><span class="cx"> self.log.info("Connecting to LDAP {uri}", uri=repr(self.uri))
</span><span class="lines">@@ -443,6 +442,7 @@
</span><span class="cx"> raise DirectoryConfigurationError()
</span><span class="cx"> return self.ldap
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def createLDAPConnection(self):
</span><span class="cx"> """
</span><span class="cx"> Create and configure LDAP connection
</span><span class="lines">@@ -478,7 +478,7 @@
</span><span class="cx"> """
</span><span class="cx"> TRIES = 3
</span><span class="cx">
</span><del>- for i in xrange(TRIES):
</del><ins>+ for _ignore_i in xrange(TRIES):
</ins><span class="cx"> self.log.debug("Authenticating %s" % (dn,))
</span><span class="cx">
</span><span class="cx"> if self.authLDAP is None:
</span><span class="lines">@@ -555,11 +555,11 @@
</span><span class="cx"> self.log.warn("LDAP timeout exceeded: %d seconds" % (timeoutSeconds,))
</span><span class="cx"> except ldap.SERVER_DOWN:
</span><span class="cx"> self.ldap = None
</span><del>- self.log.error("LDAP server unavailable (tried %d times)" % (i+1,))
</del><ins>+ self.log.error("LDAP server unavailable (tried %d times)" % (i + 1,))
</ins><span class="cx"> continue
</span><span class="cx">
</span><span class="cx"> # change format, ignoring resultsType
</span><del>- result = [resultItem for resultType, resultItem in s.allResults]
</del><ins>+ result = [resultItem for _ignore_resultType, resultItem in s.allResults]
</ins><span class="cx">
</span><span class="cx"> totalTime = time.time() - startTime
</span><span class="cx"> if totalTime > self.warningThresholdSeconds:
</span><span class="lines">@@ -769,7 +769,6 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> guid = None
</span><del>- shortNames = ()
</del><span class="cx"> authIDs = set()
</span><span class="cx"> fullName = None
</span><span class="cx"> firstName = ""
</span><span class="lines">@@ -891,30 +890,27 @@
</span><span class="cx"> autoAcceptGroup = self._getUniqueLdapAttribute(attrs,
</span><span class="cx"> self.resourceSchema["autoAcceptGroupAttr"])
</span><span class="cx">
</span><del>- serverID = partitionID = None
- if self.partitionSchema["serverIdAttr"]:
</del><ins>+ serverID = None
+ if self.poddingSchema["serverIdAttr"]:
</ins><span class="cx"> serverID = self._getUniqueLdapAttribute(attrs,
</span><del>- self.partitionSchema["serverIdAttr"])
- if self.partitionSchema["partitionIdAttr"]:
- partitionID = self._getUniqueLdapAttribute(attrs,
- self.partitionSchema["partitionIdAttr"])
</del><ins>+ self.poddingSchema["serverIdAttr"])
</ins><span class="cx">
</span><span class="cx"> record = LdapDirectoryRecord(
</span><del>- service = self,
- recordType = recordType,
- guid = guid,
- shortNames = shortNames,
- authIDs = authIDs,
- fullName = fullName,
- firstName = firstName,
- lastName = lastName,
- emailAddresses = emailAddresses,
- uid = uid,
- dn = dn,
- memberGUIDs = memberGUIDs,
- extProxies = proxyGUIDs,
- extReadOnlyProxies = readOnlyProxyGUIDs,
- attrs = attrs,
</del><ins>+ service=self,
+ recordType=recordType,
+ guid=guid,
+ shortNames=shortNames,
+ authIDs=authIDs,
+ fullName=fullName,
+ firstName=firstName,
+ lastName=lastName,
+ emailAddresses=emailAddresses,
+ uid=uid,
+ dn=dn,
+ memberGUIDs=memberGUIDs,
+ extProxies=proxyGUIDs,
+ extReadOnlyProxies=readOnlyProxyGUIDs,
+ attrs=attrs,
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> if self.augmentService is not None:
</span><span class="lines">@@ -924,7 +920,7 @@
</span><span class="cx"> # immediately.
</span><span class="cx"> d = self.augmentService.getAugmentRecord(record.guid,
</span><span class="cx"> recordType)
</span><del>- d.addCallback(lambda x:record.addAugmentInformation(x))
</del><ins>+ d.addCallback(lambda x: record.addAugmentInformation(x))
</ins><span class="cx">
</span><span class="cx"> else:
</span><span class="cx"> # Generate augment record based on information retrieved from LDAP
</span><span class="lines">@@ -932,7 +928,6 @@
</span><span class="cx"> guid,
</span><span class="cx"> enabled=True,
</span><span class="cx"> serverID=serverID,
</span><del>- partitionID=partitionID,
</del><span class="cx"> enabledForCalendaring=enabledForCalendaring,
</span><span class="cx"> autoSchedule=autoSchedule,
</span><span class="cx"> autoAcceptGroup=autoAcceptGroup,
</span><span class="lines">@@ -965,7 +960,7 @@
</span><span class="cx"> matching the indexType and indexKey parameters.
</span><span class="cx">
</span><span class="cx"> recordTypes is a list of record types to limit the search to.
</span><del>- indexType specifies one of the CachingDirectoryService contstants
</del><ins>+ indexType specifies one of the CachingDirectoryService constants
</ins><span class="cx"> identifying which attribute to search on.
</span><span class="cx"> indexKey is the value to search for.
</span><span class="cx">
</span><span class="lines">@@ -994,7 +989,8 @@
</span><span class="cx"> # Query on guid only works if guid attribute has been defined.
</span><span class="cx"> # Support for query on guid even if is auto-generated should
</span><span class="cx"> # be added.
</span><del>- if not guidAttr: return
</del><ins>+ if not guidAttr:
+ return
</ins><span class="cx"> filterstr = "(&%s(%s=%s))" % (filterstr, guidAttr, indexKey)
</span><span class="cx">
</span><span class="cx"> elif indexType == self.INDEX_TYPE_SHORTNAME:
</span><span class="lines">@@ -1157,7 +1153,6 @@
</span><span class="cx">
</span><span class="cx"> self.log.debug("LDAP search returned %d results, %d usable" % (len(results), typeCounts[recordType]))
</span><span class="cx">
</span><del>-
</del><span class="cx"> typeCountsStr = ", ".join(["%s:%d" % (rt, ct) for (rt, ct) in typeCounts.iteritems()])
</span><span class="cx"> totalTime = time.time() - startTime
</span><span class="cx"> self.log.info("Calendar user search for %s matched %d records (%s) in %.2f seconds" % (tokens, len(records), typeCountsStr, totalTime))
</span><span class="lines">@@ -1172,7 +1167,7 @@
</span><span class="cx"> """
</span><span class="cx"> records = []
</span><span class="cx">
</span><del>- self.log.debug("Peforming principal property search for %s" % (fields,))
</del><ins>+ self.log.debug("Performing principal property search for %s" % (fields,))
</ins><span class="cx">
</span><span class="cx"> if recordType is None:
</span><span class="cx"> # Make a copy since we're modifying it
</span><span class="lines">@@ -1324,6 +1319,7 @@
</span><span class="cx">
</span><span class="cx"> returnValue(recordsByAlias.values())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordTypeForDN(self, dnStr):
</span><span class="cx"> """
</span><span class="cx"> Examine a DN to determine which recordType it belongs to
</span><span class="lines">@@ -1339,6 +1335,7 @@
</span><span class="cx"> return None
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def dnContainedIn(child, parent):
</span><span class="cx"> """
</span><span class="cx"> Return True if child dn is contained within parent dn, otherwise False.
</span><span class="lines">@@ -1346,6 +1343,7 @@
</span><span class="cx"> return child[-len(parent):] == parent
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def normalizeDNstr(dnStr):
</span><span class="cx"> """
</span><span class="cx"> Convert to lowercase and remove extra whitespace
</span><span class="lines">@@ -1356,6 +1354,7 @@
</span><span class="cx"> return ' '.join(ldap.dn.dn2str(ldap.dn.str2dn(dnStr.lower())).split())
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _convertValue(value, matchType):
</span><span class="cx"> if matchType == "starts-with":
</span><span class="cx"> value = "%s*" % (ldapEsc(value),)
</span><span class="lines">@@ -1366,6 +1365,8 @@
</span><span class="cx"> value = ldapEsc(value)
</span><span class="cx"> return value
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def buildFilter(recordType, mapping, fields, operand="or", optimizeMultiName=False):
</span><span class="cx"> """
</span><span class="cx"> Create an LDAP filter string from a list of tuples representing directory
</span><span class="lines">@@ -1403,8 +1404,8 @@
</span><span class="cx"> # try the various firstName/lastName permutations:
</span><span class="cx"> if recordType == "users":
</span><span class="cx"> converted = []
</span><del>- for firstName, firstCaseless, firstMatchType in combined["firstName"]:
- for lastName, lastCaseless, lastMatchType in combined["lastName"]:
</del><ins>+ for firstName, _ignore_firstCaseless, firstMatchType in combined["firstName"]:
+ for lastName, _ignore_lastCaseless, lastMatchType in combined["lastName"]:
</ins><span class="cx"> if firstName != lastName:
</span><span class="cx"> firstValue = _convertValue(firstName, firstMatchType)
</span><span class="cx"> lastValue = _convertValue(lastName, lastMatchType)
</span><span class="lines">@@ -1427,7 +1428,7 @@
</span><span class="cx"> # name, guid)
</span><span class="cx"> additional = []
</span><span class="cx"> for key in ("recordName", "guid"):
</span><del>- if mapping.has_key(key):
</del><ins>+ if key in mapping:
</ins><span class="cx"> additional.append("(%s=*)" % (mapping.get(key),))
</span><span class="cx"> if additional:
</span><span class="cx"> filterstr = "(&%s%s)" % ("".join(additional), filterstr)
</span><span class="lines">@@ -1435,6 +1436,7 @@
</span><span class="cx"> return filterstr
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def buildFilterFromTokens(recordType, mapping, tokens, extra=None):
</span><span class="cx"> """
</span><span class="cx"> Create an LDAP filter string from a list of query tokens. Each token is
</span><span class="lines">@@ -1497,6 +1499,7 @@
</span><span class="cx"> return filterStr
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class LdapDirectoryRecord(CachingDirectoryRecord):
</span><span class="cx"> """
</span><span class="cx"> LDAP implementation of L{IDirectoryRecord}.
</span><span class="lines">@@ -1509,18 +1512,18 @@
</span><span class="cx"> attrs
</span><span class="cx"> ):
</span><span class="cx"> super(LdapDirectoryRecord, self).__init__(
</span><del>- service = service,
- recordType = recordType,
- guid = guid,
- shortNames = shortNames,
- authIDs = authIDs,
- fullName = fullName,
- firstName = firstName,
- lastName = lastName,
- emailAddresses = emailAddresses,
- extProxies = extProxies,
- extReadOnlyProxies = extReadOnlyProxies,
- uid = uid,
</del><ins>+ service=service,
+ recordType=recordType,
+ guid=guid,
+ shortNames=shortNames,
+ authIDs=authIDs,
+ fullName=fullName,
+ firstName=firstName,
+ lastName=lastName,
+ emailAddresses=emailAddresses,
+ extProxies=extProxies,
+ extReadOnlyProxies=extReadOnlyProxies,
+ uid=uid,
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # Save attributes of dn and attrs in case you might need them later
</span><span class="lines">@@ -1548,6 +1551,7 @@
</span><span class="cx"> self._members_storage = self._members()
</span><span class="cx"> return self._members_storage
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _members(self):
</span><span class="cx"> """ Fault in records for the members of this group """
</span><span class="cx">
</span><span class="lines">@@ -1578,7 +1582,7 @@
</span><span class="cx">
</span><span class="cx"> dn, attrs = result.pop()
</span><span class="cx"> dn = normalizeDNstr(dn)
</span><del>- self.log.debug("Retrieved: %s %s" % (dn,attrs))
</del><ins>+ self.log.debug("Retrieved: %s %s" % (dn, attrs))
</ins><span class="cx"> recordType = self.service.recordTypeForDN(dn)
</span><span class="cx"> if recordType is None:
</span><span class="cx"> self.log.error("Unable to map %s to a record type" % (dn,))
</span><span class="lines">@@ -1595,6 +1599,7 @@
</span><span class="cx">
</span><span class="cx"> return results
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def groups(self):
</span><span class="cx"> """ Return the records representing groups this record is a member of """
</span><span class="cx"> try:
</span><span class="lines">@@ -1603,6 +1608,7 @@
</span><span class="cx"> self._groups_storage = self._groups()
</span><span class="cx"> return self._groups_storage
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _groups(self):
</span><span class="cx"> """ Fault in the groups of which this record is a member """
</span><span class="cx">
</span><span class="lines">@@ -1618,7 +1624,7 @@
</span><span class="cx"> if len(membersAttrs) == 1:
</span><span class="cx"> filterstr = "(%s=%s)" % (membersAttrs[0], self._memberId)
</span><span class="cx"> else:
</span><del>- filterstr = "(|%s)" % ( "".join(
</del><ins>+ filterstr = "(|%s)" % ("".join(
</ins><span class="cx"> ["(%s=%s)" % (a, self._memberId) for a in membersAttrs]
</span><span class="cx"> ),
</span><span class="cx"> )
</span><span class="lines">@@ -1641,6 +1647,7 @@
</span><span class="cx">
</span><span class="cx"> return groups
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def cachedGroupsAlias(self):
</span><span class="cx"> """
</span><span class="cx"> See directory.py for full description
</span><span class="lines">@@ -1650,6 +1657,7 @@
</span><span class="cx"> """
</span><span class="cx"> return self._memberId
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def memberGUIDs(self):
</span><span class="cx"> return set(self._memberGUIDs)
</span><span class="cx">
</span><span class="lines">@@ -1717,10 +1725,13 @@
</span><span class="cx"> return super(LdapDirectoryRecord, self).verifyCredentials(credentials)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class MissingRecordNameException(Exception):
</span><span class="cx"> """ Raised when LDAP record is missing recordName """
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class MissingGuidException(Exception):
</span><span class="cx"> """ Raised when LDAP record is missing guidAttr and it's required """
</span><span class="cx"> pass
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryopendirectorybackerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/opendirectorybacker.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/opendirectorybacker.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/opendirectorybacker.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -35,9 +35,9 @@
</span><span class="cx"> from tempfile import mkstemp, gettempdir
</span><span class="cx"> from random import random
</span><span class="cx">
</span><del>-from pycalendar.n import N
-from pycalendar.adr import Adr
-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.vcard.n import N
+from pycalendar.vcard.adr import Adr
+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from socket import getfqdn
</span><span class="cx">
</span><span class="lines">@@ -1494,7 +1494,7 @@
</span><span class="cx">
</span><span class="cx"> birthdate = self.isoDateStringForDateAttribute(dsattributes.kDS1AttrBirthday)
</span><span class="cx"> if birthdate:
</span><del>- vcard.addProperty(Property("BDAY", PyCalendarDateTime.parseText(birthdate, fullISO=True)))
</del><ins>+ vcard.addProperty(Property("BDAY", DateTime.parseText(birthdate, fullISO=True)))
</ins><span class="cx">
</span><span class="cx"> # 3.2 Delivery Addressing Types http://tools.ietf.org/html/rfc2426#section-3.2
</span><span class="cx"> #
</span><span class="lines">@@ -1685,7 +1685,7 @@
</span><span class="cx"> # 3.6.4 REV Type Definition
</span><span class="cx"> revDate = self.isoDateStringForDateAttribute(dsattributes.kDS1AttrModificationTimestamp)
</span><span class="cx"> if revDate:
</span><del>- vcard.addProperty(Property("REV", PyCalendarDateTime.parseText(revDate, fullISO=True)))
</del><ins>+ vcard.addProperty(Property("REV", DateTime.parseText(revDate, fullISO=True)))
</ins><span class="cx">
</span><span class="cx"> """
</span><span class="cx"> # UNIMPLEMENTED:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryprincipalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/principal.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/principal.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/principal.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -601,7 +601,6 @@
</span><span class="cx"> record = self.resource.record
</span><span class="cx"> return tag.fillSlots(
</span><span class="cx"> hostedAt=str(record.serverURI()),
</span><del>- partition=str(record.effectivePartitionID()),
</del><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1066,14 +1065,6 @@
</span><span class="cx"> return self.record.server()
</span><span class="cx">
</span><span class="cx">
</span><del>- def partitionURI(self):
- return self.record.partitionURI()
-
-
- def locallyHosted(self):
- return self.record.locallyHosted()
-
-
</del><span class="cx"> def thisServer(self):
</span><span class="cx"> return self.record.thisServer()
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/resource.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/resource.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/resource.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -25,14 +25,13 @@
</span><span class="cx"> __all__ = ["DirectoryReverseProxyResource"]
</span><span class="cx">
</span><span class="cx"> class DirectoryReverseProxyResource(ReverseProxyResource):
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self, parent, record):
</span><span class="cx"> self.parent = parent
</span><span class="cx"> self.record = record
</span><del>-
- super(DirectoryReverseProxyResource, self).__init__(self.record.partitionID)
-
- def url(self):
- return joinURL(self.parent.url(), self.record.uid)
</del><span class="cx">
</span><ins>+ super(DirectoryReverseProxyResource, self).__init__(self.record.serverID)
</ins><span class="cx">
</span><ins>+
+ def url(self):
+ return joinURL(self.parent.url(), self.record.uid)
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestaugmentstestdefaultxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test-default.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test-default.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test-default.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -24,40 +24,34 @@
</span><span class="cx"> <enable>true</enable>
</span><span class="cx"> <enable-calendar>true</enable-calendar>
</span><span class="cx"> <enable-addressbook>true</enable-addressbook>
</span><del>- <partition-id>00001</partition-id>
</del><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>Location-Default</uid>
</span><span class="cx"> <enable>true</enable>
</span><span class="cx"> <enable-calendar>true</enable-calendar>
</span><del>- <partition-id>00004</partition-id>
</del><span class="cx"> <auto-schedule>true</auto-schedule>
</span><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>Location-AA*</uid>
</span><span class="cx"> <enable>true</enable>
</span><span class="cx"> <enable-calendar>true</enable-calendar>
</span><del>- <partition-id>00005</partition-id>
</del><span class="cx"> <auto-schedule>true</auto-schedule>
</span><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>Resource-Default</uid>
</span><span class="cx"> <enable>true</enable>
</span><span class="cx"> <enable-calendar>true</enable-calendar>
</span><del>- <partition-id>00006</partition-id>
</del><span class="cx"> <auto-schedule>true</auto-schedule>
</span><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>Resource-AA*</uid>
</span><span class="cx"> <enable>true</enable>
</span><span class="cx"> <enable-calendar>true</enable-calendar>
</span><del>- <partition-id>00007</partition-id>
</del><span class="cx"> <auto-schedule>true</auto-schedule>
</span><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>AA*</uid>
</span><span class="cx"> <enable>true</enable>
</span><del>- <partition-id>00001</partition-id>
</del><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>AB*</uid>
</span><span class="lines">@@ -66,14 +60,12 @@
</span><span class="cx"> <record>
</span><span class="cx"> <uid>B*</uid>
</span><span class="cx"> <enable>true</enable>
</span><del>- <partition-id>00002</partition-id>
</del><span class="cx"> <enable-calendar>true</enable-calendar>
</span><span class="cx"> <enable-addressbook>true</enable-addressbook>
</span><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>C*</uid>
</span><span class="cx"> <enable>true</enable>
</span><del>- <partition-id>00003</partition-id>
</del><span class="cx"> <enable-calendar>true</enable-calendar>
</span><span class="cx"> <enable-addressbook>true</enable-addressbook>
</span><span class="cx"> <auto-schedule>true</auto-schedule>
</span><span class="lines">@@ -102,17 +94,14 @@
</span><span class="cx"> <record>
</span><span class="cx"> <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
</span><span class="cx"> <enable>true</enable>
</span><del>- <partition-id>00001</partition-id>
</del><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
</span><span class="cx"> <enable>true</enable>
</span><del>- <partition-id>00002</partition-id>
</del><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>6A73326A-F781-47E7-A9F8-AF47364D4152</uid>
</span><span class="cx"> <enable>true</enable>
</span><del>- <partition-id>00002</partition-id>
</del><span class="cx"> <enable-calendar>true</enable-calendar>
</span><span class="cx"> <enable-addressbook>true</enable-addressbook>
</span><span class="cx"> <auto-schedule>true</auto-schedule>
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestaugmentstestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/augments-test.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -42,12 +42,10 @@
</span><span class="cx"> <record>
</span><span class="cx"> <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
</span><span class="cx"> <enable>true</enable>
</span><del>- <partition-id>00001</partition-id>
</del><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
</span><span class="cx"> <enable>true</enable>
</span><del>- <partition-id>00002</partition-id>
</del><span class="cx"> </record>
</span><span class="cx"> <record>
</span><span class="cx"> <uid>6A73326A-F781-47E7-A9F8-AF47364D4152</uid>
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestresourcescaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/resources/caldavd.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/resources/caldavd.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/resources/caldavd.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -175,7 +175,6 @@
</span><span class="cx"> Augments for the directory service records to add calendar specific attributes.
</span><span class="cx">
</span><span class="cx"> A variety of augment services are available for use.
</span><del>- When using a partitioned server, a service that can be accessed from each host will be needed.
</del><span class="cx"> -->
</span><span class="cx">
</span><span class="cx"> <!-- XML File Augment Service -->
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestsudoersplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,28 +0,0 @@
</span><del>-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
- <dict>
- <key>users</key>
- <array>
- <dict>
- <key>authorize-as</key>
- <dict>
- <key>allow</key>
- <true />
- <key>principals</key>
- <array>
- <string>all</string>
- </array>
- </dict>
- <key>authorize-from</key>
- <array>
- <string>127.0.0.1</string>
- </array>
- <key>password</key>
- <string>alice</string>
- <key>username</key>
- <string>alice</string>
- </dict>
- </array>
- </dict>
-</plist>
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestsudoers2plist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers2.plist (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers2.plist        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/sudoers2.plist        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,47 +0,0 @@
</span><del>-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
- <dict>
- <key>users</key>
- <array>
- <dict>
- <key>authorize-as</key>
- <dict>
- <key>allow</key>
- <true />
- <key>principals</key>
- <array>
- <string>all</string>
- </array>
- </dict>
- <key>authorize-from</key>
- <array>
- <string>127.0.0.1</string>
- </array>
- <key>password</key>
- <string>alice</string>
- <key>username</key>
- <string>alice</string>
- </dict>
- <dict>
- <key>authorize-as</key>
- <dict>
- <key>allow</key>
- <true />
- <key>principals</key>
- <array>
- <string>all</string>
- </array>
- </dict>
- <key>authorize-from</key>
- <array>
- <string>127.0.0.1</string>
- </array>
- <key>password</key>
- <string>bob</string>
- <key>username</key>
- <string>bob</string>
- </dict>
- </array>
- </dict>
-</plist>
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_aggregatepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_aggregate.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_aggregate.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_aggregate.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -36,7 +36,8 @@
</span><span class="cx"> recordTypes.add(prefix + recordType)
</span><span class="cx"> return recordTypes
</span><span class="cx">
</span><del>- def _records(key):
</del><ins>+
+ def _records(key): #@NoSelf
</ins><span class="cx"> def get(self):
</span><span class="cx"> records = {}
</span><span class="cx"> for prefix, testClass in testServices:
</span><span class="lines">@@ -58,6 +59,7 @@
</span><span class="cx">
</span><span class="cx"> recordTypePrefixes = tuple(s[0] for s in testServices)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def service(self):
</span><span class="cx"> """
</span><span class="cx"> Returns an IDirectoryService.
</span><span class="lines">@@ -71,9 +73,9 @@
</span><span class="cx"> )
</span><span class="cx"> xmlService.recordTypePrefix = xml_prefix
</span><span class="cx">
</span><del>-
</del><span class="cx"> return AggregateDirectoryService((xmlService,), None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_setRealm(self):
</span><span class="cx"> """
</span><span class="cx"> setRealm gets propagated to nested services
</span><span class="lines">@@ -82,4 +84,3 @@
</span><span class="cx"> aggregatedService.setRealm("foo.example.com")
</span><span class="cx"> for service in aggregatedService._recordTypes.values():
</span><span class="cx"> self.assertEquals("foo.example.com", service.realmName)
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_augmentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_augment.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_augment.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_augment.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -15,7 +15,7 @@
</span><span class="cx"> ##
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.test.util import TestCase
</span><del>-from twistedcaldav.directory.augment import AugmentXMLDB, AugmentSqliteDB,\
</del><ins>+from twistedcaldav.directory.augment import AugmentXMLDB, AugmentSqliteDB, \
</ins><span class="cx"> AugmentPostgreSQLDB, AugmentRecord
</span><span class="cx"> from twistedcaldav.directory.directory import DirectoryService
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="lines">@@ -32,46 +32,46 @@
</span><span class="cx"> xmlFileNormalization = os.path.join(os.path.dirname(__file__), "augments-normalization.xml")
</span><span class="cx">
</span><span class="cx"> testRecords = (
</span><del>- {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "enabled":True, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"6423F94A-6B76-4A3A-815B-D52CFD77935D", "enabled":True, "partitionID":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"5A985493-EE2C-4665-94CF-4DFEA3A89500", "enabled":False, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "enabled":True, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "enabled":True, "partitionID":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"543D28BA-F74F-4D5F-9243-B3E3A61171E5", "enabled":True, "partitionID":"00002", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"6A73326A-F781-47E7-A9F8-AF47364D4152", "enabled":True, "partitionID":"00002", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True, "autoScheduleMode":"default"},
- {"uid":"C5BAADEE-6B35-4FD5-A98A-5DF6BBAAC47A", "enabled":True, "partitionID":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True, "autoScheduleMode":"default"},
- {"uid":"8AB34DF9-0297-4BA3-AADB-DB557DDD21E7", "enabled":True, "partitionID":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True, "autoScheduleMode":"accept-always"},
- {"uid":"FC674703-8008-4A77-B80E-0DB55A9CE620", "enabledForLogin":False,}, # Explicitly false
- {"uid":"B473DC32-1B0D-45EE-9BAC-DA878AE9CE74", "enabledForLogin":True,}, # Explicitly True
- {"uid":"9F2B176D-B3F5-483A-AA63-0A1FC6E6D54B", "enabledForLogin":True,}, # Default is True
</del><ins>+ {"uid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "6423F94A-6B76-4A3A-815B-D52CFD77935D", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "5A985493-EE2C-4665-94CF-4DFEA3A89500", "enabled": False, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "543D28BA-F74F-4D5F-9243-B3E3A61171E5", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "6A73326A-F781-47E7-A9F8-AF47364D4152", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": True, "autoScheduleMode": "default"},
+ {"uid": "C5BAADEE-6B35-4FD5-A98A-5DF6BBAAC47A", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": True, "autoScheduleMode": "default"},
+ {"uid": "8AB34DF9-0297-4BA3-AADB-DB557DDD21E7", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": True, "autoScheduleMode": "accept-always"},
+ {"uid": "FC674703-8008-4A77-B80E-0DB55A9CE620", "enabledForLogin": False, }, # Explicitly false
+ {"uid": "B473DC32-1B0D-45EE-9BAC-DA878AE9CE74", "enabledForLogin": True, }, # Explicitly True
+ {"uid": "9F2B176D-B3F5-483A-AA63-0A1FC6E6D54B", "enabledForLogin": True, }, # Default is True
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> testRecordWildcardDefault = (
</span><del>- {"uid":"A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True, "partitionID":"00001", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"AA5F935F-3358-4510-A649-B391D63279F2", "enabled":True, "partitionID":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"ABF1A83B-1A29-4E04-BDC3-A6A66ECF27CA", "enabled":False, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"BC22A734-5E41-4FB7-B5C1-51DC0656DC2F", "enabled":True, "partitionID":"00002", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"C6DEEBB1-E14A-47F2-98BA-7E3BB4353E3A", "enabled":True, "partitionID":"00003", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True, "autoScheduleMode":"accept-always"},
- {"uid":"AA859321-2C72-4974-ADCF-0CBA0C76F95D", "enabled":True, "partitionID":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"AB7C488B-9ED2-4265-881C-7E2E38A63584", "enabled":False, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"BB0C0DA1-0545-45F6-8D08-917C554D93A4", "enabled":True, "partitionID":"00002", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False, "autoScheduleMode":"default"},
- {"uid":"CCD30AD3-582F-4682-8B65-2EDE92C5656E", "enabled":True, "partitionID":"00003", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True, "autoScheduleMode":"accept-always"},
</del><ins>+ {"uid": "A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "AA5F935F-3358-4510-A649-B391D63279F2", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "ABF1A83B-1A29-4E04-BDC3-A6A66ECF27CA", "enabled": False, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "BC22A734-5E41-4FB7-B5C1-51DC0656DC2F", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "C6DEEBB1-E14A-47F2-98BA-7E3BB4353E3A", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": True, "autoScheduleMode": "accept-always"},
+ {"uid": "AA859321-2C72-4974-ADCF-0CBA0C76F95D", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "AB7C488B-9ED2-4265-881C-7E2E38A63584", "enabled": False, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "BB0C0DA1-0545-45F6-8D08-917C554D93A4", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": False, "autoScheduleMode": "default"},
+ {"uid": "CCD30AD3-582F-4682-8B65-2EDE92C5656E", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": True, "autoScheduleMode": "accept-always"},
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> testRecordTypeDefault = (
</span><del>- ("locations", {"uid":"A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True, "partitionID":"00004", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True, "autoScheduleMode":"default"}),
- ("locations", {"uid":"AA5F935F-3358-4510-A649-B391D63279F2", "enabled":True, "partitionID":"00005", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True, "autoScheduleMode":"default"}),
- ("resources", {"uid":"A5318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True, "partitionID":"00006", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True, "autoScheduleMode":"default"}),
- ("resources", {"uid":"AA6F935F-3358-4510-A649-B391D63279F2", "enabled":True, "partitionID":"00007", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True, "autoScheduleMode":"default"}),
</del><ins>+ ("locations", {"uid": "A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": False, "autoSchedule": True, "autoScheduleMode": "default"}),
+ ("locations", {"uid": "AA5F935F-3358-4510-A649-B391D63279F2", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": False, "autoSchedule": True, "autoScheduleMode": "default"}),
+ ("resources", {"uid": "A5318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": False, "autoSchedule": True, "autoScheduleMode": "default"}),
+ ("resources", {"uid": "AA6F935F-3358-4510-A649-B391D63279F2", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": False, "autoSchedule": True, "autoScheduleMode": "default"}),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> testAddRecords = (
</span><del>- {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled":True, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False, "autoScheduleMode":"default"},
</del><ins>+ {"uid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled": True, "enabledForCalendaring": False, "enabledForAddressBooks": False, "autoSchedule": False, "autoScheduleMode": "default"},
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> testModifyRecords = (
</span><del>- {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled":True, "partitionID":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False, "autoScheduleMode":"default"},
</del><ins>+ {"uid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled": True, "enabledForCalendaring": True, "enabledForAddressBooks": True, "autoSchedule": False, "autoScheduleMode": "default"},
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -79,27 +79,31 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _checkRecord(self, db, items, recordType=DirectoryService.recordType_users):
</span><del>-
</del><ins>+
</ins><span class="cx"> record = (yield db.getAugmentRecord(items["uid"], recordType))
</span><span class="cx"> self.assertTrue(record is not None, "Failed record uid: %s" % (items["uid"],))
</span><del>-
- for k,v in items.iteritems():
- self.assertEqual(getattr(record, k), v, "Failed record uid: %s, attribute: %s" % (items["uid"], k, ))
</del><span class="cx">
</span><ins>+ for k, v in items.iteritems():
+ self.assertEqual(getattr(record, k), v, "Failed record uid: %s, attribute: %s" % (items["uid"], k,))
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _checkRecordExists(self, db, uid, recordType=DirectoryService.recordType_users):
</span><del>-
</del><ins>+
</ins><span class="cx"> record = (yield db.getAugmentRecord(uid, recordType))
</span><span class="cx"> self.assertTrue(record is not None, "Failed record uid: %s" % (uid,))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AugmentTestsMixin(object):
</span><span class="cx">
</span><span class="cx"> def _db(self, dbpath=None):
</span><span class="cx"> raise NotImplementedError
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_read(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> dbpath = os.path.abspath(self.mktemp())
</span><span class="cx"> db = self._db(dbpath)
</span><span class="cx">
</span><span class="lines">@@ -113,9 +117,10 @@
</span><span class="cx"> # in the DB
</span><span class="cx"> yield self._checkRecordExists(db, "D11F03A0-97EA-48AF-9A6C-FAC7F3975767")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_read_default(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> dbpath = os.path.abspath(self.mktemp())
</span><span class="cx"> db = self._db(dbpath)
</span><span class="cx">
</span><span class="lines">@@ -132,6 +137,7 @@
</span><span class="cx"> for item in testRecordWildcardDefault:
</span><span class="cx"> yield self._checkRecord(db, item)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_read_typed_default(self):
</span><span class="cx"> """
</span><span class="lines">@@ -159,7 +165,7 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_add_modify(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> dbpath = os.path.abspath(self.mktemp())
</span><span class="cx"> db = self._db(dbpath)
</span><span class="cx">
</span><span class="lines">@@ -195,11 +201,13 @@
</span><span class="cx"> yield self._checkRecord(newdb, item)
</span><span class="cx"> yield self._checkRecord(newdb, testModifyRecords[0])
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AugmentXMLTests(AugmentTests):
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_read(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> db = AugmentXMLDB((xmlFile,))
</span><span class="cx">
</span><span class="cx"> for item in testRecords:
</span><span class="lines">@@ -209,9 +217,10 @@
</span><span class="cx"> # in the DB
</span><span class="cx"> yield self._checkRecordExists(db, "D11F03A0-97EA-48AF-9A6C-FAC7F3975767")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_read_default(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> db = AugmentXMLDB((xmlFileDefault,))
</span><span class="cx">
</span><span class="cx"> for item in testRecords:
</span><span class="lines">@@ -220,8 +229,9 @@
</span><span class="cx"> for item in testRecordWildcardDefault:
</span><span class="cx"> yield self._checkRecord(db, item)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_parseErrors(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> db = {}
</span><span class="cx"> self.assertRaises(RuntimeError, XMLAugmentsParser, cStringIO.StringIO(""), db)
</span><span class="cx"> self.assertRaises(RuntimeError, XMLAugmentsParser, cStringIO.StringIO("""<?xml version="1.0" encoding="utf-8"?>
</span><span class="lines">@@ -249,13 +259,14 @@
</span><span class="cx"> </record>
</span><span class="cx"> """), db)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_add_modify(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Duplicate file as we will change it
</span><span class="cx"> newxmlfile = FilePath(self.mktemp())
</span><span class="cx"> FilePath(xmlFile).copyTo(newxmlfile)
</span><del>-
</del><ins>+
</ins><span class="cx"> db = AugmentXMLDB((newxmlfile.path,))
</span><span class="cx">
</span><span class="cx"> for item in testRecords:
</span><span class="lines">@@ -283,6 +294,7 @@
</span><span class="cx"> yield self._checkRecord(newdb, item)
</span><span class="cx"> yield self._checkRecord(newdb, testModifyRecords[0])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_shouldReparse(self):
</span><span class="cx"> """
</span><span class="cx"> Verify that a change to the file will get noticed
</span><span class="lines">@@ -294,6 +306,7 @@
</span><span class="cx"> newxmlfile.setContent("") # Change the file
</span><span class="cx"> self.assertTrue(db._shouldReparse([newxmlfile.path])) # Need to parse
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_refresh(self):
</span><span class="cx"> """
</span><span class="cx"> Ensure that a refresh without any file changes doesn't zero out the
</span><span class="lines">@@ -304,6 +317,7 @@
</span><span class="cx"> dbxml.refresh()
</span><span class="cx"> self.assertEquals(keys, dbxml.db.keys())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def uidsFromFile(self, filename):
</span><span class="cx"> """
</span><span class="cx"> Return all uids from the augments xml file
</span><span class="lines">@@ -316,6 +330,7 @@
</span><span class="cx"> uid = record_node.find(xmlaugmentsparser.ELEMENT_UID).text
</span><span class="cx"> yield uid
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_normalize(self):
</span><span class="cx"> """
</span><span class="cx"> Ensure augment uids are normalized upon opening
</span><span class="lines">@@ -328,11 +343,15 @@
</span><span class="cx"> uids = list(self.uidsFromFile(newxmlfile.path))
</span><span class="cx"> self.assertEquals(uids, ['AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA'])
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AugmentSqliteTests(AugmentTests, AugmentTestsMixin):
</span><span class="cx">
</span><span class="cx"> def _db(self, dbpath=None):
</span><span class="cx"> return AugmentSqliteDB(dbpath if dbpath else os.path.abspath(self.mktemp()))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AugmentPostgreSQLTests(AugmentTests, AugmentTestsMixin):
</span><span class="cx">
</span><span class="cx"> def _db(self, dbpath=None):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_cachedirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_cachedirectory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_cachedirectory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_cachedirectory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -38,6 +38,7 @@
</span><span class="cx"> DirectoryService.recordType_resources,
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def queryDirectory(self, recordTypes, indexType, indexKey):
</span><span class="cx">
</span><span class="cx"> self.queried = True
</span><span class="lines">@@ -58,42 +59,47 @@
</span><span class="cx">
</span><span class="cx"> if cacheIt:
</span><span class="cx"> cacheRecord = CachingDirectoryRecord(
</span><del>- service = self,
- recordType = recordType,
- guid = record.get("guid"),
- shortNames = record.get("shortname"),
- authIDs = record.get("authid"),
- fullName = record.get("fullName"),
- firstName = "",
- lastName = "",
- emailAddresses = record.get("email"),
- )
-
</del><ins>+ service=self,
+ recordType=recordType,
+ guid=record.get("guid"),
+ shortNames=record.get("shortname"),
+ authIDs=record.get("authid"),
+ fullName=record.get("fullName"),
+ firstName="",
+ lastName="",
+ emailAddresses=record.get("email"),
+ )
+
</ins><span class="cx"> augmentRecord = AugmentRecord(
</span><del>- uid = cacheRecord.guid,
</del><ins>+ uid=cacheRecord.guid,
</ins><span class="cx"> enabled=True,
</span><del>- enabledForCalendaring = True,
</del><ins>+ enabledForCalendaring=True,
</ins><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> cacheRecord.addAugmentInformation(augmentRecord)
</span><span class="cx">
</span><span class="cx"> self.recordCacheForType(recordType).addRecord(cacheRecord,
</span><span class="cx"> indexType, indexKey)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class CachingDirectoryTest(TestCase):
</span><del>-
</del><ins>+
</ins><span class="cx"> baseGUID = str(uuid4())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def setUp(self):
</span><span class="cx"> super(CachingDirectoryTest, self).setUp()
</span><span class="cx"> self.service = TestDirectoryService()
</span><span class="cx"> self.service.queried = False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def loadRecords(self, records):
</span><span class="cx"> self.service._initCaches()
</span><span class="cx"> self.service.fakerecords = records
</span><span class="cx"> self.service.queried = False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def fakeRecord(
</span><span class="cx"> self,
</span><span class="cx"> fullName,
</span><span class="lines">@@ -109,15 +115,15 @@
</span><span class="cx"> shortNames = (self.shortNameForFullName(fullName),)
</span><span class="cx"> if multinames:
</span><span class="cx"> shortNames += (fullName,)
</span><del>-
</del><ins>+
</ins><span class="cx"> if guid is None:
</span><span class="cx"> guid = self.guidForShortName(shortNames[0], recordType=recordType)
</span><span class="cx"> else:
</span><span class="cx"> guid = guid.lower()
</span><del>-
</del><ins>+
</ins><span class="cx"> if emails is None:
</span><span class="cx"> emails = ("%s@example.com" % (shortNames[0],),)
</span><del>-
</del><ins>+
</ins><span class="cx"> attrs = {
</span><span class="cx"> "fullName": fullName,
</span><span class="cx"> "guid": guid,
</span><span class="lines">@@ -126,35 +132,38 @@
</span><span class="cx"> "cua": tuple(["mailto:%s" % email for email in emails]),
</span><span class="cx"> "authid": tuple(["Kerberos:%s" % email for email in emails])
</span><span class="cx"> }
</span><del>-
</del><ins>+
</ins><span class="cx"> if members:
</span><span class="cx"> attrs["members"] = members
</span><del>-
</del><ins>+
</ins><span class="cx"> if resourceInfo:
</span><span class="cx"> attrs["resourceInfo"] = resourceInfo
</span><del>-
</del><ins>+
</ins><span class="cx"> return attrs
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def shortNameForFullName(self, fullName):
</span><span class="cx"> return fullName.lower().replace(" ", "")
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def guidForShortName(self, shortName, recordType=""):
</span><span class="cx"> return uuidFromName(self.baseGUID, "%s%s" % (recordType, shortName))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def dummyRecords(self):
</span><span class="cx"> SIZE = 10
</span><span class="cx"> records = {
</span><span class="cx"> DirectoryService.recordType_users: [
</span><del>- self.fakeRecord("User %02d" % x, DirectoryService.recordType_users, multinames=(x>5)) for x in range(1,SIZE+1)
</del><ins>+ self.fakeRecord("User %02d" % x, DirectoryService.recordType_users, multinames=(x > 5)) for x in range(1, SIZE + 1)
</ins><span class="cx"> ],
</span><span class="cx"> DirectoryService.recordType_groups: [
</span><del>- self.fakeRecord("Group %02d" % x, DirectoryService.recordType_groups) for x in range(1,SIZE+1)
</del><ins>+ self.fakeRecord("Group %02d" % x, DirectoryService.recordType_groups) for x in range(1, SIZE + 1)
</ins><span class="cx"> ],
</span><span class="cx"> DirectoryService.recordType_resources: [
</span><del>- self.fakeRecord("Resource %02d" % x, DirectoryService.recordType_resources) for x in range(1,SIZE+1)
</del><ins>+ self.fakeRecord("Resource %02d" % x, DirectoryService.recordType_resources) for x in range(1, SIZE + 1)
</ins><span class="cx"> ],
</span><span class="cx"> DirectoryService.recordType_locations: [
</span><del>- self.fakeRecord("Location %02d" % x, DirectoryService.recordType_locations) for x in range(1,SIZE+1)
</del><ins>+ self.fakeRecord("Location %02d" % x, DirectoryService.recordType_locations) for x in range(1, SIZE + 1)
</ins><span class="cx"> ],
</span><span class="cx"> }
</span><span class="cx"> # Add duplicate shortnames
</span><span class="lines">@@ -165,14 +174,17 @@
</span><span class="cx">
</span><span class="cx"> self.loadRecords(records)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def verifyRecords(self, recordType, expectedGUIDs):
</span><del>-
</del><ins>+
</ins><span class="cx"> records = self.service.listRecords(recordType)
</span><span class="cx"> recordGUIDs = set([record.guid for record in records])
</span><span class="cx"> self.assertEqual(recordGUIDs, expectedGUIDs)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class GUIDLookups(CachingDirectoryTest):
</span><del>-
</del><ins>+
</ins><span class="cx"> def test_emptylist(self):
</span><span class="cx"> self.dummyRecords()
</span><span class="cx">
</span><span class="lines">@@ -180,7 +192,8 @@
</span><span class="cx"> self.verifyRecords(DirectoryService.recordType_groups, set())
</span><span class="cx"> self.verifyRecords(DirectoryService.recordType_resources, set())
</span><span class="cx"> self.verifyRecords(DirectoryService.recordType_locations, set())
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def test_cacheoneguid(self):
</span><span class="cx"> self.dummyRecords()
</span><span class="cx">
</span><span class="lines">@@ -200,7 +213,8 @@
</span><span class="cx">
</span><span class="cx"> # Make sure guid is case-insensitive
</span><span class="cx"> self.assertTrue(self.service.recordWithGUID(self.guidForShortName("user01", recordType=DirectoryService.recordType_users).lower()) is not None)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def test_cacheoneshortname(self):
</span><span class="cx"> self.dummyRecords()
</span><span class="cx">
</span><span class="lines">@@ -224,6 +238,7 @@
</span><span class="cx"> ) is not None)
</span><span class="cx"> self.assertFalse(self.service.queried)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_cacheoneemail(self):
</span><span class="cx"> self.dummyRecords()
</span><span class="cx">
</span><span class="lines">@@ -245,6 +260,7 @@
</span><span class="cx"> ) is not None)
</span><span class="cx"> self.assertFalse(self.service.queried)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_cacheonePrincipalsURLWithUIDS(self):
</span><span class="cx"> self.dummyRecords()
</span><span class="cx">
</span><span class="lines">@@ -267,6 +283,7 @@
</span><span class="cx"> ) is not None)
</span><span class="cx"> self.assertFalse(self.service.queried)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_cacheonePrincipalsURLWithUsers(self):
</span><span class="cx"> self.dummyRecords()
</span><span class="cx">
</span><span class="lines">@@ -288,6 +305,7 @@
</span><span class="cx"> ) is not None)
</span><span class="cx"> self.assertFalse(self.service.queried)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_cacheoneauthid(self):
</span><span class="cx"> self.dummyRecords()
</span><span class="cx">
</span><span class="lines">@@ -309,6 +327,7 @@
</span><span class="cx"> ) is not None)
</span><span class="cx"> self.assertFalse(self.service.queried)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_negativeCaching(self):
</span><span class="cx"> self.dummyRecords()
</span><span class="cx">
</span><span class="lines">@@ -324,7 +343,6 @@
</span><span class="cx"> self.assertEquals(self.service.recordWithGUID(self.guidForShortName("missing")), None)
</span><span class="cx"> self.assertTrue(self.service.queried)
</span><span class="cx">
</span><del>-
</del><span class="cx"> # However, if negativeCaching is on, a miss is recorded as such,
</span><span class="cx"> # preventing a similar queryDirectory( ) until cacheTimeout passes
</span><span class="cx"> self.service.negativeCaching = True
</span><span class="lines">@@ -345,6 +363,7 @@
</span><span class="cx"> self.assertEquals(self.service.recordWithGUID(self.guidForShortName("missing")), None)
</span><span class="cx"> self.assertTrue(self.service.queried)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_duplicateShortNames(self):
</span><span class="cx"> """
</span><span class="cx"> Verify that when looking up records having duplicate short-names, the record of the
</span><span class="lines">@@ -370,6 +389,7 @@
</span><span class="cx"> "Duplicate")
</span><span class="cx"> self.assertEquals(record.recordType, DirectoryService.recordType_locations)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_generateMemcacheKey(self):
</span><span class="cx"> """
</span><span class="cx"> Verify keys are correctly generated based on the index type -- if index type is
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_digestpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_digest.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_digest.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_digest.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -55,7 +55,7 @@
</span><span class="cx"> challengeResponse = ('digest',
</span><span class="cx"> {'nonce': challengeNonce,
</span><span class="cx"> 'qop': 'auth', 'realm': 'test realm',
</span><del>- 'algorithm': 'md5',})
</del><ins>+ 'algorithm': 'md5', })
</ins><span class="cx">
</span><span class="cx"> cnonce = "29fc54aa1641c6fa0e151419361c8f23"
</span><span class="cx">
</span><span class="lines">@@ -116,6 +116,7 @@
</span><span class="cx"> self.namespace2
</span><span class="cx"> ))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getDigestResponse(self, challenge, ncount):
</span><span class="cx"> """
</span><span class="cx"> Calculate the response for the given challenge
</span><span class="lines">@@ -146,6 +147,7 @@
</span><span class="cx"> )
</span><span class="cx"> return expected
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getDigestResponseComma(self, challenge, ncount):
</span><span class="cx"> """
</span><span class="cx"> Calculate the response for the given challenge
</span><span class="lines">@@ -176,6 +178,7 @@
</span><span class="cx"> )
</span><span class="cx"> return expected
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def assertRaisesDeferred(self, exception, f, *args, **kwargs):
</span><span class="cx"> try:
</span><span class="lines">@@ -191,6 +194,7 @@
</span><span class="cx"> raise self.failureException('%s not raised (%r returned)'
</span><span class="cx"> % (exception.__name__, result))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_getChallenge(self):
</span><span class="cx"> """
</span><span class="lines">@@ -203,14 +207,15 @@
</span><span class="cx"> self.assertEquals(challenge['qop'], 'auth')
</span><span class="cx"> self.assertEquals(challenge['realm'], 'test realm')
</span><span class="cx"> self.assertEquals(challenge['algorithm'], 'md5')
</span><del>- self.assertTrue(challenge.has_key("nonce"))
</del><ins>+ self.assertTrue("nonce" in challenge)
</ins><span class="cx">
</span><span class="cx"> challenge = (yield self.credentialFactories[1].getChallenge(clientAddress))
</span><del>- self.assertFalse(challenge.has_key('qop'))
</del><ins>+ self.assertFalse('qop' in challenge)
</ins><span class="cx"> self.assertEquals(challenge['realm'], 'test realm')
</span><span class="cx"> self.assertEquals(challenge['algorithm'], 'md5')
</span><del>- self.assertTrue(challenge.has_key("nonce"))
</del><ins>+ self.assertTrue("nonce" in challenge)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_response(self):
</span><span class="cx"> """
</span><span class="lines">@@ -219,15 +224,16 @@
</span><span class="cx">
</span><span class="cx"> for ctr, factory in enumerate(self.credentialFactories):
</span><span class="cx"> challenge = (yield factory.getChallenge(clientAddress))
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse = authRequest1[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponse(challenge, "00000001"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> creds = (yield factory.decode(clientResponse, _trivial_GET()))
</span><span class="cx"> self.failUnless(creds.checkPassword('password'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_multiResponse(self):
</span><span class="cx"> """
</span><span class="lines">@@ -237,23 +243,24 @@
</span><span class="cx">
</span><span class="cx"> for ctr, factory in enumerate(self.credentialFactories):
</span><span class="cx"> challenge = (yield factory.getChallenge(clientAddress))
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse = authRequest1[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponse(challenge, "00000001"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> creds = (yield factory.decode(clientResponse, _trivial_GET()))
</span><span class="cx"> self.failUnless(creds.checkPassword('password'))
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse = authRequest2[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponse(challenge, "00000002"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> creds = (yield factory.decode(clientResponse, _trivial_GET()))
</span><span class="cx"> self.failUnless(creds.checkPassword('password'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_failsWithDifferentMethod(self):
</span><span class="cx"> """
</span><span class="lines">@@ -263,16 +270,17 @@
</span><span class="cx">
</span><span class="cx"> for ctr, factory in enumerate(self.credentialFactories):
</span><span class="cx"> challenge = (yield factory.getChallenge(clientAddress))
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse = authRequest1[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponse(challenge, "00000001"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> creds = (yield factory.decode(clientResponse,
</span><span class="cx"> SimpleRequest(None, 'POST', '/')))
</span><span class="cx"> self.failIf(creds.checkPassword('password'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_noUsername(self):
</span><span class="cx"> """
</span><span class="lines">@@ -287,7 +295,7 @@
</span><span class="cx"> namelessAuthRequest,
</span><span class="cx"> _trivial_GET()))
</span><span class="cx"> self.assertEquals(str(e), "Invalid response, no username given.")
</span><del>-
</del><ins>+
</ins><span class="cx"> # Check for an empty username
</span><span class="cx"> e = (yield self.assertRaisesDeferred(error.LoginFailed,
</span><span class="cx"> factory.decode,
</span><span class="lines">@@ -295,6 +303,7 @@
</span><span class="cx"> _trivial_GET()))
</span><span class="cx"> self.assertEquals(str(e), "Invalid response, no username given.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_noNonce(self):
</span><span class="cx"> """
</span><span class="lines">@@ -308,6 +317,7 @@
</span><span class="cx"> _trivial_GET()))
</span><span class="cx"> self.assertEquals(str(e), "Invalid response, no nonce given.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_emptyAttribute(self):
</span><span class="cx"> """
</span><span class="lines">@@ -323,6 +333,7 @@
</span><span class="cx"> _trivial_GET()))
</span><span class="cx"> self.assertEquals(str(e), "Invalid response, no username given.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_checkHash(self):
</span><span class="cx"> """
</span><span class="lines">@@ -332,20 +343,21 @@
</span><span class="cx">
</span><span class="cx"> for ctr, factory in enumerate(self.credentialFactories):
</span><span class="cx"> challenge = (yield factory.getChallenge(clientAddress))
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse = authRequest1[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponse(challenge, "00000001"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> creds = (yield factory.decode(clientResponse, _trivial_GET()))
</span><del>-
</del><ins>+
</ins><span class="cx"> self.failUnless(creds.checkHash(
</span><span class="cx"> md5('username:test realm:password').hexdigest()))
</span><del>-
</del><ins>+
</ins><span class="cx"> self.failIf(creds.checkHash(
</span><span class="cx"> md5('username:test realm:bogus').hexdigest()))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_invalidNonceCount(self):
</span><span class="cx"> """
</span><span class="lines">@@ -359,20 +371,20 @@
</span><span class="cx">
</span><span class="cx"> for ctr, factory in enumerate(credentialFactories):
</span><span class="cx"> challenge = (yield factory.getChallenge(clientAddress))
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse1 = authRequest1[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponse(challenge, "00000001"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse2 = authRequest2[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponse(challenge, "00000002"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> yield factory.decode(clientResponse1, _trivial_GET())
</span><span class="cx"> yield factory.decode(clientResponse2, _trivial_GET())
</span><del>-
</del><ins>+
</ins><span class="cx"> if challenge.get('qop') is not None:
</span><span class="cx"> yield self.assertRaisesDeferred(
</span><span class="cx"> error.LoginFailed,
</span><span class="lines">@@ -380,7 +392,7 @@
</span><span class="cx"> clientResponse2,
</span><span class="cx"> _trivial_GET()
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> challenge = (yield factory.getChallenge(clientAddress))
</span><span class="cx">
</span><span class="cx"> clientResponse1 = authRequest1[ctr] % (
</span><span class="lines">@@ -400,6 +412,7 @@
</span><span class="cx"> _trivial_GET()
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_invalidNonce(self):
</span><span class="cx"> """
</span><span class="lines">@@ -415,12 +428,12 @@
</span><span class="cx"> for ctr, factory in enumerate(credentialFactories):
</span><span class="cx"> challenge = (yield factory.getChallenge(clientAddress))
</span><span class="cx"> challenge['nonce'] = "noNoncense"
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse = authRequest1[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponse(challenge, "00000001"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> request = _trivial_GET()
</span><span class="cx"> yield self.assertRaisesDeferred(
</span><span class="cx"> error.LoginFailed,
</span><span class="lines">@@ -431,11 +444,12 @@
</span><span class="cx">
</span><span class="cx"> factory._invalidate(FAKE_STATIC_NONCE)
</span><span class="cx"> response = (yield UnauthorizedResponse.makeResponse(
</span><del>- {"Digest":factory},
</del><ins>+ {"Digest": factory},
</ins><span class="cx"> request.remoteAddr
</span><span class="cx"> ))
</span><span class="cx"> response.headers.getHeader("www-authenticate")[0][1]
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_oldNonce(self):
</span><span class="cx"> """
</span><span class="lines">@@ -452,12 +466,12 @@
</span><span class="cx"> challenge = (yield factory.getChallenge(clientAddress))
</span><span class="cx"> nonce_count, timestamp = (yield factory.db.get(challenge['nonce']))
</span><span class="cx"> factory.db.set(challenge['nonce'], (nonce_count, timestamp - 2 * digest.DigestCredentialFactory.CHALLENGE_LIFETIME_SECS))
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse = authRequest1[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponse(challenge, "00000001"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> request = _trivial_GET()
</span><span class="cx"> yield self.assertRaisesDeferred(
</span><span class="cx"> error.LoginFailed,
</span><span class="lines">@@ -465,15 +479,16 @@
</span><span class="cx"> clientResponse,
</span><span class="cx"> request
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> response = (yield UnauthorizedResponse.makeResponse(
</span><del>- {"Digest":factory},
</del><ins>+ {"Digest": factory},
</ins><span class="cx"> request.remoteAddr,
</span><span class="cx"> ))
</span><span class="cx"> wwwhdrs = response.headers.getHeader("www-authenticate")[0][1]
</span><span class="cx"> self.assertTrue('stale' in wwwhdrs, msg="No stale parameter in Digest WWW-Authenticate headers: %s" % (wwwhdrs,))
</span><span class="cx"> self.assertEquals(wwwhdrs['stale'], 'true', msg="stale parameter not set to true in Digest WWW-Authenticate headers: %s" % (wwwhdrs,))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_incompatibleCalcHA1Options(self):
</span><span class="cx"> """
</span><span class="cx"> Test that the appropriate error is raised when any of the
</span><span class="lines">@@ -500,6 +515,7 @@
</span><span class="cx"> preHA1=preHA1
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_commaURI(self):
</span><span class="cx"> """
</span><span class="lines">@@ -508,15 +524,16 @@
</span><span class="cx">
</span><span class="cx"> for ctr, factory in enumerate(self.credentialFactories):
</span><span class="cx"> challenge = (yield factory.getChallenge(clientAddress))
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse = authRequestComma[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponseComma(challenge, "00000001"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> creds = (yield factory.decode(clientResponse, _trivial_GET()))
</span><span class="cx"> self.failUnless(creds.checkPassword('password'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_stale_response(self):
</span><span class="cx"> """
</span><span class="lines">@@ -526,20 +543,20 @@
</span><span class="cx"> class newtime(object):
</span><span class="cx"> def time(self):
</span><span class="cx"> return theTime
</span><del>- from twistedcaldav.directory import digest
- self.patch(digest, "time", newtime())
</del><ins>+ from twistedcaldav.directory import digest as ddigest
+ self.patch(ddigest, "time", newtime())
</ins><span class="cx">
</span><span class="cx"> for ctr, factory in enumerate(self.credentialFactories):
</span><span class="cx"> challenge = (yield factory.getChallenge(clientAddress))
</span><del>-
</del><ins>+
</ins><span class="cx"> clientResponse = authRequest1[ctr] % (
</span><span class="cx"> challenge['nonce'],
</span><span class="cx"> self.getDigestResponse(challenge, "00000001"),
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> creds = (yield factory.decode(clientResponse, _trivial_GET()))
</span><span class="cx"> self.failUnless(creds.checkPassword('password'))
</span><del>-
</del><ins>+
</ins><span class="cx"> theTime += DigestCredentialFactory.CHALLENGE_LIFETIME_SECS + 1
</span><span class="cx"> request = _trivial_GET()
</span><span class="cx"> try:
</span><span class="lines">@@ -555,8 +572,8 @@
</span><span class="cx"> self.fail("Invalid exception from nonce timeout: %s" % e)
</span><span class="cx"> challenge = (yield factory.getChallenge(request.remoteAddr))
</span><span class="cx"> self.assertTrue(challenge.get("stale") == "true")
</span><del>-
</del><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def _trivial_GET():
</span><span class="cx"> return SimpleRequest(None, 'GET', '/')
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_directorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_directory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_directory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_directory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -121,7 +121,6 @@
</span><span class="cx"> self.count += 1
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> def test_expandedMembers(self):
</span><span class="cx"> """
</span><span class="cx"> Make sure expandedMembers( ) returns a complete, flattened set of
</span><span class="lines">@@ -889,7 +888,7 @@
</span><span class="cx"> }
</span><span class="cx"> members = pickle.loads(snapshotFile.getContent())
</span><span class="cx"> self.assertEquals(members, expected)
</span><del>-
</del><ins>+
</ins><span class="cx"> # "Corrupt" the snapshot and verify it is regenerated properly
</span><span class="cx"> snapshotFile.setContent("xyzzy")
</span><span class="cx"> cache.delete("group-cacher-populated")
</span><span class="lines">@@ -900,8 +899,8 @@
</span><span class="cx"> self.assertTrue(snapshotFile.exists())
</span><span class="cx"> members = pickle.loads(snapshotFile.getContent())
</span><span class="cx"> self.assertEquals(members, expected)
</span><del>-
</del><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_autoAcceptMembers(self):
</span><span class="cx"> """
</span><span class="cx"> autoAcceptMembers( ) returns an empty list if no autoAcceptGroup is
</span><span class="lines">@@ -926,6 +925,7 @@
</span><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def testScheduling(self):
</span><span class="cx"> """
</span><span class="lines">@@ -934,6 +934,7 @@
</span><span class="cx">
</span><span class="cx"> groupCacher = StubGroupCacher()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def decorateTransaction(txn):
</span><span class="cx"> txn._groupCacher = groupCacher
</span><span class="cx">
</span><span class="lines">@@ -945,15 +946,19 @@
</span><span class="cx">
</span><span class="cx"> testScheduling.skip = "Fix WorkProposal to track delayed calls and cancel them"
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class StubGroupCacher(object):
</span><span class="cx"> def __init__(self):
</span><span class="cx"> self.called = False
</span><span class="cx"> self.updateSeconds = 99
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def updateCache(self):
</span><span class="cx"> self.called = True
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class RecordsMatchingTokensTests(TestCase):
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -1038,6 +1043,7 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class GUIDTests(TestCase):
</span><span class="cx">
</span><span class="cx"> def setUp(self):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_guidchangepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_guidchange.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_guidchange.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_guidchange.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -52,7 +52,7 @@
</span><span class="cx"> newUID = "38D8AC00-5490-4425-BE3A-05FFB9862444"
</span><span class="cx">
</span><span class="cx"> homeResource = "/calendars/users/cdaboo/"
</span><del>-
</del><ins>+
</ins><span class="cx"> def privs1(result):
</span><span class="cx"> # Change GUID in record
</span><span class="cx"> self.xmlFile.setContent(
</span><span class="lines">@@ -65,10 +65,10 @@
</span><span class="cx">
</span><span class="cx"> # Now force the calendar home resource to be reset
</span><span class="cx"> self.resetCalendars()
</span><del>-
</del><ins>+
</ins><span class="cx"> # Make sure new user cannot access old user's calendar home
</span><span class="cx"> return self._checkPrivileges(None, homeResource, davxml.HRef("/principals/__uids__/" + newUID + "/"), davxml.Write, False)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Make sure current user has access to their calendar home
</span><span class="cx"> d = self._checkPrivileges(None, homeResource, davxml.HRef("/principals/__uids__/" + oldUID + "/"), davxml.Write, True)
</span><span class="cx"> d.addCallback(privs1)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_ldapdirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_ldapdirectory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_ldapdirectory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_ldapdirectory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -276,6 +276,7 @@
</span><span class="cx"> entry["expected"]
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class StubList(object):
</span><span class="cx"> def __init__(self, wrapper):
</span><span class="cx"> self.ldap = wrapper
</span><span class="lines">@@ -293,6 +294,7 @@
</span><span class="cx"> self.allResults = self.ldap.search_s(self.base, self.scope,
</span><span class="cx"> self.filterstr, attrlist=self.attrList)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class StubAsync(object):
</span><span class="cx"> def List(self, wrapper):
</span><span class="cx"> return StubList(wrapper)
</span><span class="lines">@@ -304,7 +306,6 @@
</span><span class="cx"> whatever you have previously called addTestResults( ) with.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-
</del><span class="cx"> def __init__(self, actual, records):
</span><span class="cx"> self.actual = actual
</span><span class="cx"> self.async = StubAsync()
</span><span class="lines">@@ -587,9 +588,8 @@
</span><span class="cx"> "readOnlyProxyAttr": "read-only-proxy",
</span><span class="cx"> "autoAcceptGroupAttr": None,
</span><span class="cx"> },
</span><del>- "partitionSchema": {
</del><ins>+ "poddingSchema": {
</ins><span class="cx"> "serverIdAttr": "server-id", # maps to augments server-id
</span><del>- "partitionIdAttr": "partition-id", # maps to augments partition-id
</del><span class="cx"> },
</span><span class="cx"> }
</span><span class="cx"> )
</span><span class="lines">@@ -803,9 +803,8 @@
</span><span class="cx"> "readOnlyProxyAttr": None,
</span><span class="cx"> "autoAcceptGroupAttr": None,
</span><span class="cx"> },
</span><del>- "partitionSchema": {
</del><ins>+ "poddingSchema": {
</ins><span class="cx"> "serverIdAttr": "server-id", # maps to augments server-id
</span><del>- "partitionIdAttr": "partition-id", # maps to augments partition-id
</del><span class="cx"> },
</span><span class="cx"> }
</span><span class="cx"> )
</span><span class="lines">@@ -1021,9 +1020,8 @@
</span><span class="cx"> "readOnlyProxyAttr": None,
</span><span class="cx"> "autoAcceptGroupAttr": None,
</span><span class="cx"> },
</span><del>- "partitionSchema": {
</del><ins>+ "poddingSchema": {
</ins><span class="cx"> "serverIdAttr": "server-id", # maps to augments server-id
</span><del>- "partitionIdAttr": "partition-id", # maps to augments partition-id
</del><span class="cx"> },
</span><span class="cx"> }
</span><span class="cx"> )
</span><span class="lines">@@ -1235,9 +1233,8 @@
</span><span class="cx"> "readOnlyProxyAttr": None,
</span><span class="cx"> "autoAcceptGroupAttr": None,
</span><span class="cx"> },
</span><del>- "partitionSchema": {
</del><ins>+ "poddingSchema": {
</ins><span class="cx"> "serverIdAttr": "server-id", # maps to augments server-id
</span><del>- "partitionIdAttr": "partition-id", # maps to augments partition-id
</del><span class="cx"> },
</span><span class="cx"> }
</span><span class="cx"> )
</span><span class="lines">@@ -1295,7 +1292,6 @@
</span><span class="cx"> self.assertEquals(record.firstName, 'Amanda')
</span><span class="cx"> self.assertEquals(record.lastName, 'Test')
</span><span class="cx"> self.assertEquals(record.serverID, None)
</span><del>- self.assertEquals(record.partitionID, None)
</del><span class="cx"> self.assertFalse(record.enabledForCalendaring)
</span><span class="cx">
</span><span class="cx"> # User with enabled-for-calendaring specified
</span><span class="lines">@@ -1325,13 +1321,11 @@
</span><span class="cx"> 'apple-generateduid': [guid],
</span><span class="cx"> 'cn': ['Amanda Test'],
</span><span class="cx"> 'server-id' : ["test-server-id"],
</span><del>- 'partition-id' : ["test-partition-id"],
</del><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> record = self.service._ldapResultToRecord(dn, attrs,
</span><span class="cx"> self.service.recordType_users)
</span><span class="cx"> self.assertEquals(record.serverID, "test-server-id")
</span><del>- self.assertEquals(record.partitionID, "test-partition-id")
</del><span class="cx">
</span><span class="cx"> # User missing guidAttr
</span><span class="cx">
</span><span class="lines">@@ -1471,7 +1465,6 @@
</span><span class="cx"> self.assertFalse(record.autoSchedule)
</span><span class="cx"> self.assertEquals(record.autoAcceptGroup, "")
</span><span class="cx">
</span><del>-
</del><span class="cx"> # Now switch off the resourceInfoAttr and switch to individual
</span><span class="cx"> # attributes...
</span><span class="cx"> self.service.resourceSchema = {
</span><span class="lines">@@ -1625,7 +1618,6 @@
</span><span class="cx"> self.assertFalse(self.service.isAllowedByRestrictToGroup(dn, attrs))
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_groupMembershipAliases(self):
</span><span class="cx"> """
</span><span class="lines">@@ -1680,7 +1672,6 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> def test_splitIntoBatches(self):
</span><span class="cx"> self.setupService(self.nestedUsingDifferentAttributeUsingDN)
</span><span class="cx"> # Data is perfect multiple of size
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_livedirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_livedirectory.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_livedirectory.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_livedirectory.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -142,8 +142,6 @@
</span><span class="cx"> record = self.svc.recordWithShortName("users", "odtestcarlene")
</span><span class="cx"> self.assertTrue(record in records)
</span><span class="cx">
</span><del>-
-
</del><span class="cx"> if runLDAPTests:
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.directory.ldapdirectory import LdapDirectoryService
</span><span class="lines">@@ -196,7 +194,6 @@
</span><span class="cx"> }
</span><span class="cx"> self.svc = LdapDirectoryService(params)
</span><span class="cx">
</span><del>-
</del><span class="cx"> if runODTests:
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.directory.appleopendirectory import OpenDirectoryService
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_modifypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_modify.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_modify.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_modify.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -47,6 +47,7 @@
</span><span class="cx"> augmentsFile = os.path.join(testRoot, "augments.xml")
</span><span class="cx"> config.AugmentService.params.xmlFiles = (augmentsFile,)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_createRecord(self):
</span><span class="cx"> directory = getDirectory()
</span><span class="cx">
</span><span class="lines">@@ -75,6 +76,7 @@
</span><span class="cx"> record = directory.recordWithUID("location01")
</span><span class="cx"> self.assertNotEquals(record, None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_destroyRecord(self):
</span><span class="cx"> directory = getDirectory()
</span><span class="cx">
</span><span class="lines">@@ -95,6 +97,7 @@
</span><span class="cx"> record = directory.recordWithUID("location01")
</span><span class="cx"> self.assertNotEquals(record, None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_updateRecord(self):
</span><span class="cx"> directory = getDirectory()
</span><span class="cx">
</span><span class="lines">@@ -124,12 +127,14 @@
</span><span class="cx"> record = directory.recordWithUID("location01")
</span><span class="cx"> self.assertNotEquals(record, None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_createDuplicateRecord(self):
</span><span class="cx"> directory = getDirectory()
</span><span class="cx">
</span><span class="cx"> directory.createRecord("resources", guid="resource01", shortNames=("resource01",), uid="resource01")
</span><span class="cx"> self.assertRaises(DirectoryError, directory.createRecord, "resources", guid="resource01", shortNames=("resource01",), uid="resource01")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_missingShortNames(self):
</span><span class="cx"> directory = getDirectory()
</span><span class="cx">
</span><span class="lines">@@ -145,6 +150,7 @@
</span><span class="cx"> self.assertEquals(record.shortNames[0], "resource01")
</span><span class="cx"> self.assertEquals(record.fullName, "Resource #1")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_missingGUID(self):
</span><span class="cx"> directory = getDirectory()
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_opendirectorybackerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_opendirectorybacker.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_opendirectorybacker.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_opendirectorybacker.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -21,7 +21,20 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_multiplePhoneNumbersAndEmailAddresses(self):
</span><del>- attributes={u'dsAttrTypeStandard:AppleMetaRecordName': ['uid=odtestamanda,cn=users,dc=dalek,dc=example,dc=com'], u'dsAttrTypeStandard:ModificationTimestamp': '20111017170937Z', u'dsAttrTypeStandard:PhoneNumber': ['408 555-1212', '415 555-1212'], u'dsAttrTypeStandard:RecordType': ['dsRecTypeStandard:Users'], u'dsAttrTypeStandard:AppleMetaNodeLocation': ['/LDAPv3/127.0.0.1'], u'dsAttrTypeStandard:RecordName': ['odtestamanda'], u'dsAttrTypeStandard:FirstName': 'Amanda', u'dsAttrTypeStandard:GeneratedUID': '9DC04A70-E6DD-11DF-9492-0800200C9A66', u'dsAttrTypeStandard:LastName': 'Test', u'dsAttrTypeStandard:CreationTimestamp': '20110927182945Z', u'dsAttrTypeStandard:EMailAddress': ['amanda@example.com', 'second@example.com'], u'dsAttrTypeStandard:RealName': 'Amanda Test'}
</del><ins>+ attributes = {
+ u'dsAttrTypeStandard:AppleMetaRecordName': ['uid=odtestamanda,cn=users,dc=dalek,dc=example,dc=com'],
+ u'dsAttrTypeStandard:ModificationTimestamp': '20111017170937Z',
+ u'dsAttrTypeStandard:PhoneNumber': ['408 555-1212', '415 555-1212'],
+ u'dsAttrTypeStandard:RecordType': ['dsRecTypeStandard:Users'],
+ u'dsAttrTypeStandard:AppleMetaNodeLocation': ['/LDAPv3/127.0.0.1'],
+ u'dsAttrTypeStandard:RecordName': ['odtestamanda'],
+ u'dsAttrTypeStandard:FirstName': 'Amanda',
+ u'dsAttrTypeStandard:GeneratedUID': '9DC04A70-E6DD-11DF-9492-0800200C9A66',
+ u'dsAttrTypeStandard:LastName': 'Test',
+ u'dsAttrTypeStandard:CreationTimestamp': '20110927182945Z',
+ u'dsAttrTypeStandard:EMailAddress': ['amanda@example.com', 'second@example.com'],
+ u'dsAttrTypeStandard:RealName': 'Amanda Test',
+ }
</ins><span class="cx"> vcardRecord = VCardRecord(StubService(), attributes)
</span><span class="cx"> vcard = vcardRecord.vCard()
</span><span class="cx"> properties = set([prop.value() for prop in vcard.properties("TEL")])
</span><span class="lines">@@ -30,6 +43,7 @@
</span><span class="cx"> self.assertEquals(properties, set(["amanda@example.com", "second@example.com"]))
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class StubService(object):
</span><span class="cx"> addDSAttrXProperties = False
</span><span class="cx"> directoryBackedAddressBook = None
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_proxyprincipaldbpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_proxyprincipaldb.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_proxyprincipaldb.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_proxyprincipaldb.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -17,7 +17,7 @@
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.directory import calendaruserproxy
</span><del>-from twistedcaldav.directory.calendaruserproxy import ProxySqliteDB,\
</del><ins>+from twistedcaldav.directory.calendaruserproxy import ProxySqliteDB, \
</ins><span class="cx"> ProxyPostgreSQLDB
</span><span class="cx"> from twistedcaldav.directory.calendaruserproxyloader import XMLCalendarUserProxyLoader
</span><span class="cx"> import twistedcaldav.test.util
</span><span class="lines">@@ -28,21 +28,21 @@
</span><span class="cx"> """
</span><span class="cx"> Directory service provisioned principals.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> class old_ProxyDB(ProxySqliteDB):
</span><del>-
</del><ins>+
</ins><span class="cx"> def _db_version(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the schema version assigned to this index.
</span><span class="cx"> """
</span><span class="cx"> return "3"
</span><del>-
</del><ins>+
</ins><span class="cx"> def _db_init_data_tables(self):
</span><span class="cx"> """
</span><span class="cx"> Initialise the underlying database tables.
</span><span class="cx"> @param q: a database cursor to use.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> #
</span><span class="cx"> # GROUPS table
</span><span class="cx"> #
</span><span class="lines">@@ -55,70 +55,77 @@
</span><span class="cx"> """
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class new_ProxyDB(ProxySqliteDB):
</span><del>-
</del><ins>+
</ins><span class="cx"> def _db_version(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the schema version assigned to this index.
</span><span class="cx"> """
</span><span class="cx"> return "11"
</span><del>-
</del><ins>+
+
</ins><span class="cx"> class newer_ProxyDB(ProxySqliteDB):
</span><del>-
</del><ins>+
</ins><span class="cx"> def _db_version(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the schema version assigned to this index.
</span><span class="cx"> """
</span><span class="cx"> return "51"
</span><del>-
</del><ins>+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_normalDB(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = ProxySqliteDB(db_path)
</span><span class="cx"> yield db.setGroupMembers("A", ("B", "C", "D",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="cx"> membershipsB = yield db.getMemberships("B")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "D",)))
</span><span class="cx"> self.assertEqual(membershipsB, set(("A",)))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_normalDBNonAscii(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = ProxySqliteDB(db_path)
</span><span class="cx"> principalID = "Test \xe4\xbd\x90\xe8\x97\xa4"
</span><span class="cx"> yield db.setGroupMembers(principalID, ("B", "C", "D",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield db.getMembers(principalID)
</span><span class="cx"> membershipsB = yield db.getMemberships("B")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "D",)))
</span><span class="cx"> self.assertEqual(membershipsB, set((principalID,)))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_DBIndexed(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = ProxySqliteDB(db_path)
</span><span class="cx"> self.assertEqual(set([row[1] for row in (yield db.query("PRAGMA index_list(GROUPS)"))]), set(("GROUPNAMES", "MEMBERS")))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_OldDB(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = self.old_ProxyDB(db_path)
</span><span class="cx"> self.assertEqual(set([row[1] for row in (yield db.query("PRAGMA index_list(GROUPS)"))]), set())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_DBUpgrade(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = self.old_ProxyDB(db_path)
</span><span class="lines">@@ -132,7 +139,7 @@
</span><span class="cx"> self.assertEqual(set([row[1] for row in (yield db.query("PRAGMA index_list(GROUPS)"))]), set())
</span><span class="cx"> db.close()
</span><span class="cx"> db = None
</span><del>-
</del><ins>+
</ins><span class="cx"> db = ProxySqliteDB(db_path)
</span><span class="cx">
</span><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="lines">@@ -144,9 +151,10 @@
</span><span class="cx"> db.close()
</span><span class="cx"> db = None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_DBUpgradeNewer(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = self.old_ProxyDB(db_path)
</span><span class="lines">@@ -160,7 +168,7 @@
</span><span class="cx"> self.assertEqual(set([row[1] for row in (yield db.query("PRAGMA index_list(GROUPS)"))]), set())
</span><span class="cx"> db.close()
</span><span class="cx"> db = None
</span><del>-
</del><ins>+
</ins><span class="cx"> db = self.new_ProxyDB(db_path)
</span><span class="cx">
</span><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="lines">@@ -172,9 +180,10 @@
</span><span class="cx"> db.close()
</span><span class="cx"> db = None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_DBNoUpgradeNewer(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = self.new_ProxyDB(db_path)
</span><span class="lines">@@ -188,7 +197,7 @@
</span><span class="cx"> self.assertEqual(set([row[1] for row in (yield db.query("PRAGMA index_list(GROUPS)"))]), set(("GROUPNAMES", "MEMBERS")))
</span><span class="cx"> db.close()
</span><span class="cx"> db = None
</span><del>-
</del><ins>+
</ins><span class="cx"> db = self.newer_ProxyDB(db_path)
</span><span class="cx">
</span><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="lines">@@ -200,40 +209,41 @@
</span><span class="cx"> db.close()
</span><span class="cx"> db = None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_cachingDBInsert(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> for processType in ("Single", "Combined",):
</span><span class="cx"> config.ProcessType = processType
</span><span class="cx">
</span><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = ProxySqliteDB(db_path)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do one insert and check the result
</span><span class="cx"> yield db.setGroupMembers("A", ("B", "C", "D",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="cx"> membershipsB = yield db.getMemberships("B")
</span><span class="cx"> membershipsC = yield db.getMemberships("C")
</span><span class="cx"> membershipsD = yield db.getMemberships("D")
</span><span class="cx"> membershipsE = yield db.getMemberships("E")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "D",)))
</span><span class="cx"> self.assertEqual(membershipsB, set(("A",)))
</span><span class="cx"> self.assertEqual(membershipsC, set(("A",)))
</span><span class="cx"> self.assertEqual(membershipsD, set(("A",)))
</span><span class="cx"> self.assertEqual(membershipsE, set(()))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Change and check the result
</span><span class="cx"> yield db.setGroupMembers("A", ("B", "C", "E",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="cx"> membershipsB = yield db.getMemberships("B")
</span><span class="cx"> membershipsC = yield db.getMemberships("C")
</span><span class="cx"> membershipsD = yield db.getMemberships("D")
</span><span class="cx"> membershipsE = yield db.getMemberships("E")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "E",)))
</span><span class="cx"> self.assertEqual(membershipsB, set(("A",)))
</span><span class="cx"> self.assertEqual(membershipsC, set(("A",)))
</span><span class="lines">@@ -242,41 +252,42 @@
</span><span class="cx">
</span><span class="cx"> yield db.clean()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_cachingDBRemove(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> for processType in ("Single", "Combined",):
</span><span class="cx"> config.ProcessType = processType
</span><span class="cx">
</span><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = ProxySqliteDB(db_path)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do one insert and check the result
</span><span class="cx"> yield db.setGroupMembers("A", ("B", "C", "D",))
</span><span class="cx"> yield db.setGroupMembers("X", ("B", "C",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="cx"> membersX = yield db.getMembers("X")
</span><span class="cx"> membershipsB = yield db.getMemberships("B")
</span><span class="cx"> membershipsC = yield db.getMemberships("C")
</span><span class="cx"> membershipsD = yield db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "D",)))
</span><span class="cx"> self.assertEqual(membersX, set(("B", "C",)))
</span><span class="cx"> self.assertEqual(membershipsB, set(("A", "X",)))
</span><span class="cx"> self.assertEqual(membershipsC, set(("A", "X",)))
</span><span class="cx"> self.assertEqual(membershipsD, set(("A",)))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Remove and check the result
</span><span class="cx"> yield db.removeGroup("A")
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="cx"> membersX = yield db.getMembers("X")
</span><span class="cx"> membershipsB = yield db.getMemberships("B")
</span><span class="cx"> membershipsC = yield db.getMemberships("C")
</span><span class="cx"> membershipsD = yield db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set())
</span><span class="cx"> self.assertEqual(membersX, set(("B", "C",)))
</span><span class="cx"> self.assertEqual(membershipsB, set("X",))
</span><span class="lines">@@ -285,33 +296,34 @@
</span><span class="cx">
</span><span class="cx"> yield db.clean()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_cachingDBRemoveSpecial(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> for processType in ("Single", "Combined",):
</span><span class="cx"> config.ProcessType = processType
</span><span class="cx">
</span><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = ProxySqliteDB(db_path)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do one insert and check the result
</span><span class="cx"> yield db.setGroupMembers("A", ("B", "C", "D",))
</span><span class="cx"> yield db.setGroupMembers("X", ("B", "C",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membershipsB = yield db.getMemberships("B")
</span><span class="cx"> membershipsC = yield db.getMemberships("C")
</span><span class="cx"> membershipsD = yield db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> # Remove and check the result
</span><span class="cx"> yield db.removeGroup("A")
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="cx"> membersX = yield db.getMembers("X")
</span><span class="cx"> membershipsB = yield db.getMemberships("B")
</span><span class="cx"> membershipsC = yield db.getMemberships("C")
</span><span class="cx"> membershipsD = yield db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set())
</span><span class="cx"> self.assertEqual(membersX, set(("B", "C",)))
</span><span class="cx"> self.assertEqual(membershipsB, set("X",))
</span><span class="lines">@@ -320,41 +332,42 @@
</span><span class="cx">
</span><span class="cx"> yield db.clean()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_cachingDBRemovePrincipal(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> for processType in ("Single", "Combined",):
</span><span class="cx"> config.ProcessType = processType
</span><span class="cx">
</span><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = ProxySqliteDB(db_path)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do one insert and check the result
</span><span class="cx"> yield db.setGroupMembers("A", ("B", "C", "D",))
</span><span class="cx"> yield db.setGroupMembers("X", ("B", "C",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="cx"> membersX = yield db.getMembers("X")
</span><span class="cx"> membershipsB = yield db.getMemberships("B")
</span><span class="cx"> membershipsC = yield db.getMemberships("C")
</span><span class="cx"> membershipsD = yield db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "D",)))
</span><span class="cx"> self.assertEqual(membersX, set(("B", "C",)))
</span><span class="cx"> self.assertEqual(membershipsB, set(("A", "X",)))
</span><span class="cx"> self.assertEqual(membershipsC, set(("A", "X",)))
</span><span class="cx"> self.assertEqual(membershipsD, set(("A",)))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Remove and check the result
</span><span class="cx"> yield db.removePrincipal("B")
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield db.getMembers("A")
</span><span class="cx"> membersX = yield db.getMembers("X")
</span><span class="cx"> membershipsB = yield db.getMemberships("B")
</span><span class="cx"> membershipsC = yield db.getMemberships("C")
</span><span class="cx"> membershipsD = yield db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("C", "D",)))
</span><span class="cx"> self.assertEqual(membersX, set(("C",)))
</span><span class="cx"> self.assertEqual(membershipsB, set())
</span><span class="lines">@@ -363,29 +376,30 @@
</span><span class="cx">
</span><span class="cx"> yield db.clean()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_cachingDBInsertUncached(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> for processType in ("Single", "Combined",):
</span><span class="cx"> config.ProcessType = processType
</span><span class="cx">
</span><span class="cx"> # Get the DB
</span><span class="cx"> db_path = os.path.abspath(self.mktemp())
</span><span class="cx"> db = ProxySqliteDB(db_path)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do one insert and check the result for the one we will remove
</span><span class="cx"> yield db.setGroupMembers("AA", ("BB", "CC", "DD",))
</span><span class="cx"> yield db.getMemberships("DD")
</span><del>-
</del><ins>+
</ins><span class="cx"> # Change and check the result
</span><span class="cx"> yield db.setGroupMembers("AA", ("BB", "CC", "EE",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersAA = yield db.getMembers("AA")
</span><span class="cx"> membershipsBB = yield db.getMemberships("BB")
</span><span class="cx"> membershipsCC = yield db.getMemberships("CC")
</span><span class="cx"> membershipsDD = yield db.getMemberships("DD")
</span><span class="cx"> membershipsEE = yield db.getMemberships("EE")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersAA, set(("BB", "CC", "EE",)))
</span><span class="cx"> self.assertEqual(membershipsBB, set(("AA",)))
</span><span class="cx"> self.assertEqual(membershipsCC, set(("AA",)))
</span><span class="lines">@@ -394,11 +408,13 @@
</span><span class="cx">
</span><span class="cx"> yield db.clean()
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class ProxyPrincipalDBPostgreSQL (twistedcaldav.test.util.TestCase):
</span><span class="cx"> """
</span><span class="cx"> Directory service provisioned principals.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def setUp(self):
</span><span class="cx">
</span><span class="lines">@@ -406,209 +422,217 @@
</span><span class="cx"> self.db = ProxyPostgreSQLDB(host="localhost", database="proxies")
</span><span class="cx"> yield self.db.clean()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def tearDown(self):
</span><span class="cx"> yield self.db.close()
</span><span class="cx"> self.db = None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_normalDB(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get the DB
</span><span class="cx"> yield self.db.clean()
</span><del>-
</del><ins>+
</ins><span class="cx"> calendaruserproxy.ProxyDBService = self.db
</span><span class="cx"> loader = XMLCalendarUserProxyLoader("/Volumes/Data/Users/cyrusdaboo/Documents/Development/Apple/eclipse/CalendarServer-3/conf/auth/proxies-test.xml")
</span><span class="cx"> yield loader.updateProxyDB()
</span><span class="cx">
</span><span class="cx"> yield self.db.setGroupMembers("A", ("B", "C", "D",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield self.db.getMembers("A")
</span><span class="cx"> membershipsB = yield self.db.getMemberships("B")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "D",)))
</span><span class="cx"> self.assertEqual(membershipsB, set(("A",)))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_DBIndexed(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get the DB
</span><span class="cx"> yield self.db.clean()
</span><span class="cx"> self.assertTrue((yield self.db.queryOne("select hasindexes from pg_tables where tablename = 'groups'")))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_cachingDBInsert(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> for processType in ("Single", "Combined",):
</span><span class="cx"> config.ProcessType = processType
</span><span class="cx">
</span><span class="cx"> # Get the DB
</span><span class="cx"> yield self.db.clean()
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do one insert and check the result
</span><span class="cx"> yield self.db.setGroupMembers("A", ("B", "C", "D",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield self.db.getMembers("A")
</span><span class="cx"> membershipsB = yield self.db.getMemberships("B")
</span><span class="cx"> membershipsC = yield self.db.getMemberships("C")
</span><span class="cx"> membershipsD = yield self.db.getMemberships("D")
</span><span class="cx"> membershipsE = yield self.db.getMemberships("E")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "D",)))
</span><span class="cx"> self.assertEqual(membershipsB, set(("A",)))
</span><span class="cx"> self.assertEqual(membershipsC, set(("A",)))
</span><span class="cx"> self.assertEqual(membershipsD, set(("A",)))
</span><span class="cx"> self.assertEqual(membershipsE, set(()))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Change and check the result
</span><span class="cx"> yield self.db.setGroupMembers("A", ("B", "C", "E",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield self.db.getMembers("A")
</span><span class="cx"> membershipsB = yield self.db.getMemberships("B")
</span><span class="cx"> membershipsC = yield self.db.getMemberships("C")
</span><span class="cx"> membershipsD = yield self.db.getMemberships("D")
</span><span class="cx"> membershipsE = yield self.db.getMemberships("E")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "E",)))
</span><span class="cx"> self.assertEqual(membershipsB, set(("A",)))
</span><span class="cx"> self.assertEqual(membershipsC, set(("A",)))
</span><span class="cx"> self.assertEqual(membershipsD, set())
</span><span class="cx"> self.assertEqual(membershipsE, set(("A",)))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_cachingDBRemove(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> for processType in ("Single", "Combined",):
</span><span class="cx"> config.ProcessType = processType
</span><span class="cx">
</span><span class="cx"> # Get the DB
</span><span class="cx"> yield self.db.clean()
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do one insert and check the result
</span><span class="cx"> yield self.db.setGroupMembers("A", ("B", "C", "D",))
</span><span class="cx"> yield self.db.setGroupMembers("X", ("B", "C",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield self.db.getMembers("A")
</span><span class="cx"> membersX = yield self.db.getMembers("X")
</span><span class="cx"> membershipsB = yield self.db.getMemberships("B")
</span><span class="cx"> membershipsC = yield self.db.getMemberships("C")
</span><span class="cx"> membershipsD = yield self.db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "D",)))
</span><span class="cx"> self.assertEqual(membersX, set(("B", "C",)))
</span><span class="cx"> self.assertEqual(membershipsB, set(("A", "X",)))
</span><span class="cx"> self.assertEqual(membershipsC, set(("A", "X",)))
</span><span class="cx"> self.assertEqual(membershipsD, set(("A",)))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Remove and check the result
</span><span class="cx"> yield self.db.removeGroup("A")
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield self.db.getMembers("A")
</span><span class="cx"> membersX = yield self.db.getMembers("X")
</span><span class="cx"> membershipsB = yield self.db.getMemberships("B")
</span><span class="cx"> membershipsC = yield self.db.getMemberships("C")
</span><span class="cx"> membershipsD = yield self.db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set())
</span><span class="cx"> self.assertEqual(membersX, set(("B", "C",)))
</span><span class="cx"> self.assertEqual(membershipsB, set("X",))
</span><span class="cx"> self.assertEqual(membershipsC, set("X",))
</span><span class="cx"> self.assertEqual(membershipsD, set())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_cachingDBRemoveSpecial(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> for processType in ("Single", "Combined",):
</span><span class="cx"> config.ProcessType = processType
</span><span class="cx">
</span><span class="cx"> # Get the DB
</span><span class="cx"> yield self.db.clean()
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do one insert and check the result
</span><span class="cx"> yield self.db.setGroupMembers("A", ("B", "C", "D",))
</span><span class="cx"> yield self.db.setGroupMembers("X", ("B", "C",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membershipsB = yield self.db.getMemberships("B")
</span><span class="cx"> membershipsC = yield self.db.getMemberships("C")
</span><span class="cx"> membershipsD = yield self.db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> # Remove and check the result
</span><span class="cx"> yield self.db.removeGroup("A")
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield self.db.getMembers("A")
</span><span class="cx"> membersX = yield self.db.getMembers("X")
</span><span class="cx"> membershipsB = yield self.db.getMemberships("B")
</span><span class="cx"> membershipsC = yield self.db.getMemberships("C")
</span><span class="cx"> membershipsD = yield self.db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set())
</span><span class="cx"> self.assertEqual(membersX, set(("B", "C",)))
</span><span class="cx"> self.assertEqual(membershipsB, set("X",))
</span><span class="cx"> self.assertEqual(membershipsC, set("X",))
</span><span class="cx"> self.assertEqual(membershipsD, set())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_cachingDBRemovePrincipal(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> for processType in ("Single", "Combined",):
</span><span class="cx"> config.ProcessType = processType
</span><span class="cx">
</span><span class="cx"> # Get the DB
</span><span class="cx"> yield self.db.clean()
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do one insert and check the result
</span><span class="cx"> yield self.db.setGroupMembers("A", ("B", "C", "D",))
</span><span class="cx"> yield self.db.setGroupMembers("X", ("B", "C",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield self.db.getMembers("A")
</span><span class="cx"> membersX = yield self.db.getMembers("X")
</span><span class="cx"> membershipsB = yield self.db.getMemberships("B")
</span><span class="cx"> membershipsC = yield self.db.getMemberships("C")
</span><span class="cx"> membershipsD = yield self.db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("B", "C", "D",)))
</span><span class="cx"> self.assertEqual(membersX, set(("B", "C",)))
</span><span class="cx"> self.assertEqual(membershipsB, set(("A", "X",)))
</span><span class="cx"> self.assertEqual(membershipsC, set(("A", "X",)))
</span><span class="cx"> self.assertEqual(membershipsD, set(("A",)))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Remove and check the result
</span><span class="cx"> yield self.db.removePrincipal("B")
</span><del>-
</del><ins>+
</ins><span class="cx"> membersA = yield self.db.getMembers("A")
</span><span class="cx"> membersX = yield self.db.getMembers("X")
</span><span class="cx"> membershipsB = yield self.db.getMemberships("B")
</span><span class="cx"> membershipsC = yield self.db.getMemberships("C")
</span><span class="cx"> membershipsD = yield self.db.getMemberships("D")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersA, set(("C", "D",)))
</span><span class="cx"> self.assertEqual(membersX, set(("C",)))
</span><span class="cx"> self.assertEqual(membershipsB, set())
</span><span class="cx"> self.assertEqual(membershipsC, set(("A", "X",)))
</span><span class="cx"> self.assertEqual(membershipsD, set(("A",),))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_cachingDBInsertUncached(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> for processType in ("Single", "Combined",):
</span><span class="cx"> config.ProcessType = processType
</span><span class="cx">
</span><span class="cx"> # Get the DB
</span><span class="cx"> yield self.db.clean()
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do one insert and check the result for the one we will remove
</span><span class="cx"> yield self.db.setGroupMembers("AA", ("BB", "CC", "DD",))
</span><span class="cx"> yield self.db.getMemberships("DD")
</span><del>-
</del><ins>+
</ins><span class="cx"> # Change and check the result
</span><span class="cx"> yield self.db.setGroupMembers("AA", ("BB", "CC", "EE",))
</span><del>-
</del><ins>+
</ins><span class="cx"> membersAA = yield self.db.getMembers("AA")
</span><span class="cx"> membershipsBB = yield self.db.getMemberships("BB")
</span><span class="cx"> membershipsCC = yield self.db.getMemberships("CC")
</span><span class="cx"> membershipsDD = yield self.db.getMemberships("DD")
</span><span class="cx"> membershipsEE = yield self.db.getMemberships("EE")
</span><del>-
</del><ins>+
</ins><span class="cx"> self.assertEqual(membersAA, set(("BB", "CC", "EE",)))
</span><span class="cx"> self.assertEqual(membershipsBB, set(("AA",)))
</span><span class="cx"> self.assertEqual(membershipsCC, set(("AA",)))
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_resourcespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_resources.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_resources.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_resources.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -41,6 +41,7 @@
</span><span class="cx"> # def test_loadConfig(self):
</span><span class="cx"> # directory = getDirectory()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordInPrimaryDirectory(self):
</span><span class="cx"> directory = getDirectory()
</span><span class="cx">
</span><span class="lines">@@ -48,6 +49,7 @@
</span><span class="cx"> record = directory.recordWithUID("user01")
</span><span class="cx"> self.assertNotEquals(record, None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordInSupplementalDirectory(self):
</span><span class="cx"> directory = getDirectory()
</span><span class="cx">
</span><span class="lines">@@ -55,6 +57,7 @@
</span><span class="cx"> record = directory.recordWithUID("resource01")
</span><span class="cx"> self.assertNotEquals(record, None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_augments(self):
</span><span class="cx"> directory = getDirectory()
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytesttest_xmlfilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_xmlfile.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_xmlfile.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/test_xmlfile.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -38,49 +38,50 @@
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="cx"> users = {
</span><del>- "admin" : { "password": "nimda", "guid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "addresses": () },
- "wsanchez" : { "password": "zehcnasw", "guid": "6423F94A-6B76-4A3A-815B-D52CFD77935D", "addresses": ("mailto:wsanchez@example.com",) },
- "cdaboo" : { "password": "oobadc", "guid": "5A985493-EE2C-4665-94CF-4DFEA3A89500", "addresses": ("mailto:cdaboo@example.com",) },
- "lecroy" : { "password": "yorcel", "guid": "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "addresses": ("mailto:lecroy@example.com",) },
- "dreid" : { "password": "dierd", "guid": "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "addresses": ("mailto:dreid@example.com",) },
- "nocalendar" : { "password": "radnelacon", "guid": "543D28BA-F74F-4D5F-9243-B3E3A61171E5", "addresses": () },
- "user01" : { "password": "01user", "guid": None , "addresses": ("mailto:c4ca4238a0@example.com",) },
- "user02" : { "password": "02user", "guid": None , "addresses": ("mailto:c81e728d9d@example.com",) },
- }
</del><ins>+ "admin" : {"password": "nimda", "guid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "addresses": ()},
+ "wsanchez" : {"password": "zehcnasw", "guid": "6423F94A-6B76-4A3A-815B-D52CFD77935D", "addresses": ("mailto:wsanchez@example.com",)},
+ "cdaboo" : {"password": "oobadc", "guid": "5A985493-EE2C-4665-94CF-4DFEA3A89500", "addresses": ("mailto:cdaboo@example.com",) },
+ "lecroy" : {"password": "yorcel", "guid": "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "addresses": ("mailto:lecroy@example.com",) },
+ "dreid" : {"password": "dierd", "guid": "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "addresses": ("mailto:dreid@example.com",) },
+ "nocalendar" : {"password": "radnelacon", "guid": "543D28BA-F74F-4D5F-9243-B3E3A61171E5", "addresses": ()},
+ "user01" : {"password": "01user", "guid": None, "addresses": ("mailto:c4ca4238a0@example.com",)},
+ "user02" : {"password": "02user", "guid": None, "addresses": ("mailto:c81e728d9d@example.com",)},
+ }
</ins><span class="cx">
</span><span class="cx"> groups = {
</span><del>- "admin" : { "password": "admin", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "managers"),)},
- "managers" : { "password": "managers", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "lecroy"),)},
- "grunts" : { "password": "grunts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "wsanchez"),
</del><ins>+ "admin" : {"password": "admin", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "managers"),)},
+ "managers" : {"password": "managers", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "lecroy"),)},
+ "grunts" : {"password": "grunts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "wsanchez"),
</ins><span class="cx"> (DirectoryService.recordType_users , "cdaboo"),
</span><span class="cx"> (DirectoryService.recordType_users , "dreid"))},
</span><del>- "right_coast": { "password": "right_coast", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "cdaboo"),)},
- "left_coast" : { "password": "left_coast", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "wsanchez"),
</del><ins>+ "right_coast": {"password": "right_coast", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "cdaboo"),)},
+ "left_coast" : {"password": "left_coast", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "wsanchez"),
</ins><span class="cx"> (DirectoryService.recordType_users , "dreid"),
</span><span class="cx"> (DirectoryService.recordType_users , "lecroy"))},
</span><del>- "both_coasts": { "password": "both_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "right_coast"),
</del><ins>+ "both_coasts": {"password": "both_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "right_coast"),
</ins><span class="cx"> (DirectoryService.recordType_groups, "left_coast"))},
</span><del>- "recursive1_coasts": { "password": "recursive1_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "recursive2_coasts"),
</del><ins>+ "recursive1_coasts": {"password": "recursive1_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "recursive2_coasts"),
</ins><span class="cx"> (DirectoryService.recordType_users, "wsanchez"))},
</span><del>- "recursive2_coasts": { "password": "recursive2_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "recursive1_coasts"),
</del><ins>+ "recursive2_coasts": {"password": "recursive2_coasts", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_groups, "recursive1_coasts"),
</ins><span class="cx"> (DirectoryService.recordType_users, "cdaboo"))},
</span><del>- "non_calendar_group": { "password": "non_calendar_group", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "cdaboo"),
</del><ins>+ "non_calendar_group": {"password": "non_calendar_group", "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users , "cdaboo"),
</ins><span class="cx"> (DirectoryService.recordType_users , "lecroy"))},
</span><del>- }
</del><ins>+ }
</ins><span class="cx">
</span><span class="cx"> locations = {
</span><del>- "mercury": { "password": "mercury", "guid": None, "addresses": ("mailto:mercury@example.com",) },
- "gemini" : { "password": "gemini", "guid": None, "addresses": ("mailto:gemini@example.com",) },
- "apollo" : { "password": "apollo", "guid": None, "addresses": ("mailto:apollo@example.com",) },
- "orion" : { "password": "orion", "guid": None, "addresses": ("mailto:orion@example.com",) },
- }
</del><ins>+ "mercury": {"password": "mercury", "guid": None, "addresses": ("mailto:mercury@example.com",)},
+ "gemini" : {"password": "gemini", "guid": None, "addresses": ("mailto:gemini@example.com",)},
+ "apollo" : {"password": "apollo", "guid": None, "addresses": ("mailto:apollo@example.com",)},
+ "orion" : {"password": "orion", "guid": None, "addresses": ("mailto:orion@example.com",)},
+ }
</ins><span class="cx">
</span><span class="cx"> resources = {
</span><del>- "transporter" : { "password": "transporter", "guid": None, "addresses": ("mailto:transporter@example.com",) },
- "ftlcpu" : { "password": "ftlcpu", "guid": None, "addresses": ("mailto:ftlcpu@example.com",) },
- "non_calendar_proxy" : { "password": "non_calendar_proxy", "guid": "non_calendar_proxy", "addresses": ("mailto:non_calendar_proxy@example.com",) },
- }
</del><ins>+ "transporter" : {"password": "transporter", "guid": None, "addresses": ("mailto:transporter@example.com",) },
+ "ftlcpu" : {"password": "ftlcpu", "guid": None, "addresses": ("mailto:ftlcpu@example.com",) },
+ "non_calendar_proxy" : {"password": "non_calendar_proxy", "guid": "non_calendar_proxy", "addresses": ("mailto:non_calendar_proxy@example.com",)},
+ }
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def xmlFile(self):
</span><span class="cx"> """
</span><span class="cx"> Create a L{FilePath} that points to a temporary file containing a copy
</span><span class="lines">@@ -155,10 +156,10 @@
</span><span class="cx"> """
</span><span class="cx"> )
</span><span class="cx"> for recordType, expectedRecords in (
</span><del>- ( DirectoryService.recordType_users , ("admin",) ),
- ( DirectoryService.recordType_groups , () ),
- ( DirectoryService.recordType_locations , () ),
- ( DirectoryService.recordType_resources , () ),
</del><ins>+ (DirectoryService.recordType_users , ("admin",)),
+ (DirectoryService.recordType_groups , ()),
+ (DirectoryService.recordType_locations , ()),
+ (DirectoryService.recordType_resources , ()),
</ins><span class="cx"> ):
</span><span class="cx"> # Fault records in
</span><span class="cx"> for name in expectedRecords:
</span><span class="lines">@@ -169,6 +170,7 @@
</span><span class="cx"> set(expectedRecords)
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_okAutoSchedule(self):
</span><span class="cx"> service = self.service()
</span><span class="cx">
</span><span class="lines">@@ -201,10 +203,10 @@
</span><span class="cx"> service.augmentService.refresh()
</span><span class="cx">
</span><span class="cx"> for recordType, expectedRecords in (
</span><del>- ( DirectoryService.recordType_users , () ),
- ( DirectoryService.recordType_groups , () ),
- ( DirectoryService.recordType_locations , ("my office",) ),
- ( DirectoryService.recordType_resources , () ),
</del><ins>+ (DirectoryService.recordType_users , ()),
+ (DirectoryService.recordType_groups , ()),
+ (DirectoryService.recordType_locations , ("my office",)),
+ (DirectoryService.recordType_resources , ()),
</ins><span class="cx"> ):
</span><span class="cx"> # Fault records in
</span><span class="cx"> for name in expectedRecords:
</span><span class="lines">@@ -239,12 +241,12 @@
</span><span class="cx"> </accounts>
</span><span class="cx"> """
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> for recordType, expectedRecords in (
</span><del>- ( DirectoryService.recordType_users , () ),
- ( DirectoryService.recordType_groups , ("enabled", "disabled") ),
- ( DirectoryService.recordType_locations , () ),
- ( DirectoryService.recordType_resources , () ),
</del><ins>+ (DirectoryService.recordType_users , ()),
+ (DirectoryService.recordType_groups , ("enabled", "disabled")),
+ (DirectoryService.recordType_locations , ()),
+ (DirectoryService.recordType_resources , ()),
</ins><span class="cx"> ):
</span><span class="cx"> # Fault records in
</span><span class="cx"> for name in expectedRecords:
</span><span class="lines">@@ -279,13 +281,14 @@
</span><span class="cx"> </accounts>
</span><span class="cx"> """
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> record = service.recordWithShortName(
</span><span class="cx"> DirectoryService.recordType_locations, "my office")
</span><span class="cx"> self.assertEquals(record.guid, "myoffice")
</span><span class="cx"> self.assertEquals(record.extras["comment"], "This is the comment")
</span><span class="cx"> self.assertEquals(record.extras["capacity"], "40")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_writeExtras(self):
</span><span class="cx"> service = self.service()
</span><span class="cx">
</span><span class="lines">@@ -302,7 +305,6 @@
</span><span class="cx"> self.assertEquals(record.extras["comment"], "Test comment")
</span><span class="cx"> self.assertEquals(record.extras["capacity"], "10")
</span><span class="cx">
</span><del>-
</del><span class="cx"> service.updateRecord(DirectoryService.recordType_locations, "newguid",
</span><span class="cx"> shortNames=("New office",),
</span><span class="cx"> fullName="My Newer Office",
</span><span class="lines">@@ -333,6 +335,7 @@
</span><span class="cx"> self.assertNotEquals(None, service._lookupInIndex(service.recordType_locations, service.INDEX_TYPE_SHORTNAME, "orion"))
</span><span class="cx"> self.assertEquals(None, service._lookupInIndex(service.recordType_users, service.INDEX_TYPE_CUA, "mailto:nobody@example.com"))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_repeat(self):
</span><span class="cx"> service = self.service()
</span><span class="cx"> record = service.recordWithShortName(
</span><span class="lines">@@ -342,6 +345,8 @@
</span><span class="cx"> self.assertEquals(record.lastName, "c4ca4238a User 01")
</span><span class="cx"> self.assertEquals(record.emailAddresses, set(['c4ca4238a0@example.com']))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class XMLFileSubset (XMLFileBase, TestCase):
</span><span class="cx"> """
</span><span class="cx"> Test the recordTypes subset feature of XMLFile service.
</span><span class="lines">@@ -351,6 +356,7 @@
</span><span class="cx"> DirectoryService.recordType_groups,
</span><span class="cx"> ))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordTypesSubset(self):
</span><span class="cx"> directory = XMLDirectoryService(
</span><span class="cx"> {
</span><span class="lines">@@ -366,4 +372,3 @@
</span><span class="cx"> alwaysStat=True
</span><span class="cx"> )
</span><span class="cx"> self.assertEquals(set(("users", "groups")), set(directory.recordTypes()))
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectorytestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/util.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/util.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/test/util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -54,12 +54,14 @@
</span><span class="cx"> # For aggregator subclasses
</span><span class="cx"> recordTypePrefixes = ("",)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_realm(self):
</span><span class="cx"> """
</span><span class="cx"> IDirectoryService.realm
</span><span class="cx"> """
</span><span class="cx"> self.failUnless(self.service().realmName)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordTypes(self):
</span><span class="cx"> """
</span><span class="cx"> IDirectoryService.recordTypes()
</span><span class="lines">@@ -69,15 +71,16 @@
</span><span class="cx">
</span><span class="cx"> self.assertEquals(set(self.service().recordTypes()), self.recordTypes)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordWithShortName(self):
</span><span class="cx"> """
</span><span class="cx"> IDirectoryService.recordWithShortName()
</span><span class="cx"> """
</span><span class="cx"> for recordType, data in (
</span><del>- ( DirectoryService.recordType_users , self.users ),
- ( DirectoryService.recordType_groups , self.groups ),
- ( DirectoryService.recordType_locations, self.locations ),
- ( DirectoryService.recordType_resources, self.resources ),
</del><ins>+ (DirectoryService.recordType_users , self.users),
+ (DirectoryService.recordType_groups , self.groups),
+ (DirectoryService.recordType_locations, self.locations),
+ (DirectoryService.recordType_resources, self.resources),
</ins><span class="cx"> ):
</span><span class="cx"> if not data:
</span><span class="cx"> raise SkipTest("No %s" % (recordType,))
</span><span class="lines">@@ -95,6 +98,7 @@
</span><span class="cx"> continue
</span><span class="cx"> self.assertEquals(record, None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordWithUID(self):
</span><span class="cx"> service = self.service()
</span><span class="cx"> record = None
</span><span class="lines">@@ -108,6 +112,7 @@
</span><span class="cx"> if record is None:
</span><span class="cx"> raise SkipTest("No GUIDs provided to test")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_recordWithCalendarUserAddress(self):
</span><span class="cx"> service = self.service()
</span><span class="cx"> record = None
</span><span class="lines">@@ -120,6 +125,7 @@
</span><span class="cx"> if record is None:
</span><span class="cx"> raise SkipTest("No calendar user addresses provided to test")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_groupMembers(self):
</span><span class="cx"> """
</span><span class="cx"> IDirectoryRecord.members()
</span><span class="lines">@@ -138,6 +144,7 @@
</span><span class="cx"> "Wrong membership for group %r: %s != %s" % (group, result, expected)
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_groupMemberships(self):
</span><span class="cx"> """
</span><span class="cx"> IDirectoryRecord.groups()
</span><span class="lines">@@ -148,8 +155,8 @@
</span><span class="cx"> raise SkipTest("No groups")
</span><span class="cx">
</span><span class="cx"> for recordType, data in (
</span><del>- ( DirectoryService.recordType_users , self.users ),
- ( DirectoryService.recordType_groups, self.groups ),
</del><ins>+ (DirectoryService.recordType_users , self.users),
+ (DirectoryService.recordType_groups, self.groups),
</ins><span class="cx"> ):
</span><span class="cx"> service = self.service()
</span><span class="cx"> for shortName, info in data.iteritems():
</span><span class="lines">@@ -162,6 +169,7 @@
</span><span class="cx"> "Wrong groups for %s %r: %s != %s" % (record.recordType, shortName, result, expected)
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordNames(self, recordType):
</span><span class="cx"> service = self.service()
</span><span class="cx"> names = set()
</span><span class="lines">@@ -176,16 +184,18 @@
</span><span class="cx">
</span><span class="cx"> return names
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def allEntries(self):
</span><span class="cx"> for data, _ignore_recordType in (
</span><del>- (self.users, DirectoryService.recordType_users ),
- (self.groups, DirectoryService.recordType_groups ),
</del><ins>+ (self.users, DirectoryService.recordType_users),
+ (self.groups, DirectoryService.recordType_groups),
</ins><span class="cx"> (self.locations, DirectoryService.recordType_locations),
</span><span class="cx"> (self.resources, DirectoryService.recordType_resources),
</span><span class="cx"> ):
</span><span class="cx"> for item in data.iteritems():
</span><span class="cx"> yield item
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def compare(self, record, shortName, data):
</span><span class="cx"> def value(key):
</span><span class="cx"> if key in data:
</span><span class="lines">@@ -217,6 +227,7 @@
</span><span class="cx"> if value("name"):
</span><span class="cx"> self.assertEquals(record.fullName, value("name"))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def servicePrefix(self):
</span><span class="cx"> service = self.service()
</span><span class="cx"> if hasattr(service, "recordTypePrefix"):
</span><span class="lines">@@ -224,6 +235,8 @@
</span><span class="cx"> else:
</span><span class="cx"> return ""
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class NonCachingTestCase (DirectoryTestCase):
</span><span class="cx">
</span><span class="cx"> def test_listRecords_user(self):
</span><span class="lines">@@ -235,6 +248,7 @@
</span><span class="cx">
</span><span class="cx"> self.assertEquals(self.recordNames(DirectoryService.recordType_users), set(self.users.keys()))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_listRecords_group(self):
</span><span class="cx"> """
</span><span class="cx"> IDirectoryService.listRecords(DirectoryService.recordType_groups)
</span><span class="lines">@@ -244,6 +258,7 @@
</span><span class="cx">
</span><span class="cx"> self.assertEquals(self.recordNames(DirectoryService.recordType_groups), set(self.groups.keys()))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_listRecords_locations(self):
</span><span class="cx"> """
</span><span class="cx"> IDirectoryService.listRecords("locations")
</span><span class="lines">@@ -253,6 +268,7 @@
</span><span class="cx">
</span><span class="cx"> self.assertEquals(self.recordNames(DirectoryService.recordType_locations), set(self.locations.keys()))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_listRecords_resources(self):
</span><span class="cx"> """
</span><span class="cx"> IDirectoryService.listRecords("resources")
</span><span class="lines">@@ -263,6 +279,7 @@
</span><span class="cx"> self.assertEquals(self.recordNames(DirectoryService.recordType_resources), set(self.resources.keys()))
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class BasicTestCase (DirectoryTestCase):
</span><span class="cx"> """
</span><span class="cx"> Tests a directory implementation with basic auth.
</span><span class="lines">@@ -279,6 +296,8 @@
</span><span class="cx"> userRecord = service.recordWithShortName(DirectoryService.recordType_users, user)
</span><span class="cx"> self.failUnless(userRecord.verifyCredentials(UsernamePassword(user, self.users[user]["password"])))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> # authRequest = {
</span><span class="cx"> # username="username",
</span><span class="cx"> # realm="test realm",
</span><span class="lines">@@ -351,6 +370,8 @@
</span><span class="cx"> else:
</span><span class="cx"> self.failIf(userRecord.verifyCredentials(credentials))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def maybeCommit(req):
</span><span class="cx"> class JustForCleanup(object):
</span><span class="cx"> def newTransaction(self, *whatever):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdirectoryxmlaugmentsparserpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/xmlaugmentsparser.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/xmlaugmentsparser.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/directory/xmlaugmentsparser.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -30,38 +30,36 @@
</span><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><del>-ELEMENT_AUGMENTS = "augments"
-ELEMENT_RECORD = "record"
</del><ins>+ELEMENT_AUGMENTS = "augments"
+ELEMENT_RECORD = "record"
</ins><span class="cx">
</span><del>-ELEMENT_UID = "uid"
-ELEMENT_ENABLE = "enable"
-ELEMENT_SERVERID = "server-id"
-ELEMENT_PARTITIONID = "partition-id"
-ELEMENT_HOSTEDAT = "hosted-at" # Backwards compatibility
-ELEMENT_ENABLECALENDAR = "enable-calendar"
</del><ins>+ELEMENT_UID = "uid"
+ELEMENT_ENABLE = "enable"
+ELEMENT_SERVERID = "server-id"
+ELEMENT_PARTITIONID = "partition-id" # Backwards compatibility
+ELEMENT_HOSTEDAT = "hosted-at" # Backwards compatibility
+ELEMENT_ENABLECALENDAR = "enable-calendar"
</ins><span class="cx"> ELEMENT_ENABLEADDRESSBOOK = "enable-addressbook"
</span><del>-ELEMENT_ENABLELOGIN = "enable-login"
-ELEMENT_AUTOSCHEDULE = "auto-schedule"
</del><ins>+ELEMENT_ENABLELOGIN = "enable-login"
+ELEMENT_AUTOSCHEDULE = "auto-schedule"
</ins><span class="cx"> ELEMENT_AUTOSCHEDULE_MODE = "auto-schedule-mode"
</span><del>-ELEMENT_AUTOACCEPTGROUP = "auto-accept-group"
</del><ins>+ELEMENT_AUTOACCEPTGROUP = "auto-accept-group"
</ins><span class="cx">
</span><del>-ATTRIBUTE_REPEAT = "repeat"
</del><ins>+ATTRIBUTE_REPEAT = "repeat"
</ins><span class="cx">
</span><del>-VALUE_TRUE = "true"
-VALUE_FALSE = "false"
</del><ins>+VALUE_TRUE = "true"
+VALUE_FALSE = "false"
</ins><span class="cx">
</span><span class="cx"> ELEMENT_AUGMENTRECORD_MAP = {
</span><del>- ELEMENT_UID: "uid",
- ELEMENT_ENABLE: "enabled",
- ELEMENT_SERVERID: "serverID",
- ELEMENT_PARTITIONID: "partitionID",
- ELEMENT_HOSTEDAT: "partitionID", # Backwards compatibility
- ELEMENT_ENABLECALENDAR: "enabledForCalendaring",
</del><ins>+ ELEMENT_UID: "uid",
+ ELEMENT_ENABLE: "enabled",
+ ELEMENT_SERVERID: "serverID",
+ ELEMENT_ENABLECALENDAR: "enabledForCalendaring",
</ins><span class="cx"> ELEMENT_ENABLEADDRESSBOOK: "enabledForAddressBooks",
</span><del>- ELEMENT_ENABLELOGIN: "enabledForLogin",
- ELEMENT_AUTOSCHEDULE: "autoSchedule",
</del><ins>+ ELEMENT_ENABLELOGIN: "enabledForLogin",
+ ELEMENT_AUTOSCHEDULE: "autoSchedule",
</ins><span class="cx"> ELEMENT_AUTOSCHEDULE_MODE: "autoScheduleMode",
</span><del>- ELEMENT_AUTOACCEPTGROUP: "autoAcceptGroup",
</del><ins>+ ELEMENT_AUTOACCEPTGROUP: "autoAcceptGroup",
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> class XMLAugmentsParser(object):
</span><span class="lines">@@ -71,6 +69,7 @@
</span><span class="cx"> def __repr__(self):
</span><span class="cx"> return "<%s %r>" % (self.__class__.__name__, self.xmlFile)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __init__(self, xmlFile, items):
</span><span class="cx">
</span><span class="cx"> self.items = items
</span><span class="lines">@@ -84,13 +83,14 @@
</span><span class="cx">
</span><span class="cx"> self._parseXML(augments_node)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _parseXML(self, rootnode):
</span><span class="cx"> """
</span><span class="cx"> Parse the XML root node from the augments configuration document.
</span><span class="cx"> @param rootnode: the L{Element} to parse.
</span><span class="cx"> """
</span><span class="cx"> for child in rootnode:
</span><del>-
</del><ins>+
</ins><span class="cx"> if child.tag != ELEMENT_RECORD:
</span><span class="cx"> raise RuntimeError("Unknown augment type: '%s' in augment file: '%s'" % (child.tag, self.xmlFile,))
</span><span class="cx">
</span><span class="lines">@@ -98,7 +98,7 @@
</span><span class="cx">
</span><span class="cx"> fields = {}
</span><span class="cx"> for node in child:
</span><del>-
</del><ins>+
</ins><span class="cx"> if node.tag in (
</span><span class="cx"> ELEMENT_UID,
</span><span class="cx"> ELEMENT_SERVERID,
</span><span class="lines">@@ -118,33 +118,35 @@
</span><span class="cx"> fields[node.tag] = node.text == VALUE_TRUE
</span><span class="cx"> else:
</span><span class="cx"> raise RuntimeError("Invalid element '%s' in augment file: '%s'" % (node.tag, self.xmlFile,))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Must have at least a uid
</span><span class="cx"> if ELEMENT_UID not in fields:
</span><span class="cx"> raise RuntimeError("Invalid record '%s' without a uid in augment file: '%s'" % (child, self.xmlFile,))
</span><del>-
</del><ins>+
</ins><span class="cx"> if repeat > 1:
</span><del>- for i in xrange(1, repeat+1):
</del><ins>+ for i in xrange(1, repeat + 1):
</ins><span class="cx"> self.buildRecord(fields, i)
</span><span class="cx"> else:
</span><span class="cx"> self.buildRecord(fields)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def buildRecord(self, fields, count=None):
</span><del>-
</del><ins>+
</ins><span class="cx"> from twistedcaldav.directory.augment import AugmentRecord
</span><span class="cx">
</span><span class="cx"> def expandCount(value, count):
</span><del>-
</del><ins>+
</ins><span class="cx"> if type(value) in types.StringTypes:
</span><span class="cx"> return value % (count,) if count and "%" in value else value
</span><span class="cx"> elif type(value) == set:
</span><span class="cx"> return set([item % (count,) if count and "%" in item else item for item in value])
</span><span class="cx"> else:
</span><span class="cx"> return value
</span><del>-
</del><ins>+
</ins><span class="cx"> actualFields = {}
</span><del>- for k,v in fields.iteritems():
- actualFields[ELEMENT_AUGMENTRECORD_MAP[k]] = expandCount(v, count)
</del><ins>+ for k, v in fields.iteritems():
+ if k in ELEMENT_AUGMENTRECORD_MAP:
+ actualFields[ELEMENT_AUGMENTRECORD_MAP[k]] = expandCount(v, count)
</ins><span class="cx">
</span><span class="cx"> record = AugmentRecord(**actualFields)
</span><span class="cx"> self.items[record.uid] = record
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavdropboxpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dropbox.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dropbox.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/dropbox.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -44,12 +44,15 @@
</span><span class="cx"> def resourceType(self):
</span><span class="cx"> return davxml.ResourceType.dropboxhome #@UndefinedVariable
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def isCollection(self):
</span><span class="cx"> return True
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def http_PUT(self, request):
</span><span class="cx"> return responsecode.FORBIDDEN
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def accessControlList(self, request, *args, **kwargs):
</span><span class="cx"> """
</span><span class="lines">@@ -57,7 +60,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> acl = (yield super(DropBoxHomeResource, self).accessControlList(request, *args, **kwargs))
</span><del>-
</del><ins>+
</ins><span class="cx"> if config.EnableProxyPrincipals:
</span><span class="cx"> owner = (yield self.ownerPrincipal(request))
</span><span class="cx">
</span><span class="lines">@@ -75,10 +78,12 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> returnValue(davxml.ACL(*newaces))
</span><del>-
</del><ins>+
</ins><span class="cx"> else:
</span><span class="cx"> returnValue(acl)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class DropBoxCollectionResource (DAVResource):
</span><span class="cx"> """
</span><span class="cx"> Drop box resource.
</span><span class="lines">@@ -99,7 +104,7 @@
</span><span class="cx"> calendar collection have the same privileges unless explicitly overridden. The same applies
</span><span class="cx"> to drop box collections as we want all resources (attachments) to have the same privileges as
</span><span class="cx"> the drop box collection.
</span><del>-
</del><ins>+
</ins><span class="cx"> @param newaces: C{list} of L{ACE} for ACL being set.
</span><span class="cx"> """
</span><span class="cx"> # Add inheritable option to each ACE in the list
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavextensionspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/extensions.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/extensions.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/extensions.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -494,13 +494,13 @@
</span><span class="cx"> size = child.contentLength()
</span><span class="cx"> lastModified = child.lastModified()
</span><span class="cx"> rtypes = []
</span><del>- fullrtype = child.resourceType()
</del><ins>+ fullrtype = child.resourceType() if hasattr(child, "resourceType") else None
</ins><span class="cx"> if fullrtype is not None:
</span><span class="cx"> for rtype in fullrtype.children:
</span><span class="cx"> rtypes.append(rtype.name)
</span><span class="cx"> if rtypes:
</span><span class="cx"> rtypes = "(%s)" % (", ".join(rtypes),)
</span><del>- if child.isCollection():
</del><ins>+ if child.isCollection() if hasattr(child, "isCollection") else False:
</ins><span class="cx"> contentType = rtypes
</span><span class="cx"> else:
</span><span class="cx"> mimeType = child.contentType()
</span><span class="lines">@@ -1004,6 +1004,7 @@
</span><span class="cx"> return tokens, context, applyTo, clientLimit, propElement
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def validateTokens(tokens):
</span><span class="cx"> """
</span><span class="cx"> Make sure there is at least one token longer than one character
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavfreebusyurlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/freebusyurl.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/freebusyurl.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/freebusyurl.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -47,8 +47,8 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser
</span><span class="cx"> from txdav.caldav.datastore.scheduling.scheduler import Scheduler
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -189,15 +189,15 @@
</span><span class="cx"> # Start/end/duration must be valid iCalendar DATE-TIME UTC or DURATION values
</span><span class="cx"> try:
</span><span class="cx"> if self.start:
</span><del>- self.start = PyCalendarDateTime.parseText(self.start)
</del><ins>+ self.start = DateTime.parseText(self.start)
</ins><span class="cx"> if not self.start.utc():
</span><span class="cx"> raise ValueError()
</span><span class="cx"> if self.end:
</span><del>- self.end = PyCalendarDateTime.parseText(self.end)
</del><ins>+ self.end = DateTime.parseText(self.end)
</ins><span class="cx"> if not self.end.utc():
</span><span class="cx"> raise ValueError()
</span><span class="cx"> if self.duration:
</span><del>- self.duration = PyCalendarDuration.parseText(self.duration)
</del><ins>+ self.duration = Duration.parseText(self.duration)
</ins><span class="cx"> except ValueError:
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.BAD_REQUEST,
</span><span class="lines">@@ -225,12 +225,12 @@
</span><span class="cx">
</span><span class="cx"> # Now fill in the missing pieces
</span><span class="cx"> if self.start is None:
</span><del>- self.start = PyCalendarDateTime.getNowUTC()
</del><ins>+ self.start = DateTime.getNowUTC()
</ins><span class="cx"> self.start.setHHMMSS(0, 0, 0)
</span><span class="cx"> if self.duration:
</span><span class="cx"> self.end = self.start + self.duration
</span><span class="cx"> if self.end is None:
</span><del>- self.end = self.start + PyCalendarDuration(days=config.FreeBusyURL.TimePeriod)
</del><ins>+ self.end = self.start + Duration(days=config.FreeBusyURL.TimePeriod)
</ins><span class="cx">
</span><span class="cx"> # End > start
</span><span class="cx"> if self.end <= self.start:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavicalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/ical.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/ical.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/ical.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -28,7 +28,6 @@
</span><span class="cx"> "tzexpand",
</span><span class="cx"> ]
</span><span class="cx">
</span><del>-import cStringIO as StringIO
</del><span class="cx"> import codecs
</span><span class="cx"> from difflib import unified_diff
</span><span class="cx"> import heapq
</span><span class="lines">@@ -46,31 +45,34 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
</span><span class="cx"> from twistedcaldav.timezones import hasTZ, TimezoneException
</span><span class="cx">
</span><del>-from pycalendar import definitions
-from pycalendar.attribute import PyCalendarAttribute
-from pycalendar.calendar import PyCalendar
-from pycalendar.componentbase import PyCalendarComponentBase
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.exceptions import PyCalendarError
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.property import PyCalendarProperty
-from pycalendar.timezone import PyCalendarTimezone
-from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
</del><ins>+from pycalendar.icalendar import definitions
+from pycalendar.parameter import Parameter
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.icalendar.component import Component as PyComponent
+from pycalendar.componentbase import ComponentBase
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.exceptions import ErrorBase
+from pycalendar.period import Period
+from pycalendar.icalendar.property import Property as PyProperty
+from pycalendar.timezone import Timezone
+from pycalendar.utcoffsetvalue import UTCOffsetValue
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="cx"> iCalendarProductID = "-//CALENDARSERVER.ORG//NONSGML Version 1//EN"
</span><span class="cx">
</span><del>-allowedComponents = (
- "VEVENT",
- "VTODO",
- "VTIMEZONE",
- # "VJOURNAL",
- "VFREEBUSY",
- # "VAVAILABILITY",
-)
</del><ins>+allowedStoreComponents = ("VEVENT", "VTODO", "VPOLL",)
+allowedSchedulingComponents = allowedStoreComponents + ("VFREEBUSY",)
+allowedComponents = allowedSchedulingComponents + ("VTIMEZONE",)
</ins><span class="cx">
</span><ins>+def _updateAllowedComponents(allowed):
+ global allowedStoreComponents, allowedSchedulingComponents, allowedComponents
+ allowedStoreComponents = allowed
+ allowedSchedulingComponents = allowedStoreComponents + ("VFREEBUSY",)
+ allowedComponents = allowedSchedulingComponents + ("VTIMEZONE",)
+
+
</ins><span class="cx"> # Additional per-user data components - see datafilters.peruserdata.py for details
</span><span class="cx"> PERUSER_COMPONENT = "X-CALENDARSERVER-PERUSER"
</span><span class="cx"> PERUSER_UID = "X-CALENDARSERVER-PERUSER-UID"
</span><span class="lines">@@ -134,19 +136,28 @@
</span><span class="cx"> "LAST-MODIFIED": (None, {"VALUE": "DATE-TIME"}),
</span><span class="cx"> "SEQUENCE": (0, {"VALUE": "INTEGER"}),
</span><span class="cx"> "REQUEST-STATUS": (None, {"VALUE": "TEXT"}),
</span><ins>+
+ "VOTER": (None, {
+ "VALUE": "CAL-ADDRESS",
+ "CUTYPE": "INDIVIDUAL",
+ "ROLE": "REQ-PARTICIPANT",
+ "RSVP": "FALSE",
+ "SCHEDULE-AGENT": "SERVER",
+ }),
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> # transformations to apply to property values
</span><span class="cx"> normalizePropsValue = {
</span><span class="cx"> "ATTENDEE": normalizeCUAddr,
</span><span class="cx"> "ORGANIZER": normalizeCUAddr,
</span><ins>+ "VOTER": normalizeCUAddr,
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> ignoredComponents = ("VTIMEZONE", PERUSER_COMPONENT,)
</span><span class="cx">
</span><span class="cx"> # Used for min/max time-range query limits
</span><del>-minDateTime = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
-maxDateTime = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+minDateTime = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+maxDateTime = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx">
</span><span class="cx"> class InvalidICalendarDataError(ValueError):
</span><span class="cx"> pass
</span><span class="lines">@@ -170,16 +181,16 @@
</span><span class="cx">
</span><span class="cx"> pyobj = kwargs["pycalendar"]
</span><span class="cx">
</span><del>- if not isinstance(pyobj, PyCalendarProperty):
- raise TypeError("Not a PyCalendarProperty: %r" % (property,))
</del><ins>+ if not isinstance(pyobj, PyProperty):
+ raise TypeError("Not a Property: %r" % (property,))
</ins><span class="cx">
</span><span class="cx"> self._pycalendar = pyobj
</span><span class="cx"> else:
</span><span class="cx"> # Convert params dictionary to list of lists format used by pycalendar
</span><span class="cx"> valuetype = kwargs.get("valuetype")
</span><del>- self._pycalendar = PyCalendarProperty(name, value, valuetype=valuetype)
</del><ins>+ self._pycalendar = PyProperty(name, value, valuetype=valuetype)
</ins><span class="cx"> for attrname, attrvalue in params.items():
</span><del>- self._pycalendar.addAttribute(PyCalendarAttribute(attrname, attrvalue))
</del><ins>+ self._pycalendar.addParameter(Parameter(attrname, attrvalue))
</ins><span class="cx">
</span><span class="cx"> self._parent = parent
</span><span class="cx">
</span><span class="lines">@@ -267,7 +278,7 @@
</span><span class="cx"> Returns a set containing parameter names for this property.
</span><span class="cx"> """
</span><span class="cx"> result = set()
</span><del>- for pyattrlist in self._pycalendar.getAttributes().values():
</del><ins>+ for pyattrlist in self._pycalendar.getParameters().values():
</ins><span class="cx"> for pyattr in pyattrlist:
</span><span class="cx"> result.add(pyattr.getName())
</span><span class="cx"> return result
</span><span class="lines">@@ -275,31 +286,40 @@
</span><span class="cx">
</span><span class="cx"> def parameterValue(self, name, default=None):
</span><span class="cx"> """
</span><del>- Returns a single value for the given parameter. Raises
- InvalidICalendarDataError if the parameter has more than one value.
</del><ins>+ Returns a single value for the given parameter.
</ins><span class="cx"> """
</span><span class="cx"> try:
</span><del>- return self._pycalendar.getAttributeValue(name)
</del><ins>+ return self._pycalendar.getParameterValue(name)
</ins><span class="cx"> except KeyError:
</span><span class="cx"> return default
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def parameterValues(self, name, default=None):
+ """
+ Returns a multi-value C{list} for the given parameter.
+ """
+ try:
+ return self._pycalendar.getParameterValues(name)
+ except KeyError:
+ return default
+
+
</ins><span class="cx"> def hasParameter(self, paramname):
</span><del>- return self._pycalendar.hasAttribute(paramname)
</del><ins>+ return self._pycalendar.hasParameter(paramname)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def setParameter(self, paramname, paramvalue):
</span><del>- self._pycalendar.replaceAttribute(PyCalendarAttribute(paramname, paramvalue))
</del><ins>+ self._pycalendar.replaceParameter(Parameter(paramname, paramvalue))
</ins><span class="cx"> self._markAsDirty()
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def removeParameter(self, paramname):
</span><del>- self._pycalendar.removeAttributes(paramname)
</del><ins>+ self._pycalendar.removeParameters(paramname)
</ins><span class="cx"> self._markAsDirty()
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def removeAllParameters(self):
</span><del>- self._pycalendar.setAttributes({})
</del><ins>+ self._pycalendar.setParameters({})
</ins><span class="cx"> self._markAsDirty()
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -308,11 +328,11 @@
</span><span class="cx"> paramname = paramname.upper()
</span><span class="cx"> for attrName in self.parameterNames():
</span><span class="cx"> if attrName.upper() == paramname:
</span><del>- for attr in tuple(self._pycalendar.getAttributes()[attrName]):
</del><ins>+ for attr in tuple(self._pycalendar.getParameters()[attrName]):
</ins><span class="cx"> for value in attr.getValues():
</span><span class="cx"> if value == paramvalue:
</span><span class="cx"> if not attr.removeValue(value):
</span><del>- self._pycalendar.removeAttributes(paramname)
</del><ins>+ self._pycalendar.removeParameters(paramname)
</ins><span class="cx"> self._markAsDirty()
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -322,8 +342,8 @@
</span><span class="cx"> start/end period.
</span><span class="cx"> The only properties allowed for this query are: COMPLETED, CREATED, DTSTAMP and
</span><span class="cx"> LAST-MODIFIED (caldav -09).
</span><del>- @param start: a L{PyCalendarDateTime} specifying the beginning of the given time span.
- @param end: a L{PyCalendarDateTime} specifying the end of the given time span.
</del><ins>+ @param start: a L{DateTime} specifying the beginning of the given time span.
+ @param end: a L{DateTime} specifying the end of the given time span.
</ins><span class="cx"> C{end} may be None, indicating that there is no end date.
</span><span class="cx"> @param defaulttz: the default L{PyTimezone} to use in datetime comparisons.
</span><span class="cx"> @return: True if the property's date/date-time value is within the given time range,
</span><span class="lines">@@ -337,7 +357,7 @@
</span><span class="cx">
</span><span class="cx"> # get date/date-time value
</span><span class="cx"> dt = self._pycalendar.getValue().getValue()
</span><del>- assert isinstance(dt, PyCalendarDateTime), "Not a date/date-time value: %r" % (self,)
</del><ins>+ assert isinstance(dt, DateTime), "Not a date/date-time value: %r" % (self,)
</ins><span class="cx">
</span><span class="cx"> return timeRangesOverlap(dt, None, start, end, defaulttz)
</span><span class="cx">
</span><span class="lines">@@ -375,66 +395,95 @@
</span><span class="cx"> # Hidden instance.
</span><span class="cx"> HIDDEN_INSTANCE_PROPERTY = "X-CALENDARSERVER-HIDDEN-INSTANCE"
</span><span class="cx">
</span><ins>+ allowedTypesList = None
+
+
</ins><span class="cx"> @classmethod
</span><del>- def allFromString(clazz, string):
</del><ins>+ def allowedTypes(cls):
+ if cls.allowedTypesList is None:
+ cls.allowedTypesList = ["text/calendar"]
+ if config.EnableJSONData:
+ cls.allowedTypesList.append("application/calendar+json")
+ return cls.allowedTypesList
+
+
+ @classmethod
+ def allFromString(clazz, string, format=None):
</ins><span class="cx"> """
</span><span class="cx"> Just default to reading a single VCALENDAR
</span><span class="cx"> """
</span><del>- return clazz.fromString(string)
</del><ins>+ return clazz.fromString(string, format)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><del>- def allFromStream(clazz, stream):
</del><ins>+ def allFromStream(clazz, stream, format=None):
</ins><span class="cx"> """
</span><span class="cx"> Just default to reading a single VCALENDAR
</span><span class="cx"> """
</span><del>- return clazz.fromStream(stream)
</del><ins>+ return clazz.fromStream(stream, format)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><del>- def fromString(clazz, string):
</del><ins>+ def fromString(clazz, string, format=None):
</ins><span class="cx"> """
</span><span class="cx"> Construct a L{Component} from a string.
</span><span class="cx"> @param string: a string containing iCalendar data.
</span><span class="cx"> @return: a L{Component} representing the first component described by
</span><span class="cx"> C{string}.
</span><span class="cx"> """
</span><del>- if type(string) is unicode:
- string = string.encode("utf-8")
- else:
- # Valid utf-8 please
- string.decode("utf-8")
</del><ins>+ return clazz._fromData(string, False, format)
</ins><span class="cx">
</span><del>- # No BOMs please
- if string[:3] == codecs.BOM_UTF8:
- string = string[3:]
</del><span class="cx">
</span><del>- return clazz.fromStream(StringIO.StringIO(string))
</del><ins>+ @classmethod
+ def fromStream(clazz, stream, format=None):
+ """
+ Construct a L{Component} from a stream.
+ @param stream: a C{read()}able stream containing iCalendar data.
+ @return: a L{Component} representing the first component described by
+ C{stream}.
+ """
+ return clazz._fromData(stream, True, format)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><del>- def fromStream(clazz, stream):
</del><ins>+ def _fromData(clazz, data, isstream, format=None):
</ins><span class="cx"> """
</span><span class="cx"> Construct a L{Component} from a stream.
</span><span class="cx"> @param stream: a C{read()}able stream containing iCalendar data.
</span><ins>+ @param format: a C{str} indicating whether the data is iCalendar or jCal
</ins><span class="cx"> @return: a L{Component} representing the first component described by
</span><span class="cx"> C{stream}.
</span><span class="cx"> """
</span><del>- cal = PyCalendar()
</del><ins>+
+ if isstream:
+ pass
+ else:
+ if type(data) is unicode:
+ data = data.encode("utf-8")
+ else:
+ # Valid utf-8 please
+ data.decode("utf-8")
+
+ # No BOMs please
+ if data[:3] == codecs.BOM_UTF8:
+ data = data[3:]
+
</ins><span class="cx"> errmsg = "Unknown"
</span><span class="cx"> try:
</span><del>- result = cal.parse(stream)
- except PyCalendarError, e:
</del><ins>+ result = Calendar.parseData(data, format)
+ except ErrorBase, e:
</ins><span class="cx"> errmsg = "%s: %s" % (e.mReason, e.mData,)
</span><span class="cx"> result = None
</span><span class="cx"> if not result:
</span><del>- stream.seek(0)
- raise InvalidICalendarDataError("%s\n%s" % (errmsg, stream.read(),))
- return clazz(None, pycalendar=cal)
</del><ins>+ if isstream:
+ data.seek(0)
+ data = data.read()
+ raise InvalidICalendarDataError("%s\n%s" % (errmsg, data,))
+ return clazz(None, pycalendar=result)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><del>- def fromIStream(clazz, stream):
</del><ins>+ def fromIStream(clazz, stream, format=None):
</ins><span class="cx"> """
</span><span class="cx"> Construct a L{Component} from a stream.
</span><span class="cx"> @param stream: an L{IStream} containing iCalendar data.
</span><span class="lines">@@ -448,7 +497,7 @@
</span><span class="cx"> # request stream.
</span><span class="cx"> #
</span><span class="cx"> def parse(data):
</span><del>- return clazz.fromString(data)
</del><ins>+ return clazz.fromString(data, format)
</ins><span class="cx"> return allDataFromStream(IStream(stream), parse)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -480,8 +529,8 @@
</span><span class="cx"> pyobj = kwargs["pycalendar"]
</span><span class="cx">
</span><span class="cx"> if pyobj is not None:
</span><del>- if not isinstance(pyobj, PyCalendarComponentBase):
- raise TypeError("Not a PyCalendarComponentBase: %r" % (pyobj,))
</del><ins>+ if not isinstance(pyobj, ComponentBase):
+ raise TypeError("Not a ComponentBase: %r" % (pyobj,))
</ins><span class="cx">
</span><span class="cx"> self._pycalendar = pyobj
</span><span class="cx"> else:
</span><span class="lines">@@ -499,7 +548,7 @@
</span><span class="cx"> self._parent = None
</span><span class="cx"> else:
</span><span class="cx"> # FIXME: figure out creating an arbitrary component
</span><del>- self._pycalendar = PyCalendar(add_defaults=False) if name == "VCALENDAR" else PyCalendar.makeComponent(name, None)
</del><ins>+ self._pycalendar = Calendar(add_defaults=False) if name == "VCALENDAR" else PyComponent.makeComponent(name, None)
</ins><span class="cx"> self._parent = None
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -542,13 +591,20 @@
</span><span class="cx"> return self._pycalendar == other._pycalendar
</span><span class="cx">
</span><span class="cx">
</span><del>- def getTextWithTimezones(self, includeTimezones):
</del><ins>+ def getText(self, format=None):
+ return self.getTextWithTimezones(False, format)
+
+
+ def getTextWithTimezones(self, includeTimezones, format=None):
</ins><span class="cx"> """
</span><del>- Return text representation and include timezones if the option is on
</del><ins>+ Return text representation and include timezones if the option is on.
</ins><span class="cx"> """
</span><span class="cx"> assert self.name() == "VCALENDAR", "Must be a VCALENDAR: %r" % (self,)
</span><span class="cx">
</span><del>- return self._pycalendar.getText(includeTimezones=includeTimezones)
</del><ins>+ result = self._pycalendar.getText(includeTimezones=includeTimezones, format=format)
+ if result is None:
+ raise ValueError("Unknown format requested for calendar data.")
+ return result
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> # FIXME: Should this not be in __eq__?
</span><span class="lines">@@ -627,14 +683,14 @@
</span><span class="cx"> This also returns the matching master component if recurrence_id is C{None}.
</span><span class="cx">
</span><span class="cx"> @param recurrence_id: The RECURRENCE-ID property value to match.
</span><del>- @type recurrence_id: L{PyCalendarDateTime}
</del><ins>+ @type recurrence_id: L{DateTime}
</ins><span class="cx"> @return: the L{Component} for the overridden component,
</span><span class="cx"> or C{None} if there isn't one.
</span><span class="cx"> """
</span><span class="cx"> assert self.name() == "VCALENDAR", "Must be a VCALENDAR: %r" % (self,)
</span><span class="cx">
</span><span class="cx"> if isinstance(recurrence_id, str):
</span><del>- recurrence_id = PyCalendarDateTime.parseText(recurrence_id) if recurrence_id else None
</del><ins>+ recurrence_id = DateTime.parseText(recurrence_id) if recurrence_id else None
</ins><span class="cx">
</span><span class="cx"> for component in self.subcomponents():
</span><span class="cx"> if component.name() in ignoredComponents:
</span><span class="lines">@@ -760,7 +816,7 @@
</span><span class="cx"> Return the start date or date-time for the specified component
</span><span class="cx"> converted to UTC.
</span><span class="cx"> @param component: the Component whose start should be returned.
</span><del>- @return: the L{PyCalendarDateTime} for the start.
</del><ins>+ @return: the L{DateTime} for the start.
</ins><span class="cx"> """
</span><span class="cx"> dtstart = self.propertyValue("DTSTART")
</span><span class="cx"> return dtstart.duplicateAsUTC() if dtstart is not None else None
</span><span class="lines">@@ -772,7 +828,7 @@
</span><span class="cx"> taking into account the presence or absence of DTEND/DURATION properties.
</span><span class="cx"> The returned date-time is converted to UTC.
</span><span class="cx"> @param component: the Component whose end should be returned.
</span><del>- @return: the L{PyCalendarDateTime} for the end.
</del><ins>+ @return: the L{DateTime} for the end.
</ins><span class="cx"> """
</span><span class="cx"> dtend = self.propertyValue("DTEND")
</span><span class="cx"> if dtend is None:
</span><span class="lines">@@ -789,7 +845,7 @@
</span><span class="cx"> Return the due date or date-time for the specified component
</span><span class="cx"> converted to UTC. Use DTSTART/DURATION if no DUE property.
</span><span class="cx"> @param component: the Component whose start should be returned.
</span><del>- @return: the L{PyCalendarDateTime} for the start.
</del><ins>+ @return: the L{DateTime} for the start.
</ins><span class="cx"> """
</span><span class="cx"> due = self.propertyValue("DUE")
</span><span class="cx"> if due is None:
</span><span class="lines">@@ -827,7 +883,7 @@
</span><span class="cx"> """
</span><span class="cx"> Return the recurrence-id for the specified component.
</span><span class="cx"> @param component: the Component whose r-id should be returned.
</span><del>- @return: the L{PyCalendarDateTime} for the r-id.
</del><ins>+ @return: the L{DateTime} for the r-id.
</ins><span class="cx"> """
</span><span class="cx"> rid = self.propertyValue("RECURRENCE-ID")
</span><span class="cx"> return rid.duplicateAsUTC() if rid is not None else None
</span><span class="lines">@@ -1086,7 +1142,7 @@
</span><span class="cx"> to match the new limit, remove RDATEs/EXDATEs and overridden components beyond the limit.
</span><span class="cx">
</span><span class="cx"> @param rid: the recurrence-id limit
</span><del>- @type rid: L{PyCalendarDateTime}
</del><ins>+ @type rid: L{DateTime}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> if not self.isRecurring():
</span><span class="lines">@@ -1148,7 +1204,7 @@
</span><span class="cx"> match any RRULE pattern.
</span><span class="cx">
</span><span class="cx"> @param rid: the recurrence-id limit
</span><del>- @type rid: L{PyCalendarDateTime}
</del><ins>+ @type rid: L{DateTime}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> if not self.isRecurring():
</span><span class="lines">@@ -1169,7 +1225,7 @@
</span><span class="cx"> rrule_expanded = []
</span><span class="cx"> rrule.expand(
</span><span class="cx"> master.propertyValue("DTSTART"),
</span><del>- PyCalendarPeriod(PyCalendarDateTime(1900, 1, 1), upperlimit),
</del><ins>+ Period(DateTime(1900, 1, 1), upperlimit),
</ins><span class="cx"> rrule_expanded,
</span><span class="cx"> )
</span><span class="cx"> for i in sorted(rrule_expanded):
</span><span class="lines">@@ -1244,14 +1300,14 @@
</span><span class="cx"> instance in the specified range. Date-times are converted to UTC. A
</span><span class="cx"> new calendar object is returned.
</span><span class="cx">
</span><del>- @param start: the L{PyCalendarDateTime} for the start of the range.
- @param end: the L{PyCalendarDateTime} for the end of the range.
- @param timezone: the L{Component} or L{PyCalendarTimezone} of the VTIMEZONE to use for floating/all-day.
</del><ins>+ @param start: the L{DateTime} for the start of the range.
+ @param end: the L{DateTime} for the end of the range.
+ @param timezone: the L{Component} or L{Timezone} of the VTIMEZONE to use for floating/all-day.
</ins><span class="cx"> @return: the L{Component} for the new calendar with expanded instances.
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> if timezone is not None and isinstance(timezone, Component):
</span><del>- pytz = PyCalendarTimezone(tzid=timezone.propertyValue("TZID"))
</del><ins>+ pytz = Timezone(tzid=timezone.propertyValue("TZID"))
</ins><span class="cx"> else:
</span><span class="cx"> pytz = timezone
</span><span class="cx">
</span><span class="lines">@@ -1297,7 +1353,7 @@
</span><span class="cx"> # Convert all datetime properties to UTC unless they are floating
</span><span class="cx"> for property in newcomp.properties():
</span><span class="cx"> value = property.value()
</span><del>- if isinstance(value, PyCalendarDateTime) and value.local():
</del><ins>+ if isinstance(value, DateTime) and value.local():
</ins><span class="cx"> property.removeParameter("TZID")
</span><span class="cx"> property.setValue(value.duplicateAsUTC())
</span><span class="cx">
</span><span class="lines">@@ -1324,7 +1380,7 @@
</span><span class="cx"> breathing room to return results for future instances.
</span><span class="cx">
</span><span class="cx"> @param limit: the max datetime to cache up to.
</span><del>- @type limit: L{PyCalendarDateTime}
</del><ins>+ @type limit: L{DateTime}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Checked for cached values first
</span><span class="lines">@@ -1335,7 +1391,7 @@
</span><span class="cx"> # so return cached instances
</span><span class="cx"> return self.cachedInstances
</span><span class="cx">
</span><del>- lookAheadLimit = limit + PyCalendarDuration(days=365)
</del><ins>+ lookAheadLimit = limit + Duration(days=365)
</ins><span class="cx"> self.cachedInstances = self.expandTimeRanges(
</span><span class="cx"> lookAheadLimit,
</span><span class="cx"> ignoreInvalidInstances=ignoreInvalidInstances
</span><span class="lines">@@ -1349,7 +1405,7 @@
</span><span class="cx"> contained within this VCALENDAR component. We will assume
</span><span class="cx"> that this component has already been validated as a CalDAV resource
</span><span class="cx"> (i.e. only one type of component, all with the same UID)
</span><del>- @param limit: L{PyCalendarDateTime} value representing the end of the expansion.
</del><ins>+ @param limit: L{DateTime} value representing the end of the expansion.
</ins><span class="cx"> @param ignoreInvalidInstances: C{bool} whether to ignore instance errors.
</span><span class="cx"> @return: a set of Instances for each recurrence in the set.
</span><span class="cx"> """
</span><span class="lines">@@ -1368,14 +1424,14 @@
</span><span class="cx"> @param componentSet: the set of components that are to make up the
</span><span class="cx"> recurrence set. These MUST all be components with the same UID
</span><span class="cx"> and type, forming a proper recurring set.
</span><del>- @param limit: L{PyCalendarDateTime} value representing the end of the expansion.
</del><ins>+ @param limit: L{DateTime} value representing the end of the expansion.
</ins><span class="cx">
</span><span class="cx"> @param componentSet: the set of components that are to make up the recurrence set.
</span><span class="cx"> These MUST all be components with the same UID and type, forming a proper
</span><span class="cx"> recurring set.
</span><span class="cx"> @type componentSet: C{list}
</span><span class="cx"> @param limit: the end of the expansion
</span><del>- @type limit: L{PyCalendarDateTime}
</del><ins>+ @type limit: L{DateTime}
</ins><span class="cx"> @param ignoreInvalidInstances: whether or not invalid recurrences raise an exception
</span><span class="cx"> @type ignoreInvalidInstances: C{bool}
</span><span class="cx"> @param normalizeFunction: a function used to normalize date/time values in instances
</span><span class="lines">@@ -1451,7 +1507,7 @@
</span><span class="cx"> There is always some new thing that will surprise you.
</span><span class="cx">
</span><span class="cx"> @param rid: recurrence-id value
</span><del>- @type rid: L{PyCalendarDateTime} or C{str}
</del><ins>+ @type rid: L{DateTime} or C{str}
</ins><span class="cx"> @param allowCancelled: whether to allow a STATUS:CANCELLED override
</span><span class="cx"> @type allowCancelled: C{bool}
</span><span class="cx"> @param allowExcluded: whether to derive an instance for an existing EXDATE
</span><span class="lines">@@ -1469,7 +1525,7 @@
</span><span class="cx"> return None
</span><span class="cx">
</span><span class="cx"> if isinstance(rid, str):
</span><del>- rid = PyCalendarDateTime.parseText(rid) if rid else None
</del><ins>+ rid = DateTime.parseText(rid) if rid else None
</ins><span class="cx">
</span><span class="cx"> # TODO: Check that the recurrence-id is a valid instance
</span><span class="cx"> # For now we just check that there is no matching EXDATE
</span><span class="lines">@@ -1586,7 +1642,7 @@
</span><span class="cx"> # Pre-cache instance expansion up to the highest rid
</span><span class="cx"> highest_rid = max(non_master_rids)
</span><span class="cx"> self.cacheExpandedTimeRanges(
</span><del>- highest_rid + PyCalendarDuration(days=1),
</del><ins>+ highest_rid + Duration(days=1),
</ins><span class="cx"> ignoreInvalidInstances=ignoreInvalidInstances
</span><span class="cx"> )
</span><span class="cx"> for rid in rids:
</span><span class="lines">@@ -1600,7 +1656,7 @@
</span><span class="cx"> Test whether the specified recurrence-id is a valid instance in this event.
</span><span class="cx">
</span><span class="cx"> @param rid: recurrence-id value
</span><del>- @type rid: L{PyCalendarDateTime}
</del><ins>+ @type rid: L{DateTime}
</ins><span class="cx">
</span><span class="cx"> @return: C{bool}
</span><span class="cx"> """
</span><span class="lines">@@ -1984,12 +2040,12 @@
</span><span class="cx">
</span><span class="cx"> def gettimezone(self):
</span><span class="cx"> """
</span><del>- Get the PyCalendarTimezone for a Timezone component.
</del><ins>+ Get the Timezone for a Timezone component.
</ins><span class="cx">
</span><del>- @return: L{PyCalendarTimezone} if this is a VTIMEZONE, otherwise None.
</del><ins>+ @return: L{Timezone} if this is a VTIMEZONE, otherwise None.
</ins><span class="cx"> """
</span><span class="cx"> if self.name() == "VTIMEZONE":
</span><del>- return PyCalendarTimezone(tzid=self._pycalendar.getID())
</del><ins>+ return Timezone(tzid=self._pycalendar.getID())
</ins><span class="cx"> elif self.name() == "VCALENDAR":
</span><span class="cx"> for component in self.subcomponents():
</span><span class="cx"> if component.name() == "VTIMEZONE":
</span><span class="lines">@@ -2137,6 +2193,10 @@
</span><span class="cx"> return is_server
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def recipientPropertyName(self):
+ return "VOTER" if self.name() == "VPOLL" else "ATTENDEE"
+
+
</ins><span class="cx"> def getAttendees(self):
</span><span class="cx"> """
</span><span class="cx"> Get the attendee value. Works on either a VCALENDAR or on a component.
</span><span class="lines">@@ -2152,7 +2212,7 @@
</span><span class="cx"> return component.getAttendees()
</span><span class="cx"> else:
</span><span class="cx"> # Find the property values
</span><del>- return [p.value() for p in self.properties("ATTENDEE")]
</del><ins>+ return [p.value() for p in self.properties(self.recipientPropertyName())]
</ins><span class="cx">
</span><span class="cx"> return None
</span><span class="cx">
</span><span class="lines">@@ -2179,7 +2239,7 @@
</span><span class="cx"> result = ()
</span><span class="cx"> attendees = set()
</span><span class="cx"> rid = self.getRecurrenceIDUTC()
</span><del>- for attendee in tuple(self.properties("ATTENDEE")):
</del><ins>+ for attendee in tuple(self.properties(self.recipientPropertyName())):
</ins><span class="cx">
</span><span class="cx"> if onlyScheduleAgentServer:
</span><span class="cx"> if attendee.hasParameter("SCHEDULE-AGENT"):
</span><span class="lines">@@ -2195,6 +2255,27 @@
</span><span class="cx"> return result
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def getVoterProperty(self, match):
+ """
+ Get the voters matching a value.
+
+ @param match: a C{list} of calendar user address strings to try and match.
+ @return: the matching Voter property, or None
+ """
+
+ # Need to normalize http/https cu addresses
+ test = set()
+ for item in match:
+ test.add(normalizeCUAddr(item))
+
+ # Find the primary subcomponent
+ for voter in self.properties("VOTER"):
+ if normalizeCUAddr(voter.value()) in test:
+ return voter
+
+ return None
+
+
</ins><span class="cx"> def getAttendeeProperty(self, match):
</span><span class="cx"> """
</span><span class="cx"> Get the attendees matching a value. Works on either a VCALENDAR or on a component.
</span><span class="lines">@@ -2217,7 +2298,7 @@
</span><span class="cx"> return attendee
</span><span class="cx"> else:
</span><span class="cx"> # Find the primary subcomponent
</span><del>- for attendee in self.properties("ATTENDEE"):
</del><ins>+ for attendee in self.properties(self.recipientPropertyName()):
</ins><span class="cx"> if normalizeCUAddr(attendee.value()) in test:
</span><span class="cx"> return attendee
</span><span class="cx">
</span><span class="lines">@@ -2260,7 +2341,7 @@
</span><span class="cx"> yield attendee
</span><span class="cx"> else:
</span><span class="cx"> # Find the primary subcomponent
</span><del>- for attendee in self.properties("ATTENDEE"):
</del><ins>+ for attendee in self.properties(self.recipientPropertyName()):
</ins><span class="cx"> yield attendee
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -2641,7 +2722,7 @@
</span><span class="cx"> for component in self.subcomponents():
</span><span class="cx"> if component.name() in ignoredComponents:
</span><span class="cx"> continue
</span><del>- [component.removeProperty(p) for p in tuple(component.properties("ATTENDEE")) if p.value().lower() != attendee.lower()]
</del><ins>+ [component.removeProperty(p) for p in tuple(component.properties(component.recipientPropertyName())) if p.value().lower() != attendee.lower()]
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def removeAllButTheseAttendees(self, attendees):
</span><span class="lines">@@ -2656,7 +2737,7 @@
</span><span class="cx"> for component in self.subcomponents():
</span><span class="cx"> if component.name() in ignoredComponents:
</span><span class="cx"> continue
</span><del>- [component.removeProperty(p) for p in tuple(component.properties("ATTENDEE")) if p.value().lower() not in attendees]
</del><ins>+ [component.removeProperty(p) for p in tuple(component.properties(component.recipientPropertyName())) if p.value().lower() not in attendees]
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def hasAlarm(self):
</span><span class="lines">@@ -2977,7 +3058,7 @@
</span><span class="cx"> # Bump all components
</span><span class="cx"> self.replacePropertyInAllComponents(Property("SEQUENCE", newseq))
</span><span class="cx">
</span><del>- self.replacePropertyInAllComponents(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
</del><ins>+ self.replacePropertyInAllComponents(Property("DTSTAMP", DateTime.getNowUTC()))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def sequenceInSync(self, oldcalendar):
</span><span class="lines">@@ -3059,7 +3140,7 @@
</span><span class="cx"> dtend = self.getProperty("DTEND")
</span><span class="cx"> duration = self.getProperty("DURATION")
</span><span class="cx">
</span><del>- timeRange = PyCalendarPeriod(
</del><ins>+ timeRange = Period(
</ins><span class="cx"> start=dtstart.value(),
</span><span class="cx"> end=dtend.value() if dtend is not None else None,
</span><span class="cx"> duration=duration.value() if duration is not None else None,
</span><span class="lines">@@ -3163,7 +3244,8 @@
</span><span class="cx"> continue
</span><span class="cx"> for prop in itertools.chain(
</span><span class="cx"> component.properties("ORGANIZER"),
</span><del>- component.properties("ATTENDEE")
</del><ins>+ component.properties("ATTENDEE"),
+ component.properties("VOTER")
</ins><span class="cx"> ):
</span><span class="cx">
</span><span class="cx"> # Check that we can lookup this calendar user address - if not
</span><span class="lines">@@ -3261,7 +3343,11 @@
</span><span class="cx"> else:
</span><span class="cx"> prop.removeParameter("EMAIL")
</span><span class="cx">
</span><ins>+ # For VPOLL also do immediate children
+ if component.name() == "VPOLL":
+ component.normalizeCalendarUserAddresses(lookupFunction, principalFunction, toUUID)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def allPerUserUIDs(self):
</span><span class="cx">
</span><span class="cx"> results = set()
</span><span class="lines">@@ -3311,7 +3397,7 @@
</span><span class="cx"> Determine whether an event exists completely prior to a given moment.
</span><span class="cx">
</span><span class="cx"> @param limit: the moment to compare against.
</span><del>- @type limit: L{PyCalendarDateTime}
</del><ins>+ @type limit: L{DateTime}
</ins><span class="cx">
</span><span class="cx"> @return: a C{bool}, True if the event has any instances occurring after
</span><span class="cx"> limit, False otherwise.
</span><span class="lines">@@ -3369,13 +3455,13 @@
</span><span class="cx"> start.setDateOnly(False)
</span><span class="cx"> if tzexpanded:
</span><span class="cx"> if start != tzexpanded[0][0]:
</span><del>- results.append((str(start), PyCalendarUTCOffsetValue(tzexpanded[0][1]).getText(),))
</del><ins>+ results.append((str(start), UTCOffsetValue(tzexpanded[0][1]).getText(),))
</ins><span class="cx"> else:
</span><del>- results.append((str(start), PyCalendarUTCOffsetValue(tzcomp._pycalendar.getTimezoneOffsetSeconds(start)).getText(),))
</del><ins>+ results.append((str(start), UTCOffsetValue(tzcomp._pycalendar.getTimezoneOffsetSeconds(start)).getText(),))
</ins><span class="cx"> for tzstart, _ignore_tzoffsetfrom, tzoffsetto in tzexpanded:
</span><span class="cx"> results.append((
</span><span class="cx"> tzstart.getText(),
</span><del>- PyCalendarUTCOffsetValue(tzoffsetto).getText(),
</del><ins>+ UTCOffsetValue(tzoffsetto).getText(),
</ins><span class="cx"> ))
</span><span class="cx">
</span><span class="cx"> return results
</span><span class="lines">@@ -3388,7 +3474,7 @@
</span><span class="cx"> time range.
</span><span class="cx">
</span><span class="cx"> @param tzdata: the iCalendar data containing a VTIMEZONE.
</span><del>- @type tzdata: L{PyCalendar}
</del><ins>+ @type tzdata: L{Calendar}
</ins><span class="cx"> @param start: date for the start of the expansion.
</span><span class="cx"> @type start: C{date}
</span><span class="cx"> @param end: date for the end of the expansion.
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavicaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/icaldav.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/icaldav.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/icaldav.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -29,14 +29,14 @@
</span><span class="cx"> """
</span><span class="cx"> CalDAV resource.
</span><span class="cx"> """
</span><del>- def isCalendarCollection():
</del><ins>+ def isCalendarCollection(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> (CalDAV-access-10, Section 4.2)
</span><span class="cx"> @return: True if this resource is a calendar collection, False
</span><span class="cx"> otherwise.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def isSpecialCollection(collectiontype):
</del><ins>+ def isSpecialCollection(collectiontype): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> (CalDAV-access-10, Section 4.2)
</span><span class="cx"> @param collectiontype: L{WebDAVElement} for the collection type to test for.
</span><span class="lines">@@ -44,30 +44,30 @@
</span><span class="cx"> False otherwise.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def isPseudoCalendarCollection():
</del><ins>+ def isPseudoCalendarCollection(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @return: True if this resource is a calendar collection like (e.g.
</span><span class="cx"> a regular calendar collection or schedule inbox/outbox), False
</span><span class="cx"> otherwise.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def findCalendarCollections(depth):
</del><ins>+ def findCalendarCollections(depth): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> Returns an iterable of child calendar collection resources for the given
</span><span class="cx"> depth.
</span><del>- Because resources do not know their request URIs, chidren are returned
</del><ins>+ Because resources do not know their request URIs, children are returned
</ins><span class="cx"> as tuples C{(resource, uri)}, where C{resource} is the child resource
</span><span class="cx"> and C{uri} is a URL path relative to this resource.
</span><span class="cx"> @param depth: the search depth (one of "0", "1", or "infinity")
</span><span class="cx"> @return: an iterable of tuples C{(resource, uri)}.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def createCalendar(request):
</del><ins>+ def createCalendar(request): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> Create a calendar collection for this resource.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def iCalendar():
</del><ins>+ def iCalendar(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> Instantiate an iCalendar component object representing this resource or
</span><span class="cx"> its child with the given name.
</span><span class="lines">@@ -78,52 +78,42 @@
</span><span class="cx"> @return: a L{twistedcaldav.ical.Component} of type C{"VCALENDAR"}.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def iCalendarText():
- """
- Obtains the iCalendar text 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.
</del><span class="cx">
</span><del>- @return: a string containing iCalendar text with a top-level component
- of type C{"VCALENDAR"}.
- """
</del><span class="cx">
</span><span class="cx"> class ICalendarPrincipalResource(IDAVResource):
</span><span class="cx"> """
</span><span class="cx"> CalDAV principle resource.
</span><span class="cx"> """
</span><del>- def principalUID():
</del><ins>+ def principalUID(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @return: the user id for this principal.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def calendarHomeURLs():
</del><ins>+ def calendarHomeURLs(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @return: a list of calendar home URLs for this principal's calendar user.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def calendarUserAddresses():
</del><ins>+ def calendarUserAddresses(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @return: a list of calendar user addresses for this principal's calendar
</span><span class="cx"> user.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def calendarFreeBusyURIs(self, request):
</del><ins>+ def calendarFreeBusyURIs(request): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> @param request: the request being processed.
</span><span class="cx"> @return: a L{Deferred} list of URIs for calendars that contribute to
</span><span class="cx"> free-busy for this principal's calendar user.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def scheduleInboxURL():
</del><ins>+ def scheduleInboxURL(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> Get the schedule INBOX URL for this principal's calendar user.
</span><span class="cx"> @return: a string containing the URL from the schedule-inbox-URL property.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def scheduleOutboxURL():
</del><ins>+ def scheduleOutboxURL(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> Get the schedule OUTBOX URL for this principal's calendar user.
</span><span class="cx"> @return: a string containing the URL from the schedule-outbox-URL property.
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavinstancepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/instance.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/instance.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/instance.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -21,10 +21,10 @@
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.dateops import normalizeForIndex, differenceDateTime
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.period import Period
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> class TooManyInstancesError(Exception):
</span><span class="cx">
</span><span class="lines">@@ -80,7 +80,7 @@
</span><span class="cx"> (trigger, related, repeat, duration) = alarm.getTriggerDetails()
</span><span class="cx">
</span><span class="cx"> # Handle relative vs absolute triggers
</span><del>- if isinstance(trigger, PyCalendarDateTime):
</del><ins>+ if isinstance(trigger, DateTime):
</ins><span class="cx"> # Absolute trigger
</span><span class="cx"> start = trigger
</span><span class="cx"> else:
</span><span class="lines">@@ -135,7 +135,7 @@
</span><span class="cx"> @param componentSet: the set of components that are to make up the
</span><span class="cx"> recurrence set. These MUST all be components with the same UID
</span><span class="cx"> and type, forming a proper recurring set.
</span><del>- @param limit: L{PyCalendarDateTime} value representing the end of the expansion.
</del><ins>+ @param limit: L{DateTime} value representing the end of the expansion.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Look at each component type
</span><span class="lines">@@ -232,10 +232,10 @@
</span><span class="cx"> if end is None:
</span><span class="cx"> if not start.isDateOnly():
</span><span class="cx"> # Timed event with zero duration
</span><del>- duration = PyCalendarDuration(days=0)
</del><ins>+ duration = Duration(days=0)
</ins><span class="cx"> else:
</span><span class="cx"> # All day event default duration is one day
</span><del>- duration = PyCalendarDuration(days=1)
</del><ins>+ duration = Duration(days=1)
</ins><span class="cx"> end = start + duration
</span><span class="cx"> else:
</span><span class="cx"> duration = differenceDateTime(start, end)
</span><span class="lines">@@ -248,7 +248,7 @@
</span><span class="cx"> Add the specified master VEVENT Component to the instance list, expanding it
</span><span class="cx"> within the supplied time range.
</span><span class="cx"> @param component: the Component to expand
</span><del>- @param limit: the end L{PyCalendarDateTime} for expansion
</del><ins>+ @param limit: the end L{DateTime} for expansion
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> details = self._getMasterEventDetails(component)
</span><span class="lines">@@ -330,7 +330,7 @@
</span><span class="cx"> Add the specified master VTODO Component to the instance list, expanding it
</span><span class="cx"> within the supplied time range.
</span><span class="cx"> @param component: the Component to expand
</span><del>- @param limit: the end L{PyCalendarDateTime} for expansion
</del><ins>+ @param limit: the end L{DateTime} for expansion
</ins><span class="cx"> """
</span><span class="cx"> details = self._getMasterToDoDetails(component)
</span><span class="cx"> if details is None:
</span><span class="lines">@@ -370,7 +370,7 @@
</span><span class="cx"> # than the master DTSTART, and if we exclude those, the associated
</span><span class="cx"> # overridden instances will cause an InvalidOverriddenInstance.
</span><span class="cx"> limited = rrules.expand(rulestart,
</span><del>- PyCalendarPeriod(PyCalendarDateTime(1900, 1, 1), upperlimit), expanded)
</del><ins>+ Period(DateTime(1900, 1, 1), upperlimit), expanded)
</ins><span class="cx"> for startDate in expanded:
</span><span class="cx"> startDate = self.normalizeFunction(startDate)
</span><span class="cx"> endDate = startDate + duration
</span><span class="lines">@@ -478,7 +478,7 @@
</span><span class="cx"> Add the specified master VFREEBUSY Component to the instance list, expanding it
</span><span class="cx"> within the supplied time range.
</span><span class="cx"> @param component: the Component to expand
</span><del>- @param limit: the end L{PyCalendarDateTime} for expansion
</del><ins>+ @param limit: the end L{DateTime} for expansion
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> start = component.getStartDateUTC()
</span><span class="lines">@@ -519,7 +519,7 @@
</span><span class="cx"> depending on the presence of the properties. If unbounded at one or both ends, we will
</span><span class="cx"> set the time to 1/1/1900 in the past and 1/1/3000 in the future.
</span><span class="cx"> @param component: the Component to expand
</span><del>- @param limit: the end L{PyCalendarDateTime} for expansion
</del><ins>+ @param limit: the end L{DateTime} for expansion
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> start = component.getStartDateUTC()
</span><span class="lines">@@ -531,7 +531,7 @@
</span><span class="cx"> # If the availability is beyond the end of the range we want, ignore it
</span><span class="cx"> return
</span><span class="cx"> if start is None:
</span><del>- start = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ start = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> start = self.normalizeFunction(start)
</span><span class="cx">
</span><span class="cx"> end = component.getEndDateUTC()
</span><span class="lines">@@ -539,7 +539,7 @@
</span><span class="cx"> # If the availability is before the start of the range we want, ignore it
</span><span class="cx"> return
</span><span class="cx"> if end is None:
</span><del>- end = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ end = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> end = self.normalizeFunction(end)
</span><span class="cx">
</span><span class="cx"> self.addInstance(Instance(component, start, end))
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavlinkresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/linkresource.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/linkresource.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/linkresource.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -47,16 +47,17 @@
</span><span class="cx"> case of a missing underlying resource (broken link) as indicated by self._linkedResource being None.
</span><span class="cx"> """
</span><span class="cx"> log = Logger()
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self, parent, link_url):
</span><span class="cx"> self.parent = parent
</span><span class="cx"> self.linkURL = link_url
</span><span class="cx"> self.loopDetect = set()
</span><span class="cx"> super(LinkResource, self).__init__(self.parent.principalCollections())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def linkedResource(self, request):
</span><del>-
</del><ins>+
</ins><span class="cx"> if not hasattr(self, "_linkedResource"):
</span><span class="cx"> if self.linkURL in self.loopDetect:
</span><span class="cx"> raise HTTPError(StatusResponse(responsecode.LOOP_DETECTED, "Recursive link target: %s" % (self.linkURL,)))
</span><span class="lines">@@ -67,17 +68,20 @@
</span><span class="cx">
</span><span class="cx"> if self._linkedResource is None:
</span><span class="cx"> raise HTTPError(StatusResponse(responsecode.NOT_FOUND, "Missing link target: %s" % (self.linkURL,)))
</span><del>-
</del><ins>+
</ins><span class="cx"> returnValue(self._linkedResource)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def isCollection(self):
</span><span class="cx"> return True if hasattr(self, "_linkedResource") else False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def resourceType(self):
</span><span class="cx"> return self._linkedResource.resourceType() if hasattr(self, "_linkedResource") else davxml.ResourceType.link
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def locateChild(self, request, segments):
</span><del>-
</del><ins>+
</ins><span class="cx"> def _defer(result):
</span><span class="cx"> if result is None:
</span><span class="cx"> return (self, server.StopTraversal)
</span><span class="lines">@@ -87,6 +91,7 @@
</span><span class="cx"> d.addCallback(_defer)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def renderHTTP(self, request):
</span><span class="cx"> linked_to = (yield self.linkedResource(request))
</span><span class="lines">@@ -95,27 +100,33 @@
</span><span class="cx"> else:
</span><span class="cx"> returnValue(http.StatusResponse(responsecode.OK, "Link resource with missing target: %s" % (self.linkURL,)))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getChild(self, name):
</span><span class="cx"> return self._linkedResource.getChild(name) if hasattr(self, "_linkedResource") else None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def hasProperty(self, property, request):
</span><span class="cx"> hosted = (yield self.linkedResource(request))
</span><span class="cx"> result = (yield hosted.hasProperty(property, request)) if hosted else False
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def readProperty(self, property, request):
</span><span class="cx"> hosted = (yield self.linkedResource(request))
</span><span class="cx"> result = (yield hosted.readProperty(property, request)) if hosted else None
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def writeProperty(self, property, request):
</span><span class="cx"> hosted = (yield self.linkedResource(request))
</span><span class="cx"> result = (yield hosted.writeProperty(property, request)) if hosted else None
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class LinkFollowerMixIn(object):
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -128,6 +139,5 @@
</span><span class="cx"> if linked_to is None:
</span><span class="cx"> break
</span><span class="cx"> resource = linked_to
</span><del>-
</del><ins>+
</ins><span class="cx"> returnValue((resource, path))
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavlocalizationpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/localization.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/localization.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/localization.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -24,7 +24,7 @@
</span><span class="cx"> from locale import normalize
</span><span class="cx">
</span><span class="cx"> from twext.python.log import Logger
</span><del>-from pycalendar.duration import PyCalendarDuration
</del><ins>+from pycalendar.duration import Duration
</ins><span class="cx">
</span><span class="cx"> try:
</span><span class="cx"> from Foundation import (
</span><span class="lines">@@ -89,12 +89,12 @@
</span><span class="cx"> helper methods for date formatting:
</span><span class="cx">
</span><span class="cx"> with translationTo('en') as trans:
</span><del>- print(trans.dtDate(PyCalendarDateTime.getToday()))
</del><ins>+ print(trans.dtDate(DateTime.getToday()))
</ins><span class="cx">
</span><span class="cx"> ... Thursday, October 23, 2008
</span><span class="cx">
</span><span class="cx"> with translationTo('fr') as trans:
</span><del>- print(trans.dtDate(PyCalendarDateTime.getToday()))
</del><ins>+ print(trans.dtDate(DateTime.getToday()))
</ins><span class="cx">
</span><span class="cx"> ... Jeudi, Octobre 23, 2008
</span><span class="cx">
</span><span class="lines">@@ -124,12 +124,13 @@
</span><span class="cx"> localedir=localeDir, languages=[lang, 'en'], fallback=True)
</span><span class="cx"> self.translations[key] = self.translation
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __enter__(self):
</span><span class="cx"> # Get the caller's globals so we can rebind their '_' to our translator
</span><span class="cx"> caller_globals = inspect.stack()[1][0].f_globals
</span><span class="cx">
</span><span class="cx"> # Store whatever '_' is already bound to so we can restore it later
</span><del>- if caller_globals.has_key('_'):
</del><ins>+ if '_' in caller_globals:
</ins><span class="cx"> self.prev = caller_globals['_']
</span><span class="cx">
</span><span class="cx"> # Rebind '_' to our translator
</span><span class="lines">@@ -138,6 +139,7 @@
</span><span class="cx"> # What we return here is accessible to the caller via the 'as' clause
</span><span class="cx"> return self
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __exit__(self, type, value, traceback):
</span><span class="cx"> # Restore '_' if it previously had a value
</span><span class="cx"> if hasattr(self, 'prev'):
</span><span class="lines">@@ -146,13 +148,16 @@
</span><span class="cx"> # Don't swallow exceptions
</span><span class="cx"> return False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def monthAbbreviation(self, monthNumber):
</span><span class="cx"> return self.translation.ugettext(monthsAbbrev[monthNumber])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def date(self, component):
</span><span class="cx"> dtStart = component.propertyValue("DTSTART")
</span><span class="cx"> return self.dtDate(dtStart)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def time(self, component):
</span><span class="cx"> """
</span><span class="cx"> Examples:
</span><span class="lines">@@ -191,9 +196,9 @@
</span><span class="cx"> else:
</span><span class="cx"> if dtStart.isDateOnly():
</span><span class="cx"> dtEnd = None
</span><del>- duration = PyCalendarDuration(days=1)
</del><ins>+ duration = Duration(days=1)
</ins><span class="cx"> else:
</span><del>- dtEnd = dtStart + PyCalendarDuration(days=1)
</del><ins>+ dtEnd = dtStart + Duration(days=1)
</ins><span class="cx"> dtEnd.setHHMMSS(0, 0, 0)
</span><span class="cx"> duration = dtEnd - dtStart
</span><span class="cx">
</span><span class="lines">@@ -225,6 +230,7 @@
</span><span class="cx"> }
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def dtTime(self, val, includeTimezone=True):
</span><span class="cx"> if val.isDateOnly():
</span><span class="cx"> return ""
</span><span class="lines">@@ -252,6 +258,7 @@
</span><span class="cx">
</span><span class="cx"> return result
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def dtDuration(self, val):
</span><span class="cx">
</span><span class="cx"> # Bind to '_' so pygettext.py will pick this up for translation
</span><span class="lines">@@ -265,7 +272,7 @@
</span><span class="cx"> parts.append(_("1 day"))
</span><span class="cx"> elif days > 1:
</span><span class="cx"> parts.append(_("%(dayCount)d days") %
</span><del>- { 'dayCount' : days })
</del><ins>+ {'dayCount' : days})
</ins><span class="cx">
</span><span class="cx"> hours = divmod(total / 3600, 24)[1]
</span><span class="cx"> minutes = divmod(total / 60, 60)[1]
</span><span class="lines">@@ -275,19 +282,19 @@
</span><span class="cx"> parts.append(_("1 hour"))
</span><span class="cx"> elif hours > 1:
</span><span class="cx"> parts.append(_("%(hourCount)d hours") %
</span><del>- { 'hourCount' : hours })
</del><ins>+ {'hourCount' : hours})
</ins><span class="cx">
</span><span class="cx"> if minutes == 1:
</span><span class="cx"> parts.append(_("1 minute"))
</span><span class="cx"> elif minutes > 1:
</span><span class="cx"> parts.append(_("%(minuteCount)d minutes") %
</span><del>- { 'minuteCount' : minutes })
</del><ins>+ {'minuteCount' : minutes})
</ins><span class="cx">
</span><span class="cx"> if seconds == 1:
</span><span class="cx"> parts.append(_("1 second"))
</span><span class="cx"> elif seconds > 1:
</span><span class="cx"> parts.append(_("%(secondCount)d seconds") %
</span><del>- { 'secondCount' : seconds })
</del><ins>+ {'secondCount' : seconds})
</ins><span class="cx">
</span><span class="cx"> return " ".join(parts)
</span><span class="cx">
</span><span class="lines">@@ -398,12 +405,14 @@
</span><span class="cx"> else:
</span><span class="cx"> log.info("%s is up to date" % (moFile,))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class ParseError(Exception):
</span><span class="cx"> pass
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def convertStringsFile(src, dest):
</span><del>- strings = { }
</del><span class="cx">
</span><span class="cx"> dir = os.path.dirname(dest)
</span><span class="cx">
</span><span class="lines">@@ -458,13 +467,13 @@
</span><span class="cx">
</span><span class="cx"> result = struct.pack(
</span><span class="cx"> "Iiiiiii",
</span><del>- 0x950412DEL, # magic number
- 0, # file format revision
- len(originals), # number of strings
- 28, # offset of table with original strings
- 28+len(originals)*8, # offset of table with translation strings
- 0, # size of hashing table
- 0 # offset of hashing table
</del><ins>+ 0x950412DEL, # magic number
+ 0, # file format revision
+ len(originals), # number of strings
+ 28, # offset of table with original strings
+ 28 + len(originals) * 8, # offset of table with translation strings
+ 0, # size of hashing table
+ 0 # offset of hashing table
</ins><span class="cx"> )
</span><span class="cx"> result += array.array("i", keyDescriptors).tostring()
</span><span class="cx"> result += array.array("i", valueDescriptors).tostring()
</span><span class="lines">@@ -475,6 +484,7 @@
</span><span class="cx"> outFile.write(result)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getLanguage(config):
</span><span class="cx"> """
</span><span class="cx"> If the language has been specified explicitly in the config, return it. Otherwise
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmemcachelockpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachelock.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachelock.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachelock.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -15,7 +15,7 @@
</span><span class="cx"> ##
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.memcacher import Memcacher
</span><del>-from twisted.internet.defer import inlineCallbacks, Deferred, returnValue,\
</del><ins>+from twisted.internet.defer import inlineCallbacks, Deferred, returnValue, \
</ins><span class="cx"> succeed
</span><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> import time
</span><span class="lines">@@ -24,7 +24,7 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, namespace, locktoken, timeout=5.0, retry_interval=0.1, expire_time=0):
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> @param namespace: a unique namespace for this lock's tokens
</span><span class="cx"> @type namespace: C{str}
</span><span class="cx"> @param locktoken: the name of the locktoken
</span><span class="lines">@@ -44,31 +44,33 @@
</span><span class="cx"> self._expire_time = expire_time
</span><span class="cx"> self._hasLock = False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _getMemcacheProtocol(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> result = super(MemcacheLock, self)._getMemcacheProtocol()
</span><span class="cx">
</span><span class="cx"> if isinstance(result, Memcacher.nullCacher):
</span><span class="cx"> raise AssertionError("No implementation of shared locking without memcached")
</span><del>-
</del><ins>+
</ins><span class="cx"> return result
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def acquire(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> assert not self._hasLock, "Lock already acquired."
</span><del>-
</del><ins>+
</ins><span class="cx"> timeout_at = time.time() + self._timeout
</span><span class="cx"> waiting = False
</span><span class="cx"> while True:
</span><del>-
</del><ins>+
</ins><span class="cx"> result = (yield self.add(self._locktoken, "1", expireTime=self._expire_time))
</span><span class="cx"> if result:
</span><span class="cx"> self._hasLock = True
</span><span class="cx"> if waiting:
</span><span class="cx"> self.log.debug("Got lock after waiting on %s" % (self._locktoken,))
</span><span class="cx"> break
</span><del>-
</del><ins>+
</ins><span class="cx"> if self._timeout and time.time() < timeout_at:
</span><span class="cx"> waiting = True
</span><span class="cx"> self.log.debug("Waiting for lock on %s" % (self._locktoken,))
</span><span class="lines">@@ -80,13 +82,14 @@
</span><span class="cx"> else:
</span><span class="cx"> self.log.debug("Timed out lock after waiting on %s" % (self._locktoken,))
</span><span class="cx"> raise MemcacheLockTimeoutError()
</span><del>-
</del><ins>+
</ins><span class="cx"> returnValue(True)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def release(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> assert self._hasLock, "Lock not acquired."
</span><del>-
</del><ins>+
</ins><span class="cx"> def _done(result):
</span><span class="cx"> self._hasLock = False
</span><span class="cx"> return result
</span><span class="lines">@@ -95,18 +98,20 @@
</span><span class="cx"> d.addCallback(_done)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def clean(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> if self._hasLock:
</span><span class="cx"> return self.release()
</span><span class="cx"> else:
</span><span class="cx"> return succeed(True)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def locked(self):
</span><span class="cx"> """
</span><span class="cx"> Test if the lock is currently being held.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def _gotit(value):
</span><span class="cx"> return value is not None
</span><span class="cx">
</span><span class="lines">@@ -114,5 +119,7 @@
</span><span class="cx"> d.addCallback(_gotit)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class MemcacheLockTimeoutError(Exception):
</span><span class="cx"> pass
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmemcachepoolpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachepool.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachepool.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/memcachepool.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -99,6 +99,7 @@
</span><span class="cx"> connector,
</span><span class="cx"> reason)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def buildProtocol(self, addr):
</span><span class="cx"> """
</span><span class="cx"> Attach the C{self.connectionPool} to the protocol so it can tell it,
</span><span class="lines">@@ -167,6 +168,7 @@
</span><span class="cx"> self._pendingConnects = 0
</span><span class="cx"> self._commands = []
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _isIdle(self):
</span><span class="cx"> return (
</span><span class="cx"> len(self._busyClients) == 0 and
</span><span class="lines">@@ -174,6 +176,7 @@
</span><span class="cx"> self._pendingConnects == 0
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _shutdownCallback(self):
</span><span class="cx"> self.shutdown_requested = True
</span><span class="cx"> if self._isIdle():
</span><span class="lines">@@ -181,13 +184,14 @@
</span><span class="cx"> self.shutdown_deferred = Deferred()
</span><span class="cx"> return self.shutdown_deferred
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _newClientConnection(self):
</span><span class="cx"> """
</span><span class="cx"> Create a new client connection.
</span><span class="cx">
</span><span class="cx"> @return: A L{Deferred} that fires with the L{IProtocol} instance.
</span><span class="cx"> """
</span><del>- self.log.debug("Initating new client connection to: %r" % (
</del><ins>+ self.log.debug("Initiating new client connection to: %r" % (
</ins><span class="cx"> self._endpoint,))
</span><span class="cx"> self._logClientStats()
</span><span class="cx">
</span><span class="lines">@@ -219,7 +223,7 @@
</span><span class="cx">
</span><span class="cx"> @param command: A C{str} representing an attribute of
</span><span class="cx"> L{MemCacheProtocol}.
</span><del>- @parma args: Any positional arguments that should be passed to
</del><ins>+ @param args: Any positional arguments that should be passed to
</ins><span class="cx"> C{command}.
</span><span class="cx"> @param kwargs: Any keyword arguments that should be passed to
</span><span class="cx"> C{command}.
</span><span class="lines">@@ -258,7 +262,7 @@
</span><span class="cx">
</span><span class="cx"> @param command: A C{str} representing an attribute of
</span><span class="cx"> L{MemCacheProtocol}.
</span><del>- @parma args: Any positional arguments that should be passed to
</del><ins>+ @param args: Any positional arguments that should be passed to
</ins><span class="cx"> C{command}.
</span><span class="cx"> @param kwargs: Any keyword arguments that should be passed to
</span><span class="cx"> C{command}.
</span><span class="lines">@@ -371,24 +375,31 @@
</span><span class="cx"> def get(self, *args, **kwargs):
</span><span class="cx"> return self.performRequest('get', *args, **kwargs)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def set(self, *args, **kwargs):
</span><span class="cx"> return self.performRequest('set', *args, **kwargs)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def checkAndSet(self, *args, **kwargs):
</span><span class="cx"> return self.performRequest('checkAndSet', *args, **kwargs)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def delete(self, *args, **kwargs):
</span><span class="cx"> return self.performRequest('delete', *args, **kwargs)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def add(self, *args, **kwargs):
</span><span class="cx"> return self.performRequest('add', *args, **kwargs)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def incr(self, *args, **kwargs):
</span><span class="cx"> return self.performRequest('increment', *args, **kwargs)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def decr(self, *args, **kwargs):
</span><span class="cx"> return self.performRequest('decrement', *args, **kwargs)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def flushAll(self, *args, **kwargs):
</span><span class="cx"> return self.performRequest('flushAll', *args, **kwargs)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmethodgetpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/get.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/get.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/get.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -25,7 +25,7 @@
</span><span class="cx"> from txdav.xml import element as davxml
</span><span class="cx"> from twext.web2.dav.http import ErrorResponse
</span><span class="cx"> from twext.web2.dav.util import parentForURL
</span><del>-from twext.web2.http import HTTPError
</del><ins>+from twext.web2.http import HTTPError, StatusResponse
</ins><span class="cx"> from twext.web2.http import Response
</span><span class="cx"> from twext.web2.http_headers import MimeType
</span><span class="cx"> from twext.web2.stream import MemoryStream
</span><span class="lines">@@ -34,8 +34,10 @@
</span><span class="cx"> from twistedcaldav.customxml import calendarserver_namespace
</span><span class="cx"> from twistedcaldav.datafilters.hiddeninstance import HiddenInstanceFilter
</span><span class="cx"> from twistedcaldav.datafilters.privateevents import PrivateEventFilter
</span><ins>+from twistedcaldav.ical import Component
</ins><span class="cx"> from twistedcaldav.resource import isPseudoCalendarCollectionResource, \
</span><span class="cx"> CalDAVResource
</span><ins>+from twistedcaldav.util import bestAcceptType
</ins><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def http_GET(self, request):
</span><span class="lines">@@ -70,6 +72,8 @@
</span><span class="cx"> returnValue(response)
</span><span class="cx">
</span><span class="cx"> else:
</span><ins>+ # FIXME: this should be implemented in storebridge.CalendarObject.render
+
</ins><span class="cx"> # Look for calendar access restriction on existing resource.
</span><span class="cx"> parentURL = parentForURL(request.uri)
</span><span class="cx"> parent = (yield request.locateResource(parentURL))
</span><span class="lines">@@ -78,6 +82,11 @@
</span><span class="cx"> # Check authorization first
</span><span class="cx"> yield self.authorize(request, (davxml.Read(),))
</span><span class="cx">
</span><ins>+ # Accept header handling
+ accepted_type = bestAcceptType(request.headers.getHeader("accept"), Component.allowedTypes())
+ if accepted_type is None:
+ raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))
+
</ins><span class="cx"> caldata = (yield self.iCalendarForUser(request))
</span><span class="cx">
</span><span class="cx"> # Filter any attendee hidden instances
</span><span class="lines">@@ -92,8 +101,8 @@
</span><span class="cx"> caldata = PrivateEventFilter(self.accessMode, isowner).filter(caldata)
</span><span class="cx">
</span><span class="cx"> response = Response()
</span><del>- response.stream = MemoryStream(caldata.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference))
- response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8"))
</del><ins>+ response.stream = MemoryStream(caldata.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference, format=accepted_type))
+ response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))
</ins><span class="cx">
</span><span class="cx"> # Add Schedule-Tag header if property is present
</span><span class="cx"> if self.scheduleTag:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmethodpropfindpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/propfind.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/propfind.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/propfind.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -98,7 +98,7 @@
</span><span class="cx"> search_properties = "names"
</span><span class="cx"> elif isinstance(container, davxml.PropertyContainer):
</span><span class="cx"> properties = container.children
</span><del>- search_properties = [(p.namespace, p.name) for p in properties]
</del><ins>+ search_properties = properties
</ins><span class="cx"> else:
</span><span class="cx"> raise AssertionError("Unexpected element type in %s: %s"
</span><span class="cx"> % (davxml.PropertyFind.sname(), container))
</span><span class="lines">@@ -245,7 +245,11 @@
</span><span class="cx"> # Utilities
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-def propertyName(name):
</del><ins>+def propertyName(prop):
+ if type(prop) is tuple:
+ name = prop
+ else:
+ name = prop.qname()
</ins><span class="cx"> property_namespace, property_name = name
</span><span class="cx"> pname = davxml.WebDAVUnknownElement()
</span><span class="cx"> pname.namespace = property_namespace
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmethodreport_calendar_querypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_calendar_query.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_calendar_query.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_calendar_query.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -32,7 +32,8 @@
</span><span class="cx"> from twext.web2.http import HTTPError, StatusResponse
</span><span class="cx">
</span><span class="cx"> from twistedcaldav import caldavxml
</span><del>-from twistedcaldav.caldavxml import caldav_namespace, MaxInstances
</del><ins>+from twistedcaldav.caldavxml import caldav_namespace, MaxInstances, \
+ CalendarTimeZone
</ins><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from txdav.common.icommondatastore import IndexedSearchException, \
</span><span class="cx"> ConcurrentModification
</span><span class="lines">@@ -171,10 +172,10 @@
</span><span class="cx"> if calresource.isPseudoCalendarCollection():
</span><span class="cx"> # Get the timezone property from the collection if one was not set in the query,
</span><span class="cx"> # and store in the query filter for later use
</span><del>- has_prop = (yield calresource.hasProperty((caldav_namespace, "calendar-timezone"), request))
</del><ins>+ has_prop = (yield calresource.hasProperty(CalendarTimeZone(), request))
</ins><span class="cx"> timezone = query_timezone
</span><span class="cx"> if query_tz is None and has_prop:
</span><del>- tz = (yield calresource.readProperty((caldav_namespace, "calendar-timezone"), request))
</del><ins>+ tz = (yield calresource.readProperty(CalendarTimeZone(), request))
</ins><span class="cx"> filter.settimezone(tz)
</span><span class="cx"> timezone = tuple(tz.calendar().subcomponents())[0]
</span><span class="cx">
</span><span class="lines">@@ -233,9 +234,9 @@
</span><span class="cx"> parent = (yield calresource.locateParent(request, uri))
</span><span class="cx"> assert parent is not None and parent.isPseudoCalendarCollection()
</span><span class="cx">
</span><del>- has_prop = (yield parent.hasProperty((caldav_namespace, "calendar-timezone"), request))
</del><ins>+ has_prop = (yield parent.hasProperty(CalendarTimeZone(), request))
</ins><span class="cx"> if has_prop:
</span><del>- tz = (yield parent.readProperty((caldav_namespace, "calendar-timezone"), request))
</del><ins>+ tz = (yield parent.readProperty(CalendarTimeZone(), request))
</ins><span class="cx"> filter.settimezone(tz)
</span><span class="cx"> timezone = tuple(tz.calendar().subcomponents())[0]
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmethodreport_commonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_common.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_common.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_common.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -52,7 +52,7 @@
</span><span class="cx">
</span><span class="cx"> from twistedcaldav import caldavxml
</span><span class="cx"> from twistedcaldav import carddavxml
</span><del>-from twistedcaldav.caldavxml import caldav_namespace, CalendarData, TimeRange
</del><ins>+from twistedcaldav.caldavxml import CalendarData, CalendarTimeZone, TimeRange
</ins><span class="cx"> from twistedcaldav.carddavxml import AddressData
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.datafilters.calendardata import CalendarDataFilter
</span><span class="lines">@@ -69,10 +69,10 @@
</span><span class="cx">
</span><span class="cx"> from txdav.common.icommondatastore import IndexedSearchException
</span><span class="cx">
</span><del>-from pycalendar.duration import PyCalendarDuration
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
-from pycalendar.period import PyCalendarPeriod
</del><ins>+from pycalendar.duration import Duration
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
+from pycalendar.period import Period
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -299,7 +299,7 @@
</span><span class="cx"> generate_calendar_data = False
</span><span class="cx"> for property in prop.children:
</span><span class="cx"> if isinstance(property, caldavxml.CalendarData):
</span><del>- if not property.verifyTypeVersion([("text/calendar", "2.0")]):
</del><ins>+ if not property.verifyTypeVersion():
</ins><span class="cx"> result = False
</span><span class="cx"> message = "Calendar-data element type/version not supported: content-type: %s, version: %s" % (property.content_type, property.version)
</span><span class="cx"> generate_calendar_data = True
</span><span class="lines">@@ -324,7 +324,7 @@
</span><span class="cx"> generate_address_data = False
</span><span class="cx"> for property in prop.children:
</span><span class="cx"> if isinstance(property, carddavxml.AddressData):
</span><del>- if not property.verifyTypeVersion([("text/vcard", "3.0")]):
</del><ins>+ if not property.verifyTypeVersion():
</ins><span class="cx"> result = False
</span><span class="cx"> message = "Address-data element type/version not supported: content-type: %s, version: %s" % (property.content_type, property.version)
</span><span class="cx"> generate_address_data = True
</span><span class="lines">@@ -371,7 +371,7 @@
</span><span class="cx"> filtered = HiddenInstanceFilter().filter(calendar)
</span><span class="cx"> filtered = PrivateEventFilter(resource.accessMode, isowner).filter(filtered)
</span><span class="cx"> filtered = CalendarDataFilter(property, timezone).filter(filtered)
</span><del>- propvalue = CalendarData().fromCalendar(filtered)
</del><ins>+ propvalue = CalendarData.fromCalendar(filtered, format=property.content_type)
</ins><span class="cx"> properties_by_status[responsecode.OK].append(propvalue)
</span><span class="cx"> continue
</span><span class="cx">
</span><span class="lines">@@ -379,7 +379,7 @@
</span><span class="cx"> if vcard is None:
</span><span class="cx"> vcard = (yield resource.vCard())
</span><span class="cx"> filtered = AddressDataFilter(property).filter(vcard)
</span><del>- propvalue = AddressData().fromAddress(filtered)
</del><ins>+ propvalue = AddressData.fromAddress(filtered, format=property.content_type)
</ins><span class="cx"> properties_by_status[responsecode.OK].append(propvalue)
</span><span class="cx"> continue
</span><span class="cx">
</span><span class="lines">@@ -392,7 +392,7 @@
</span><span class="cx">
</span><span class="cx"> if has:
</span><span class="cx"> try:
</span><del>- prop = (yield resource.readProperty(qname, request))
</del><ins>+ prop = (yield resource.readProperty(property, request))
</ins><span class="cx"> if prop is not None:
</span><span class="cx"> properties_by_status[responsecode.OK].append(prop)
</span><span class="cx"> elif not returnMinimal:
</span><span class="lines">@@ -436,8 +436,8 @@
</span><span class="cx"> if entry:
</span><span class="cx">
</span><span class="cx"> # Offset one day at either end to account for floating
</span><del>- cached_start = entry.timerange.start + PyCalendarDuration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
- cached_end = entry.timerange.end - PyCalendarDuration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
</del><ins>+ cached_start = entry.timerange.start + Duration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
+ cached_end = entry.timerange.end - Duration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
</ins><span class="cx">
</span><span class="cx"> # Verify that the requested timerange lies within the cache timerange
</span><span class="cx"> if compareDateTime(timerange.end, cached_end) <= 0 and compareDateTime(timerange.start, cached_start) >= 0:
</span><span class="lines">@@ -515,9 +515,9 @@
</span><span class="cx"> useruid = ""
</span><span class="cx">
</span><span class="cx"> # Get the timezone property from the collection.
</span><del>- has_prop = (yield calresource.hasProperty((caldav_namespace, "calendar-timezone"), request))
</del><ins>+ has_prop = (yield calresource.hasProperty(CalendarTimeZone(), request))
</ins><span class="cx"> if has_prop:
</span><del>- tz = (yield calresource.readProperty((caldav_namespace, "calendar-timezone"), request))
</del><ins>+ tz = (yield calresource.readProperty(CalendarTimeZone(), request))
</ins><span class="cx"> else:
</span><span class="cx"> tz = None
</span><span class="cx">
</span><span class="lines">@@ -559,8 +559,8 @@
</span><span class="cx"> request.extendedLogItems["fb-uncached"] = request.extendedLogItems.get("fb-uncached", 0) + 1
</span><span class="cx">
</span><span class="cx"> # We want to cache a large range of time based on the current date
</span><del>- cache_start = normalizeToUTC(PyCalendarDateTime.getToday() + PyCalendarDuration(days=0 - config.FreeBusyCacheDaysBack))
- cache_end = normalizeToUTC(PyCalendarDateTime.getToday() + PyCalendarDuration(days=config.FreeBusyCacheDaysForward))
</del><ins>+ cache_start = normalizeToUTC(DateTime.getToday() + Duration(days=0 - config.FreeBusyCacheDaysBack))
+ cache_end = normalizeToUTC(DateTime.getToday() + Duration(days=config.FreeBusyCacheDaysForward))
</ins><span class="cx">
</span><span class="cx"> # If the requested timerange would fit in our allowed cache range, trigger the cache creation
</span><span class="cx"> if compareDateTime(timerange.start, cache_start) >= 0 and compareDateTime(timerange.end, cache_end) <= 0:
</span><span class="lines">@@ -602,7 +602,7 @@
</span><span class="cx"> request.extendedLogItems["fb-cached"] = request.extendedLogItems.get("fb-cached", 0) + 1
</span><span class="cx">
</span><span class="cx"> # Determine appropriate timezone (UTC is the default)
</span><del>- tzinfo = tz.gettimezone() if tz is not None else PyCalendarTimezone(utc=True)
</del><ins>+ tzinfo = tz.gettimezone() if tz is not None else Timezone(utc=True)
</ins><span class="cx">
</span><span class="cx"> # We care about separate instances for VEVENTs only
</span><span class="cx"> aggregated_resources = {}
</span><span class="lines">@@ -645,15 +645,15 @@
</span><span class="cx"> if float == 'Y':
</span><span class="cx"> fbstart.setTimezone(tzinfo)
</span><span class="cx"> else:
</span><del>- fbstart.setTimezone(PyCalendarTimezone(utc=True))
</del><ins>+ fbstart.setTimezone(Timezone(utc=True))
</ins><span class="cx"> fbend = parseSQLTimestampToPyCalendar(end)
</span><span class="cx"> if float == 'Y':
</span><span class="cx"> fbend.setTimezone(tzinfo)
</span><span class="cx"> else:
</span><del>- fbend.setTimezone(PyCalendarTimezone(utc=True))
</del><ins>+ fbend.setTimezone(Timezone(utc=True))
</ins><span class="cx">
</span><span class="cx"> # Clip instance to time range
</span><del>- clipped = clipPeriod(PyCalendarPeriod(fbstart, duration=fbend - fbstart), PyCalendarPeriod(timerange.start, timerange.end))
</del><ins>+ clipped = clipPeriod(Period(fbstart, duration=fbend - fbstart), Period(timerange.start, timerange.end))
</ins><span class="cx">
</span><span class="cx"> # Double check for overlap
</span><span class="cx"> if clipped:
</span><span class="lines">@@ -735,7 +735,7 @@
</span><span class="cx"> @param timerange: the time-range in which to expand
</span><span class="cx"> @type timerange: L{TimeRange}
</span><span class="cx"> @param tzinfo: timezone for floating time calculations
</span><del>- @type tzinfo: L{PyCalendarTimezone}
</del><ins>+ @type tzinfo: L{Timezone}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # First expand the component
</span><span class="lines">@@ -775,7 +775,7 @@
</span><span class="cx"> @param calendar: the L{Component} that is the VCALENDAR containing the VEVENT's.
</span><span class="cx"> @param fbinfo: the tuple used to store the three types of fb data.
</span><span class="cx"> @param timerange: the time range to restrict free busy data to.
</span><del>- @param tzinfo: the L{PyCalendarTimezone} for the timezone to use for floating/all-day events.
</del><ins>+ @param tzinfo: the L{Timezone} for the timezone to use for floating/all-day events.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Expand out the set of instances for the event with in the required range
</span><span class="lines">@@ -820,10 +820,10 @@
</span><span class="cx"> # Clip period for this instance - use duration for period end if that
</span><span class="cx"> # is what original component used
</span><span class="cx"> if instance.component.hasProperty("DURATION"):
</span><del>- period = PyCalendarPeriod(fbstart, duration=fbend - fbstart)
</del><ins>+ period = Period(fbstart, duration=fbend - fbstart)
</ins><span class="cx"> else:
</span><del>- period = PyCalendarPeriod(fbstart, fbend)
- clipped = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
</del><ins>+ period = Period(fbstart, fbend)
+ clipped = clipPeriod(period, Period(timerange.start, timerange.end))
</ins><span class="cx">
</span><span class="cx"> # Double check for overlap
</span><span class="cx"> if clipped:
</span><span class="lines">@@ -861,7 +861,7 @@
</span><span class="cx"> assert isinstance(fb.value(), list), "FREEBUSY property does not contain a list of values: %r" % (fb,)
</span><span class="cx"> for period in fb.value():
</span><span class="cx"> # Clip period for this instance
</span><del>- clipped = clipPeriod(period.getValue(), PyCalendarPeriod(timerange.start, timerange.end))
</del><ins>+ clipped = clipPeriod(period.getValue(), Period(timerange.start, timerange.end))
</ins><span class="cx"> if clipped:
</span><span class="cx"> fbinfo[fbtype_mapper.get(fbtype, 0)].append(clipped)
</span><span class="cx">
</span><span class="lines">@@ -880,12 +880,12 @@
</span><span class="cx"> # Get overall start/end
</span><span class="cx"> start = vav.getStartDateUTC()
</span><span class="cx"> if start is None:
</span><del>- start = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ start = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> end = vav.getEndDateUTC()
</span><span class="cx"> if end is None:
</span><del>- end = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- period = PyCalendarPeriod(start, end)
- overall = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
</del><ins>+ end = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ period = Period(start, end)
+ overall = clipPeriod(period, Period(timerange.start, timerange.end))
</ins><span class="cx"> if overall is None:
</span><span class="cx"> continue
</span><span class="cx">
</span><span class="lines">@@ -897,10 +897,10 @@
</span><span class="cx"> last_end = timerange.start
</span><span class="cx"> for period in periods:
</span><span class="cx"> if last_end < period.getStart():
</span><del>- busyperiods.append(PyCalendarPeriod(last_end, period.getStart()))
</del><ins>+ busyperiods.append(Period(last_end, period.getStart()))
</ins><span class="cx"> last_end = period.getEnd()
</span><span class="cx"> if last_end < timerange.end:
</span><del>- busyperiods.append(PyCalendarPeriod(last_end, timerange.end))
</del><ins>+ busyperiods.append(Period(last_end, timerange.end))
</ins><span class="cx">
</span><span class="cx"> # Add to actual results mapped by busy type
</span><span class="cx"> fbtype = vav.propertyValue("BUSYTYPE")
</span><span class="lines">@@ -947,10 +947,10 @@
</span><span class="cx"> # Clip period for this instance - use duration for period end if that
</span><span class="cx"> # is what original component used
</span><span class="cx"> if instance.component.hasProperty("DURATION"):
</span><del>- period = PyCalendarPeriod(start, duration=end - start)
</del><ins>+ period = Period(start, duration=end - start)
</ins><span class="cx"> else:
</span><del>- period = PyCalendarPeriod(start, end)
- clipped = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
</del><ins>+ period = Period(start, end)
+ clipped = clipPeriod(period, Period(timerange.start, timerange.end))
</ins><span class="cx"> if clipped:
</span><span class="cx"> periods.append(clipped)
</span><span class="cx">
</span><span class="lines">@@ -993,7 +993,7 @@
</span><span class="cx"> fb.addProperty(attendee)
</span><span class="cx"> fb.addProperty(Property("DTSTART", timerange.start))
</span><span class="cx"> fb.addProperty(Property("DTEND", timerange.end))
</span><del>- fb.addProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
</del><ins>+ fb.addProperty(Property("DTSTAMP", DateTime.getNowUTC()))
</ins><span class="cx"> if len(fbinfo[0]) != 0:
</span><span class="cx"> fb.addProperty(Property("FREEBUSY", fbinfo[0], {"FBTYPE": "BUSY"}))
</span><span class="cx"> if len(fbinfo[1]) != 0:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmethodreport_freebusypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_freebusy.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_freebusy.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/method/report_freebusy.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -31,7 +31,9 @@
</span><span class="cx"> from twext.web2.stream import MemoryStream
</span><span class="cx">
</span><span class="cx"> from twistedcaldav import caldavxml
</span><ins>+from twistedcaldav.ical import Component
</ins><span class="cx"> from twistedcaldav.method import report_common
</span><ins>+from twistedcaldav.util import bestAcceptType
</ins><span class="cx">
</span><span class="cx"> from txdav.caldav.icalendarstore import TimeRangeLowerLimit, TimeRangeUpperLimit
</span><span class="cx"> from txdav.xml import element as davxml
</span><span class="lines">@@ -60,6 +62,11 @@
</span><span class="cx">
</span><span class="cx"> matchcount = [0]
</span><span class="cx">
</span><ins>+ accepted_type = bestAcceptType(request.headers.getHeader("accept"), Component.allowedTypes())
+ if accepted_type is None:
+ raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))
+
+
</ins><span class="cx"> def generateFreeBusyInfo(calresource, uri): #@UnusedVariable
</span><span class="cx"> """
</span><span class="cx"> Run a free busy report on the specified calendar collection
</span><span class="lines">@@ -104,7 +111,7 @@
</span><span class="cx"> fbcalendar = report_common.buildFreeBusyResult(fbinfo, timerange)
</span><span class="cx">
</span><span class="cx"> response = Response()
</span><del>- response.stream = MemoryStream(str(fbcalendar))
- response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8"))
</del><ins>+ response.stream = MemoryStream(fbcalendar.getText(accepted_type))
+ response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))
</ins><span class="cx">
</span><span class="cx"> returnValue(response)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavmkcolxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/mkcolxml.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/mkcolxml.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/mkcolxml.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -45,11 +45,12 @@
</span><span class="cx"> """
</span><span class="cx"> name = "mkcol"
</span><span class="cx">
</span><del>- allowed_children = { (davxml.dav_namespace, "set"): (0, 1) }
</del><ins>+ allowed_children = {(davxml.dav_namespace, "set"): (0, 1)}
</ins><span class="cx">
</span><del>- child_types = { "WebDAVUnknownElement": (0, None) }
</del><ins>+ child_types = {"WebDAVUnknownElement": (0, None)}
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> class MakeCollectionResponse (davxml.WebDAVElement):
</span><span class="cx"> """
</span><span class="lines">@@ -58,4 +59,4 @@
</span><span class="cx"> """
</span><span class="cx"> name = "mkcol-response"
</span><span class="cx">
</span><del>- allowed_children = { davxml.WebDAVElement: (0, None) }
</del><ins>+ allowed_children = {davxml.WebDAVElement: (0, None)}
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavnotificationspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/notifications.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/notifications.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/notifications.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -48,43 +48,53 @@
</span><span class="cx"> self._parent = parent
</span><span class="cx"> CalDAVResource.__init__(self)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def principalCollections(self):
</span><span class="cx"> return self._parent.principalCollections()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def isCollection(self):
</span><span class="cx"> return False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def resourceName(self):
</span><span class="cx"> raise NotImplementedError
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def http_PUT(self, request):
</span><span class="cx"> return responsecode.FORBIDDEN
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def http_DELETE(self, request):
</span><del>-
</del><ins>+
</ins><span class="cx"> response = (yield super(NotificationResource, self).http_DELETE(request))
</span><span class="cx"> if response == responsecode.NO_CONTENT:
</span><span class="cx"> yield self._parent.removedNotifictionMessage(request, self.resourceName())
</span><span class="cx"> returnValue(response)
</span><del>-
</del><ins>+
+
+
</ins><span class="cx"> class NotificationCollectionResource(ReadOnlyNoCopyResourceMixIn, CalDAVResource):
</span><span class="cx">
</span><span class="cx"> def notificationsDB(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> if not hasattr(self, "_notificationsDB"):
</span><span class="cx"> self._notificationsDB = NotificationsDatabase(self)
</span><span class="cx"> return self._notificationsDB
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def isCollection(self):
</span><span class="cx"> return True
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def resourceType(self):
</span><span class="cx"> return davxml.ResourceType.notification
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def addNotification(self, request, uid, xmltype, xmldata):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Write data to file
</span><span class="cx"> rname = uid + ".xml"
</span><span class="cx"> yield self._writeNotification(request, uid, rname, xmltype, xmldata)
</span><span class="lines">@@ -103,7 +113,7 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def deleteNotifictionMessageByUID(self, request, uid):
</span><del>-
</del><ins>+
</ins><span class="cx"> # See if it exists and delete the resource
</span><span class="cx"> record = yield self.notificationsDB().recordForUID(uid)
</span><span class="cx"> if record:
</span><span class="lines">@@ -117,7 +127,7 @@
</span><span class="cx"> record = yield self.notificationsDB().recordForName(rname)
</span><span class="cx"> if record:
</span><span class="cx"> yield self.deleteNotification(request, record)
</span><del>-
</del><ins>+
</ins><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -131,13 +141,16 @@
</span><span class="cx"> return maybeDeferred(self.notificationsDB().removeRecordForName, rname)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class NotificationRecord(object):
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self, uid, name, xmltype):
</span><span class="cx"> self.uid = uid
</span><span class="cx"> self.name = name
</span><span class="cx"> self.xmltype = xmltype
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class NotificationsDatabase(AbstractSQLDatabase):
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -154,35 +167,40 @@
</span><span class="cx"> db_filename = os.path.join(self.resource.fp.path, NotificationsDatabase.db_basename)
</span><span class="cx"> super(NotificationsDatabase, self).__init__(db_filename, True, autocommit=True)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def allRecords(self):
</span><del>-
</del><ins>+
</ins><span class="cx"> records = self._db_execute("select * from NOTIFICATIONS")
</span><span class="cx"> return [self._makeRecord(row) for row in (records if records is not None else ())]
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def recordForUID(self, uid):
</span><del>-
</del><ins>+
</ins><span class="cx"> row = self._db_execute("select * from NOTIFICATIONS where UID = :1", uid)
</span><span class="cx"> return self._makeRecord(row[0]) if row else None
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def addOrUpdateRecord(self, record):
</span><span class="cx">
</span><span class="cx"> self._db_execute("""insert or replace into NOTIFICATIONS (UID, NAME, TYPE)
</span><span class="cx"> values (:1, :2, :3)
</span><span class="cx"> """, record.uid, record.name, record.xmltype,
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> self._db_execute(
</span><span class="cx"> """
</span><span class="cx"> insert or replace into REVISIONS (NAME, REVISION, DELETED)
</span><span class="cx"> values (:1, :2, :3)
</span><span class="cx"> """, record.name, self.bumpRevision(fast=True), 'N',
</span><span class="cx"> )
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def removeRecordForUID(self, uid):
</span><span class="cx">
</span><span class="cx"> record = self.recordForUID(uid)
</span><span class="cx"> self.removeRecordForName(record.name)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def removeRecordForName(self, rname):
</span><span class="cx">
</span><span class="cx"> self._db_execute("delete from NOTIFICATIONS where NAME = :1", rname)
</span><span class="lines">@@ -192,12 +210,13 @@
</span><span class="cx"> where NAME = :3
</span><span class="cx"> """, self.bumpRevision(fast=True), 'Y', rname
</span><span class="cx"> )
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def whatchanged(self, revision):
</span><span class="cx">
</span><span class="cx"> results = [(name.encode("utf-8"), deleted) for name, deleted in self._db_execute("select NAME, DELETED from REVISIONS where REVISION > :1", revision)]
</span><del>- results.sort(key=lambda x:x[1])
-
</del><ins>+ results.sort(key=lambda x: x[1])
+
</ins><span class="cx"> changed = []
</span><span class="cx"> deleted = []
</span><span class="cx"> for name, wasdeleted in results:
</span><span class="lines">@@ -209,14 +228,16 @@
</span><span class="cx"> changed.append(name)
</span><span class="cx"> else:
</span><span class="cx"> raise SyncTokenValidException
</span><del>-
</del><ins>+
</ins><span class="cx"> return changed, deleted,
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lastRevision(self):
</span><span class="cx"> return self._db_value_for_sql(
</span><span class="cx"> "select REVISION from REVISION_SEQUENCE"
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def bumpRevision(self, fast=False):
</span><span class="cx"> self._db_execute(
</span><span class="cx"> """
</span><span class="lines">@@ -230,18 +251,21 @@
</span><span class="cx"> """,
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_version(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the schema version assigned to this index.
</span><span class="cx"> """
</span><span class="cx"> return NotificationsDatabase.schema_version
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_type(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the collection type assigned to this index.
</span><span class="cx"> """
</span><span class="cx"> return NotificationsDatabase.db_type
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_init_data_tables(self, q):
</span><span class="cx"> """
</span><span class="cx"> Initialise the underlying database tables.
</span><span class="lines">@@ -302,6 +326,7 @@
</span><span class="cx"> """
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_upgrade_data_tables(self, q, old_version):
</span><span class="cx"> """
</span><span class="cx"> Upgrade the data from an older version of the DB.
</span><span class="lines">@@ -310,7 +335,7 @@
</span><span class="cx"> # Nothing to do as we have not changed the schema
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _makeRecord(self, row):
</span><del>-
</del><ins>+
</ins><span class="cx"> return NotificationRecord(*[str(item) if type(item) == types.UnicodeType else item for item in row])
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavquerycalendarqueryfilterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/calendarqueryfilter.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/calendarqueryfilter.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/calendarqueryfilter.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -28,8 +28,8 @@
</span><span class="cx"> from twistedcaldav.dateops import timeRangesOverlap
</span><span class="cx"> from twistedcaldav.ical import Component, Property
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -89,7 +89,7 @@
</span><span class="cx"> instances = None
</span><span class="cx"> else:
</span><span class="cx"> # Expand the instances up to infinity
</span><del>- instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), ignoreInvalidInstances=True)
</del><ins>+ instances = component.expandTimeRanges(DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)), ignoreInvalidInstances=True)
</ins><span class="cx"> else:
</span><span class="cx"> instances = component.expandTimeRanges(maxend, ignoreInvalidInstances=True)
</span><span class="cx"> else:
</span><span class="lines">@@ -117,7 +117,7 @@
</span><span class="cx"> Set the default timezone to use with this query.
</span><span class="cx"> @param calendar: a L{Component} for the VCALENDAR containing the one
</span><span class="cx"> VTIMEZONE that we want
</span><del>- @return: the L{PyCalendarTimezone} derived from the VTIMEZONE or utc.
</del><ins>+ @return: the L{Timezone} derived from the VTIMEZONE or utc.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> if tzelement is None:
</span><span class="lines">@@ -127,7 +127,7 @@
</span><span class="cx"> elif isinstance(tzelement, Component):
</span><span class="cx"> tz = tzelement.gettimezone()
</span><span class="cx"> if tz is None:
</span><del>- tz = PyCalendarTimezone(utc=True)
</del><ins>+ tz = Timezone(utc=True)
</ins><span class="cx"> self.child.settzinfo(tz)
</span><span class="cx"> return tz
</span><span class="cx">
</span><span class="lines">@@ -357,7 +357,7 @@
</span><span class="cx"> def settzinfo(self, tzinfo):
</span><span class="cx"> """
</span><span class="cx"> Set the default timezone to use with this query.
</span><del>- @param tzinfo: a L{PyCalendarTimezone} to use.
</del><ins>+ @param tzinfo: a L{Timezone} to use.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Give tzinfo to any TimeRange we have
</span><span class="lines">@@ -374,7 +374,7 @@
</span><span class="cx"> Get the date farthest into the future in any time-range elements
</span><span class="cx">
</span><span class="cx"> @param currentMaximum: current future value to compare with
</span><del>- @type currentMaximum: L{PyCalendarDateTime}
</del><ins>+ @type currentMaximum: L{DateTime}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Give tzinfo to any TimeRange we have
</span><span class="lines">@@ -471,7 +471,7 @@
</span><span class="cx"> def settzinfo(self, tzinfo):
</span><span class="cx"> """
</span><span class="cx"> Set the default timezone to use with this query.
</span><del>- @param tzinfo: a L{PyCalendarTimezone} to use.
</del><ins>+ @param tzinfo: a L{Timezone} to use.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Give tzinfo to any TimeRange we have
</span><span class="lines">@@ -484,7 +484,7 @@
</span><span class="cx"> Get the date farthest into the future in any time-range elements
</span><span class="cx">
</span><span class="cx"> @param currentMaximum: current future value to compare with
</span><del>- @type currentMaximum: L{PyCalendarDateTime}
</del><ins>+ @type currentMaximum: L{DateTime}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Give tzinfo to any TimeRange we have
</span><span class="lines">@@ -652,15 +652,15 @@
</span><span class="cx"> if "start" not in xml_element.attributes and "end" not in xml_element.attributes:
</span><span class="cx"> raise ValueError("One of 'start' or 'end' must be present in CALDAV:time-range")
</span><span class="cx">
</span><del>- self.start = PyCalendarDateTime.parseText(xml_element.attributes["start"]) if "start" in xml_element.attributes else None
- self.end = PyCalendarDateTime.parseText(xml_element.attributes["end"]) if "end" in xml_element.attributes else None
</del><ins>+ self.start = DateTime.parseText(xml_element.attributes["start"]) if "start" in xml_element.attributes else None
+ self.end = DateTime.parseText(xml_element.attributes["end"]) if "end" in xml_element.attributes else None
</ins><span class="cx"> self.tzinfo = None
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def settzinfo(self, tzinfo):
</span><span class="cx"> """
</span><span class="cx"> Set the default timezone to use with this query.
</span><del>- @param tzinfo: a L{PyCalendarTimezone} to use.
</del><ins>+ @param tzinfo: a L{Timezone} to use.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Give tzinfo to any TimeRange we have
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavquerytesttest_calendarquerypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_calendarquery.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_calendarquery.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_calendarquery.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -17,7 +17,7 @@
</span><span class="cx"> from twistedcaldav import caldavxml
</span><span class="cx"> from twistedcaldav.query import calendarqueryfilter
</span><span class="cx"> import twistedcaldav.test.util
</span><del>-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.timezone import Timezone
</ins><span class="cx"> from twistedcaldav.query.calendarquery import sqlcalendarquery
</span><span class="cx">
</span><span class="cx"> class Tests(twistedcaldav.test.util.TestCase):
</span><span class="lines">@@ -36,7 +36,7 @@
</span><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx"> filter = calendarqueryfilter.Filter(filter)
</span><del>- filter.child.settzinfo(PyCalendarTimezone(tzid="America/New_York"))
</del><ins>+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
</ins><span class="cx">
</span><span class="cx"> sql, args = sqlcalendarquery(filter)
</span><span class="cx"> self.assertTrue(sql.find("RESOURCE") != -1)
</span><span class="lines">@@ -60,7 +60,7 @@
</span><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx"> filter = calendarqueryfilter.Filter(filter)
</span><del>- filter.child.settzinfo(PyCalendarTimezone(tzid="America/New_York"))
</del><ins>+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
</ins><span class="cx">
</span><span class="cx"> sql, args = sqlcalendarquery(filter)
</span><span class="cx"> self.assertTrue(sql.find("RESOURCE") != -1)
</span><span class="lines">@@ -88,7 +88,7 @@
</span><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx"> filter = calendarqueryfilter.Filter(filter)
</span><del>- filter.child.settzinfo(PyCalendarTimezone(tzid="America/New_York"))
</del><ins>+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
</ins><span class="cx">
</span><span class="cx"> sql, args = sqlcalendarquery(filter)
</span><span class="cx"> self.assertTrue(sql.find("RESOURCE") != -1)
</span><span class="lines">@@ -119,7 +119,7 @@
</span><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx"> filter = calendarqueryfilter.Filter(filter)
</span><del>- filter.child.settzinfo(PyCalendarTimezone(tzid="America/New_York"))
</del><ins>+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
</ins><span class="cx">
</span><span class="cx"> sql, args = sqlcalendarquery(filter)
</span><span class="cx"> self.assertTrue(sql.find("RESOURCE") != -1)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavquerytesttest_queryfilterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_queryfilter.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_queryfilter.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/query/test/test_queryfilter.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,7 +18,7 @@
</span><span class="cx"> from twistedcaldav.query import calendarqueryfilter
</span><span class="cx"> import twistedcaldav.test.util
</span><span class="cx"> from twistedcaldav.caldavxml import TimeZone
</span><del>-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> class Tests(twistedcaldav.test.util.TestCase):
</span><span class="cx">
</span><span class="lines">@@ -227,4 +227,4 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """))
</span><span class="cx">
</span><del>- self.assertTrue(isinstance(tz, PyCalendarTimezone))
</del><ins>+ self.assertTrue(isinstance(tz, Timezone))
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/resource.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/resource.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/resource.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -71,6 +71,7 @@
</span><span class="cx"> from twistedcaldav.directory.internal import InternalDirectoryRecord
</span><span class="cx"> from twistedcaldav.extensions import DAVResource, DAVPrincipalResource, \
</span><span class="cx"> DAVResourceWithChildrenMixin
</span><ins>+from twistedcaldav import ical
</ins><span class="cx"> from twistedcaldav.ical import Component
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.icaldav import ICalDAVResource, ICalendarPrincipalResource
</span><span class="lines">@@ -257,10 +258,11 @@
</span><span class="cx"> # Redirect to include trailing '/' in URI
</span><span class="cx"> return RedirectResponse(request.unparseURL(path=urllib.quote(urllib.unquote(request.path), safe=':/') + '/'))
</span><span class="cx">
</span><del>- def _defer(data):
</del><ins>+ def _defer(result):
+ data, accepted_type = result
</ins><span class="cx"> response = Response()
</span><del>- response.stream = MemoryStream(str(data))
- response.headers.setHeader("content-type", MimeType.fromString("text/calendar"))
</del><ins>+ response.stream = MemoryStream(data.getText(accepted_type))
+ response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))
</ins><span class="cx"> return response
</span><span class="cx">
</span><span class="cx"> d = self.iCalendarRolledup(request)
</span><span class="lines">@@ -375,7 +377,7 @@
</span><span class="cx"> (self,))
</span><span class="cx">
</span><span class="cx">
</span><del>- def storeStream(self, stream):
</del><ins>+ def storeStream(self, stream, format):
</ins><span class="cx"> """
</span><span class="cx"> Store the content of the stream in this resource, as it would via a PUT.
</span><span class="cx">
</span><span class="lines">@@ -385,8 +387,7 @@
</span><span class="cx"> @return: a L{Deferred} which fires with an HTTP response.
</span><span class="cx"> @rtype: L{Deferred}
</span><span class="cx"> """
</span><del>- raise NotImplementedError("%s does not implement storeStream" %
- (self,))
</del><ins>+ raise NotImplementedError("%s does not implement storeStream" % (self,))
</ins><span class="cx">
</span><span class="cx"> # End transitional new-store interface
</span><span class="cx">
</span><span class="lines">@@ -605,12 +606,21 @@
</span><span class="cx"> returnValue(self.getSupportedComponentSet())
</span><span class="cx">
</span><span class="cx"> elif qname == caldavxml.SupportedCalendarData.qname() and self.isPseudoCalendarCollection():
</span><del>- returnValue(caldavxml.SupportedCalendarData(
</del><ins>+ dataTypes = []
+ dataTypes.append(
</ins><span class="cx"> caldavxml.CalendarData(**{
</span><span class="cx"> "content-type": "text/calendar",
</span><span class="cx"> "version" : "2.0",
</span><span class="cx"> }),
</span><del>- ))
</del><ins>+ )
+ if config.EnableJSONData:
+ dataTypes.append(
+ caldavxml.CalendarData(**{
+ "content-type": "application/calendar+json",
+ "version" : "2.0",
+ }),
+ )
+ returnValue(caldavxml.SupportedCalendarData(*dataTypes))
</ins><span class="cx">
</span><span class="cx"> elif qname == caldavxml.MaxResourceSize.qname() and self.isPseudoCalendarCollection():
</span><span class="cx"> if config.MaxResourceSize:
</span><span class="lines">@@ -642,12 +652,21 @@
</span><span class="cx">
</span><span class="cx"> elif qname == carddavxml.SupportedAddressData.qname() and self.isAddressBookCollection():
</span><span class="cx"> # CardDAV, section 6.2.2
</span><del>- returnValue(carddavxml.SupportedAddressData(
</del><ins>+ dataTypes = []
+ dataTypes.append(
</ins><span class="cx"> carddavxml.AddressDataType(**{
</span><span class="cx"> "content-type": "text/vcard",
</span><span class="cx"> "version" : "3.0",
</span><span class="cx"> }),
</span><del>- ))
</del><ins>+ )
+ if config.EnableJSONData:
+ dataTypes.append(
+ carddavxml.AddressDataType(**{
+ "content-type": "application/vcard+json",
+ "version" : "3.0",
+ }),
+ )
+ returnValue(carddavxml.SupportedAddressData(*dataTypes))
</ins><span class="cx">
</span><span class="cx"> elif qname == carddavxml.MaxResourceSize.qname() and self.isAddressBookCollection() and not self.isDirectoryBackedAddressBookCollection():
</span><span class="cx"> # CardDAV, section 6.2.3
</span><span class="lines">@@ -1446,11 +1465,6 @@
</span><span class="cx"> returnValue(caldata)
</span><span class="cx">
</span><span class="cx">
</span><del>- def iCalendarText(self):
- # storebridge handles this method
- raise NotImplementedError()
-
-
</del><span class="cx"> def iCalendar(self):
</span><span class="cx"> # storebridge handles this method
</span><span class="cx"> raise NotImplementedError()
</span><span class="lines">@@ -2446,18 +2460,13 @@
</span><span class="cx">
</span><span class="cx"> if qname == caldavxml.SupportedCalendarComponentSets.qname():
</span><span class="cx"> if config.RestrictCalendarsToOneComponentType:
</span><del>- prop = caldavxml.SupportedCalendarComponentSets(
</del><ins>+ prop = caldavxml.SupportedCalendarComponentSets(*[
</ins><span class="cx"> caldavxml.SupportedCalendarComponentSet(
</span><span class="cx"> caldavxml.CalendarComponent(
</span><del>- name="VEVENT",
</del><ins>+ name=name,
</ins><span class="cx"> ),
</span><del>- ),
- caldavxml.SupportedCalendarComponentSet(
- caldavxml.CalendarComponent(
- name="VTODO",
- ),
- ),
- )
</del><ins>+ ) for name in ical.allowedStoreComponents
+ ])
</ins><span class="cx"> else:
</span><span class="cx"> prop = caldavxml.SupportedCalendarComponentSets()
</span><span class="cx"> returnValue(prop)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavscheduling_storecaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/scheduling_store/caldav/resource.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/scheduling_store/caldav/resource.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/scheduling_store/caldav/resource.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -45,7 +45,7 @@
</span><span class="cx"> from twistedcaldav import caldavxml, customxml
</span><span class="cx"> from twistedcaldav.caldavxml import caldav_namespace, CalendarFreeBusySet
</span><span class="cx"> from twistedcaldav.customxml import calendarserver_namespace
</span><del>-from twistedcaldav.ical import allowedComponents, Component
</del><ins>+from twistedcaldav.ical import Component, allowedSchedulingComponents
</ins><span class="cx"> from twistedcaldav.resource import CalDAVResource
</span><span class="cx"> from twistedcaldav.resource import isCalendarCollectionResource
</span><span class="cx">
</span><span class="lines">@@ -300,8 +300,14 @@
</span><span class="cx"> """
</span><span class="cx"> Write either the default VEVENT or VTODO calendar property, validating and canonicalizing the value
</span><span class="cx"> """
</span><del>- tasks = property.qname() == customxml.ScheduleDefaultTasksURL
- error_element = (calendarserver_namespace, "valid-schedule-default-tasks-URL") if tasks else (caldav_namespace, "valid-schedule-default-calendar-URL")
</del><ins>+ if property.qname() == caldavxml.ScheduleDefaultCalendarURL.qname():
+ ctype = "VEVENT"
+ error_element = (caldav_namespace, "valid-schedule-default-calendar-URL")
+ elif property.qname() == customxml.ScheduleDefaultTasksURL.qname():
+ ctype = "VTODO"
+ error_element = (calendarserver_namespace, "valid-schedule-default-tasks-URL")
+ else:
+ returnValue(None)
</ins><span class="cx">
</span><span class="cx"> # Verify that the calendar added in the PROPPATCH is valid.
</span><span class="cx"> property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
</span><span class="lines">@@ -325,7 +331,7 @@
</span><span class="cx">
</span><span class="cx"> try:
</span><span class="cx"> # Now set it on the new store object
</span><del>- yield self.parent._newStoreHome.setDefaultCalendar(cal._newStoreObject, tasks)
</del><ins>+ yield self.parent._newStoreHome.setDefaultCalendar(cal._newStoreObject, ctype)
</ins><span class="cx"> except InvalidDefaultCalendar as e:
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.CONFLICT,
</span><span class="lines">@@ -390,7 +396,7 @@
</span><span class="cx">
</span><span class="cx"> def getSupportedComponentSet(self):
</span><span class="cx"> return caldavxml.SupportedCalendarComponentSet(
</span><del>- *[caldavxml.CalendarComponent(name=item) for item in allowedComponents]
</del><ins>+ *[caldavxml.CalendarComponent(name=item) for item in allowedSchedulingComponents]
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -407,7 +413,7 @@
</span><span class="cx"> # Check authentication and access controls
</span><span class="cx"> yield self.authorize(request, (caldavxml.ScheduleSend(),))
</span><span class="cx">
</span><del>- calendar = (yield self.loadCalendarFromRequest(request))
</del><ins>+ calendar, format = (yield self.loadCalendarFromRequest(request))
</ins><span class="cx"> originator = (yield self.loadOriginatorFromRequestDetails(request))
</span><span class="cx"> recipients = self.loadRecipientsFromCalendarData(calendar)
</span><span class="cx">
</span><span class="lines">@@ -431,14 +437,25 @@
</span><span class="cx">
</span><span class="cx"> # Do the POST processing treating
</span><span class="cx"> result = (yield scheduler.doSchedulingViaPOST(originator, recipients, calendar))
</span><del>- returnValue(result.response())
</del><ins>+ returnValue(result.response(format=format))
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ def determineType(self, content_type):
+ """
+ Determine if the supplied content-type is valid for storing and return the matching PyCalendar type.
+ """
+ format = None
+ if content_type is not None:
+ format = "%s/%s" % (content_type.mediaType, content_type.mediaSubtype,)
+ return format if format in Component.allowedTypes() else None
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def loadCalendarFromRequest(self, request):
</span><span class="cx"> # Must be content-type text/calendar
</span><span class="cx"> contentType = request.headers.getHeader("content-type")
</span><del>- if contentType is not None and (contentType.mediaType, contentType.mediaSubtype) != ("text", "calendar"):
</del><ins>+ format = self.determineType(contentType)
+ if format is None:
</ins><span class="cx"> self.log.error("MIME type %s not allowed in calendar collection" % (contentType,))
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="lines">@@ -448,7 +465,7 @@
</span><span class="cx">
</span><span class="cx"> # Parse the calendar object from the HTTP request stream
</span><span class="cx"> try:
</span><del>- calendar = (yield Component.fromIStream(request.stream))
</del><ins>+ calendar = (yield Component.fromIStream(request.stream, format=format))
</ins><span class="cx"> except:
</span><span class="cx"> # FIXME: Bare except
</span><span class="cx"> self.log.error("Error while handling POST: %s" % (Failure(),))
</span><span class="lines">@@ -458,7 +475,7 @@
</span><span class="cx"> description="Can't parse calendar data"
</span><span class="cx"> ))
</span><span class="cx">
</span><del>- returnValue(calendar)
</del><ins>+ returnValue((calendar, format,))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavsharingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sharing.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sharing.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sharing.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -46,7 +46,7 @@
</span><span class="cx"> from twistedcaldav.directory.wiki import WikiDirectoryService, getWikiAccess
</span><span class="cx"> from twistedcaldav.linkresource import LinkFollowerMixIn
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> # FIXME: Get rid of these imports
</span><span class="lines">@@ -740,7 +740,7 @@
</span><span class="cx"> typeAttr = {'shared-type': self.sharedResourceType()}
</span><span class="cx"> xmltype = customxml.InviteNotification(**typeAttr)
</span><span class="cx"> xmldata = customxml.Notification(
</span><del>- customxml.DTStamp.fromString(PyCalendarDateTime.getNowUTC().getText()),
</del><ins>+ customxml.DTStamp.fromString(DateTime.getNowUTC().getText()),
</ins><span class="cx"> customxml.InviteNotification(
</span><span class="cx"> customxml.UID.fromString(invitation.uid()),
</span><span class="cx"> element.HRef.fromString(userid),
</span><span class="lines">@@ -1362,7 +1362,7 @@
</span><span class="cx"> record = shareePrincipal.record
</span><span class="cx">
</span><span class="cx"> xmldata = customxml.Notification(
</span><del>- customxml.DTStamp.fromString(PyCalendarDateTime.getNowUTC().getText()),
</del><ins>+ customxml.DTStamp.fromString(DateTime.getNowUTC().getText()),
</ins><span class="cx"> customxml.InviteReply(
</span><span class="cx"> *(
</span><span class="cx"> (
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavsqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sql.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sql.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/sql.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,7 +18,7 @@
</span><span class="cx"> Generic SQL database access object.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-__all__ = [
</del><ins>+__all__ = [
</ins><span class="cx"> "db_prefix",
</span><span class="cx"> "DatabaseError",
</span><span class="cx"> "AbstractSQLDatabase",
</span><span class="lines">@@ -50,7 +50,7 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, dbpath, persistent, autocommit=False):
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> @param dbpath: the path where the db file is stored.
</span><span class="cx"> @type dbpath: str
</span><span class="cx"> @param persistent: C{True} if the data in the DB must be perserved during upgrades,
</span><span class="lines">@@ -63,21 +63,25 @@
</span><span class="cx"> self.persistent = persistent
</span><span class="cx"> self.autocommit = autocommit
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __repr__(self):
</span><span class="cx"> return "<%s %r>" % (self.__class__.__name__, self.dbpath)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_version(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the schema version assigned to this index.
</span><span class="cx"> """
</span><span class="cx"> raise NotImplementedError
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _db_type(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the collection type assigned to this index.
</span><span class="cx"> """
</span><span class="cx"> raise NotImplementedError
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _db(self):
</span><span class="cx"> """
</span><span class="cx"> Access the underlying database.
</span><span class="lines">@@ -108,7 +112,7 @@
</span><span class="cx"> # Create CALDAV table if needed
</span><span class="cx">
</span><span class="cx"> if self._test_schema_table(q):
</span><del>-
</del><ins>+
</ins><span class="cx"> version, dbtype = self._get_schema_version(q)
</span><span class="cx">
</span><span class="cx"> if (version != self._db_version()) or (dbtype != self._db_type()):
</span><span class="lines">@@ -130,7 +134,7 @@
</span><span class="cx"> if version != self._db_version():
</span><span class="cx"> log.error("Database %s has different schema (v.%s vs. v.%s)"
</span><span class="cx"> % (db_filename, version, self._db_version()))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Upgrade the DB
</span><span class="cx"> return self._db_upgrade(version)
</span><span class="cx">
</span><span class="lines">@@ -139,9 +143,11 @@
</span><span class="cx">
</span><span class="cx"> self._db_connection.commit()
</span><span class="cx"> finally:
</span><del>- if q is not None: q.close()
</del><ins>+ if q is not None:
+ q.close()
</ins><span class="cx"> return self._db_connection
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _test_schema_table(self, q):
</span><span class="cx"> q.execute("""
</span><span class="cx"> select (1) from SQLITE_MASTER
</span><span class="lines">@@ -149,6 +155,7 @@
</span><span class="cx"> """)
</span><span class="cx"> return q.fetchone()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _get_schema_version(self, q):
</span><span class="cx"> q.execute(
</span><span class="cx"> """
</span><span class="lines">@@ -157,7 +164,8 @@
</span><span class="cx"> """)
</span><span class="cx"> version = q.fetchone()
</span><span class="cx">
</span><del>- if version is not None: version = version[0]
</del><ins>+ if version is not None:
+ version = version[0]
</ins><span class="cx">
</span><span class="cx"> q.execute(
</span><span class="cx"> """
</span><span class="lines">@@ -166,10 +174,12 @@
</span><span class="cx"> """)
</span><span class="cx"> dbtype = q.fetchone()
</span><span class="cx">
</span><del>- if dbtype is not None: dbtype = dbtype[0]
</del><ins>+ if dbtype is not None:
+ dbtype = dbtype[0]
</ins><span class="cx">
</span><span class="cx"> return version, dbtype
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_init(self, db_filename, q):
</span><span class="cx"> """
</span><span class="cx"> Initialise the underlying database tables.
</span><span class="lines">@@ -183,7 +193,7 @@
</span><span class="cx"> old_isolation = self._db_connection.isolation_level
</span><span class="cx"> self._db_connection.isolation_level = None
</span><span class="cx"> q.execute("begin exclusive transaction")
</span><del>-
</del><ins>+
</ins><span class="cx"> # We re-check whether the schema table is present again AFTER we've got an exclusive
</span><span class="cx"> # lock as some other server process may have snuck in and already created it
</span><span class="cx"> # before we got the lock, or whilst we were waiting for it.
</span><span class="lines">@@ -199,6 +209,7 @@
</span><span class="cx">
</span><span class="cx"> self._db_connection.isolation_level = old_isolation
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_init_schema_table(self, q):
</span><span class="cx"> """
</span><span class="cx"> Initialise the underlying database tables.
</span><span class="lines">@@ -229,6 +240,7 @@
</span><span class="cx"> """, [self._db_type()]
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_init_data_tables(self, q):
</span><span class="cx"> """
</span><span class="cx"> Initialise the underlying database tables.
</span><span class="lines">@@ -237,6 +249,7 @@
</span><span class="cx"> """
</span><span class="cx"> raise NotImplementedError
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_recreate(self, do_commit=True):
</span><span class="cx"> """
</span><span class="cx"> Recreate the database tables.
</span><span class="lines">@@ -246,15 +259,17 @@
</span><span class="cx"> if do_commit:
</span><span class="cx"> self._db_commit()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_can_upgrade(self, old_version):
</span><del>-
</del><ins>+
</ins><span class="cx"> return self.persistent
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_upgrade(self, old_version):
</span><span class="cx"> """
</span><span class="cx"> Upgrade the database tables.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> if self._db_can_upgrade(old_version):
</span><span class="cx"> self._db_connection = sqlite.connect(self.dbpath, isolation_level=None)
</span><span class="cx"> q = self._db_connection.cursor()
</span><span class="lines">@@ -278,6 +293,7 @@
</span><span class="cx">
</span><span class="cx"> return self._db()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_upgrade_data_tables(self, q, old_version):
</span><span class="cx"> """
</span><span class="cx"> Upgrade the data from an older version of the DB.
</span><span class="lines">@@ -286,6 +302,7 @@
</span><span class="cx"> # cannot be thrown away.
</span><span class="cx"> raise NotImplementedError("Persistent databases MUST support an upgrade method.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_upgrade_schema(self, q):
</span><span class="cx"> """
</span><span class="cx"> Upgrade the stored schema version to the current one.
</span><span class="lines">@@ -297,11 +314,13 @@
</span><span class="cx"> """, [self._db_version()]
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_close(self):
</span><span class="cx"> if hasattr(self, "_db_connection"):
</span><span class="cx"> self._db_connection.close()
</span><span class="cx"> del self._db_connection
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_values_for_sql(self, sql, *query_params):
</span><span class="cx"> """
</span><span class="cx"> Execute an SQL query and obtain the resulting values.
</span><span class="lines">@@ -313,6 +332,7 @@
</span><span class="cx"> """
</span><span class="cx"> return (row[0] for row in self._db_execute(sql, *query_params))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_value_for_sql(self, sql, *query_params):
</span><span class="cx"> """
</span><span class="cx"> Execute an SQL query and obtain a single value.
</span><span class="lines">@@ -328,6 +348,7 @@
</span><span class="cx"> value = row
</span><span class="cx"> return value
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _db_execute(self, sql, *query_params):
</span><span class="cx"> """
</span><span class="cx"> Execute an SQL query and obtain the resulting values.
</span><span class="lines">@@ -347,5 +368,10 @@
</span><span class="cx"> finally:
</span><span class="cx"> q.close()
</span><span class="cx">
</span><del>- def _db_commit (self): self._db_connection.commit()
- def _db_rollback(self): self._db_connection.rollback()
</del><ins>+
+ def _db_commit(self):
+ self._db_connection.commit()
+
+
+ def _db_rollback(self):
+ self._db_connection.rollback()
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/stdconfig.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/stdconfig.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/stdconfig.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -36,6 +36,7 @@
</span><span class="cx"> from twistedcaldav.util import computeProcessCount
</span><span class="cx">
</span><span class="cx"> from calendarserver.push.util import getAPNTopicFromCertificate
</span><ins>+from twistedcaldav import ical
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -156,9 +157,8 @@
</span><span class="cx"> "resourceInfoAttr": None, # contains location/resource info
</span><span class="cx"> "autoAcceptGroupAttr": None, # auto accept group
</span><span class="cx"> },
</span><del>- "partitionSchema": {
</del><ins>+ "poddingSchema": {
</ins><span class="cx"> "serverIdAttr": None, # maps to augments server-id
</span><del>- "partitionIdAttr": None, # maps to augments partition-id
</del><span class="cx"> },
</span><span class="cx"> },
</span><span class="cx"> }
</span><span class="lines">@@ -304,7 +304,7 @@
</span><span class="cx"> # the master process, rather than having
</span><span class="cx"> # each client make its connections directly.
</span><span class="cx">
</span><del>- "FailIfUpgradeNeeded" : True, # Set to True to prevent the server or utility tools
</del><ins>+ "FailIfUpgradeNeeded" : True, # Set to True to prevent the server or utility
</ins><span class="cx"> # tools from running if the database needs a schema
</span><span class="cx"> # upgrade.
</span><span class="cx"> "StopAfterUpgradeTriggerFile" : "stop_after_upgrade", # if this file exists in ConfigRoot, stop
</span><span class="lines">@@ -546,6 +546,11 @@
</span><span class="cx"> "EnableManagedAttachments" : False, # Support Managed Attachments
</span><span class="cx">
</span><span class="cx"> #
</span><ins>+ # Generic CalDAV/CardDAV extensions
+ #
+ "EnableJSONData" : True, # Allow clients to send/receive JSON jCal and jCard format data
+
+ #
</ins><span class="cx"> # Non-standard CalDAV extensions
</span><span class="cx"> #
</span><span class="cx"> "EnableDropBox" : False, # Calendar Drop Box
</span><span class="lines">@@ -595,6 +600,12 @@
</span><span class="cx"> # If on, it will also cause new accounts to provision with separate
</span><span class="cx"> # calendars for events and tasks.
</span><span class="cx">
</span><ins>+ "SupportedComponents" : [ # Set of supported iCalendar components
+ "VEVENT",
+ "VTODO",
+ #"VPOLL",
+ ],
+
</ins><span class="cx"> "ParallelUpgrades" : False, # Perform upgrades - currently only the
</span><span class="cx"> # database -> filesystem migration - but in
</span><span class="cx"> # the future, hopefully all relevant
</span><span class="lines">@@ -806,11 +817,11 @@
</span><span class="cx"> # Support multiple hosts within a domain
</span><span class="cx"> #
</span><span class="cx"> "Servers" : {
</span><del>- "Enabled": False, # Multiple servers/partitions enabled or not
- "ConfigFile": "localservers.xml", # File path for server information
- "MaxClients": 5, # Pool size for connections to each partition
</del><ins>+ "Enabled": False, # Multiple servers enabled or not
+ "ConfigFile": "localservers.xml", # File path for server information
+ "MaxClients": 5, # Pool size for connections to between servers
+ "InboxName": "podding", # Name for top-level inbox resource
</ins><span class="cx"> },
</span><del>- "ServerPartitionID": "", # Unique ID for this server's partition instance.
</del><span class="cx">
</span><span class="cx"> #
</span><span class="cx"> # Performance tuning
</span><span class="lines">@@ -881,7 +892,8 @@
</span><span class="cx">
</span><span class="cx"> # Support for Content-Encoding compression options as specified in
</span><span class="cx"> # RFC2616 Section 3.5
</span><del>- "ResponseCompression": True,
</del><ins>+ # Defaults off, because it weakens TLS (CRIME attack).
+ "ResponseCompression": False,
</ins><span class="cx">
</span><span class="cx"> # The retry-after value (in seconds) to return with a 503 error
</span><span class="cx"> "HTTPRetryAfter": 180,
</span><span class="lines">@@ -1011,7 +1023,8 @@
</span><span class="cx"> # means no automatic shutdown.
</span><span class="cx"> "AgentInactivityTimeoutSeconds" : 4 * 60 * 60,
</span><span class="cx">
</span><del>- # These two aren't relative to ConfigRoot:
</del><ins>+ # These aren't relative to ConfigRoot:
+ "ImportConfig": "", # Config to read first and merge
</ins><span class="cx"> "Includes": [], # Other plists to parse after this one
</span><span class="cx"> "WritableConfigFile" : "", # which config file calendarserver_config should
</span><span class="cx"> # write to for changes; empty string means the main config file.
</span><span class="lines">@@ -1043,18 +1056,41 @@
</span><span class="cx"> if self._configFileName:
</span><span class="cx"> configDict = self._parseConfigFromFile(self._configFileName)
</span><span class="cx"> configDict = ConfigDict(configDict)
</span><del>- # Now check for Includes and parse and add each of those
- if "Includes" in configDict:
- for include in configDict.Includes:
- # Includes are not relative to ConfigRoot
- path = _expandPath(include)
</del><ins>+
+ def _loadImport(childDict):
+ # Look for an import and read that one as the main config and merge the current one into that
+ if "ImportConfig" in childDict and childDict.ImportConfig:
+ configRoot = os.path.join(childDict.ServerRoot, childDict.ConfigRoot)
+ path = _expandPath(fullServerPath(configRoot, childDict.ImportConfig))
</ins><span class="cx"> if os.path.exists(path):
</span><del>- additionalDict = ConfigDict(self._parseConfigFromFile(path))
- if additionalDict:
- log.info("Adding configuration from file: '%s'" % (path,))
- mergeData(configDict, additionalDict)
- else:
- log.debug("Missing configuration file: '%s'" % (path,))
</del><ins>+ importDict = ConfigDict(self._parseConfigFromFile(path))
+ if importDict:
+ self.importedFiles.append(path)
+ importDict = _loadImport(importDict)
+ mergeData(importDict, childDict)
+ return importDict
+ raise ConfigurationError("Import configuration file '{path}' must exist and be valid.".format(path=path))
+ else:
+ return childDict
+
+ def _loadIncludes(parentDict):
+ # Now check for Includes and parse and add each of those
+ if "Includes" in parentDict:
+ configRoot = os.path.join(parentDict.ServerRoot, parentDict.ConfigRoot)
+ for include in parentDict.Includes:
+ # Includes are not relative to ConfigRoot
+ path = _expandPath(fullServerPath(configRoot, include))
+ if os.path.exists(path):
+ additionalDict = ConfigDict(self._parseConfigFromFile(path))
+ if additionalDict:
+ self.includedFiles.append(path)
+ _loadIncludes(additionalDict)
+ mergeData(parentDict, additionalDict)
+ else:
+ self.missingFiles.append(path)
+
+ configDict = _loadImport(configDict)
+ _loadIncludes(configDict)
</ins><span class="cx"> return configDict
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1479,6 +1515,14 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><ins>+def _updateICalendar(configDict, reloading=False):
+ """
+ Updated support iCalendar components.
+ """
+ ical._updateAllowedComponents(tuple(configDict.SupportedComponents))
+
+
+
</ins><span class="cx"> def _updateScheduling(configDict, reloading=False):
</span><span class="cx"> #
</span><span class="cx"> # Scheduling
</span><span class="lines">@@ -1518,8 +1562,7 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers
</span><span class="cx"> if configDict.Servers.Enabled:
</span><span class="cx"> Servers.load()
</span><del>- Servers.getThisServer().installReverseProxies(
- configDict.ServerPartitionID,
</del><ins>+ Servers.installReverseProxies(
</ins><span class="cx"> configDict.Servers.MaxClients,
</span><span class="cx"> )
</span><span class="cx"> else:
</span><span class="lines">@@ -1590,6 +1633,7 @@
</span><span class="cx"> _updateRejectClients,
</span><span class="cx"> _updateLogLevels,
</span><span class="cx"> _updateNotifications,
</span><ins>+ _updateICalendar,
</ins><span class="cx"> _updateScheduling,
</span><span class="cx"> _updateServers,
</span><span class="cx"> _updateCompliance,
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavstorebridgepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/storebridge.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -15,7 +15,7 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from twext.web2.dav.http import ErrorResponse, ResponseQueue, MultiStatusResponse
</span><span class="lines">@@ -36,14 +36,14 @@
</span><span class="cx"> from twisted.python.hashlib import md5
</span><span class="cx"> from twisted.python.util import FancyEqMixin
</span><span class="cx">
</span><del>-from twistedcaldav import customxml, carddavxml, caldavxml
</del><ins>+from twistedcaldav import customxml, carddavxml, caldavxml, ical
</ins><span class="cx"> from twistedcaldav.caldavxml import caldav_namespace, MaxAttendeesPerInstance, \
</span><span class="cx"> MaxInstances, NoUIDConflict
</span><span class="cx"> from twistedcaldav.carddavxml import carddav_namespace, NoUIDConflict as NovCardUIDConflict
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.directory.wiki import WikiDirectoryService, getWikiAccess
</span><span class="cx"> from twistedcaldav.ical import Component as VCalendar, Property as VProperty, \
</span><del>- InvalidICalendarDataError, iCalendarProductID, allowedComponents, Component
</del><ins>+ InvalidICalendarDataError, iCalendarProductID, Component
</ins><span class="cx"> from twistedcaldav.memcachelock import MemcacheLockTimeoutError
</span><span class="cx"> from twistedcaldav.notifications import NotificationCollectionResource, NotificationResource
</span><span class="cx"> from twistedcaldav.resource import CalDAVResource, GlobalAddressBookResource, \
</span><span class="lines">@@ -82,6 +82,7 @@
</span><span class="cx"> from twistedcaldav.customxml import calendarserver_namespace
</span><span class="cx"> from twistedcaldav.instance import InvalidOverriddenInstanceError, \
</span><span class="cx"> TooManyInstancesError
</span><ins>+from twistedcaldav.util import bestAcceptType
</ins><span class="cx"> import collections
</span><span class="cx">
</span><span class="cx"> """
</span><span class="lines">@@ -626,14 +627,17 @@
</span><span class="cx"> # Read in all data
</span><span class="cx"> data = (yield allDataFromStream(request.stream))
</span><span class="cx">
</span><del>- components = self.componentsFromData(data)
</del><ins>+ format = request.headers.getHeader("content-type")
+ if format:
+ format = "%s/%s" % (format.mediaType, format.mediaSubtype,)
+ components = self.componentsFromData(data, format)
</ins><span class="cx"> if components is None:
</span><span class="cx"> raise HTTPError(StatusResponse(BAD_REQUEST, "Could not parse valid data from request body"))
</span><span class="cx">
</span><span class="cx"> # Build response
</span><span class="cx"> xmlresponses = [None] * len(components)
</span><span class="cx"> indexedComponents = [idxComponent for idxComponent in enumerate(components)]
</span><del>- yield self.bulkCreate(indexedComponents, request, return_changed, xmlresponses)
</del><ins>+ yield self.bulkCreate(indexedComponents, request, return_changed, xmlresponses, format)
</ins><span class="cx">
</span><span class="cx"> result = MultiStatusResponse(xmlresponses)
</span><span class="cx">
</span><span class="lines">@@ -650,7 +654,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def bulkCreate(self, indexedComponents, request, return_changed, xmlresponses):
</del><ins>+ def bulkCreate(self, indexedComponents, request, return_changed, xmlresponses, format):
</ins><span class="cx"> """
</span><span class="cx"> Do create from simpleBatchPOST or crudCreate()
</span><span class="cx"> Subclasses may override
</span><span class="lines">@@ -664,7 +668,7 @@
</span><span class="cx"> # Get a resource for the new item
</span><span class="cx"> newchildURL = joinURL(request.path, name)
</span><span class="cx"> newchild = (yield request.locateResource(newchildURL))
</span><del>- changedData = (yield self.storeResourceData(newchild, component, returnChangedData=return_changed))
</del><ins>+ changedComponent = (yield self.storeResourceData(newchild, component, returnChangedData=return_changed))
</ins><span class="cx">
</span><span class="cx"> except HTTPError, e:
</span><span class="cx"> # Extract the pre-condition
</span><span class="lines">@@ -674,30 +678,30 @@
</span><span class="cx"> error = (error.namespace, error.name,)
</span><span class="cx">
</span><span class="cx"> xmlresponses[index] = (
</span><del>- yield self.bulkCreateResponse(component, newchildURL, newchild, None, code, error)
</del><ins>+ yield self.bulkCreateResponse(component, newchildURL, newchild, None, code, error, format)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> except Exception:
</span><span class="cx"> xmlresponses[index] = (
</span><del>- yield self.bulkCreateResponse(component, newchildURL, newchild, None, code=BAD_REQUEST, error=None)
</del><ins>+ yield self.bulkCreateResponse(component, newchildURL, newchild, None, BAD_REQUEST, None, format)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> else:
</span><span class="cx"> if not return_changed:
</span><del>- changedData = None
</del><ins>+ changedComponent = None
</ins><span class="cx"> xmlresponses[index] = (
</span><del>- yield self.bulkCreateResponse(component, newchildURL, newchild, changedData, code=None, error=None)
</del><ins>+ yield self.bulkCreateResponse(component, newchildURL, newchild, changedComponent, None, None, format)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def bulkCreateResponse(self, component, newchildURL, newchild, changedData, code, error):
</del><ins>+ def bulkCreateResponse(self, component, newchildURL, newchild, changedComponent, code, error, format):
</ins><span class="cx"> """
</span><span class="cx"> generate one xmlresponse for bulk create
</span><span class="cx"> """
</span><span class="cx"> if code is None:
</span><span class="cx"> etag = (yield newchild.etag())
</span><del>- if changedData is None:
</del><ins>+ if changedComponent is None:
</ins><span class="cx"> returnValue(
</span><span class="cx"> davxml.PropertyStatusResponse(
</span><span class="cx"> davxml.HRef.fromString(newchildURL),
</span><span class="lines">@@ -717,7 +721,7 @@
</span><span class="cx"> davxml.PropertyStatus(
</span><span class="cx"> davxml.PropertyContainer(
</span><span class="cx"> davxml.GETETag.fromString(etag.generate()),
</span><del>- self.xmlDataElementType().fromTextData(changedData),
</del><ins>+ self.xmlDataElementType().fromComponent(changedComponent, format),
</ins><span class="cx"> ),
</span><span class="cx"> davxml.Status.fromResponseCode(OK),
</span><span class="cx"> )
</span><span class="lines">@@ -822,6 +826,7 @@
</span><span class="cx"> for index, xmldata in crudCreateInfo:
</span><span class="cx">
</span><span class="cx"> component = xmldata.generateComponent()
</span><ins>+ format = xmldata.content_type
</ins><span class="cx">
</span><span class="cx"> if hasPrivilege is not True:
</span><span class="cx"> e = hasPrivilege # use same code pattern as exception
</span><span class="lines">@@ -830,13 +835,13 @@
</span><span class="cx"> error = e.response.error
</span><span class="cx"> error = (error.namespace, error.name,)
</span><span class="cx">
</span><del>- xmlresponse = yield self.bulkCreateResponse(component, None, None, None, code, error)
</del><ins>+ xmlresponse = yield self.bulkCreateResponse(component, None, None, None, code, error, format)
</ins><span class="cx"> xmlresponses[index] = xmlresponse
</span><span class="cx">
</span><span class="cx"> else:
</span><span class="cx"> indexedComponents.append((index, component,))
</span><span class="cx">
</span><del>- yield self.bulkCreate(indexedComponents, request, return_changed, xmlresponses)
</del><ins>+ yield self.bulkCreate(indexedComponents, request, return_changed, xmlresponses, format)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -847,8 +852,8 @@
</span><span class="cx"> code = None
</span><span class="cx"> error = None
</span><span class="cx"> try:
</span><del>- componentdata = xmldata.textData()
</del><span class="cx"> component = xmldata.generateComponent()
</span><ins>+ format = xmldata.content_type
</ins><span class="cx">
</span><span class="cx"> updateResource = (yield request.locateResource(href))
</span><span class="cx"> if not updateResource.exists():
</span><span class="lines">@@ -862,7 +867,7 @@
</span><span class="cx"> if ifmatch and ifmatch != etag.generate():
</span><span class="cx"> raise HTTPError(PRECONDITION_FAILED)
</span><span class="cx">
</span><del>- changedData = yield self.storeResourceData(updateResource, component, componentdata)
</del><ins>+ changedComponent = yield self.storeResourceData(updateResource, component, returnChangedData=return_changed)
</ins><span class="cx">
</span><span class="cx"> except HTTPError, e:
</span><span class="cx"> # Extract the pre-condition
</span><span class="lines">@@ -875,7 +880,7 @@
</span><span class="cx"> code = BAD_REQUEST
</span><span class="cx">
</span><span class="cx"> if code is None:
</span><del>- if not return_changed or changedData is None:
</del><ins>+ if changedComponent is None:
</ins><span class="cx"> xmlresponses[index] = davxml.PropertyStatusResponse(
</span><span class="cx"> davxml.HRef.fromString(href),
</span><span class="cx"> davxml.PropertyStatus(
</span><span class="lines">@@ -891,7 +896,7 @@
</span><span class="cx"> davxml.PropertyStatus(
</span><span class="cx"> davxml.PropertyContainer(
</span><span class="cx"> davxml.GETETag.fromString(etag.generate()),
</span><del>- self.xmlDataElementType().fromTextData(changedData),
</del><ins>+ self.xmlDataElementType().fromComponentData(changedComponent, format),
</ins><span class="cx"> ),
</span><span class="cx"> davxml.Status.fromResponseCode(OK),
</span><span class="cx"> )
</span><span class="lines">@@ -989,7 +994,7 @@
</span><span class="cx"> if comps:
</span><span class="cx"> comps = comps.split(",")
</span><span class="cx"> else:
</span><del>- comps = allowedComponents
</del><ins>+ comps = ical.allowedStoreComponents
</ins><span class="cx"> return caldavxml.SupportedCalendarComponentSet(
</span><span class="cx"> *[caldavxml.CalendarComponent(name=item) for item in comps]
</span><span class="cx"> )
</span><span class="lines">@@ -1016,7 +1021,7 @@
</span><span class="cx"> if comps:
</span><span class="cx"> comps = comps.split(",")
</span><span class="cx"> else:
</span><del>- comps = allowedComponents
</del><ins>+ comps = ical.allowedStoreComponents
</ins><span class="cx"> return comps
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1053,6 +1058,8 @@
</span><span class="cx">
</span><span class="cx"> if config.EnableBatchUpload:
</span><span class="cx"> self._postHandlers[("text", "calendar")] = _CommonHomeChildCollectionMixin.simpleBatchPOST
</span><ins>+ if config.EnableJSONData:
+ self._postHandlers[("application", "calendar+json")] = _CommonHomeChildCollectionMixin.simpleBatchPOST
</ins><span class="cx"> self.xmlDocHandlers[customxml.Multiput] = _CommonHomeChildCollectionMixin.crudBatchPOST
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1088,6 +1095,11 @@
</span><span class="cx"> def iCalendarRolledup(self, request):
</span><span class="cx"> # FIXME: uncached: implement cache in the storage layer
</span><span class="cx">
</span><ins>+ # Accept header handling
+ accepted_type = bestAcceptType(request.headers.getHeader("accept"), Component.allowedTypes())
+ if accepted_type is None:
+ raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))
+
</ins><span class="cx"> # Generate a monolithic calendar
</span><span class="cx"> calendar = VCalendar("VCALENDAR")
</span><span class="cx"> calendar.addProperty(VProperty("VERSION", "2.0"))
</span><span class="lines">@@ -1138,17 +1150,13 @@
</span><span class="cx">
</span><span class="cx"> calendar.addComponent(component)
</span><span class="cx">
</span><del>- # Cache the data
- data = str(calendar)
- data = (yield self.getInternalSyncToken()) + "\r\n" + data
</del><ins>+ returnValue((calendar, accepted_type,))
</ins><span class="cx">
</span><del>- returnValue(calendar)
-
</del><span class="cx"> createCalendarCollection = _CommonHomeChildCollectionMixin.createCollection
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><del>- def componentsFromData(cls, data):
</del><ins>+ def componentsFromData(cls, data, format):
</ins><span class="cx"> """
</span><span class="cx"> Need to split a single VCALENDAR into separate ones based on UID with the
</span><span class="cx"> appropriate VTIEMZONES included.
</span><span class="lines">@@ -1158,7 +1166,7 @@
</span><span class="cx">
</span><span class="cx"> # Split into components by UID and TZID
</span><span class="cx"> try:
</span><del>- vcal = VCalendar.fromString(data)
</del><ins>+ vcal = VCalendar.fromString(data, format)
</ins><span class="cx"> except InvalidICalendarDataError:
</span><span class="cx"> return None
</span><span class="cx">
</span><span class="lines">@@ -1242,7 +1250,8 @@
</span><span class="cx">
</span><span class="cx"> elif qname == caldavxml.CalendarTimeZone.qname():
</span><span class="cx"> timezone = self._newStoreObject.getTimezone()
</span><del>- returnValue(caldavxml.CalendarTimeZone.fromString(str(timezone)) if timezone else None)
</del><ins>+ format = property.content_type if isinstance(property, caldavxml.CalendarTimeZone) else None
+ returnValue(caldavxml.CalendarTimeZone.fromCalendar(timezone, format=format) if timezone else None)
</ins><span class="cx">
</span><span class="cx"> result = (yield super(CalendarCollectionResource, self).readProperty(property, request))
</span><span class="cx"> returnValue(result)
</span><span class="lines">@@ -1288,7 +1297,7 @@
</span><span class="cx"> yield newchild.storeComponent(component)
</span><span class="cx"> if returnChangedData and newchild._newStoreObject._componentChanged:
</span><span class="cx"> result = (yield newchild.componentForUser())
</span><del>- returnValue(str(result))
</del><ins>+ returnValue(result)
</ins><span class="cx"> else:
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="lines">@@ -2210,16 +2219,38 @@
</span><span class="cx"> return self._newStoreObject.component()
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def allowedTypes(self):
+ """
+ Return a dict of allowed MIME types for storing, mapped to equivalent PyCalendar types.
+ """
+ raise NotImplementedError
+
+
+ def determineType(self, content_type):
+ """
+ Determine if the supplied content-type is valid for storing and return the matching PyCalendar type.
+ """
+ format = None
+ if content_type is not None:
+ format = "%s/%s" % (content_type.mediaType, content_type.mediaSubtype,)
+ return format if format in self.allowedTypes() else None
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def render(self, request):
</span><span class="cx"> if not self.exists():
</span><span class="cx"> log.debug("Resource not found: %s" % (self,))
</span><span class="cx"> raise HTTPError(NOT_FOUND)
</span><span class="cx">
</span><ins>+ # Accept header handling
+ accepted_type = bestAcceptType(request.headers.getHeader("accept"), self.allowedTypes())
+ if accepted_type is None:
+ raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))
+
</ins><span class="cx"> output = yield self.component()
</span><span class="cx">
</span><del>- response = Response(OK, {}, str(output))
- response.headers.setHeader("content-type", self.contentType())
</del><ins>+ response = Response(OK, {}, output.getText(accepted_type))
+ response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))
</ins><span class="cx"> returnValue(response)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -2379,10 +2410,10 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def storeStream(self, stream):
</del><ins>+ def storeStream(self, stream, format):
</ins><span class="cx">
</span><span class="cx"> # FIXME: direct tests
</span><del>- component = self._componentFromStream((yield allDataFromStream(stream)))
</del><ins>+ component = self._componentFromStream((yield allDataFromStream(stream)), format)
</ins><span class="cx"> result = (yield self.storeComponent(component))
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><span class="lines">@@ -2514,6 +2545,13 @@
</span><span class="cx">
</span><span class="cx"> _componentFromStream = VCalendar.fromString
</span><span class="cx">
</span><ins>+ def allowedTypes(self):
+ """
+ Return a tuple of allowed MIME types for storing.
+ """
+ return Component.allowedTypes()
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def inNewTransaction(self, request, label=""):
</span><span class="cx"> """
</span><span class="lines">@@ -2541,12 +2579,6 @@
</span><span class="cx"> self._initializeWithObject(newObject, newParent)
</span><span class="cx"> returnValue(txn)
</span><span class="cx">
</span><del>-
- @inlineCallbacks
- def iCalendarText(self):
- data = yield self.iCalendar()
- returnValue(str(data))
-
</del><span class="cx"> iCalendar = _CommonObjectResource.component
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -2710,7 +2742,8 @@
</span><span class="cx">
</span><span class="cx"> # Content-type check
</span><span class="cx"> content_type = request.headers.getHeader("content-type")
</span><del>- if content_type is not None and (content_type.mediaType, content_type.mediaSubtype) != ("text", "calendar"):
</del><ins>+ format = self.determineType(content_type)
+ if format is None:
</ins><span class="cx"> log.error("MIME type {content_type} not allowed in calendar collection", content_type=content_type)
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="lines">@@ -2745,13 +2778,13 @@
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="cx"> try:
</span><del>- component = Component.fromString(calendardata)
</del><ins>+ component = Component.fromString(calendardata, format)
</ins><span class="cx"> except ValueError, e:
</span><span class="cx"> log.error(str(e))
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "valid-calendar-data"),
</span><del>- "Can't parse calendar data"
</del><ins>+ "Can't parse calendar data: %s" % (str(e),)
</ins><span class="cx"> ))
</span><span class="cx">
</span><span class="cx"> # storeComponent needs to know who the auth'd user is for access control
</span><span class="lines">@@ -2864,7 +2897,7 @@
</span><span class="cx"> if rids is not None:
</span><span class="cx"> rids = rids[0].split(",")
</span><span class="cx"> try:
</span><del>- rids = [PyCalendarDateTime.parseText(rid) if rid != "M" else None for rid in rids]
</del><ins>+ rids = [DateTime.parseText(rid) if rid != "M" else None for rid in rids]
</ins><span class="cx"> except ValueError:
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> FORBIDDEN,
</span><span class="lines">@@ -3001,6 +3034,8 @@
</span><span class="cx">
</span><span class="cx"> if config.EnableBatchUpload:
</span><span class="cx"> self._postHandlers[("text", "vcard")] = AddressBookCollectionResource.simpleBatchPOST
</span><ins>+ if config.EnableJSONData:
+ self._postHandlers[("application", "vcard+json")] = _CommonHomeChildCollectionMixin.simpleBatchPOST
</ins><span class="cx"> self.xmlDocHandlers[customxml.Multiput] = AddressBookCollectionResource.crudBatchPOST
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -3032,9 +3067,9 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><del>- def componentsFromData(cls, data):
</del><ins>+ def componentsFromData(cls, data, format):
</ins><span class="cx"> try:
</span><del>- return VCard.allFromString(data)
</del><ins>+ return VCard.allFromString(data, format)
</ins><span class="cx"> except InvalidVCardDataError:
</span><span class="cx"> return None
</span><span class="cx">
</span><span class="lines">@@ -3055,7 +3090,7 @@
</span><span class="cx"> yield newchild.storeComponent(component)
</span><span class="cx"> if returnChangedData and newchild._newStoreObject._componentChanged:
</span><span class="cx"> result = (yield newchild.component())
</span><del>- returnValue(str(result))
</del><ins>+ returnValue(result)
</ins><span class="cx"> else:
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="lines">@@ -3096,7 +3131,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def bulkCreate(self, indexedComponents, request, return_changed, xmlresponses):
</del><ins>+ def bulkCreate(self, indexedComponents, request, return_changed, xmlresponses, format):
</ins><span class="cx"> """
</span><span class="cx"> bulk create allowing groups to contain member UIDs added during the same bulk create
</span><span class="cx"> """
</span><span class="lines">@@ -3111,7 +3146,7 @@
</span><span class="cx"> # Get a resource for the new item
</span><span class="cx"> newchildURL = joinURL(request.path, name)
</span><span class="cx"> newchild = (yield request.locateResource(newchildURL))
</span><del>- changedData = (yield self.storeResourceData(newchild, component, returnChangedData=return_changed))
</del><ins>+ changedComponent = (yield self.storeResourceData(newchild, component, returnChangedData=return_changed))
</ins><span class="cx">
</span><span class="cx"> except GroupWithUnsharedAddressNotAllowedError, e:
</span><span class="cx"> # save off info and try again below
</span><span class="lines">@@ -3126,20 +3161,20 @@
</span><span class="cx"> error = (error.namespace, error.name,)
</span><span class="cx">
</span><span class="cx"> xmlresponses[index] = (
</span><del>- yield self.bulkCreateResponse(component, newchildURL, newchild, None, code, error)
</del><ins>+ yield self.bulkCreateResponse(component, newchildURL, newchild, None, code, error, format)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> except Exception:
</span><span class="cx"> xmlresponses[index] = (
</span><del>- yield self.bulkCreateResponse(component, newchildURL, newchild, None, code=BAD_REQUEST, error=None)
</del><ins>+ yield self.bulkCreateResponse(component, newchildURL, newchild, None, BAD_REQUEST, None, format)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> else:
</span><span class="cx"> if not return_changed:
</span><del>- changedData = None
</del><ins>+ changedComponent = None
</ins><span class="cx"> coaddedUIDs |= set([component.resourceUID()])
</span><span class="cx"> xmlresponses[index] = (
</span><del>- yield self.bulkCreateResponse(component, newchildURL, newchild, changedData, code=None, error=None)
</del><ins>+ yield self.bulkCreateResponse(component, newchildURL, newchild, changedComponent, None, None, format)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> if groupRetries:
</span><span class="lines">@@ -3157,7 +3192,7 @@
</span><span class="cx"> # give FORBIDDEN response
</span><span class="cx"> index, component, newchildURL, newchild, missingUIDs = groupRetry
</span><span class="cx"> xmlresponses[index] = (
</span><del>- yield self.bulkCreateResponse(component, newchildURL, newchild, changedData=None, code=FORBIDDEN, error=None)
</del><ins>+ yield self.bulkCreateResponse(component, newchildURL, newchild, None, FORBIDDEN, None, format)
</ins><span class="cx"> )
</span><span class="cx"> coaddedUIDs -= set([component.resourceUID()]) # group uid not added
</span><span class="cx"> groupRetries.remove(groupRetry) # remove this retry
</span><span class="lines">@@ -3167,11 +3202,11 @@
</span><span class="cx"> newchild._metadata["coaddedUIDs"] = coaddedUIDs
</span><span class="cx">
</span><span class="cx"> # don't catch errors, abort the whole transaction
</span><del>- changedData = yield self.storeResourceData(newchild, component, returnChangedData=return_changed)
</del><ins>+ changedComponent = yield self.storeResourceData(newchild, component, returnChangedData=return_changed)
</ins><span class="cx"> if not return_changed:
</span><del>- changedData = None
</del><ins>+ changedComponent = None
</ins><span class="cx"> xmlresponses[index] = (
</span><del>- yield self.bulkCreateResponse(component, newchildURL, newchild, changedData, code=None, error=None)
</del><ins>+ yield self.bulkCreateResponse(component, newchildURL, newchild, changedComponent, None, None, format)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -3266,6 +3301,13 @@
</span><span class="cx">
</span><span class="cx"> _componentFromStream = VCard.fromString
</span><span class="cx">
</span><ins>+ def allowedTypes(self):
+ """
+ Return a tuple of allowed MIME types for storing.
+ """
+ return VCard.allowedTypes()
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def vCardText(self):
</span><span class="cx"> data = yield self.vCard()
</span><span class="lines">@@ -3340,7 +3382,8 @@
</span><span class="cx">
</span><span class="cx"> # Content-type check
</span><span class="cx"> content_type = request.headers.getHeader("content-type")
</span><del>- if content_type is not None and (content_type.mediaType, content_type.mediaSubtype) != ("text", "vcard"):
</del><ins>+ format = self.determineType(content_type)
+ if format is None:
</ins><span class="cx"> log.error("MIME type {content_type} not allowed in vcard collection", content_type=content_type)
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="lines">@@ -3365,7 +3408,7 @@
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="cx"> try:
</span><del>- component = VCard.fromString(vcarddata)
</del><ins>+ component = VCard.fromString(vcarddata, format)
</ins><span class="cx"> except ValueError, e:
</span><span class="cx"> log.error(str(e))
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_addressbookmultigetpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_addressbookmultiget.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_addressbookmultiget.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_addressbookmultiget.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -19,17 +19,19 @@
</span><span class="cx"> from twext.web2 import responsecode
</span><span class="cx"> from twext.web2.iweb import IResponse
</span><span class="cx"> from twext.web2.stream import MemoryStream
</span><del>-from txdav.xml import element as davxml
</del><span class="cx"> from twext.web2.dav.util import davXMLFromStream, joinURL
</span><ins>+from twext.web2.http_headers import Headers, MimeType
</ins><span class="cx">
</span><span class="cx"> from twistedcaldav import carddavxml
</span><span class="cx"> from twistedcaldav import vcard
</span><del>-
</del><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase, SimpleStoreRequest
</span><ins>+
</ins><span class="cx"> from twisted.python.filepath import FilePath
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx">
</span><ins>+from txdav.xml import element as davxml
+
</ins><span class="cx"> class AddressBookMultiget (StoreTestCase):
</span><span class="cx"> """
</span><span class="cx"> addressbook-multiget REPORT
</span><span class="lines">@@ -214,7 +216,13 @@
</span><span class="cx"> '''
</span><span class="cx"> if data:
</span><span class="cx"> for filename, icaldata in data.iteritems():
</span><del>- request = SimpleStoreRequest(self, "PUT", joinURL(addressbook_uri, filename + ".vcf"), authid="wsanchez")
</del><ins>+ request = SimpleStoreRequest(
+ self,
+ "PUT",
+ joinURL(addressbook_uri, filename + ".vcf"),
+ headers=Headers({"content-type": MimeType.fromString("text/vcard")}),
+ authid="wsanchez"
+ )
</ins><span class="cx"> request.stream = MemoryStream(icaldata)
</span><span class="cx"> yield self.send(request)
</span><span class="cx"> else:
</span><span class="lines">@@ -222,7 +230,13 @@
</span><span class="cx"> for child in FilePath(self.vcards_dir).children():
</span><span class="cx"> if os.path.splitext(child.basename())[1] != ".vcf":
</span><span class="cx"> continue
</span><del>- request = SimpleStoreRequest(self, "PUT", joinURL(addressbook_uri, child.basename()), authid="wsanchez")
</del><ins>+ request = SimpleStoreRequest(
+ self,
+ "PUT",
+ joinURL(addressbook_uri, child.basename()),
+ headers=Headers({"content-type": MimeType.fromString("text/vcard")}),
+ authid="wsanchez"
+ )
</ins><span class="cx"> request.stream = MemoryStream(child.getContent())
</span><span class="cx"> yield self.send(request)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_caldavxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_caldavxml.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_caldavxml.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_caldavxml.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,9 +14,16 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><ins>+import twistedcaldav.test.util
</ins><span class="cx"> from twistedcaldav import caldavxml
</span><del>-import twistedcaldav.test.util
</del><ins>+from twistedcaldav.caldavxml import CalendarData
+from twistedcaldav.ical import normalize_iCalStr, Component
</ins><span class="cx">
</span><ins>+def normalizeJSON(j):
+ return "".join(map(str.strip, j.splitlines())).replace(", ", ",").replace(": ", ":")
+
+
+
</ins><span class="cx"> class CustomXML (twistedcaldav.test.util.TestCase):
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -50,3 +57,99 @@
</span><span class="cx">
</span><span class="cx"> tr = caldavxml.CalDAVTimeRangeElement(start="20110201T120000Z", end="20110202")
</span><span class="cx"> self.assertFalse(tr.valid())
</span><ins>+
+
+ def test_CalendarDataTextAndJSON(self):
+ """
+ Text that we can both parse and generate CalendarData elements with both text and json formats.
+ """
+ dataText = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1@example.com
+ATTENDEE:mailto:user2@example.com
+DTSTAMP:20080601T120000Z
+EXDATE:20080602T120000Z
+EXDATE:20080603T120000Z
+ORGANIZER;CN=User 01:mailto:user1@example.com
+RRULE:FREQ=DAILY;COUNT=400
+SUMMARY:Test
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+ dataXML = """<?xml version='1.0' encoding='UTF-8'?>
+<calendar-data xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[%s]]></calendar-data>""" % (dataText,)
+
+ jsonText = """[
+ "vcalendar",
+ [
+ ["version", {}, "text", "2.0"],
+ ["prodid", {}, "text", "-//CALENDARSERVER.ORG//NONSGML Version 1//EN"]
+ ],
+ [
+ ["vevent",
+ [
+ ["uid", {}, "text", "12345-67890"],
+ ["dtstart", {}, "date-time", "2008-06-01T12:00:00Z"],
+ ["dtend", {}, "date-time", "2008-06-01T13:00:00Z"],
+ ["attendee", {}, "cal-address", "mailto:user1@example.com"],
+ ["attendee", {}, "cal-address", "mailto:user2@example.com"],
+ ["dtstamp", {}, "date-time", "2008-06-01T12:00:00Z"],
+ ["exdate", {}, "date-time", "2008-06-02T12:00:00Z"],
+ ["exdate", {}, "date-time", "2008-06-03T12:00:00Z"],
+ ["organizer", {"cn": "User 01"}, "cal-address", "mailto:user1@example.com"],
+ ["rrule", {}, "recur", {"count": 400, "freq": "DAILY"}],
+ ["summary", {}, "text", "Test"]
+ ],
+ [
+ ]
+ ]
+ ]
+]
+"""
+
+ jsonXML = """<?xml version='1.0' encoding='UTF-8'?>
+<calendar-data content-type='application/calendar+json' xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[%s]]></calendar-data>""" % (jsonText,)
+
+ cd = CalendarData.fromTextData(dataText)
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "text/calendar")
+ self.assertEqual(cd.toxml(), dataXML)
+
+ comp = Component.fromString(dataText)
+ cd = CalendarData.fromCalendar(comp)
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "text/calendar")
+ self.assertEqual(cd.toxml(), dataXML)
+
+ cd = CalendarData.fromCalendar(comp, format="application/calendar+json")
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "application/calendar+json")
+ self.assertEqual(normalizeJSON(cd.toxml()), normalizeJSON(jsonXML))
+
+ cd = CalendarData.fromTextData(jsonText, format="application/calendar+json")
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "application/calendar+json")
+ self.assertEqual(cd.toxml(), jsonXML)
+
+ comp = Component.fromString(jsonText, format="application/calendar+json")
+ cd = CalendarData.fromCalendar(comp)
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "text/calendar")
+ self.assertEqual(cd.toxml(), dataXML)
+
+ cd = CalendarData.fromCalendar(comp, format="application/calendar+json")
+ self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText))
+ self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText))
+ self.assertEqual(cd.content_type, "application/calendar+json")
+ self.assertEqual(normalizeJSON(cd.toxml()), normalizeJSON(jsonXML))
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_calendarquerypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_calendarquery.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_calendarquery.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_calendarquery.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -32,7 +32,7 @@
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase, SimpleStoreRequest
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx"> from twistedcaldav.ical import Component
</span><span class="cx"> from txdav.caldav.icalendarstore import ComponentUpdateState
</span><span class="cx"> from twistedcaldav.directory.directory import DirectoryService
</span><span class="lines">@@ -124,8 +124,8 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> query_timerange = caldavxml.TimeRange(
</span><del>- start="%04d1001T000000Z" % (PyCalendarDateTime.getToday().getYear(),),
- end="%04d1101T000000Z" % (PyCalendarDateTime.getToday().getYear(),),
</del><ins>+ start="%04d1001T000000Z" % (DateTime.getToday().getYear(),),
+ end="%04d1101T000000Z" % (DateTime.getToday().getYear(),),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> query = caldavxml.CalendarQuery(
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_configpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_config.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_config.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_config.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -34,7 +34,7 @@
</span><span class="cx"> <dict>
</span><span class="cx">
</span><span class="cx"> <key>ResponseCompression</key>
</span><del>- <false/>
</del><ins>+ <true/>
</ins><span class="cx">
</span><span class="cx"> <key>HTTPPort</key>
</span><span class="cx"> <integer>8008</integer>
</span><span class="lines">@@ -73,7 +73,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def _testResponseCompression(testCase):
</span><del>- testCase.assertEquals(config.ResponseCompression, False)
</del><ins>+ testCase.assertEquals(config.ResponseCompression, True)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -114,19 +114,19 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def testLoadConfig(self):
</span><del>- self.assertEquals(config.ResponseCompression, True)
</del><ins>+ self.assertEquals(config.ResponseCompression, False)
</ins><span class="cx">
</span><span class="cx"> config.load(self.testConfig)
</span><span class="cx">
</span><del>- self.assertEquals(config.ResponseCompression, False)
</del><ins>+ self.assertEquals(config.ResponseCompression, True)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def testScoping(self):
</span><del>- self.assertEquals(config.ResponseCompression, True)
</del><ins>+ self.assertEquals(config.ResponseCompression, False)
</ins><span class="cx">
</span><span class="cx"> config.load(self.testConfig)
</span><span class="cx">
</span><del>- self.assertEquals(config.ResponseCompression, False)
</del><ins>+ self.assertEquals(config.ResponseCompression, True)
</ins><span class="cx">
</span><span class="cx"> _testResponseCompression(self)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_dateopspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_dateops.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_dateops.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_dateops.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx">
</span><span class="cx"> import twistedcaldav.test.util
</span><span class="cx"> from twisted.trial.unittest import SkipTest
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from twistedcaldav.dateops import parseSQLTimestampToPyCalendar, \
</span><span class="cx"> parseSQLDateToPyCalendar, pyCalendarTodatetime, \
</span><span class="lines">@@ -24,7 +24,7 @@
</span><span class="cx">
</span><span class="cx"> import datetime
</span><span class="cx"> import dateutil
</span><del>-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.timezone import Timezone
</ins><span class="cx"> from twistedcaldav.timezones import TimezoneCache
</span><span class="cx">
</span><span class="cx"> class Dateops(twistedcaldav.test.util.TestCase):
</span><span class="lines">@@ -43,10 +43,10 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> data = (
</span><del>- (PyCalendarDateTime(2012, 1, 1), PyCalendarDateTime(2012, 1, 1, 0, 0, 0)),
- (PyCalendarDateTime(2012, 1, 1, 10, 0, 0), PyCalendarDateTime(2012, 1, 1, 10, 0, 0)),
- (PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)), PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- (PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")), PyCalendarDateTime(2012, 1, 1, 17, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ (DateTime(2012, 1, 1), DateTime(2012, 1, 1, 0, 0, 0)),
+ (DateTime(2012, 1, 1, 10, 0, 0), DateTime(2012, 1, 1, 10, 0, 0)),
+ (DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)), DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True))),
+ (DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(tzid="America/New_York")), DateTime(2012, 1, 1, 17, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> for value, result in data:
</span><span class="lines">@@ -59,10 +59,10 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> data = (
</span><del>- (PyCalendarDateTime(2012, 1, 1), PyCalendarDateTime(2012, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- (PyCalendarDateTime(2012, 1, 1, 10, 0, 0), PyCalendarDateTime(2012, 1, 1, 10, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- (PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)), PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- (PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")), PyCalendarDateTime(2012, 1, 1, 17, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ (DateTime(2012, 1, 1), DateTime(2012, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))),
+ (DateTime(2012, 1, 1, 10, 0, 0), DateTime(2012, 1, 1, 10, 0, 0, tzid=Timezone(utc=True))),
+ (DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)), DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True))),
+ (DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(tzid="America/New_York")), DateTime(2012, 1, 1, 17, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> for value, result in data:
</span><span class="lines">@@ -75,10 +75,10 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> data = (
</span><del>- (PyCalendarDateTime(2012, 1, 1), PyCalendarDateTime(2012, 1, 1)),
- (PyCalendarDateTime(2012, 1, 1, 10, 0, 0), PyCalendarDateTime(2012, 1, 1, 10, 0, 0)),
- (PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)), PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- (PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")), PyCalendarDateTime(2012, 1, 1, 17, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ (DateTime(2012, 1, 1), DateTime(2012, 1, 1)),
+ (DateTime(2012, 1, 1, 10, 0, 0), DateTime(2012, 1, 1, 10, 0, 0)),
+ (DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)), DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True))),
+ (DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(tzid="America/New_York")), DateTime(2012, 1, 1, 17, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> for value, result in data:
</span><span class="lines">@@ -107,132 +107,132 @@
</span><span class="cx"> # Timed
</span><span class="cx"> (
</span><span class="cx"> "Start within, end within - overlap",
</span><del>- PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> True,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Start before, end before - no overlap",
</span><del>- PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 3, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> False,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Start before, end right before - no overlap",
</span><del>- PyCalendarDateTime(2012, 1, 1, 23, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 1, 23, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 3, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> False,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Start before, end within - overlap",
</span><del>- PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 3, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> True,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Start after, end after - no overlap",
</span><del>- PyCalendarDateTime(2012, 1, 2, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 2, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 12, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> False,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Start right after, end after - no overlap",
</span><del>- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 1, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> False,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Start within, end after - overlap",
</span><del>- PyCalendarDateTime(2012, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 1, 12, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 12, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> True,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Start before, end after - overlap",
</span><del>- PyCalendarDateTime(2012, 1, 1, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 3, 11, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 1, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 3, 11, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 3, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> True,
</span><span class="cx"> ),
</span><span class="cx">
</span><span class="cx"> # All day
</span><span class="cx"> (
</span><span class="cx"> "All day: Start within, end within - overlap",
</span><del>- PyCalendarDateTime(2012, 1, 9),
- PyCalendarDateTime(2012, 1, 10),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 9),
+ DateTime(2012, 1, 10),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> True,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "All day: Start before, end before - no overlap",
</span><del>- PyCalendarDateTime(2012, 1, 1),
- PyCalendarDateTime(2012, 1, 2),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 1),
+ DateTime(2012, 1, 2),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> False,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "All day: Start before, end right before - no overlap",
</span><del>- PyCalendarDateTime(2012, 1, 7),
- PyCalendarDateTime(2012, 1, 8),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 7),
+ DateTime(2012, 1, 8),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> False,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "All day: Start before, end within - overlap",
</span><del>- PyCalendarDateTime(2012, 1, 7),
- PyCalendarDateTime(2012, 1, 9),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 7),
+ DateTime(2012, 1, 9),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> True,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "All day: Start after, end after - no overlap",
</span><del>- PyCalendarDateTime(2012, 1, 16),
- PyCalendarDateTime(2012, 1, 17),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 16),
+ DateTime(2012, 1, 17),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> False,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "All day: Start right after, end after - no overlap",
</span><del>- PyCalendarDateTime(2012, 1, 15),
- PyCalendarDateTime(2012, 1, 16),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 15),
+ DateTime(2012, 1, 16),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> False,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "All day: Start within, end after - overlap",
</span><del>- PyCalendarDateTime(2012, 1, 14),
- PyCalendarDateTime(2012, 1, 16),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 14),
+ DateTime(2012, 1, 16),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> True,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "All day: Start before, end after - overlap",
</span><del>- PyCalendarDateTime(2012, 1, 7),
- PyCalendarDateTime(2012, 1, 16),
- PyCalendarDateTime(2012, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2012, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2012, 1, 7),
+ DateTime(2012, 1, 16),
+ DateTime(2012, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2012, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> True,
</span><span class="cx"> ),
</span><span class="cx"> )
</span><span class="lines">@@ -254,8 +254,8 @@
</span><span class="cx"> dateops.pyCalendarTodatetime
</span><span class="cx"> """
</span><span class="cx"> tests = (
</span><del>- (PyCalendarDateTime(2012, 4, 4, 12, 34, 56), datetime.datetime(2012, 4, 4, 12, 34, 56, tzinfo=dateutil.tz.tzutc())),
- (PyCalendarDateTime(2012, 12, 31), datetime.date(2012, 12, 31)),
</del><ins>+ (DateTime(2012, 4, 4, 12, 34, 56), datetime.datetime(2012, 4, 4, 12, 34, 56, tzinfo=dateutil.tz.tzutc())),
+ (DateTime(2012, 12, 31), datetime.date(2012, 12, 31)),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> for pycal, result in tests:
</span><span class="lines">@@ -267,8 +267,8 @@
</span><span class="cx"> dateops.parseSQLTimestampToPyCalendar
</span><span class="cx"> """
</span><span class="cx"> tests = (
</span><del>- ("2012-04-04 12:34:56", PyCalendarDateTime(2012, 4, 4, 12, 34, 56)),
- ("2012-12-31 01:01:01", PyCalendarDateTime(2012, 12, 31, 1, 1, 1)),
</del><ins>+ ("2012-04-04 12:34:56", DateTime(2012, 4, 4, 12, 34, 56)),
+ ("2012-12-31 01:01:01", DateTime(2012, 12, 31, 1, 1, 1)),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> for sqlStr, result in tests:
</span><span class="lines">@@ -281,8 +281,8 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> tests = (
</span><del>- ("2012-04-04", PyCalendarDateTime(2012, 4, 4)),
- ("2012-12-31 00:00:00", PyCalendarDateTime(2012, 12, 31)),
</del><ins>+ ("2012-04-04", DateTime(2012, 4, 4)),
+ ("2012-12-31 00:00:00", DateTime(2012, 12, 31)),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> for sqlStr, result in tests:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_icalendarpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_icalendar.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_icalendar.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_icalendar.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -21,16 +21,16 @@
</span><span class="cx"> from twisted.trial.unittest import SkipTest
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.ical import Component, Property, InvalidICalendarDataError, \
</span><del>- normalizeCUAddress
</del><ins>+ normalizeCUAddress, normalize_iCalStr
</ins><span class="cx"> from twistedcaldav.instance import InvalidOverriddenInstanceError
</span><span class="cx"> import twistedcaldav.test.util
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx"> from twistedcaldav.ical import iCalendarProductID
</span><del>-from pycalendar.duration import PyCalendarDuration
</del><ins>+from pycalendar.duration import Duration
</ins><span class="cx"> from twistedcaldav.dateops import normalizeForExpand
</span><del>-from pycalendar.value import PyCalendarValue
</del><ins>+from pycalendar.value import Value
</ins><span class="cx">
</span><span class="cx"> class iCalendar (twistedcaldav.test.util.TestCase):
</span><span class="cx"> """
</span><span class="lines">@@ -469,7 +469,7 @@
</span><span class="cx"> calendar.validCalendarData(doFix=False, validateRecurrences=True)
</span><span class="cx">
</span><span class="cx"> # Verify expansion works, even for an RDATE prior to master DTSTART:
</span><del>- calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
</del><ins>+ calendar.expandTimeRanges(DateTime(2100, 1, 1))
</ins><span class="cx">
</span><span class="cx"> # Test EXDATEs *prior* to master (as the result of client splitting a
</span><span class="cx"> # a recurring event and copying *all* EXDATEs to new event):
</span><span class="lines">@@ -568,13 +568,13 @@
</span><span class="cx">
</span><span class="cx"> year = 2004
</span><span class="cx">
</span><del>- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
</del><ins>+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
</ins><span class="cx"> for key in instances:
</span><span class="cx"> instance = instances[key]
</span><span class="cx"> start = instance.start
</span><span class="cx"> end = instance.end
</span><del>- self.assertEqual(start, PyCalendarDateTime(year, 7, 4))
- self.assertEqual(end , PyCalendarDateTime(year, 7, 5))
</del><ins>+ self.assertEqual(start, DateTime(year, 7, 4))
+ self.assertEqual(end , DateTime(year, 7, 5))
</ins><span class="cx"> if year == 2050:
</span><span class="cx"> break
</span><span class="cx"> year += 1
</span><span class="lines">@@ -594,14 +594,14 @@
</span><span class="cx"> }
</span><span class="cx"> year = 2004
</span><span class="cx">
</span><del>- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
</del><ins>+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
</ins><span class="cx"> for key in instances:
</span><span class="cx"> instance = instances[key]
</span><span class="cx"> start = instance.start
</span><span class="cx"> end = instance.end
</span><span class="cx"> if year in results:
</span><del>- self.assertEqual(start, PyCalendarDateTime(year, results[year][0], results[year][1]))
- self.assertEqual(end , PyCalendarDateTime(year, results[year][0], results[year][2]))
</del><ins>+ self.assertEqual(start, DateTime(year, results[year][0], results[year][1]))
+ self.assertEqual(end , DateTime(year, results[year][0], results[year][2]))
</ins><span class="cx"> if year == 2050:
</span><span class="cx"> break
</span><span class="cx"> year += 1
</span><span class="lines">@@ -621,14 +621,14 @@
</span><span class="cx"> }
</span><span class="cx"> year = 2002
</span><span class="cx">
</span><del>- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
</del><ins>+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
</ins><span class="cx"> for key in instances:
</span><span class="cx"> instance = instances[key]
</span><span class="cx"> start = instance.start
</span><span class="cx"> end = instance.end
</span><span class="cx"> if year in results:
</span><del>- self.assertEqual(start, PyCalendarDateTime(year, results[year][0], results[year][1]))
- self.assertEqual(end , PyCalendarDateTime(year, results[year][0], results[year][2]))
</del><ins>+ self.assertEqual(start, DateTime(year, results[year][0], results[year][1]))
+ self.assertEqual(end , DateTime(year, results[year][0], results[year][2]))
</ins><span class="cx"> if year == 2050:
</span><span class="cx"> break
</span><span class="cx"> year += 1
</span><span class="lines">@@ -642,13 +642,13 @@
</span><span class="cx"> """
</span><span class="cx"> calendar = Component.fromStream(file(os.path.join(self.data_dir, "Holidays", "C318ABFE-1ED0-11D9-A5E0-000A958A3252.ics")))
</span><span class="cx">
</span><del>- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
</del><ins>+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
</ins><span class="cx"> for key in instances:
</span><span class="cx"> instance = instances[key]
</span><span class="cx"> start = instance.start
</span><span class="cx"> end = instance.end
</span><del>- self.assertEqual(start, PyCalendarDateTime(2004, 11, 25))
- self.assertEqual(end, PyCalendarDateTime(2004, 11, 27))
</del><ins>+ self.assertEqual(start, DateTime(2004, 11, 25))
+ self.assertEqual(end, DateTime(2004, 11, 27))
</ins><span class="cx"> break
</span><span class="cx">
</span><span class="cx"> # test_component_timerange.todo = "recurrence expansion should give us no end date here"
</span><span class="lines">@@ -658,44 +658,44 @@
</span><span class="cx"> """
</span><span class="cx"> parse_date()
</span><span class="cx"> """
</span><del>- self.assertEqual(PyCalendarDateTime.parseText("19970714"), PyCalendarDateTime(1997, 7, 14))
</del><ins>+ self.assertEqual(DateTime.parseText("19970714"), DateTime(1997, 7, 14))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_parse_datetime(self):
</span><span class="cx"> """
</span><span class="cx"> parse_datetime()
</span><span class="cx"> """
</span><del>- dt = PyCalendarDateTime.parseText("19980118T230000")
- self.assertEqual(dt, PyCalendarDateTime(1998, 1, 18, 23, 0, 0))
</del><ins>+ dt = DateTime.parseText("19980118T230000")
+ self.assertEqual(dt, DateTime(1998, 1, 18, 23, 0, 0))
</ins><span class="cx"> self.assertTrue(dt.floating())
</span><span class="cx">
</span><del>- dt = PyCalendarDateTime.parseText("19980119T070000Z")
- self.assertEqual(dt, PyCalendarDateTime(1998, 1, 19, 7, 0, 0, tzid=PyCalendarTimezone(utc=True)))
</del><ins>+ dt = DateTime.parseText("19980119T070000Z")
+ self.assertEqual(dt, DateTime(1998, 1, 19, 7, 0, 0, tzid=Timezone(utc=True)))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_parse_date_or_datetime(self):
</span><span class="cx"> """
</span><span class="cx"> parse_date_or_datetime()
</span><span class="cx"> """
</span><del>- self.assertEqual(PyCalendarDateTime.parseText("19970714"), PyCalendarDateTime(1997, 7, 14))
</del><ins>+ self.assertEqual(DateTime.parseText("19970714"), DateTime(1997, 7, 14))
</ins><span class="cx">
</span><del>- dt = PyCalendarDateTime.parseText("19980118T230000")
- self.assertEqual(dt, PyCalendarDateTime(1998, 1, 18, 23, 0, 0))
</del><ins>+ dt = DateTime.parseText("19980118T230000")
+ self.assertEqual(dt, DateTime(1998, 1, 18, 23, 0, 0))
</ins><span class="cx"> self.assertTrue(dt.floating())
</span><span class="cx">
</span><del>- dt = PyCalendarDateTime.parseText("19980119T070000Z")
- self.assertEqual(dt, PyCalendarDateTime(1998, 1, 19, 7, 0, 0, tzid=PyCalendarTimezone(utc=True)))
</del><ins>+ dt = DateTime.parseText("19980119T070000Z")
+ self.assertEqual(dt, DateTime(1998, 1, 19, 7, 0, 0, tzid=Timezone(utc=True)))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_parse_duration(self):
</span><span class="cx"> """
</span><span class="cx"> parse_duration()
</span><span class="cx"> """
</span><del>- self.assertEqual(PyCalendarDuration.parseText("P15DT5H0M20S"), PyCalendarDuration(days=15, hours=5, minutes=0, seconds=20))
- self.assertEqual(PyCalendarDuration.parseText("+P15DT5H0M20S"), PyCalendarDuration(days=15, hours=5, minutes=0, seconds=20))
- self.assertEqual(PyCalendarDuration.parseText("-P15DT5H0M20S"), PyCalendarDuration(days=15 * -1, hours=5 * -1, minutes=0, seconds=20 * -1))
</del><ins>+ self.assertEqual(Duration.parseText("P15DT5H0M20S"), Duration(days=15, hours=5, minutes=0, seconds=20))
+ self.assertEqual(Duration.parseText("+P15DT5H0M20S"), Duration(days=15, hours=5, minutes=0, seconds=20))
+ self.assertEqual(Duration.parseText("-P15DT5H0M20S"), Duration(days=15 * -1, hours=5 * -1, minutes=0, seconds=20 * -1))
</ins><span class="cx">
</span><del>- self.assertEqual(PyCalendarDuration.parseText("P7W"), PyCalendarDuration(weeks=7))
</del><ins>+ self.assertEqual(Duration.parseText("P7W"), Duration(weeks=7))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_correct_attendee_properties(self):
</span><span class="lines">@@ -807,7 +807,7 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> ("mailto:user1@example.com", None),
</span><del>- ("mailto:user1@example.com", PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
</del><ins>+ ("mailto:user1@example.com", DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)))
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -833,7 +833,7 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> ("mailto:user1@example.com", None),
</span><del>- ("mailto:user3@example.com", PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
</del><ins>+ ("mailto:user3@example.com", DateTime(2009, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)))
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -946,8 +946,8 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> ("mailto:user2@example.com", None),
</span><del>- ("mailto:user2@example.com", PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3@example.com", PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
</del><ins>+ ("mailto:user2@example.com", DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3@example.com", DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)))
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -1152,6 +1152,82 @@
</span><span class="cx"> self.assertEqual(result, str(component).replace("\r", ""))
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def test_parameter_multi_values(self):
+ caldata = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user01@example.com
+ATTENDEE;MEMBER="urn:uuid:group01","urn:uuid:group02";PARTSTAT=NEEDS-ACTION:mailto:user02@example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01@example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ caldata2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user01@example.com
+ATTENDEE;MEMBER="urn:uuid:group01","urn:uuid:group02","urn:uuid:group03";PARTSTAT=NEEDS-ACTION:mailto:user02@example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01@example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ caldata3 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user01@example.com
+ATTENDEE;MEMBER="urn:uuid:group01";PARTSTAT=NEEDS-ACTION:mailto:user02@example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01@example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ component = Component.fromString(caldata)
+ attendee = component.masterComponent().getAttendeeProperty(["mailto:user02@example.com", ])
+ self.assertTrue(attendee is not None)
+
+ # Single value retrieved as multi-value
+ partstat = attendee.parameterValues("PARTSTAT")
+ self.assertEqual(partstat, ["NEEDS-ACTION"])
+
+ # Multi-value retrieved as single-value
+ member = attendee.parameterValue("MEMBER")
+ self.assertEqual(member, "urn:uuid:group01")
+
+ # Multi-value retrieved as multi-value
+ members = attendee.parameterValues("MEMBER")
+ self.assertEqual(members, ["urn:uuid:group01", "urn:uuid:group02"])
+
+ # Multi-value add a new value
+ members = attendee.parameterValues("MEMBER")
+ members.append("urn:uuid:group03")
+ attendee.setParameter("MEMBER", members)
+ members = attendee.parameterValues("MEMBER")
+ self.assertEqual(members, ["urn:uuid:group01", "urn:uuid:group02", "urn:uuid:group03"])
+ self.assertEqual(normalize_iCalStr(str(component)), normalize_iCalStr(caldata2))
+
+ # Multi-value back to one
+ members = attendee.parameterValues("MEMBER")
+ del members[1:]
+ attendee.setParameter("MEMBER", members)
+ members = attendee.parameterValues("MEMBER")
+ self.assertEqual(members, ["urn:uuid:group01"])
+ self.assertEqual(normalize_iCalStr(str(component)), normalize_iCalStr(caldata3))
+
+
</ins><span class="cx"> def test_add_property_with_valuetype(self):
</span><span class="cx"> data = """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="lines">@@ -1177,7 +1253,7 @@
</span><span class="cx">
</span><span class="cx"> component = Component.fromString(data)
</span><span class="cx"> vevent = component.mainComponent()
</span><del>- vevent.addProperty(Property("ATTACH", "foobar", valuetype=PyCalendarValue.VALUETYPE_BINARY))
</del><ins>+ vevent.addProperty(Property("ATTACH", "foobar", valuetype=Value.VALUETYPE_BINARY))
</ins><span class="cx"> self.assertEqual(str(component), result)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -2277,8 +2353,8 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2299,12 +2375,12 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2326,16 +2402,16 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 16, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 16, 1, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 16, 2, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2357,12 +2433,12 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 16, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 16, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 16, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2384,12 +2460,12 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 16, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 16, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 16, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2417,12 +2493,12 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 2, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2474,12 +2550,12 @@
</span><span class="cx"> True,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2488,9 +2564,9 @@
</span><span class="cx"> for description, original, ignoreInvalidInstances, results in data:
</span><span class="cx"> component = Component.fromString(original)
</span><span class="cx"> if results is None:
</span><del>- self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
</del><ins>+ self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, DateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
</ins><span class="cx"> else:
</span><del>- instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
</del><ins>+ instances = component.expandTimeRanges(DateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
</ins><span class="cx"> self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
</span><span class="cx"> periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
</span><span class="cx"> self.assertEqual(periods, results)
</span><span class="lines">@@ -2518,8 +2594,8 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2539,8 +2615,8 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15),
- PyCalendarDateTime(2007, 11, 16),
</del><ins>+ DateTime(2007, 11, 15),
+ DateTime(2007, 11, 16),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2561,12 +2637,12 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2587,12 +2663,12 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15),
- PyCalendarDateTime(2007, 11, 16),
</del><ins>+ DateTime(2007, 11, 15),
+ DateTime(2007, 11, 16),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 16),
- PyCalendarDateTime(2007, 11, 17),
</del><ins>+ DateTime(2007, 11, 16),
+ DateTime(2007, 11, 17),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2614,16 +2690,16 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 16, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 16, 1, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 16, 2, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2645,16 +2721,16 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15),
- PyCalendarDateTime(2007, 11, 16),
</del><ins>+ DateTime(2007, 11, 15),
+ DateTime(2007, 11, 16),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 16),
- PyCalendarDateTime(2007, 11, 17),
</del><ins>+ DateTime(2007, 11, 16),
+ DateTime(2007, 11, 17),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 18),
- PyCalendarDateTime(2007, 11, 19),
</del><ins>+ DateTime(2007, 11, 18),
+ DateTime(2007, 11, 19),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2676,12 +2752,12 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 16, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 16, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 16, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2703,12 +2779,12 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15),
- PyCalendarDateTime(2007, 11, 16),
</del><ins>+ DateTime(2007, 11, 15),
+ DateTime(2007, 11, 16),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 17),
- PyCalendarDateTime(2007, 11, 18),
</del><ins>+ DateTime(2007, 11, 17),
+ DateTime(2007, 11, 18),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2736,12 +2812,12 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 2, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2769,12 +2845,12 @@
</span><span class="cx"> False,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15),
- PyCalendarDateTime(2007, 11, 16),
</del><ins>+ DateTime(2007, 11, 15),
+ DateTime(2007, 11, 16),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 16),
- PyCalendarDateTime(2007, 11, 18),
</del><ins>+ DateTime(2007, 11, 16),
+ DateTime(2007, 11, 18),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -2831,9 +2907,9 @@
</span><span class="cx"> for description, original, ignoreInvalidInstances, results in data:
</span><span class="cx"> component = Component.fromString(original)
</span><span class="cx"> if results is None:
</span><del>- self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
</del><ins>+ self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, DateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
</ins><span class="cx"> else:
</span><del>- instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances, normalizeFunction=normalizeForExpand)
</del><ins>+ instances = component.expandTimeRanges(DateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances, normalizeFunction=normalizeForExpand)
</ins><span class="cx"> self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
</span><span class="cx"> periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
</span><span class="cx"> self.assertEqual(periods, results)
</span><span class="lines">@@ -2861,8 +2937,8 @@
</span><span class="cx"> None,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> None,
</span><span class="lines">@@ -2880,11 +2956,11 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2007, 1, 1),
</del><ins>+ DateTime(2007, 1, 1),
</ins><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> None,
</span><span class="lines">@@ -2902,9 +2978,9 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2010, 1, 1),
</del><ins>+ DateTime(2010, 1, 1),
</ins><span class="cx"> (),
</span><del>- PyCalendarDateTime(2010, 1, 1),
</del><ins>+ DateTime(2010, 1, 1),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Simple recurring - no limit",
</span><span class="lines">@@ -2923,20 +2999,20 @@
</span><span class="cx"> None,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2008, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2008, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2010, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2010, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> None,
</span><span class="lines">@@ -2955,23 +3031,23 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2007, 1, 1),
</del><ins>+ DateTime(2007, 1, 1),
</ins><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2008, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2008, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2010, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2010, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> None,
</span><span class="lines">@@ -2990,14 +3066,14 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2010, 1, 1),
</del><ins>+ DateTime(2010, 1, 1),
</ins><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2010, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2010, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><del>- PyCalendarDateTime(2010, 1, 1),
</del><ins>+ DateTime(2010, 1, 1),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Simple recurring - limit effective full",
</span><span class="lines">@@ -3013,9 +3089,9 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2012, 1, 1),
</del><ins>+ DateTime(2012, 1, 1),
</ins><span class="cx"> (),
</span><del>- PyCalendarDateTime(2012, 1, 1),
</del><ins>+ DateTime(2012, 1, 1),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Complex recurring - no limit",
</span><span class="lines">@@ -3048,20 +3124,20 @@
</span><span class="cx"> None,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2008, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2008, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2010, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2010, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> None,
</span><span class="lines">@@ -3094,23 +3170,23 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2007, 1, 1),
</del><ins>+ DateTime(2007, 1, 1),
</ins><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2008, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2008, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 11, 14, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2010, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2010, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> None,
</span><span class="lines">@@ -3143,14 +3219,14 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2010, 1, 1),
</del><ins>+ DateTime(2010, 1, 1),
</ins><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2010, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2010, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2010, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2010, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><del>- PyCalendarDateTime(2010, 1, 1),
</del><ins>+ DateTime(2010, 1, 1),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> "Complex recurring - limit effective full",
</span><span class="lines">@@ -3180,15 +3256,15 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2012, 1, 1),
</del><ins>+ DateTime(2012, 1, 1),
</ins><span class="cx"> (),
</span><del>- PyCalendarDateTime(2012, 1, 1),
</del><ins>+ DateTime(2012, 1, 1),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> for description, original, lowerLimit, results, limited in data:
</span><span class="cx"> component = Component.fromString(original)
</span><del>- instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), lowerLimit=lowerLimit)
</del><ins>+ instances = component.expandTimeRanges(DateTime(2100, 1, 1), lowerLimit=lowerLimit)
</ins><span class="cx"> self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
</span><span class="cx"> periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
</span><span class="cx"> self.assertEqual(periods, results)
</span><span class="lines">@@ -4040,7 +4116,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VEVENT
</span><span class="cx"> UID:12345-67890-1
</span><span class="cx"> RECURRENCE-ID:20090102T080000Z
</span><span class="lines">@@ -4065,7 +4141,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 2, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 2, 18, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VEVENT
</span><span class="cx"> UID:12345-67890-1
</span><span class="cx"> RECURRENCE-ID:20090102T180000Z
</span><span class="lines">@@ -4091,7 +4167,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 3, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 3, 18, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VEVENT
</span><span class="cx"> UID:12345-67890-1
</span><span class="cx"> RECURRENCE-ID:20090103T180000Z
</span><span class="lines">@@ -4115,7 +4191,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 2, 9, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 2, 9, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> None,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4133,7 +4209,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 2, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 2, 19, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> None,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4152,7 +4228,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 3, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 3, 19, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> None,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4169,7 +4245,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 8),
</del><ins>+ DateTime(2009, 1, 8),
</ins><span class="cx"> """BEGIN:VEVENT
</span><span class="cx"> UID:12345-67890-1
</span><span class="cx"> RECURRENCE-ID;VALUE=DATE:20090108
</span><span class="lines">@@ -4194,7 +4270,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 3),
</del><ins>+ DateTime(2009, 1, 3),
</ins><span class="cx"> """BEGIN:VEVENT
</span><span class="cx"> UID:12345-67890-1
</span><span class="cx"> RECURRENCE-ID;VALUE=DATE:20090103
</span><span class="lines">@@ -4220,7 +4296,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 10),
</del><ins>+ DateTime(2009, 1, 10),
</ins><span class="cx"> """BEGIN:VEVENT
</span><span class="cx"> UID:12345-67890-1
</span><span class="cx"> RECURRENCE-ID;VALUE=DATE:20090110
</span><span class="lines">@@ -4244,7 +4320,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 3),
</del><ins>+ DateTime(2009, 1, 3),
</ins><span class="cx"> None,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4262,7 +4338,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 5),
</del><ins>+ DateTime(2009, 1, 5),
</ins><span class="cx"> None,
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4281,7 +4357,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 19),
</del><ins>+ DateTime(2009, 1, 19),
</ins><span class="cx"> None,
</span><span class="cx"> ),
</span><span class="cx"> )
</span><span class="lines">@@ -4311,8 +4387,8 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 4, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 4, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> """BEGIN:VEVENT
</span><span class="lines">@@ -4349,8 +4425,8 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2009, 1, 2, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 4, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 2, 18, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 4, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> """BEGIN:VEVENT
</span><span class="lines">@@ -4388,8 +4464,8 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2009, 1, 3, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 5, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 3, 18, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 5, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> """BEGIN:VEVENT
</span><span class="lines">@@ -4425,8 +4501,8 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2009, 1, 2, 9, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 2, 9, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 3, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> None,
</span><span class="lines">@@ -4456,8 +4532,8 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2009, 1, 2, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 2, 19, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 3, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> None,
</span><span class="lines">@@ -4488,8 +4564,8 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2009, 1, 3, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 3, 19, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2009, 1, 3, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> None,
</span><span class="lines">@@ -4534,7 +4610,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VEVENT
</span><span class="cx"> UID:12345-67890-1
</span><span class="cx"> RECURRENCE-ID:20090102T080000Z
</span><span class="lines">@@ -4561,7 +4637,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VEVENT
</span><span class="cx"> UID:12345-67890-1
</span><span class="cx"> RECURRENCE-ID:20090102T080000Z
</span><span class="lines">@@ -4604,19 +4680,19 @@
</span><span class="cx"> self.assertFalse(hasattr(ical, "cachedInstances"))
</span><span class="cx">
</span><span class="cx"> # Derive one day apart - no re-cache
</span><del>- ical.deriveInstance(PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
</del><ins>+ ical.deriveInstance(DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)))
</ins><span class="cx"> self.assertTrue(hasattr(ical, "cachedInstances"))
</span><span class="cx"> oldLimit = ical.cachedInstances.limit
</span><del>- ical.deriveInstance(PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
</del><ins>+ ical.deriveInstance(DateTime(2009, 1, 3, 8, 0, 0, tzid=Timezone(utc=True)))
</ins><span class="cx"> self.assertEqual(ical.cachedInstances.limit, oldLimit)
</span><span class="cx">
</span><span class="cx"> # Derive several years ahead - re-cached
</span><del>- ical.deriveInstance(PyCalendarDateTime(2011, 1, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
</del><ins>+ ical.deriveInstance(DateTime(2011, 1, 1, 8, 0, 0, tzid=Timezone(utc=True)))
</ins><span class="cx"> self.assertNotEqual(ical.cachedInstances.limit, oldLimit)
</span><span class="cx"> oldLimit = ical.cachedInstances.limit
</span><span class="cx">
</span><span class="cx"> # Check one day ahead again - no re-cache
</span><del>- ical.deriveInstance(PyCalendarDateTime(2011, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)))
</del><ins>+ ical.deriveInstance(DateTime(2011, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)))
</ins><span class="cx"> self.assertEqual(ical.cachedInstances.limit, oldLimit)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -4661,13 +4737,13 @@
</span><span class="cx"> masterDerived = ical.masterDerived()
</span><span class="cx">
</span><span class="cx"> # Derive one day apart - no re-cache
</span><del>- result = ical.deriveInstance(PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)), newcomp=masterDerived)
</del><ins>+ result = ical.deriveInstance(DateTime(2009, 1, 2, 8, 0, 0, tzid=Timezone(utc=True)), newcomp=masterDerived)
</ins><span class="cx"> self.assertEqual(str(result), derived1)
</span><span class="cx">
</span><del>- result = ical.deriveInstance(PyCalendarDateTime(2009, 2, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)), newcomp=masterDerived)
</del><ins>+ result = ical.deriveInstance(DateTime(2009, 2, 3, 8, 0, 0, tzid=Timezone(utc=True)), newcomp=masterDerived)
</ins><span class="cx"> self.assertEqual(str(result), derived2)
</span><span class="cx">
</span><del>- result = ical.deriveInstance(PyCalendarDateTime(2009, 3, 3, 9, 0, 0, tzid=PyCalendarTimezone(utc=True)), newcomp=masterDerived)
</del><ins>+ result = ical.deriveInstance(DateTime(2009, 3, 3, 9, 0, 0, tzid=Timezone(utc=True)), newcomp=masterDerived)
</ins><span class="cx"> self.assertEqual(result, None)
</span><span class="cx">
</span><span class="cx"> self.assertEqual(str(ical), event)
</span><span class="lines">@@ -4847,8 +4923,8 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> (None, True),
</span><del>- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
</del><ins>+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), False),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4866,9 +4942,9 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> (None, True),
</span><del>- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 5, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
</del><ins>+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 5, 0, 0, 0, tzid=Timezone(utc=True)), False),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4886,10 +4962,10 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> (None, True),
</span><del>- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
</del><ins>+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 1, 0, 0, tzid=Timezone(utc=True)), False),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4908,11 +4984,11 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> (None, True),
</span><del>- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
</del><ins>+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 1, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 2, 0, 0, tzid=Timezone(utc=True)), False),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4932,12 +5008,12 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> (None, True),
</span><del>- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
- (PyCalendarDateTime(2009, 10, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
</del><ins>+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 1, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 2, 0, 0, tzid=Timezone(utc=True)), False),
+ (DateTime(2009, 10, 3, 0, 0, 0, tzid=Timezone(utc=True)), False),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4960,11 +5036,11 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> (None, True),
</span><del>- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
</del><ins>+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)), False),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 1, 0, 0, tzid=Timezone(utc=True)), False),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -4989,12 +5065,12 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> (None, True),
</span><del>- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2007, 11, 15, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
</del><ins>+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 1, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2007, 11, 15, 2, 0, 0, tzid=Timezone(utc=True)), False),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 1, 0, 0, tzid=Timezone(utc=True)), False),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -5012,9 +5088,9 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> (None, False),
</span><del>- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
- (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
</del><ins>+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), False),
+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), True),
+ (DateTime(2009, 10, 4, 0, 0, 0, tzid=Timezone(utc=True)), False),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -5046,7 +5122,7 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
</del><ins>+ (DateTime(2007, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)), True),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -5062,7 +5138,7 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
</del><ins>+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), False),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -5084,7 +5160,7 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
</del><ins>+ (DateTime(2007, 11, 15, 0, 0, 0, tzid=Timezone(utc=True)), False),
</ins><span class="cx"> )
</span><span class="cx"> ),
</span><span class="cx"> )
</span><span class="lines">@@ -5396,12 +5472,12 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 14, 20, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 14, 21, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 14, 20, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 14, 21, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2007, 11, 15, 21, 0, 0, tzid=PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 11, 15, 22, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 11, 15, 21, 0, 0, tzid=Timezone(utc=True)),
+ DateTime(2007, 11, 15, 22, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx"> ),
</span><span class="lines">@@ -5409,7 +5485,7 @@
</span><span class="cx">
</span><span class="cx"> for description, original, fixed, results in data:
</span><span class="cx"> component = Component.fromString(original)
</span><del>- instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=False)
</del><ins>+ instances = component.expandTimeRanges(DateTime(2100, 1, 1), ignoreInvalidInstances=False)
</ins><span class="cx"> self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
</span><span class="cx"> periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
</span><span class="cx"> self.assertEqual(periods, results)
</span><span class="lines">@@ -5696,7 +5772,7 @@
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", False,),
</span><span class="cx"> ),
</span><span class="lines">@@ -5741,7 +5817,7 @@
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", False,),
</span><span class="cx"> ("user01", False,),
</span><span class="lines">@@ -5795,7 +5871,7 @@
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", False,),
</span><span class="cx"> ("user01", False,),
</span><span class="lines">@@ -5841,19 +5917,19 @@
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", False,),
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 3, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 3, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", True,),
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 4, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 4, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", True,),
</span><span class="cx"> ),
</span><span class="lines">@@ -5916,21 +5992,21 @@
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", False,),
</span><span class="cx"> ("user01", True,),
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 3, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 3, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", False,),
</span><span class="cx"> ("user01", True,),
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 4, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 4, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", False,),
</span><span class="cx"> ("user01", False,),
</span><span class="lines">@@ -6015,7 +6091,7 @@
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 2, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", False,),
</span><span class="cx"> ("user01", True,),
</span><span class="lines">@@ -6023,7 +6099,7 @@
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 3, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 3, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", False,),
</span><span class="cx"> ("user01", True,),
</span><span class="lines">@@ -6031,7 +6107,7 @@
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><del>- PyCalendarDateTime(2008, 6, 4, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2008, 6, 4, 12, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> (
</span><span class="cx"> ("", False,),
</span><span class="cx"> ("user01", False,),
</span><span class="lines">@@ -7415,7 +7491,7 @@
</span><span class="cx"> """,
</span><span class="cx"> ),
</span><span class="cx"> )
</span><del>- cutoff = PyCalendarDateTime(2011, 11, 30, 0, 0, 0)
</del><ins>+ cutoff = DateTime(2011, 11, 30, 0, 0, 0)
</ins><span class="cx"> for _ignore_title, expected, body in data:
</span><span class="cx"> ical = Component.fromString(body)
</span><span class="cx"> self.assertEquals(expected, ical.hasInstancesAfter(cutoff))
</span><span class="lines">@@ -9437,7 +9513,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
</span><span class="lines">@@ -9505,7 +9581,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
</span><span class="lines">@@ -9577,7 +9653,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
</span><span class="lines">@@ -9626,7 +9702,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
</span><span class="lines">@@ -9676,7 +9752,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 31, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 31, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
</span><span class="lines">@@ -9725,7 +9801,7 @@
</span><span class="cx"> END:VEVENT
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 1, 31, 6, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 1, 31, 6, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
</span><span class="lines">@@ -9795,7 +9871,7 @@
</span><span class="cx"> END:X-CALENDARSERVER-PERUSER
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
</span><span class="lines">@@ -9944,7 +10020,7 @@
</span><span class="cx"> END:X-CALENDARSERVER-PERUSER
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><del>- PyCalendarDateTime(2009, 2, 1, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2009, 2, 1, 8, 0, 0, tzid=Timezone(utc=True)),
</ins><span class="cx"> """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_localizationpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_localization.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_localization.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_localization.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -20,7 +20,8 @@
</span><span class="cx"> from twistedcaldav.ical import Component
</span><span class="cx"> from twistedcaldav.test.util import TestCase
</span><span class="cx"> from twistedcaldav.config import ConfigDict
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from twistedcaldav.timezones import TimezoneCache
+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> import os
</span><span class="cx">
</span><span class="lines">@@ -52,6 +53,11 @@
</span><span class="cx">
</span><span class="cx"> class LocalizationTests(TestCase):
</span><span class="cx">
</span><ins>+ def setUp(self):
+ super(LocalizationTests, self).setUp()
+ TimezoneCache.create()
+
+
</ins><span class="cx"> def test_BasicStringLocalization(self):
</span><span class="cx">
</span><span class="cx"> with translationTo('pig', localeDir=localeDir):
</span><span class="lines">@@ -68,22 +74,22 @@
</span><span class="cx">
</span><span class="cx"> with translationTo('en', localeDir=localeDir) as t:
</span><span class="cx">
</span><del>- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 0, 0, 0)), "12:00 AM")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 12, 0, 0)), "12:00 PM")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 23, 59, 0)), "11:59 PM")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 6, 5, 0)), "6:05 AM")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 16, 5, 0)), "4:05 PM")
</del><ins>+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 0, 0, 0)), "12:00 AM")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 12, 0, 0)), "12:00 PM")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 23, 59, 0)), "11:59 PM")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 6, 5, 0)), "6:05 AM")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 16, 5, 0)), "4:05 PM")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_TimeFormatting24Hour(self):
</span><span class="cx">
</span><span class="cx"> with translationTo('pig', localeDir=localeDir) as t:
</span><span class="cx">
</span><del>- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 0, 0, 0)), "00:00")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 12, 0, 0)), "12:00")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 23, 59, 0)), "23:59")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 6, 5, 0)), "06:05")
- self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 16, 5, 0)), "16:05")
</del><ins>+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 0, 0, 0)), "00:00")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 12, 0, 0)), "12:00")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 23, 59, 0)), "23:59")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 6, 5, 0)), "06:05")
+ self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 16, 5, 0)), "16:05")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_CalendarFormatting(self):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_multigetpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_multiget.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_multiget.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_multiget.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,6 +16,7 @@
</span><span class="cx"> from twext.python.filepath import CachingFilePath as FilePath
</span><span class="cx"> from twext.web2 import responsecode
</span><span class="cx"> from twext.web2.dav.util import davXMLFromStream, joinURL
</span><ins>+from twext.web2.http_headers import Headers, MimeType
</ins><span class="cx"> from twext.web2.iweb import IResponse
</span><span class="cx"> from twext.web2.stream import MemoryStream
</span><span class="cx">
</span><span class="lines">@@ -268,7 +269,13 @@
</span><span class="cx">
</span><span class="cx"> if data:
</span><span class="cx"> for filename, icaldata in data.iteritems():
</span><del>- request = SimpleStoreRequest(self, "PUT", joinURL(calendar_uri, filename + ".ics"), authid="wsanchez")
</del><ins>+ request = SimpleStoreRequest(
+ self,
+ "PUT",
+ joinURL(calendar_uri, filename + ".ics"),
+ headers=Headers({"content-type": MimeType.fromString("text/calendar")}),
+ authid="wsanchez"
+ )
</ins><span class="cx"> request.stream = MemoryStream(icaldata)
</span><span class="cx"> yield self.send(request)
</span><span class="cx"> else:
</span><span class="lines">@@ -276,7 +283,13 @@
</span><span class="cx"> for child in FilePath(self.holidays_dir).children():
</span><span class="cx"> if os.path.splitext(child.basename())[1] != ".ics":
</span><span class="cx"> continue
</span><del>- request = SimpleStoreRequest(self, "PUT", joinURL(calendar_uri, child.basename()), authid="wsanchez")
</del><ins>+ request = SimpleStoreRequest(
+ self,
+ "PUT",
+ joinURL(calendar_uri, child.basename()),
+ headers=Headers({"content-type": MimeType.fromString("text/calendar")}),
+ authid="wsanchez"
+ )
</ins><span class="cx"> request.stream = MemoryStream(child.getContent())
</span><span class="cx"> yield self.send(request)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_propspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_props.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_props.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_props.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -88,7 +88,7 @@
</span><span class="cx"> self.fail("Expected CalDAV:supported-calendar-data element; but got none.")
</span><span class="cx">
</span><span class="cx"> for calendar in supported_calendar.children:
</span><del>- if calendar.content_type != "text/calendar":
</del><ins>+ if calendar.content_type not in ("text/calendar", "application/calendar+json"):
</ins><span class="cx"> self.fail("Expected a text/calendar calendar-data type restriction")
</span><span class="cx"> if calendar.version != "2.0":
</span><span class="cx"> self.fail("Expected a version 2.0 calendar-data restriction")
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_timezonespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_timezones.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_timezones.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_timezones.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -19,8 +19,8 @@
</span><span class="cx"> from twistedcaldav.ical import Component
</span><span class="cx"> from twistedcaldav.timezones import TimezoneCache, TimezoneException
</span><span class="cx"> from twistedcaldav.timezones import readTZ, listTZs
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> import os
</span><span class="cx"> import threading
</span><span class="lines">@@ -49,7 +49,7 @@
</span><span class="cx"> if calendar.name() != "VCALENDAR":
</span><span class="cx"> self.fail("Calendar is not a VCALENDAR")
</span><span class="cx">
</span><del>- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
</del><ins>+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
</ins><span class="cx"> for key in instances:
</span><span class="cx"> instance = instances[key]
</span><span class="cx"> start = instance.start
</span><span class="lines">@@ -69,8 +69,8 @@
</span><span class="cx">
</span><span class="cx"> self.doTest(
</span><span class="cx"> "TruncatedApr01.ics",
</span><del>- PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True))
</del><ins>+ DateTime(2007, 04, 01, 16, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 04, 01, 17, 0, 0, Timezone(utc=True))
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -84,8 +84,8 @@
</span><span class="cx">
</span><span class="cx"> self.doTest(
</span><span class="cx"> "TruncatedDec10.ics",
</span><del>- PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True))
</del><ins>+ DateTime(2007, 12, 10, 17, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 12, 10, 18, 0, 0, Timezone(utc=True))
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -99,13 +99,13 @@
</span><span class="cx">
</span><span class="cx"> self.doTest(
</span><span class="cx"> "TruncatedApr01.ics",
</span><del>- PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 04, 01, 16, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 04, 01, 17, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> )
</span><span class="cx"> self.doTest(
</span><span class="cx"> "TruncatedDec10.ics",
</span><del>- PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 12, 10, 17, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 12, 10, 18, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> testEqual=False
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -119,13 +119,13 @@
</span><span class="cx">
</span><span class="cx"> self.doTest(
</span><span class="cx"> "TruncatedApr01.ics",
</span><del>- PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 04, 01, 16, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 04, 01, 17, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> )
</span><span class="cx"> self.doTest(
</span><span class="cx"> "TruncatedDec10.ics",
</span><del>- PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True)),
</del><ins>+ DateTime(2007, 12, 10, 17, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 12, 10, 18, 0, 0, Timezone(utc=True)),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -139,13 +139,13 @@
</span><span class="cx">
</span><span class="cx"> self.doTest(
</span><span class="cx"> "TruncatedDec10.ics",
</span><del>- PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True))
</del><ins>+ DateTime(2007, 12, 10, 17, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 12, 10, 18, 0, 0, Timezone(utc=True))
</ins><span class="cx"> )
</span><span class="cx"> self.doTest(
</span><span class="cx"> "TruncatedApr01.ics",
</span><del>- PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
- PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True))
</del><ins>+ DateTime(2007, 04, 01, 16, 0, 0, Timezone(utc=True)),
+ DateTime(2007, 04, 01, 17, 0, 0, Timezone(utc=True))
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -201,13 +201,13 @@
</span><span class="cx"> calendar = Component.fromString(data)
</span><span class="cx"> if calendar.name() != "VCALENDAR":
</span><span class="cx"> self.fail("Calendar is not a VCALENDAR")
</span><del>- instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
</del><ins>+ instances = calendar.expandTimeRanges(DateTime(2100, 1, 1))
</ins><span class="cx"> for key in instances:
</span><span class="cx"> instance = instances[key]
</span><span class="cx"> start = instance.start
</span><span class="cx"> end = instance.end
</span><del>- self.assertEqual(start, PyCalendarDateTime(2007, 12, 25, 05, 0, 0, PyCalendarTimezone(utc=True)))
- self.assertEqual(end, PyCalendarDateTime(2007, 12, 25, 06, 0, 0, PyCalendarTimezone(utc=True)))
</del><ins>+ self.assertEqual(start, DateTime(2007, 12, 25, 05, 0, 0, Timezone(utc=True)))
+ self.assertEqual(end, DateTime(2007, 12, 25, 06, 0, 0, Timezone(utc=True)))
</ins><span class="cx"> break
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_upgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_upgrade.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_upgrade.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_upgrade.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1675,8 +1675,8 @@
</span><span class="cx"> ATTENDEE;CN=Double 'quotey' Quotes;CUTYPE=INDIVIDUAL;EMAIL=doublequotes@ex
</span><span class="cx"> ample.com;PARTSTAT=ACCEPTED:urn:uuid:8E04787E-336D-41ED-A70B-D233AD0DCE6F
</span><span class="cx"> ATTENDEE;CN=Cyrus Daboo;CUTYPE=INDIVIDUAL;EMAIL=cdaboo@example.com;PARTSTA
</span><del>- T=ACCEPTED;ROLE=REQ-PARTICIPANT:urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89
- 500
</del><ins>+ T=ACCEPTED;ROLE=REQ-PARTICIPANT:urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A8
+ 9500
</ins><span class="cx"> CREATED:20090203T181910Z
</span><span class="cx"> DESCRIPTION:This has " Bad Quotes " in it
</span><span class="cx"> DTSTAMP:20090203T181924Z
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtesttest_utilpyfromrev12016CalendarServertrunktwistedcaldavtesttest_utilpy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_util.py (from rev 12016, CalendarServer/trunk/twistedcaldav/test/test_util.py) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_util.py         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/test/test_util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,144 @@
</span><ins>+##
+# Copyright (c) 2005-2013 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 twext.web2.http_headers import Headers
+
+import twistedcaldav.test.util
+from twistedcaldav.util import bestAcceptType
+
+class AcceptType(twistedcaldav.test.util.TestCase):
+ """
+ L{bestAcceptType} tests
+ """
+ def test_bestAcceptType(self):
+
+ data = (
+ (
+ "#1.1",
+ ("Accept", "text/plain"),
+ ["text/plain"],
+ "text/plain",
+ ),
+ (
+ "#1.2",
+ ("Accept", "text/plain"),
+ ["text/calendar"],
+ None,
+ ),
+ (
+ "#1.3",
+ ("Accept", "text/*"),
+ ["text/plain"],
+ "text/plain",
+ ),
+ (
+ "#1.4",
+ ("Accept", "*/*"),
+ ["text/plain"],
+ "text/plain",
+ ),
+ (
+ "#2.1",
+ ("Accept", "text/plain"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#2.2",
+ ("Accept", "text/plain"),
+ ["text/calendar", "application/text", ],
+ None,
+ ),
+ (
+ "#2.3",
+ ("Accept", "text/*"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#2.4",
+ ("Accept", "*/*"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#2.5",
+ ("Accept", "application/text"),
+ ["text/plain", "application/text", ],
+ "application/text",
+ ),
+ (
+ "#2.6",
+ ("Accept", "application/*"),
+ ["text/plain", "application/text", ],
+ "application/text",
+ ),
+ (
+ "#3.1",
+ ("Accept", "text/plain;q=0.5, application/text;q=0.3"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#3.2",
+ ("Accept", "text/plain;q=0.5, application/text;q=0.3"),
+ ["text/calendar", "application/calendar", ],
+ None,
+ ),
+ (
+ "#3.3",
+ ("Accept", "text/plain;q=0.5, application/text;q=0.3"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#3.4",
+ ("Accept", "text/plain;q=0.5, application/text;q=0.3"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#3.5",
+ ("Accept", "text/plain;q=0.3, application/text;q=0.5"),
+ ["text/plain", "application/text", ],
+ "application/text",
+ ),
+ (
+ "#3.6",
+ ("Accept", "text/plain;q=0.5, application/*;q=0.3"),
+ ["text/plain", "application/text", ],
+ "text/plain",
+ ),
+ (
+ "#4.1",
+ ("Accept", "text/plain;q=0.5, application/text;q=0.2, text/*;q=0.3"),
+ ["text/calendar", "application/text", ],
+ "text/calendar",
+ ),
+ (
+ "#5.1",
+ None,
+ ["text/calendar", "application/text", ],
+ "text/calendar",
+ ),
+ )
+
+ for title, hdr, allowedTypes, result in data:
+ hdrs = Headers()
+ if hdr:
+ hdrs.addRawHeader(*hdr)
+ check = bestAcceptType(hdrs.getHeader("accept"), allowedTypes)
+ self.assertEqual(check, result, msg="Failed %s" % (title,))
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtimezonespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezones.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezones.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezones.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -22,7 +22,7 @@
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx">
</span><del>-from pycalendar.timezonedb import PyCalendarTimezoneDatabase
</del><ins>+from pycalendar.timezonedb import TimezoneDatabase
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -95,7 +95,7 @@
</span><span class="cx"> TimezoneCache.dirName = None
</span><span class="cx"> TimezoneCache.validatePath()
</span><span class="cx"> TimezoneCache.version = TimezoneCache.getTZVersion(TimezoneCache.getDBPath())
</span><del>- PyCalendarTimezoneDatabase.createTimezoneDatabase(TimezoneCache.getDBPath())
</del><ins>+ TimezoneDatabase.createTimezoneDatabase(TimezoneCache.getDBPath())
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @staticmethod
</span><span class="lines">@@ -171,7 +171,7 @@
</span><span class="cx">
</span><span class="cx"> @staticmethod
</span><span class="cx"> def clear():
</span><del>- PyCalendarTimezoneDatabase.clearTimezoneDatabase()
</del><ins>+ TimezoneDatabase.clearTimezoneDatabase()
</ins><span class="cx">
</span><span class="cx"> # zoneinfo never changes in a running instance so cache all this data as we use it
</span><span class="cx"> cachedTZs = {}
</span><span class="lines">@@ -207,7 +207,7 @@
</span><span class="cx">
</span><span class="cx"> if tzid not in cachedVTZs:
</span><span class="cx">
</span><del>- tzcal = PyCalendarTimezoneDatabase.getTimezoneInCalendar(tzid)
</del><ins>+ tzcal = TimezoneDatabase.getTimezoneInCalendar(tzid)
</ins><span class="cx"> if tzcal:
</span><span class="cx"> cachedVTZs[tzid] = tzcal
</span><span class="cx"> else:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtimezoneservicepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezoneservice.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezoneservice.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezoneservice.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -46,7 +46,7 @@
</span><span class="cx"> from twistedcaldav.timezones import listTZs
</span><span class="cx"> from twistedcaldav.timezones import readTZ
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> class TimezoneServiceResource (ReadOnlyNoCopyResourceMixIn, DAVResourceWithoutChildrenMixin, DAVResource):
</span><span class="cx"> """
</span><span class="lines">@@ -66,20 +66,25 @@
</span><span class="cx"> self.parent = parent
</span><span class="cx"> self.cache = {}
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def deadProperties(self):
</span><span class="cx"> if not hasattr(self, "_dead_properties"):
</span><span class="cx"> self._dead_properties = NonePropertyStore(self)
</span><span class="cx"> return self._dead_properties
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def etag(self):
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def checkPreconditions(self, request):
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def checkPrivileges(self, request, privileges, recurse=False, principal=None, inherited_aces=None):
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def defaultAccessControlList(self):
</span><span class="cx"> return davxml.ACL(
</span><span class="cx"> # DAV:Read for all principals (includes anonymous)
</span><span class="lines">@@ -92,21 +97,27 @@
</span><span class="cx"> ),
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def contentType(self):
</span><span class="cx"> return MimeType.fromString("text/xml")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def resourceType(self):
</span><span class="cx"> return davxml.ResourceType.timezones
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def isCollection(self):
</span><span class="cx"> return False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def isCalendarCollection(self):
</span><span class="cx"> return False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def isPseudoCalendarCollection(self):
</span><span class="cx"> return False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def render(self, request):
</span><span class="cx"> output = """<html>
</span><span class="cx"> <head>
</span><span class="lines">@@ -127,10 +138,11 @@
</span><span class="cx"> """
</span><span class="cx"> The timezone service POST method.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> # GET and POST do the same thing
</span><span class="cx"> return self.http_POST(request)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def http_POST(self, request):
</span><span class="cx"> """
</span><span class="cx"> The timezone service POST method.
</span><span class="lines">@@ -138,11 +150,11 @@
</span><span class="cx">
</span><span class="cx"> # Check authentication and access controls
</span><span class="cx"> def _gotResult(_):
</span><del>-
</del><ins>+
</ins><span class="cx"> if not request.args:
</span><span class="cx"> # Do normal GET behavior
</span><span class="cx"> return self.render(request)
</span><del>-
</del><ins>+
</ins><span class="cx"> method = request.args.get("method", ("",))
</span><span class="cx"> if len(method) != 1:
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="lines">@@ -151,41 +163,43 @@
</span><span class="cx"> "Invalid method query parameter",
</span><span class="cx"> ))
</span><span class="cx"> method = method[0]
</span><del>-
</del><ins>+
</ins><span class="cx"> action = {
</span><span class="cx"> "list" : self.doPOSTList,
</span><span class="cx"> "get" : self.doPOSTGet,
</span><span class="cx"> "expand" : self.doPOSTExpand,
</span><span class="cx"> }.get(method, None)
</span><del>-
</del><ins>+
</ins><span class="cx"> if action is None:
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.BAD_REQUEST,
</span><span class="cx"> (calendarserver_namespace, "supported-method"),
</span><span class="cx"> "Unknown method query parameter",
</span><span class="cx"> ))
</span><del>-
</del><ins>+
</ins><span class="cx"> return action(request)
</span><del>-
</del><ins>+
</ins><span class="cx"> d = self.authorize(request, (davxml.Read(),))
</span><span class="cx"> d.addCallback(_gotResult)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def doPOSTList(self, request):
</span><span class="cx"> """
</span><span class="cx"> Return a list of all timezones known to the server.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> tzids = listTZs()
</span><span class="cx"> tzids.sort()
</span><span class="cx"> result = customxml.TZIDs(*[customxml.TZID(tzid) for tzid in tzids])
</span><span class="cx"> return XMLResponse(responsecode.OK, result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def doPOSTGet(self, request):
</span><span class="cx"> """
</span><span class="cx"> Return the specified timezone data.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> tzid = request.args.get("tzid", ())
</span><span class="cx"> if len(tzid) != 1:
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="lines">@@ -209,6 +223,7 @@
</span><span class="cx"> response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8"))
</span><span class="cx"> return response
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def doPOSTExpand(self, request):
</span><span class="cx"> """
</span><span class="cx"> Expand a timezone within specified start/end dates.
</span><span class="lines">@@ -235,7 +250,7 @@
</span><span class="cx"> start = request.args.get("start", ())
</span><span class="cx"> if len(start) != 1:
</span><span class="cx"> raise ValueError()
</span><del>- start = PyCalendarDateTime.parseText(start[0])
</del><ins>+ start = DateTime.parseText(start[0])
</ins><span class="cx"> except ValueError:
</span><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.BAD_REQUEST,
</span><span class="lines">@@ -247,7 +262,7 @@
</span><span class="cx"> end = request.args.get("end", ())
</span><span class="cx"> if len(end) != 1:
</span><span class="cx"> raise ValueError()
</span><del>- end = PyCalendarDateTime.parseText(end[0])
</del><ins>+ end = DateTime.parseText(end[0])
</ins><span class="cx"> if end <= start:
</span><span class="cx"> raise ValueError()
</span><span class="cx"> except ValueError:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavtimezonestdservicepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezonestdservice.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezonestdservice.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/timezonestdservice.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -49,9 +49,9 @@
</span><span class="cx"> addVTZ
</span><span class="cx"> from twistedcaldav.xmlutil import addSubElement
</span><span class="cx">
</span><del>-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.exceptions import PyCalendarInvalidData
</del><ins>+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.datetime import DateTime
+from pycalendar.exceptions import InvalidData
</ins><span class="cx">
</span><span class="cx"> import hashlib
</span><span class="cx"> import itertools
</span><span class="lines">@@ -89,7 +89,13 @@
</span><span class="cx"> else:
</span><span class="cx"> raise ValueError("Invalid TimezoneService mode: %s" % (config.TimezoneService.Mode,))
</span><span class="cx">
</span><ins>+ self.formats = []
+ self.formats.append("text/calendar")
+ self.formats.append("text/plain")
+ if config.EnableJSONData:
+ self.formats.append("application/calendar+json")
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def _initPrimaryService(self):
</span><span class="cx"> tzpath = TimezoneCache.getDBPath()
</span><span class="cx"> xmlfile = os.path.join(tzpath, "timezones.xml")
</span><span class="lines">@@ -268,7 +274,7 @@
</span><span class="cx"> {
</span><span class="cx"> "name": "get",
</span><span class="cx"> "parameters": [
</span><del>- {"name": "format", "required": False, "multi": False, "values": ["text/calendar", "text/plain", ], },
</del><ins>+ {"name": "format", "required": False, "multi": False, "values": self.formats, },
</ins><span class="cx"> {"name": "tzid", "required": True, "multi": False, },
</span><span class="cx"> ],
</span><span class="cx"> },
</span><span class="lines">@@ -303,7 +309,7 @@
</span><span class="cx"> # Validate a date-time stamp
</span><span class="cx"> changedsince = changedsince[0]
</span><span class="cx"> try:
</span><del>- dt = PyCalendarDateTime.parseText(changedsince)
</del><ins>+ dt = DateTime.parseText(changedsince)
</ins><span class="cx"> except ValueError:
</span><span class="cx"> raise HTTPError(JSONResponse(
</span><span class="cx"> responsecode.BAD_REQUEST,
</span><span class="lines">@@ -348,7 +354,7 @@
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="cx"> format = request.args.get("format", ("text/calendar",))
</span><del>- if len(format) != 1 or format[0] not in ("text/calendar", "text/plain",):
</del><ins>+ if len(format) != 1 or format[0] not in self.formats:
</ins><span class="cx"> raise HTTPError(JSONResponse(
</span><span class="cx"> responsecode.BAD_REQUEST,
</span><span class="cx"> {
</span><span class="lines">@@ -368,7 +374,7 @@
</span><span class="cx"> }
</span><span class="cx"> ))
</span><span class="cx">
</span><del>- tzdata = calendar.getText()
</del><ins>+ tzdata = calendar.getText(format=format if format != "text/plain" else None)
</ins><span class="cx">
</span><span class="cx"> response = Response()
</span><span class="cx"> response.stream = MemoryStream(tzdata)
</span><span class="lines">@@ -396,9 +402,9 @@
</span><span class="cx"> if len(start) > 1:
</span><span class="cx"> raise ValueError()
</span><span class="cx"> elif len(start) == 1:
</span><del>- start = PyCalendarDateTime.parseText(start[0])
</del><ins>+ start = DateTime.parseText(start[0])
</ins><span class="cx"> else:
</span><del>- start = PyCalendarDateTime.getToday()
</del><ins>+ start = DateTime.getToday()
</ins><span class="cx"> start.setDay(1)
</span><span class="cx"> start.setMonth(1)
</span><span class="cx"> except ValueError:
</span><span class="lines">@@ -415,9 +421,9 @@
</span><span class="cx"> if len(end) > 1:
</span><span class="cx"> raise ValueError()
</span><span class="cx"> elif len(end) == 1:
</span><del>- end = PyCalendarDateTime.parseText(end[0])
</del><ins>+ end = DateTime.parseText(end[0])
</ins><span class="cx"> else:
</span><del>- end = PyCalendarDateTime.getToday()
</del><ins>+ end = DateTime.getToday()
</ins><span class="cx"> end.setDay(1)
</span><span class="cx"> end.setMonth(1)
</span><span class="cx"> end.offsetYear(10)
</span><span class="lines">@@ -558,7 +564,7 @@
</span><span class="cx"> Generate a PyCalendar containing the requested timezone.
</span><span class="cx"> """
</span><span class="cx"> # We will just use our existing TimezoneCache here
</span><del>- calendar = PyCalendar()
</del><ins>+ calendar = Calendar()
</ins><span class="cx"> try:
</span><span class="cx"> vtz = readVTZ(tzid)
</span><span class="cx"> calendar.addComponent(vtz.getComponents()[0].duplicate())
</span><span class="lines">@@ -618,7 +624,7 @@
</span><span class="cx"> Create a new DB xml file from scratch by scanning zoneinfo.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- self.dtstamp = PyCalendarDateTime.getNowUTC().getXMLText()
</del><ins>+ self.dtstamp = DateTime.getNowUTC().getXMLText()
</ins><span class="cx"> self._scanTZs("")
</span><span class="cx"> self._dumpTZs()
</span><span class="cx">
</span><span class="lines">@@ -672,7 +678,7 @@
</span><span class="cx"> """
</span><span class="cx"> Update existing DB info by comparing md5's.
</span><span class="cx"> """
</span><del>- self.dtstamp = PyCalendarDateTime.getNowUTC().getXMLText()
</del><ins>+ self.dtstamp = DateTime.getNowUTC().getXMLText()
</ins><span class="cx"> self.changeCount = 0
</span><span class="cx"> self.changed = set()
</span><span class="cx"> self._scanTZs("", checkIfChanged=True)
</span><span class="lines">@@ -847,8 +853,8 @@
</span><span class="cx">
</span><span class="cx"> ical = response.data
</span><span class="cx"> try:
</span><del>- calendar = PyCalendar.parseText(ical)
- except PyCalendarInvalidData:
</del><ins>+ calendar = Calendar.parseText(ical)
+ except InvalidData:
</ins><span class="cx"> log.error("Invalid calendar data for tzid: %s" % (tzinfo.tzid,))
</span><span class="cx"> returnValue(None)
</span><span class="cx"> ical = calendar.getText()
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/upgrade.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/upgrade.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/upgrade.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1065,7 +1065,6 @@
</span><span class="cx"> # Migrate mail tokens from sqlite to store
</span><span class="cx"> yield migrateTokensToStore(self.config.DataRoot, self.store)
</span><span class="cx">
</span><del>-
</del><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def processInboxItems(self):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/util.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/util.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -517,3 +517,52 @@
</span><span class="cx"> cuas = principal.record.calendarUserAddresses
</span><span class="cx">
</span><span class="cx"> return (fullName, rec.guid, cuas)
</span><ins>+
+
+
+def bestAcceptType(accepts, allowedTypes):
+ """
+ Given a set of Accept headers and the set of types the server can return, determine the best choice
+ of format to return to the client.
+
+ @param accepts: parsed accept headers
+ @type accepts: C{dict}
+ @param allowedTypes: list of allowed types in server preferred order
+ @type allowedTypes: C{list}
+ """
+
+ # If no Accept present just use the first allowed type - the server's preference
+ if not accepts:
+ return allowedTypes[0]
+
+ # Get mapping for ordered top-level types for use in subtype wildcard match
+ toptypes = {}
+ for allowed in allowedTypes:
+ mediaType = allowed.split("/")[0]
+ if mediaType not in toptypes:
+ toptypes[mediaType] = allowed
+
+ result = None
+ result_qval = 0.0
+ for content_type, qval in accepts.items():
+ # Exact match
+ ctype = "%s/%s" % (content_type.mediaType, content_type.mediaSubtype,)
+ if ctype in allowedTypes:
+ if qval > result_qval:
+ result = ctype
+ result_qval = qval
+
+ # Subtype wildcard match
+ elif content_type.mediaType != "*" and content_type.mediaSubtype == "*":
+ if content_type.mediaType in toptypes:
+ if qval > result_qval:
+ result = toptypes[content_type.mediaType]
+ result_qval = qval
+
+ # Full wildcard match
+ elif content_type.mediaType == "*" and content_type.mediaSubtype == "*":
+ if qval > result_qval:
+ result = allowedTypes[0]
+ result_qval = qval
+
+ return result
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavvcardpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/vcard.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/vcard.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/vcard.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -31,9 +31,11 @@
</span><span class="cx"> from twext.web2.stream import IStream
</span><span class="cx"> from twext.web2.dav.util import allDataFromStream
</span><span class="cx">
</span><del>-from pycalendar.attribute import PyCalendarAttribute
-from pycalendar.componentbase import PyCalendarComponentBase
-from pycalendar.exceptions import PyCalendarError
</del><ins>+from twistedcaldav.config import config
+
+from pycalendar.parameter import Parameter
+from pycalendar.componentbase import ComponentBase
+from pycalendar.exceptions import ErrorBase
</ins><span class="cx"> from pycalendar.vcard.card import Card
</span><span class="cx"> from pycalendar.vcard.property import Property as pyProperty
</span><span class="cx">
</span><span class="lines">@@ -44,6 +46,8 @@
</span><span class="cx"> class InvalidVCardDataError(ValueError):
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class Property (object):
</span><span class="cx"> """
</span><span class="cx"> vCard Property
</span><span class="lines">@@ -73,31 +77,55 @@
</span><span class="cx"> for attrname, attrvalue in params.items():
</span><span class="cx"> if isinstance(attrvalue, unicode):
</span><span class="cx"> attrvalue = attrvalue.encode("utf-8")
</span><del>- self._pycard.addAttribute(PyCalendarAttribute(attrname, attrvalue))
</del><ins>+ self._pycard.addParameter(Parameter(attrname, attrvalue))
</ins><span class="cx">
</span><del>- def __str__ (self): return str(self._pycard)
- def __repr__(self): return "<%s: %r: %r>" % (self.__class__.__name__, self.name(), self.value())
</del><span class="cx">
</span><del>- def __hash__(self): return hash(str(self))
</del><ins>+ def __str__(self):
+ return str(self._pycard)
</ins><span class="cx">
</span><del>- def __ne__(self, other): return not self.__eq__(other)
</del><ins>+
+ def __repr__(self):
+ return "<%s: %r: %r>" % (self.__class__.__name__, self.name(), self.value())
+
+
+ def __hash__(self):
+ return hash(str(self))
+
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
</ins><span class="cx"> def __eq__(self, other):
</span><del>- if not isinstance(other, Property): return False
</del><ins>+ if not isinstance(other, Property):
+ return False
</ins><span class="cx"> return self._pycard == other._pycard
</span><span class="cx">
</span><del>- def __gt__(self, other): return not (self.__eq__(other) or self.__lt__(other))
</del><ins>+
+ def __gt__(self, other):
+ return not (self.__eq__(other) or self.__lt__(other))
+
+
</ins><span class="cx"> def __lt__(self, other):
</span><span class="cx"> my_name = self.name()
</span><span class="cx"> other_name = other.name()
</span><span class="cx">
</span><del>- if my_name < other_name: return True
- if my_name > other_name: return False
</del><ins>+ if my_name < other_name:
+ return True
+ if my_name > other_name:
+ return False
</ins><span class="cx">
</span><span class="cx"> return self.value() < other.value()
</span><span class="cx">
</span><del>- def __ge__(self, other): return self.__eq__(other) or self.__gt__(other)
- def __le__(self, other): return self.__eq__(other) or self.__lt__(other)
</del><span class="cx">
</span><ins>+ def __ge__(self, other):
+ return self.__eq__(other) or self.__gt__(other)
+
+
+ def __le__(self, other):
+ return self.__eq__(other) or self.__lt__(other)
+
+
</ins><span class="cx"> def duplicate(self):
</span><span class="cx"> """
</span><span class="cx"> Duplicate this object and all its contents.
</span><span class="lines">@@ -105,35 +133,45 @@
</span><span class="cx"> """
</span><span class="cx"> return Property(None, None, params=None, pycard=self._pycard.duplicate())
</span><span class="cx">
</span><del>- def name (self): return self._pycard.getName()
</del><span class="cx">
</span><del>- def value (self): return self._pycard.getValue().getValue()
</del><ins>+ def name(self):
+ return self._pycard.getName()
</ins><span class="cx">
</span><del>- def strvalue (self): return str(self._pycard.getValue())
</del><span class="cx">
</span><ins>+ def value(self):
+ return self._pycard.getValue().getValue()
+
+
+ def strvalue(self):
+ return str(self._pycard.getValue())
+
+
</ins><span class="cx"> def setValue(self, value):
</span><span class="cx"> self._pycard.setValue(value)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def parameterNames(self):
</span><span class="cx"> """
</span><span class="cx"> Returns a set containing parameter names for this property.
</span><span class="cx"> """
</span><span class="cx"> result = set()
</span><del>- for pyattrlist in self._pycard.getAttributes().values():
</del><ins>+ for pyattrlist in self._pycard.getParameters().values():
</ins><span class="cx"> for pyattr in pyattrlist:
</span><span class="cx"> result.add(pyattr.getName())
</span><span class="cx"> return result
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def parameterValue(self, name, default=None):
</span><span class="cx"> """
</span><span class="cx"> Returns a single value for the given parameter. Raises
</span><span class="cx"> InvalidICalendarDataError if the parameter has more than one value.
</span><span class="cx"> """
</span><span class="cx"> try:
</span><del>- return self._pycard.getAttributeValue(name)
</del><ins>+ return self._pycard.getParameterValue(name)
</ins><span class="cx"> except KeyError:
</span><span class="cx"> return default
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def parameterValues(self, name):
</span><span class="cx"> """
</span><span class="cx"> Returns a single value for the given parameter. Raises
</span><span class="lines">@@ -141,7 +179,7 @@
</span><span class="cx"> """
</span><span class="cx"> results = []
</span><span class="cx"> try:
</span><del>- attrs = self._pycard.getAttributes()[name.upper()]
</del><ins>+ attrs = self._pycard.getParameters()[name.upper()]
</ins><span class="cx"> except KeyError:
</span><span class="cx"> return []
</span><span class="cx">
</span><span class="lines">@@ -149,28 +187,33 @@
</span><span class="cx"> results.extend(attr.getValues())
</span><span class="cx"> return results
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def hasParameter(self, paramname):
</span><del>- return self._pycard.hasAttribute(paramname)
</del><ins>+ return self._pycard.hasParameter(paramname)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def setParameter(self, paramname, paramvalue):
</span><del>- self._pycard.replaceAttribute(PyCalendarAttribute(paramname, paramvalue))
</del><ins>+ self._pycard.replaceParameter(Parameter(paramname, paramvalue))
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def removeParameter(self, paramname):
</span><del>- self._pycard.removeAttributes(paramname)
</del><ins>+ self._pycard.removeParameters(paramname)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def removeAllParameters(self):
</span><del>- self._pycard.setAttributes({})
</del><ins>+ self._pycard.setParameters({})
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def removeParameterValue(self, paramname, paramvalue):
</span><span class="cx">
</span><span class="cx"> paramname = paramname.upper()
</span><span class="cx"> for attrName in self.parameterNames():
</span><span class="cx"> if attrName.upper() == paramname:
</span><del>- for attr in tuple(self._pycard.getAttributes()[attrName]):
</del><ins>+ for attr in tuple(self._pycard.getParameters()[attrName]):
</ins><span class="cx"> for value in attr.getValues():
</span><span class="cx"> if value == paramvalue:
</span><span class="cx"> if not attr.removeValue(value):
</span><del>- self._pycard.removeAttributes(paramname)
</del><ins>+ self._pycard.removeParameters(paramname)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -178,8 +221,20 @@
</span><span class="cx"> """
</span><span class="cx"> X{vCard} component.
</span><span class="cx"> """
</span><ins>+ allowedTypesList = None
+
+
</ins><span class="cx"> @classmethod
</span><del>- def allFromString(clazz, string):
</del><ins>+ def allowedTypes(cls):
+ if cls.allowedTypesList is None:
+ cls.allowedTypesList = ["text/vcard"]
+ if config.EnableJSONData:
+ cls.allowedTypesList.append("application/vcard+json")
+ return cls.allowedTypesList
+
+
+ @classmethod
+ def allFromString(clazz, string, format=None):
</ins><span class="cx"> """
</span><span class="cx"> FIXME: Just default to reading a single VCARD - actually need more
</span><span class="cx"> """
</span><span class="lines">@@ -193,62 +248,85 @@
</span><span class="cx"> if string[:3] == codecs.BOM_UTF8:
</span><span class="cx"> string = string[3:]
</span><span class="cx">
</span><del>- return clazz.allFromStream(StringIO.StringIO(string))
</del><ins>+ return clazz.allFromStream(StringIO.StringIO(string), format)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> @classmethod
</span><del>- def allFromStream(clazz, stream):
</del><ins>+ def allFromStream(clazz, stream, format=None):
</ins><span class="cx"> """
</span><span class="cx"> FIXME: Just default to reading a single VCARD - actually need more
</span><span class="cx"> """
</span><span class="cx"> try:
</span><del>- results = Card.parseMultiple(stream)
- except PyCalendarError:
</del><ins>+ results = Card.parseMultipleData(stream, format)
+ except ErrorBase:
</ins><span class="cx"> results = None
</span><span class="cx"> if not results:
</span><span class="cx"> stream.seek(0)
</span><span class="cx"> raise InvalidVCardDataError("%s" % (stream.read(),))
</span><span class="cx"> return [clazz(None, pycard=result) for result in results]
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @classmethod
</span><del>- def fromString(clazz, string):
</del><ins>+ def fromString(clazz, string, format=None):
</ins><span class="cx"> """
</span><span class="cx"> Construct a L{Component} from a string.
</span><span class="cx"> @param string: a string containing vCard data.
</span><span class="cx"> @return: a L{Component} representing the first component described by
</span><span class="cx"> C{string}.
</span><span class="cx"> """
</span><del>- if type(string) is unicode:
- string = string.encode("utf-8")
- else:
- # Valid utf-8 please
- string.decode("utf-8")
</del><ins>+ return clazz._fromData(string, False, format)
</ins><span class="cx">
</span><del>- # No BOMs please
- if string[:3] == codecs.BOM_UTF8:
- string = string[3:]
</del><span class="cx">
</span><del>- return clazz.fromStream(StringIO.StringIO(string))
</del><ins>+ @classmethod
+ def fromStream(clazz, stream, format=None):
+ """
+ Construct a L{Component} from a stream.
+ @param stream: a C{read()}able stream containing vCard data.
+ @return: a L{Component} representing the first component described by
+ C{stream}.
+ """
+ return clazz._fromData(stream, True, format)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> @classmethod
</span><del>- def fromStream(clazz, stream):
</del><ins>+ def _fromData(clazz, data, isstream, format=None):
</ins><span class="cx"> """
</span><span class="cx"> Construct a L{Component} from a stream.
</span><span class="cx"> @param stream: a C{read()}able stream containing vCard data.
</span><ins>+ @param format: a C{str} indicating whether the data is vCard or jCard
</ins><span class="cx"> @return: a L{Component} representing the first component described by
</span><span class="cx"> C{stream}.
</span><span class="cx"> """
</span><del>- cal = Card()
</del><ins>+
+ if isstream:
+ pass
+ else:
+ if type(data) is unicode:
+ data = data.encode("utf-8")
+ else:
+ # Valid utf-8 please
+ data.decode("utf-8")
+
+ # No BOMs please
+ if data[:3] == codecs.BOM_UTF8:
+ data = data[3:]
+
+ errmsg = "Unknown"
</ins><span class="cx"> try:
</span><del>- result = cal.parse(stream)
- except PyCalendarError:
</del><ins>+ result = Card.parseData(data, format)
+ except ErrorBase, e:
+ errmsg = "%s: %s" % (e.mReason, e.mData,)
</ins><span class="cx"> result = None
</span><span class="cx"> if not result:
</span><del>- stream.seek(0)
- raise InvalidVCardDataError("%s" % (stream.read(),))
- return clazz(None, pycard=cal)
</del><ins>+ if isstream:
+ data.seek(0)
+ data = data.read()
+ raise InvalidVCardDataError("%s\n%s" % (errmsg, data,))
+ return clazz(None, pycard=result)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> @classmethod
</span><del>- def fromIStream(clazz, stream):
</del><ins>+ def fromIStream(clazz, stream, format=None):
</ins><span class="cx"> """
</span><span class="cx"> Construct a L{Component} from a stream.
</span><span class="cx"> @param stream: an L{IStream} containing vCard data.
</span><span class="lines">@@ -261,9 +339,11 @@
</span><span class="cx"> # A better solution would parse directly and incrementally from the
</span><span class="cx"> # request stream.
</span><span class="cx"> #
</span><del>- def parse(data): return clazz.fromString(data)
</del><ins>+ def parse(data):
+ return clazz.fromString(data, format)
</ins><span class="cx"> return allDataFromStream(IStream(stream), parse)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __init__(self, name, **kwargs):
</span><span class="cx"> """
</span><span class="cx"> Use this constructor to initialize an empty L{Component}.
</span><span class="lines">@@ -277,8 +357,8 @@
</span><span class="cx"> pyobj = kwargs["pycard"]
</span><span class="cx">
</span><span class="cx"> if pyobj is not None:
</span><del>- if not isinstance(pyobj, PyCalendarComponentBase):
- raise TypeError("Not a PyCalendarComponentBase: %r" % (pyobj,))
</del><ins>+ if not isinstance(pyobj, ComponentBase):
+ raise TypeError("Not a ComponentBase: %r" % (pyobj,))
</ins><span class="cx">
</span><span class="cx"> self._pycard = pyobj
</span><span class="cx"> else:
</span><span class="lines">@@ -302,28 +382,53 @@
</span><span class="cx"> else:
</span><span class="cx"> raise ValueError("VCards have no child components")
</span><span class="cx">
</span><del>- def __str__ (self): return str(self._pycard)
- def __repr__(self): return "<%s: %r>" % (self.__class__.__name__, str(self._pycard))
</del><span class="cx">
</span><ins>+ def __str__(self):
+ return str(self._pycard)
+
+
+ def __repr__(self):
+ return "<%s: %r>" % (self.__class__.__name__, str(self._pycard))
+
+
</ins><span class="cx"> def __hash__(self):
</span><span class="cx"> return hash(str(self))
</span><span class="cx">
</span><del>- def __ne__(self, other): return not self.__eq__(other)
</del><ins>+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
</ins><span class="cx"> def __eq__(self, other):
</span><span class="cx"> if not isinstance(other, Component):
</span><span class="cx"> return False
</span><span class="cx"> return self._pycard == other._pycard
</span><span class="cx">
</span><ins>+
+ def getText(self, format=None):
+ """
+ Return text representation
+ """
+ assert self.name() == "VCARD", "Must be a VCARD: %r" % (self,)
+
+ result = self._pycard.getText(format)
+ if result is None:
+ raise ValueError("Unknown format requested for address data.")
+ return result
+
+
</ins><span class="cx"> # FIXME: Should this not be in __eq__?
</span><span class="cx"> def same(self, other):
</span><span class="cx"> return self._pycard == other._pycard
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def name(self):
</span><span class="cx"> """
</span><del>- @return: the name of the iCalendar type of this component.
</del><ins>+ @return: the name of the vCard type of this component.
</ins><span class="cx"> """
</span><span class="cx"> return self._pycard.getType()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def duplicate(self):
</span><span class="cx"> """
</span><span class="cx"> Duplicate this object and all its contents.
</span><span class="lines">@@ -331,6 +436,7 @@
</span><span class="cx"> """
</span><span class="cx"> return Component(None, pycard=self._pycard.duplicate())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def hasProperty(self, name):
</span><span class="cx"> """
</span><span class="cx"> @param name: the name of the property whose existence is being tested.
</span><span class="lines">@@ -338,6 +444,7 @@
</span><span class="cx"> """
</span><span class="cx"> return self._pycard.hasProperty(name)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getProperty(self, name):
</span><span class="cx"> """
</span><span class="cx"> Get one property from the property list.
</span><span class="lines">@@ -346,10 +453,13 @@
</span><span class="cx"> @raise: L{ValueError} if there is more than one property of the given name.
</span><span class="cx"> """
</span><span class="cx"> properties = tuple(self.properties(name))
</span><del>- if len(properties) == 1: return properties[0]
- if len(properties) > 1: raise InvalidVCardDataError("More than one %s property in component %r" % (name, self))
</del><ins>+ if len(properties) == 1:
+ return properties[0]
+ if len(properties) > 1:
+ raise InvalidVCardDataError("More than one %s property in component %r" % (name, self))
</ins><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def properties(self, name=None):
</span><span class="cx"> """
</span><span class="cx"> @param name: if given and not C{None}, restricts the returned properties
</span><span class="lines">@@ -368,6 +478,7 @@
</span><span class="cx"> for p in properties
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def propertyValue(self, name):
</span><span class="cx"> properties = tuple(self.properties(name))
</span><span class="cx"> if len(properties) == 1:
</span><span class="lines">@@ -385,6 +496,7 @@
</span><span class="cx"> self._pycard.addProperty(property._pycard)
</span><span class="cx"> self._pycard.finalise()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def removeProperty(self, property):
</span><span class="cx"> """
</span><span class="cx"> Remove a property from this component.
</span><span class="lines">@@ -393,6 +505,7 @@
</span><span class="cx"> self._pycard.removeProperty(property._pycard)
</span><span class="cx"> self._pycard.finalise()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def removeProperties(self, name):
</span><span class="cx"> """
</span><span class="cx"> remove all properties with name
</span><span class="lines">@@ -400,6 +513,7 @@
</span><span class="cx"> """
</span><span class="cx"> self._pycard.removeProperties(name)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def replaceProperty(self, property):
</span><span class="cx"> """
</span><span class="cx"> Add or replace a property in this component.
</span><span class="lines">@@ -410,6 +524,7 @@
</span><span class="cx"> self._pycard.removeProperties(property.name())
</span><span class="cx"> self.addProperty(property)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def resourceUID(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the UID of the subcomponents in this component.
</span><span class="lines">@@ -421,6 +536,7 @@
</span><span class="cx">
</span><span class="cx"> return self._resource_uid
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def resourceKind(self):
</span><span class="cx"> """
</span><span class="cx"> @return: the kind of the subcomponents in this component.
</span><span class="lines">@@ -432,6 +548,7 @@
</span><span class="cx">
</span><span class="cx"> return self._resource_kind
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def resourceMemberAddresses(self):
</span><span class="cx"> """
</span><span class="cx"> @return: an iterable of X-ADDRESSBOOKSERVER-MEMBER property values
</span><span class="lines">@@ -440,6 +557,7 @@
</span><span class="cx">
</span><span class="cx"> return [prop.value() for prop in list(self.properties("X-ADDRESSBOOKSERVER-MEMBER"))]
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def validVCardData(self, doFix=True, doRaise=True):
</span><span class="cx"> """
</span><span class="cx"> @return: tuple of fixed, unfixed issues
</span><span class="lines">@@ -454,17 +572,19 @@
</span><span class="cx"> if unfixed:
</span><span class="cx"> log.debug("vCard data had unfixable problems:\n %s" % ("\n ".join(unfixed),))
</span><span class="cx"> if doRaise:
</span><del>- raise InvalidVCardDataError("Calendar data had unfixable problems:\n %s" % ("\n ".join(unfixed),))
</del><ins>+ raise InvalidVCardDataError("Address data had unfixable problems:\n %s" % ("\n ".join(unfixed),))
</ins><span class="cx"> if fixed:
</span><span class="cx"> log.debug("vCard data had fixable problems:\n %s" % ("\n ".join(fixed),))
</span><span class="cx">
</span><span class="cx"> return fixed, unfixed
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def validForCardDAV(self):
</span><span class="cx"> """
</span><span class="cx"> @raise ValueError: if the given vcard data is not valid.
</span><span class="cx"> """
</span><del>- if self.name() != "VCARD": raise InvalidVCardDataError("Not a vcard")
</del><ins>+ if self.name() != "VCARD":
+ raise InvalidVCardDataError("Not a vcard")
</ins><span class="cx">
</span><span class="cx"> version = self.propertyValue("VERSION")
</span><span class="cx"> if version != "3.0":
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavxmlutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/xmlutil.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/xmlutil.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/xmlutil.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -24,17 +24,21 @@
</span><span class="cx"> except ImportError:
</span><span class="cx"> from xml.parsers.expat import ExpatError as XMLParseError
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> # Utilities for working with ElementTree
</span><span class="cx">
</span><span class="cx"> def readXMLString(xmldata, expectedRootTag=None):
</span><span class="cx"> io = StringIO.StringIO(xmldata)
</span><span class="cx"> return readXML(io, expectedRootTag)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def readXML(xmlfile, expectedRootTag=None):
</span><span class="cx"> """
</span><span class="cx"> Read in XML data from a file and parse into ElementTree. Optionally verify
</span><span class="cx"> the root node is what we expect.
</span><del>-
</del><ins>+
</ins><span class="cx"> @param xmlfile: file to read from
</span><span class="cx"> @type xmlfile: C{File}
</span><span class="cx"> @param expectedRootTag: root tag (qname) to test or C{None}
</span><span class="lines">@@ -52,14 +56,18 @@
</span><span class="cx"> root = etree.getroot()
</span><span class="cx"> if root.tag != expectedRootTag:
</span><span class="cx"> raise ValueError("Ignoring file '%s' because it is not a %s file" % (xmlfile, expectedRootTag,))
</span><del>-
</del><ins>+
</ins><span class="cx"> return etree, etree.getroot()
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def elementToXML(element):
</span><span class="cx"> return XML.tostring(element, "utf-8")
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def writeXML(xmlfile, root):
</span><del>-
</del><ins>+
</ins><span class="cx"> data = """<?xml version="1.0" encoding="utf-8"?>
</span><span class="cx"> <!DOCTYPE %s SYSTEM "%s.dtd">
</span><span class="cx">
</span><span class="lines">@@ -69,7 +77,7 @@
</span><span class="cx">
</span><span class="cx"> # Generate indentation
</span><span class="cx"> def _indentNode(node, level=0):
</span><del>-
</del><ins>+
</ins><span class="cx"> if node.text is not None and node.text.strip():
</span><span class="cx"> return
</span><span class="cx"> elif len(node):
</span><span class="lines">@@ -87,27 +95,35 @@
</span><span class="cx"> with open(xmlfile, "w") as f:
</span><span class="cx"> f.write(data)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def newElementTreeWithRoot(roottag):
</span><span class="cx">
</span><span class="cx"> root = createElement(roottag)
</span><span class="cx"> etree = XML.ElementTree(root)
</span><del>-
</del><ins>+
</ins><span class="cx"> return etree, root
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def createElement(tag, text=None, **attrs):
</span><span class="cx">
</span><span class="cx"> child = XML.Element(tag, attrs)
</span><span class="cx"> child.text = text
</span><span class="cx"> return child
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def addSubElement(parent, tag, text=None):
</span><span class="cx">
</span><span class="cx"> child = XML.SubElement(parent, tag)
</span><span class="cx"> child.text = text
</span><span class="cx"> return child
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def changeSubElementText(parent, tag, text):
</span><del>-
</del><ins>+
</ins><span class="cx"> child = parent.find(tag)
</span><span class="cx"> if child is not None:
</span><span class="cx"> child.text = text
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAfricaCasablancaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Casablanca.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Casablanca.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Casablanca.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -54,6 +54,7 @@
</span><span class="cx"> RDATE:20100808T000000
</span><span class="cx"> RDATE:20110731T000000
</span><span class="cx"> RDATE:20120720T030000
</span><ins>+RDATE:20120930T030000
</ins><span class="cx"> RDATE:20130707T030000
</span><span class="cx"> RDATE:20140629T030000
</span><span class="cx"> RDATE:20150618T030000
</span><span class="lines">@@ -61,6 +62,9 @@
</span><span class="cx"> RDATE:20170527T030000
</span><span class="cx"> RDATE:20180516T030000
</span><span class="cx"> RDATE:20190506T030000
</span><ins>+RDATE:20200424T030000
+RDATE:20210413T030000
+RDATE:20220403T030000
</ins><span class="cx"> TZNAME:WET
</span><span class="cx"> TZOFFSETFROM:+0100
</span><span class="cx"> TZOFFSETTO:+0000
</span><span class="lines">@@ -88,17 +92,24 @@
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:DAYLIGHT
</span><span class="cx"> DTSTART:20120429T020000
</span><del>-RRULE:FREQ=YEARLY;UNTIL=20190428T020000Z;BYDAY=-1SU;BYMONTH=4
</del><ins>+RRULE:FREQ=YEARLY;UNTIL=20130428T020000Z;BYDAY=-1SU;BYMONTH=4
</ins><span class="cx"> TZNAME:WEST
</span><span class="cx"> TZOFFSETFROM:+0000
</span><span class="cx"> TZOFFSETTO:+0100
</span><span class="cx"> END:DAYLIGHT
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:20120930T030000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=9
</del><ins>+DTSTART:20131027T030000
+RRULE:FREQ=YEARLY;UNTIL=20221030T020000Z;BYDAY=-1SU;BYMONTH=10
</ins><span class="cx"> TZNAME:WET
</span><span class="cx"> TZOFFSETFROM:+0100
</span><span class="cx"> TZOFFSETTO:+0000
</span><span class="cx"> END:STANDARD
</span><ins>+BEGIN:DAYLIGHT
+DTSTART:20140330T020000
+RRULE:FREQ=YEARLY;UNTIL=20220327T020000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
</ins><span class="cx"> END:VTIMEZONE
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAfricaEl_Aaiunics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -15,9 +15,84 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19760414T000000
</span><span class="cx"> RDATE:19760414T000000
</span><del>-TZNAME:WET
</del><ins>+TZNAME:WEST
</ins><span class="cx"> TZOFFSETFROM:-0100
</span><span class="cx"> TZOFFSETTO:+0000
</span><span class="cx"> END:STANDARD
</span><ins>+BEGIN:DAYLIGHT
+DTSTART:19760501T000000
+RRULE:FREQ=YEARLY;UNTIL=19770501T000000Z;BYMONTH=5
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19760801T000000
+RDATE:19760801T000000
+RDATE:19770928T000000
+RDATE:19780804T000000
+RDATE:20080901T000000
+RDATE:20090821T000000
+RDATE:20100808T000000
+RDATE:20110731T000000
+RDATE:20120720T030000
+RDATE:20120930T030000
+RDATE:20130707T030000
+RDATE:20140629T030000
+RDATE:20150618T030000
+RDATE:20160607T030000
+RDATE:20170527T030000
+RDATE:20180516T030000
+RDATE:20190506T030000
+RDATE:20200424T030000
+RDATE:20210413T030000
+RDATE:20220403T030000
+TZNAME:WET
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0000
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19780601T000000
+RDATE:19780601T000000
+RDATE:20080601T000000
+RDATE:20090601T000000
+RDATE:20100502T000000
+RDATE:20110403T000000
+RDATE:20120820T020000
+RDATE:20130810T020000
+RDATE:20140729T020000
+RDATE:20150718T020000
+RDATE:20160707T020000
+RDATE:20170626T020000
+RDATE:20180615T020000
+RDATE:20190605T020000
+RDATE:20200524T020000
+RDATE:20210513T020000
+RDATE:20220503T020000
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:20120429T020000
+RRULE:FREQ=YEARLY;UNTIL=20130428T020000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20131027T030000
+RRULE:FREQ=YEARLY;UNTIL=20221030T020000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:WET
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0000
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20140330T020000
+RRULE:FREQ=YEARLY;UNTIL=20220327T020000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
</ins><span class="cx"> END:VTIMEZONE
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAfricaTripoliics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Tripoli.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Tripoli.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Africa/Tripoli.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -20,6 +20,7 @@
</span><span class="cx"> RDATE:19850406T000000
</span><span class="cx"> RDATE:19860404T000000
</span><span class="cx"> RDATE:19970404T000000
</span><ins>+RDATE:20130329T010000
</ins><span class="cx"> TZNAME:CEST
</span><span class="cx"> TZOFFSETFROM:+0100
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="lines">@@ -82,23 +83,10 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19971004T000000
</span><span class="cx"> RDATE:19971004T000000
</span><ins>+RDATE:20131025T020000
</ins><span class="cx"> TZNAME:EET
</span><span class="cx"> TZOFFSETFROM:+0200
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="cx"> END:STANDARD
</span><del>-BEGIN:DAYLIGHT
-DTSTART:20130329T010000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=3
-TZNAME:CEST
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20131025T020000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=10
-TZNAME:CET
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-END:STANDARD
</del><span class="cx"> END:VTIMEZONE
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAmericaEirunepeics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Eirunepe.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Eirunepe.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Eirunepe.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -51,6 +51,7 @@
</span><span class="cx"> RDATE:19870214T000000
</span><span class="cx"> RDATE:19880207T000000
</span><span class="cx"> RDATE:19940220T000000
</span><ins>+RDATE:20131110T000000
</ins><span class="cx"> TZNAME:ACT
</span><span class="cx"> TZOFFSETFROM:-0400
</span><span class="cx"> TZOFFSETTO:-0500
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAmericaPorto_Acreics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Porto_Acre.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Porto_Acre.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Porto_Acre.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -49,6 +49,7 @@
</span><span class="cx"> RDATE:19860315T000000
</span><span class="cx"> RDATE:19870214T000000
</span><span class="cx"> RDATE:19880207T000000
</span><ins>+RDATE:20131110T000000
</ins><span class="cx"> TZNAME:ACT
</span><span class="cx"> TZOFFSETFROM:-0400
</span><span class="cx"> TZOFFSETTO:-0500
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoAmericaRio_Brancoics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Rio_Branco.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Rio_Branco.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/America/Rio_Branco.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -49,6 +49,7 @@
</span><span class="cx"> RDATE:19860315T000000
</span><span class="cx"> RDATE:19870214T000000
</span><span class="cx"> RDATE:19880207T000000
</span><ins>+RDATE:20131110T000000
</ins><span class="cx"> TZNAME:ACT
</span><span class="cx"> TZOFFSETFROM:-0400
</span><span class="cx"> TZOFFSETTO:-0500
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoBrazilAcreics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Brazil/Acre.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Brazil/Acre.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Brazil/Acre.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -49,6 +49,7 @@
</span><span class="cx"> RDATE:19860315T000000
</span><span class="cx"> RDATE:19870214T000000
</span><span class="cx"> RDATE:19880207T000000
</span><ins>+RDATE:20131110T000000
</ins><span class="cx"> TZNAME:ACT
</span><span class="cx"> TZOFFSETFROM:-0400
</span><span class="cx"> TZOFFSETTO:-0500
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoLibyaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Libya.ics (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Libya.ics        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/Libya.ics        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -20,6 +20,7 @@
</span><span class="cx"> RDATE:19850406T000000
</span><span class="cx"> RDATE:19860404T000000
</span><span class="cx"> RDATE:19970404T000000
</span><ins>+RDATE:20130329T010000
</ins><span class="cx"> TZNAME:CEST
</span><span class="cx"> TZOFFSETFROM:+0100
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="lines">@@ -82,23 +83,10 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19971004T000000
</span><span class="cx"> RDATE:19971004T000000
</span><ins>+RDATE:20131025T020000
</ins><span class="cx"> TZNAME:EET
</span><span class="cx"> TZOFFSETFROM:+0200
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="cx"> END:STANDARD
</span><del>-BEGIN:DAYLIGHT
-DTSTART:20130329T010000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=3
-TZNAME:CEST
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20131025T020000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=10
-TZNAME:CET
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-END:STANDARD
</del><span class="cx"> END:VTIMEZONE
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfotimezonesxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/timezones.xml (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/timezones.xml        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/timezones.xml        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -2,7 +2,7 @@
</span><span class="cx"> <!DOCTYPE timezones SYSTEM "timezones.dtd">
</span><span class="cx">
</span><span class="cx"> <timezones>
</span><del>- <dtstamp>2013-10-01T01:19:11Z</dtstamp>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
</ins><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Abidjan</tzid>
</span><span class="cx"> <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</span><span class="lines">@@ -78,8 +78,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Casablanca</tzid>
</span><del>- <dtstamp>2013-07-11T02:11:45Z</dtstamp>
- <md5>b4e345b053c4699911078dcd16854bab</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>4f58dbcb4f7e6dfa7af3d710aeff97b4</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Ceuta</tzid>
</span><span class="lines">@@ -113,8 +113,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/El_Aaiun</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>494500808e8542fd83e4706654c43545</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>7933d45461e9f988ebfc9d062ce894e4</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Freetown</tzid>
</span><span class="lines">@@ -264,9 +264,9 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Tripoli</tzid>
</span><del>- <dtstamp>2013-01-14T15:32:16Z</dtstamp>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
</ins><span class="cx"> <alias>Libya</alias>
</span><del>- <md5>f59e5f16eec995c112b8b27580922fdd</md5>
</del><ins>+ <md5>6e8040bfd898654905bfd0a49c64e365</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Tunis</tzid>
</span><span class="lines">@@ -570,8 +570,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Eirunepe</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>d3c4df84162ebac445e1c2dcdb81f3b2</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>cc34b66260adda629311a54df088470b</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/El_Salvador</tzid>
</span><span class="lines">@@ -964,8 +964,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Porto_Acre</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>ce88b8461b7217e1d9ec8f4dfac34670</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>b2b04e3c9c1dab12a3764b99dd8536fc</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Porto_Velho</tzid>
</span><span class="lines">@@ -1006,10 +1006,10 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Rio_Branco</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
</ins><span class="cx"> <alias>America/Porto_Acre</alias>
</span><span class="cx"> <alias>Brazil/Acre</alias>
</span><del>- <md5>c41ff8b67906037ce014d42019e5831f</md5>
</del><ins>+ <md5>51273c4521cd00a8ecdcff336f0c9503</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Rosario</tzid>
</span><span class="lines">@@ -1885,8 +1885,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Brazil/Acre</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>b034140cdfc442b0f989f6a6dd6ec620</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>ad33124888f5c5dd9bed9317ef0bb953</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Brazil/DeNoronha</tzid>
</span><span class="lines">@@ -2641,8 +2641,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Libya</tzid>
</span><del>- <dtstamp>2013-01-14T15:32:16Z</dtstamp>
- <md5>f514b497bd861c14aa21612f9101c50c</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>e6ac54ea0ab33dd6fd125a71fc34cf46</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>MET</tzid>
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestwistedcaldavzoneinfoversiontxt"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/version.txt (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/version.txt        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/twistedcaldav/zoneinfo/version.txt        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1 +1 @@
</span><del>-IANA Timezone Registry: 2013f
</del><span class="cx">\ No newline at end of file
</span><ins>+IANA Timezone Registry: 2013h
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavbasepropertystorebasepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/base.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/base.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/base.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -214,7 +214,13 @@
</span><span class="cx">
</span><span class="cx"> def __delitem__(self, key):
</span><span class="cx"> # Handle per-user behavior
</span><del>- if self.isGlobalProperty(key):
</del><ins>+ if self.isShadowableProperty(key):
+ try:
+ self._delitem_uid(key, self._perUser)
+ except KeyError:
+ # It is OK for shadowable delete to fail
+ pass
+ elif self.isGlobalProperty(key):
</ins><span class="cx"> self._delitem_uid(key, self._defaultUser)
</span><span class="cx"> else:
</span><span class="cx"> self._delitem_uid(key, self._perUser)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavbasepropertystoretestbasepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/test/base.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/test/base.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/base/propertystore/test/base.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -206,6 +206,41 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><ins>+ def test_peruserShadow_delete(self):
+ """
+ Delete a shadowable property that has not been overridden by the sharee.
+ """
+
+ name = propertyName("shadow")
+
+ self.propertyStore1.setSpecialProperties((name,), ())
+ self.propertyStore2.setSpecialProperties((name,), ())
+
+ value1 = propertyValue("Hello, World1!")
+
+ self.propertyStore1[name] = value1
+ yield self._changed(self.propertyStore1)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value1)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+
+ del self.propertyStore2[name]
+ yield self._changed(self.propertyStore2)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value1)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+
+ del self.propertyStore1[name]
+ yield self._changed(self.propertyStore1)
+ self.assertEquals(self.propertyStore1.get(name, None), None)
+ self.assertEquals(self.propertyStore2.get(name, None), None)
+ self.failIf(name in self.propertyStore1)
+ self.failIf(name in self.propertyStore2)
+
+
+ @inlineCallbacks
</ins><span class="cx"> def test_peruser_global(self):
</span><span class="cx">
</span><span class="cx"> name = propertyName("global")
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastorefilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/file.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/file.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/file.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -41,8 +41,8 @@
</span><span class="cx"> from twext.web2.dav.resource import TwistedGETContentMD5
</span><span class="cx"> from twext.web2.http_headers import generateContentType, MimeType
</span><span class="cx">
</span><del>-from twistedcaldav import caldavxml, customxml
-from twistedcaldav.caldavxml import ScheduleCalendarTransp, Opaque
</del><ins>+from twistedcaldav import caldavxml, customxml, ical
+from twistedcaldav.caldavxml import ScheduleCalendarTransp, Opaque, Transparent
</ins><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.ical import InvalidICalendarDataError
</span><span class="cx">
</span><span class="lines">@@ -84,6 +84,14 @@
</span><span class="cx"> _topPath = "calendars"
</span><span class="cx"> _notifierPrefix = "CalDAV"
</span><span class="cx">
</span><ins>+ _componentCalendarName = {
+ "VEVENT": "calendar",
+ "VTODO": "tasks",
+ "VJOURNAL": "journals",
+ "VAVAILABILITY": "available",
+ "VPOLL": "polls",
+ }
+
</ins><span class="cx"> def __init__(self, uid, path, calendarStore, transaction):
</span><span class="cx"> super(CalendarHome, self).__init__(uid, path, calendarStore, transaction)
</span><span class="cx">
</span><span class="lines">@@ -177,20 +185,19 @@
</span><span class="cx">
</span><span class="cx"> def createdHome(self):
</span><span class="cx">
</span><del>- # Default calendar
- defaultCal = self.createCalendarWithName("calendar")
- props = defaultCal.properties()
- props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(Opaque())
-
</del><span class="cx"> # Check whether components type must be separate
</span><span class="cx"> if config.RestrictCalendarsToOneComponentType:
</span><del>- defaultCal.setSupportedComponents("VEVENT")
</del><ins>+ for name in ical.allowedStoreComponents:
+ cal = self.createCalendarWithName(self._componentCalendarName[name])
+ cal.setSupportedComponents(name)
+ props = cal.properties()
+ if name not in ("VEVENT", "VAVAILABILITY",):
+ props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(Transparent())
+ else:
+ props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(Opaque())
+ else:
+ cal = self.createCalendarWithName("calendar")
</ins><span class="cx">
</span><del>- # Default tasks
- defaultTasks = self.createCalendarWithName("tasks")
- props = defaultTasks.properties()
- defaultTasks.setSupportedComponents("VTODO")
-
</del><span class="cx"> self.createCalendarWithName("inbox")
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreindex_filepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/index_file.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/index_file.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/index_file.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -55,9 +55,9 @@
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.memcachepool import CachePoolUserMixIn
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -341,7 +341,7 @@
</span><span class="cx"> maxDate = maxDate.duplicate()
</span><span class="cx"> maxDate.setDateOnly(True)
</span><span class="cx"> if isStartDate:
</span><del>- maxDate += PyCalendarDuration(days=365)
</del><ins>+ maxDate += Duration(days=365)
</ins><span class="cx"> self.testAndUpdateIndex(maxDate)
</span><span class="cx"> else:
</span><span class="cx"> # We cannot handle this filter in an indexed search
</span><span class="lines">@@ -671,7 +671,7 @@
</span><span class="cx"> if master is None or not calendar.isRecurring():
</span><span class="cx"> # When there is no master we have a set of overridden components - index them all.
</span><span class="cx"> # When there is one instance - index it.
</span><del>- expand = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ expand = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> doInstanceIndexing = True
</span><span class="cx"> else:
</span><span class="cx"> # If migrating or re-creating or config option for delayed indexing is off, always index
</span><span class="lines">@@ -682,8 +682,8 @@
</span><span class="cx"> # by default. This is a caching parameter which affects the size of the index;
</span><span class="cx"> # it does not affect search results beyond this period, but it may affect
</span><span class="cx"> # performance of such a search.
</span><del>- expand = (PyCalendarDateTime.getToday() +
- PyCalendarDuration(days=config.FreeBusyIndexExpandAheadDays))
</del><ins>+ expand = (DateTime.getToday() +
+ Duration(days=config.FreeBusyIndexExpandAheadDays))
</ins><span class="cx">
</span><span class="cx"> if expand_until and expand_until > expand:
</span><span class="cx"> expand = expand_until
</span><span class="lines">@@ -700,8 +700,8 @@
</span><span class="cx"> # occurrences into some obscenely far-in-the-future date, so we cap the caching
</span><span class="cx"> # period. Searches beyond this period will always be relatively expensive for
</span><span class="cx"> # resources with occurrences beyond this period.
</span><del>- if expand > (PyCalendarDateTime.getToday() +
- PyCalendarDuration(days=config.FreeBusyIndexExpandMaxDays)):
</del><ins>+ if expand > (DateTime.getToday() +
+ Duration(days=config.FreeBusyIndexExpandMaxDays)):
</ins><span class="cx"> raise IndexedSearchException()
</span><span class="cx">
</span><span class="cx"> # Always do recurrence expansion even if we do not intend to index - we need this to double-check the
</span><span class="lines">@@ -716,7 +716,7 @@
</span><span class="cx"> # Now coerce indexing to off if needed
</span><span class="cx"> if not doInstanceIndexing:
</span><span class="cx"> instances = None
</span><del>- recurrenceLimit = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ recurrenceLimit = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx">
</span><span class="cx"> self._delete_from_db(name, uid, False)
</span><span class="cx">
</span><span class="lines">@@ -782,8 +782,8 @@
</span><span class="cx"> # Special - for unbounded recurrence we insert a value for "infinity"
</span><span class="cx"> # that will allow an open-ended time-range to always match it.
</span><span class="cx"> if calendar.isRecurringUnbounded():
</span><del>- start = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- end = PyCalendarDateTime(2100, 1, 1, 1, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ start = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ end = DateTime(2100, 1, 1, 1, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> float = 'N'
</span><span class="cx"> self._db_execute(
</span><span class="cx"> """
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingaddressmappingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/addressmapping.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/addressmapping.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/addressmapping.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -53,7 +53,7 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def getCalendarUser(self, cuaddr, principal):
</span><span class="cx">
</span><del>- # If we have a principal always treat the user as local or partitioned
</del><ins>+ # If we have a principal always treat the user as local
</ins><span class="cx"> if principal:
</span><span class="cx"> returnValue(calendarUserFromPrincipal(cuaddr, principal))
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingcaldavdeliverypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/delivery.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/delivery.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/delivery.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -26,8 +26,7 @@
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx">
</span><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><del>-from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser, RemoteCalendarUser, \
- PartitionedCalendarUser, OtherServerCalendarUser
</del><ins>+from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser, RemoteCalendarUser, OtherServerCalendarUser
</ins><span class="cx"> from txdav.caldav.datastore.scheduling.delivery import DeliveryService
</span><span class="cx"> from txdav.caldav.datastore.scheduling.freebusy import processAvailabilityFreeBusy, \
</span><span class="cx"> generateFreeBusyInfo, buildFreeBusyResult
</span><span class="lines">@@ -99,7 +98,7 @@
</span><span class="cx"> uid = self.scheduler.calendar.resourceUID()
</span><span class="cx">
</span><span class="cx"> organizerPrincipal = None
</span><del>- if type(self.scheduler.organizer) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,):
</del><ins>+ if type(self.scheduler.organizer) in (LocalCalendarUser, OtherServerCalendarUser,):
</ins><span class="cx"> organizerPrincipal = self.scheduler.organizer.principal.uid
</span><span class="cx">
</span><span class="cx"> for recipient in self.recipients:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingcaldavschedulerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/scheduler.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/scheduler.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/scheduler.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -101,6 +101,14 @@
</span><span class="cx"> "No principal for originator",
</span><span class="cx"> ))
</span><span class="cx"> else:
</span><ins>+ if not (originatorPrincipal.calendarsEnabled() and originatorPrincipal.thisServer()):
+ log.error("Originator not enabled or hosted on this server: %s" % (self.originator,))
+ raise HTTPError(self.errorResponse(
+ responsecode.FORBIDDEN,
+ self.errorElements["originator-denied"],
+ "Originator cannot be scheduled",
+ ))
+
</ins><span class="cx"> self.originator = LocalCalendarUser(self.originator, originatorPrincipal)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -127,8 +135,8 @@
</span><span class="cx"> else:
</span><span class="cx"> # Map recipient to their inbox
</span><span class="cx"> inbox = None
</span><del>- if principal.calendarsEnabled() and principal.thisServer():
- if principal.locallyHosted():
</del><ins>+ if principal.calendarsEnabled():
+ if principal.thisServer():
</ins><span class="cx"> recipient_home = yield self.txn.calendarHomeWithUID(principal.uid, create=True)
</span><span class="cx"> if recipient_home:
</span><span class="cx"> inbox = (yield recipient_home.calendarWithName("inbox"))
</span><span class="lines">@@ -138,7 +146,7 @@
</span><span class="cx"> if inbox:
</span><span class="cx"> results.append(calendarUserFromPrincipal(recipient, principal, inbox))
</span><span class="cx"> else:
</span><del>- log.error("No schedule inbox for principal: %s" % (principal,))
</del><ins>+ log.error("Recipient not enabled for calendaring: %s" % (principal,))
</ins><span class="cx"> results.append(InvalidCalendarUser(recipient))
</span><span class="cx">
</span><span class="cx"> self.recipients = results
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingcaldavtesttest_schedulerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,7 +14,7 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from twext.python.clsprop import classproperty
</span><span class="cx">
</span><span class="lines">@@ -45,7 +45,7 @@
</span><span class="cx"> self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
</span><span class="cx"> yield self.populate()
</span><span class="cx">
</span><del>- self.now = PyCalendarDateTime.getNowUTC()
</del><ins>+ self.now = DateTime.getNowUTC()
</ins><span class="cx"> self.now.setHHMMSS(0, 0, 0)
</span><span class="cx">
</span><span class="cx"> self.now_12H = self.now.duplicate()
</span><span class="lines">@@ -170,9 +170,9 @@
</span><span class="cx"> scheduler = CalDAVScheduler(self.transactionUnderTest(), "user01")
</span><span class="cx"> result = (yield scheduler.doSchedulingViaPOST("mailto:user01@example.com", ["mailto:user01@example.com", ], Component.fromString(data_request)))
</span><span class="cx"> self.assertEqual(len(result.responses), 1)
</span><del>- self.assertEqual(str(result.responses[0].children[0].children[0]), "mailto:user01@example.com")
- self.assertTrue(str(result.responses[0].children[1]).startswith("2"))
- self.assertEqual(normalizeiCalendarText(str(result.responses[0].children[2].children[0])), data_reply.replace("\n", "\r\n"))
</del><ins>+ self.assertEqual(str(result.responses[0].recipient.children[0]), "mailto:user01@example.com")
+ self.assertTrue(str(result.responses[0].reqstatus).startswith("2"))
+ self.assertEqual(normalizeiCalendarText(str(result.responses[0].calendar)), data_reply.replace("\n", "\r\n"))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -227,9 +227,9 @@
</span><span class="cx"> scheduler = CalDAVScheduler(self.transactionUnderTest(), "user01")
</span><span class="cx"> result = (yield scheduler.doSchedulingViaPOST("mailto:user01@example.com", ["mailto:user01@example.com", ], Component.fromString(data_request)))
</span><span class="cx"> self.assertEqual(len(result.responses), 1)
</span><del>- self.assertEqual(str(result.responses[0].children[0].children[0]), "mailto:user01@example.com")
- self.assertTrue(str(result.responses[0].children[1]).startswith("2"))
- self.assertEqual(normalizeiCalendarText(str(result.responses[0].children[2].children[0])), data_reply.replace("\n", "\r\n"))
</del><ins>+ self.assertEqual(str(result.responses[0].recipient.children[0]), "mailto:user01@example.com")
+ self.assertTrue(str(result.responses[0].reqstatus).startswith("2"))
+ self.assertEqual(normalizeiCalendarText(str(result.responses[0].calendar)), data_reply.replace("\n", "\r\n"))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -294,6 +294,6 @@
</span><span class="cx"> scheduler = CalDAVScheduler(self.transactionUnderTest(), "user01")
</span><span class="cx"> result = (yield scheduler.doSchedulingViaPOST("mailto:user01@example.com", ["mailto:user01@example.com", ], Component.fromString(data_request)))
</span><span class="cx"> self.assertEqual(len(result.responses), 1)
</span><del>- self.assertEqual(str(result.responses[0].children[0].children[0]), "mailto:user01@example.com")
- self.assertTrue(str(result.responses[0].children[1]).startswith("2"))
- self.assertEqual(normalizeiCalendarText(str(result.responses[0].children[2].children[0])), data_reply.replace("\n", "\r\n"))
</del><ins>+ self.assertEqual(str(result.responses[0].recipient.children[0]), "mailto:user01@example.com")
+ self.assertTrue(str(result.responses[0].reqstatus).startswith("2"))
+ self.assertEqual(normalizeiCalendarText(str(result.responses[0].calendar)), data_reply.replace("\n", "\r\n"))
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingcuaddresspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/cuaddress.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/cuaddress.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/cuaddress.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -21,7 +21,6 @@
</span><span class="cx">
</span><span class="cx"> __all__ = [
</span><span class="cx"> "LocalCalendarUser",
</span><del>- "PartitionedCalendarUser",
</del><span class="cx"> "OtherServerCalendarUser",
</span><span class="cx"> "RemoteCalendarUser",
</span><span class="cx"> "EmailCalendarUser",
</span><span class="lines">@@ -53,19 +52,6 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class PartitionedCalendarUser(CalendarUser):
-
- def __init__(self, cuaddr, principal):
- self.cuaddr = cuaddr
- self.principal = principal
- self.serviceType = DeliveryService.serviceType_ischedule
-
-
- def __str__(self):
- return "Partitioned calendar user: %s" % (self.cuaddr,)
-
-
-
</del><span class="cx"> class OtherServerCalendarUser(CalendarUser):
</span><span class="cx">
</span><span class="cx"> def __init__(self, cuaddr, principal):
</span><span class="lines">@@ -145,9 +131,7 @@
</span><span class="cx"> Get the appropriate calendar user address class for the provided principal.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- if principal.locallyHosted():
</del><ins>+ if principal.thisServer():
</ins><span class="cx"> return LocalCalendarUser(recipient, principal, inbox)
</span><del>- elif principal.thisServer():
- return PartitionedCalendarUser(recipient, principal)
</del><span class="cx"> else:
</span><span class="cx"> return OtherServerCalendarUser(recipient, principal)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingfreebusypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/freebusy.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/freebusy.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/freebusy.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,10 +14,10 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.period import Period
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx">
</span><span class="lines">@@ -68,8 +68,8 @@
</span><span class="cx"> if entry:
</span><span class="cx">
</span><span class="cx"> # Offset one day at either end to account for floating
</span><del>- cached_start = entry.timerange.start + PyCalendarDuration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
- cached_end = entry.timerange.end - PyCalendarDuration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
</del><ins>+ cached_start = entry.timerange.start + Duration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
+ cached_end = entry.timerange.end - Duration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
</ins><span class="cx">
</span><span class="cx"> # Verify that the requested time range lies within the cache time range
</span><span class="cx"> if compareDateTime(timerange.end, cached_end) <= 0 and compareDateTime(timerange.start, cached_start) >= 0:
</span><span class="lines">@@ -187,8 +187,8 @@
</span><span class="cx"> logItems["fb-uncached"] = logItems.get("fb-uncached", 0) + 1
</span><span class="cx">
</span><span class="cx"> # We want to cache a large range of time based on the current date
</span><del>- cache_start = normalizeToUTC(PyCalendarDateTime.getToday() + PyCalendarDuration(days=0 - config.FreeBusyCacheDaysBack))
- cache_end = normalizeToUTC(PyCalendarDateTime.getToday() + PyCalendarDuration(days=config.FreeBusyCacheDaysForward))
</del><ins>+ cache_start = normalizeToUTC(DateTime.getToday() + Duration(days=0 - config.FreeBusyCacheDaysBack))
+ cache_end = normalizeToUTC(DateTime.getToday() + Duration(days=config.FreeBusyCacheDaysForward))
</ins><span class="cx">
</span><span class="cx"> # If the requested time range would fit in our allowed cache range, trigger the cache creation
</span><span class="cx"> if compareDateTime(timerange.start, cache_start) >= 0 and compareDateTime(timerange.end, cache_end) <= 0:
</span><span class="lines">@@ -227,7 +227,7 @@
</span><span class="cx"> logItems["fb-cached"] = logItems.get("fb-cached", 0) + 1
</span><span class="cx">
</span><span class="cx"> # Determine appropriate timezone (UTC is the default)
</span><del>- tzinfo = tz.gettimezone() if tz is not None else PyCalendarTimezone(utc=True)
</del><ins>+ tzinfo = tz.gettimezone() if tz is not None else Timezone(utc=True)
</ins><span class="cx">
</span><span class="cx"> # We care about separate instances for VEVENTs only
</span><span class="cx"> aggregated_resources = {}
</span><span class="lines">@@ -270,15 +270,15 @@
</span><span class="cx"> if float == 'Y':
</span><span class="cx"> fbstart.setTimezone(tzinfo)
</span><span class="cx"> else:
</span><del>- fbstart.setTimezone(PyCalendarTimezone(utc=True))
</del><ins>+ fbstart.setTimezone(Timezone(utc=True))
</ins><span class="cx"> fbend = parseSQLTimestampToPyCalendar(end)
</span><span class="cx"> if float == 'Y':
</span><span class="cx"> fbend.setTimezone(tzinfo)
</span><span class="cx"> else:
</span><del>- fbend.setTimezone(PyCalendarTimezone(utc=True))
</del><ins>+ fbend.setTimezone(Timezone(utc=True))
</ins><span class="cx">
</span><span class="cx"> # Clip instance to time range
</span><del>- clipped = clipPeriod(PyCalendarPeriod(fbstart, duration=fbend - fbstart), PyCalendarPeriod(timerange.start, timerange.end))
</del><ins>+ clipped = clipPeriod(Period(fbstart, duration=fbend - fbstart), Period(timerange.start, timerange.end))
</ins><span class="cx">
</span><span class="cx"> # Double check for overlap
</span><span class="cx"> if clipped:
</span><span class="lines">@@ -364,7 +364,7 @@
</span><span class="cx"> @param timerange: the time-range in which to expand
</span><span class="cx"> @type timerange: L{TimeRange}
</span><span class="cx"> @param tzinfo: timezone for floating time calculations
</span><del>- @type tzinfo: L{PyCalendarTimezone}
</del><ins>+ @type tzinfo: L{Timezone}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # First expand the component
</span><span class="lines">@@ -404,7 +404,7 @@
</span><span class="cx"> @param calendar: the L{Component} that is the VCALENDAR containing the VEVENT's.
</span><span class="cx"> @param fbinfo: the tuple used to store the three types of fb data.
</span><span class="cx"> @param timerange: the time range to restrict free busy data to.
</span><del>- @param tzinfo: the L{PyCalendarTimezone} for the timezone to use for floating/all-day events.
</del><ins>+ @param tzinfo: the L{Timezone} for the timezone to use for floating/all-day events.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Expand out the set of instances for the event with in the required range
</span><span class="lines">@@ -449,10 +449,10 @@
</span><span class="cx"> # Clip period for this instance - use duration for period end if that
</span><span class="cx"> # is what original component used
</span><span class="cx"> if instance.component.hasProperty("DURATION"):
</span><del>- period = PyCalendarPeriod(fbstart, duration=fbend - fbstart)
</del><ins>+ period = Period(fbstart, duration=fbend - fbstart)
</ins><span class="cx"> else:
</span><del>- period = PyCalendarPeriod(fbstart, fbend)
- clipped = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
</del><ins>+ period = Period(fbstart, fbend)
+ clipped = clipPeriod(period, Period(timerange.start, timerange.end))
</ins><span class="cx">
</span><span class="cx"> # Double check for overlap
</span><span class="cx"> if clipped:
</span><span class="lines">@@ -490,7 +490,7 @@
</span><span class="cx"> assert isinstance(fb.value(), list), "FREEBUSY property does not contain a list of values: %r" % (fb,)
</span><span class="cx"> for period in fb.value():
</span><span class="cx"> # Clip period for this instance
</span><del>- clipped = clipPeriod(period.getValue(), PyCalendarPeriod(timerange.start, timerange.end))
</del><ins>+ clipped = clipPeriod(period.getValue(), Period(timerange.start, timerange.end))
</ins><span class="cx"> if clipped:
</span><span class="cx"> fbinfo[fbtype_mapper.get(fbtype, 0)].append(clipped)
</span><span class="cx">
</span><span class="lines">@@ -509,12 +509,12 @@
</span><span class="cx"> # Get overall start/end
</span><span class="cx"> start = vav.getStartDateUTC()
</span><span class="cx"> if start is None:
</span><del>- start = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ start = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> end = vav.getEndDateUTC()
</span><span class="cx"> if end is None:
</span><del>- end = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- period = PyCalendarPeriod(start, end)
- overall = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
</del><ins>+ end = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ period = Period(start, end)
+ overall = clipPeriod(period, Period(timerange.start, timerange.end))
</ins><span class="cx"> if overall is None:
</span><span class="cx"> continue
</span><span class="cx">
</span><span class="lines">@@ -526,10 +526,10 @@
</span><span class="cx"> last_end = timerange.start
</span><span class="cx"> for period in periods:
</span><span class="cx"> if last_end < period.getStart():
</span><del>- busyperiods.append(PyCalendarPeriod(last_end, period.getStart()))
</del><ins>+ busyperiods.append(Period(last_end, period.getStart()))
</ins><span class="cx"> last_end = period.getEnd()
</span><span class="cx"> if last_end < timerange.end:
</span><del>- busyperiods.append(PyCalendarPeriod(last_end, timerange.end))
</del><ins>+ busyperiods.append(Period(last_end, timerange.end))
</ins><span class="cx">
</span><span class="cx"> # Add to actual results mapped by busy type
</span><span class="cx"> fbtype = vav.propertyValue("BUSYTYPE")
</span><span class="lines">@@ -576,10 +576,10 @@
</span><span class="cx"> # Clip period for this instance - use duration for period end if that
</span><span class="cx"> # is what original component used
</span><span class="cx"> if instance.component.hasProperty("DURATION"):
</span><del>- period = PyCalendarPeriod(start, duration=end - start)
</del><ins>+ period = Period(start, duration=end - start)
</ins><span class="cx"> else:
</span><del>- period = PyCalendarPeriod(start, end)
- clipped = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
</del><ins>+ period = Period(start, end)
+ clipped = clipPeriod(period, Period(timerange.start, timerange.end))
</ins><span class="cx"> if clipped:
</span><span class="cx"> periods.append(clipped)
</span><span class="cx">
</span><span class="lines">@@ -622,7 +622,7 @@
</span><span class="cx"> fb.addProperty(attendee)
</span><span class="cx"> fb.addProperty(Property("DTSTART", timerange.start))
</span><span class="cx"> fb.addProperty(Property("DTEND", timerange.end))
</span><del>- fb.addProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
</del><ins>+ fb.addProperty(Property("DTSTAMP", DateTime.getNowUTC()))
</ins><span class="cx"> if len(fbinfo[0]) != 0:
</span><span class="cx"> fb.addProperty(Property("FREEBUSY", fbinfo[0], {"FBTYPE": "BUSY"}))
</span><span class="cx"> if len(fbinfo[1]) != 0:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingicaldiffpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icaldiff.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icaldiff.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icaldiff.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,8 +16,8 @@
</span><span class="cx">
</span><span class="cx"> from difflib import unified_diff
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.period import PyCalendarPeriod
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.period import Period
</ins><span class="cx">
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx">
</span><span class="lines">@@ -484,7 +484,7 @@
</span><span class="cx">
</span><span class="cx"> # If PARTSTAT was changed by the attendee, add a timestamp if needed
</span><span class="cx"> if config.Scheduling.Options.TimestampAttendeePartStatChanges:
</span><del>- serverAttendee.setParameter("X-CALENDARSERVER-DTSTAMP", PyCalendarDateTime.getNowUTC().getText())
</del><ins>+ serverAttendee.setParameter("X-CALENDARSERVER-DTSTAMP", DateTime.getNowUTC().getText())
</ins><span class="cx">
</span><span class="cx"> replyNeeded = True
</span><span class="cx">
</span><span class="lines">@@ -516,6 +516,10 @@
</span><span class="cx"> if comp.name() == "VALARM":
</span><span class="cx"> serverComponent.addComponent(comp)
</span><span class="cx">
</span><ins>+ # VPOLL
+ if serverComponent.name() == "VPOLL":
+ replyNeeded = self._transferVPOLLData(serverComponent, clientComponent)
+
</ins><span class="cx"> return True, replyNeeded
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -556,6 +560,44 @@
</span><span class="cx"> return True
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def _transferVPOLLData(self, serverComponent, clientComponent):
+
+ changed = False
+
+ # Get the VOTER properties in sub-components of the VPOLL as set by the attendee
+ poll_items = {}
+ for component in clientComponent.subcomponents():
+ poll_id = component.propertyValue("POLL-ITEM-ID")
+ if poll_id is not None:
+ poll_items[poll_id] = component.getVoterProperty((self.attendee,))
+
+ # Transfer attendee data with the master set
+ for component in serverComponent.subcomponents():
+ poll_id = component.propertyValue("POLL-ITEM-ID")
+ if poll_id is not None:
+ voter = component.getVoterProperty((self.attendee,))
+ attendee_voter = poll_items.get(poll_id)
+ if attendee_voter is None:
+ if voter is not None:
+ component.removeProperty(voter)
+ changed = True
+ elif voter is None:
+ component.addProperty(attendee_voter)
+ changed = True
+ else:
+ for paramname in ("RESPONSE",):
+ paramvalue = attendee_voter.parameterValue(paramname)
+ if paramvalue is None:
+ voter.removeParameter(paramname)
+ changed = True
+ else:
+ if paramvalue != voter.parameterValue(paramname):
+ voter.setParameter(paramname, paramvalue)
+ changed = True
+
+ return changed
+
+
</ins><span class="cx"> def _checkInvalidChanges(self, serverComponent, clientComponent, declines):
</span><span class="cx">
</span><span class="cx"> # Properties we care about: DTSTART, DTEND, DURATION, RRULE, RDATE, EXDATE
</span><span class="lines">@@ -585,12 +627,12 @@
</span><span class="cx"> def _getNormalizedDateTimeProperties(self, component):
</span><span class="cx">
</span><span class="cx"> # Basic time properties
</span><del>- if component.name() in ("VEVENT", "VJOURNAL",):
</del><ins>+ if component.name() in ("VEVENT", "VJOURNAL", "VPOLL"):
</ins><span class="cx"> dtstart = component.getProperty("DTSTART")
</span><span class="cx"> dtend = component.getProperty("DTEND")
</span><span class="cx"> duration = component.getProperty("DURATION")
</span><span class="cx">
</span><del>- timeRange = PyCalendarPeriod(
</del><ins>+ timeRange = Period(
</ins><span class="cx"> start=dtstart.value() if dtstart is not None else None,
</span><span class="cx"> end=dtend.value() if dtend is not None else None,
</span><span class="cx"> duration=duration.value() if duration is not None else None,
</span><span class="lines">@@ -602,16 +644,19 @@
</span><span class="cx"> duration = component.getProperty("DURATION")
</span><span class="cx">
</span><span class="cx"> if dtstart or duration:
</span><del>- timeRange = PyCalendarPeriod(
</del><ins>+ timeRange = Period(
</ins><span class="cx"> start=dtstart.value() if dtstart is not None else None,
</span><span class="cx"> duration=duration.value() if duration is not None else None,
</span><span class="cx"> )
</span><span class="cx"> else:
</span><del>- timeRange = PyCalendarPeriod()
</del><ins>+ timeRange = Period()
</ins><span class="cx">
</span><span class="cx"> newdue = component.getProperty("DUE")
</span><span class="cx"> if newdue is not None:
</span><span class="cx"> newdue = newdue.value().duplicate().adjustToUTC()
</span><ins>+ else:
+ timeRange = Period()
+ newdue = None
</ins><span class="cx">
</span><span class="cx"> # Recurrence rules - we need to normalize the order of the value parts
</span><span class="cx"> newrrules = set()
</span><span class="lines">@@ -627,7 +672,7 @@
</span><span class="cx"> rdates = component.properties("RDATE")
</span><span class="cx"> for rdate in rdates:
</span><span class="cx"> for value in rdate.value():
</span><del>- if isinstance(value, PyCalendarDateTime):
</del><ins>+ if isinstance(value, DateTime):
</ins><span class="cx"> value = value.duplicate().adjustToUTC()
</span><span class="cx"> newrdates.add(value)
</span><span class="cx">
</span><span class="lines">@@ -734,6 +779,7 @@
</span><span class="cx"> comp.normalizePropertyValueLists("EXDATE")
</span><span class="cx"> comp.removePropertyParameters("ORGANIZER", ("SCHEDULE-STATUS",))
</span><span class="cx"> comp.removePropertyParameters("ATTENDEE", ("SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND",))
</span><ins>+ comp.removePropertyParameters("VOTER", ("SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND",))
</ins><span class="cx"> comp.removeAlarms()
</span><span class="cx"> comp.normalizeAll()
</span><span class="cx"> comp.normalizeAttachments()
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingicalsplitterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icalsplitter.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icalsplitter.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/icalsplitter.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,7 +14,7 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from twistedcaldav.ical import Property
</span><span class="cx">
</span><span class="lines">@@ -34,10 +34,10 @@
</span><span class="cx">
</span><span class="cx"> """
</span><span class="cx"> self.threshold = threshold
</span><del>- self.past = PyCalendarDateTime.getNowUTC()
</del><ins>+ self.past = DateTime.getNowUTC()
</ins><span class="cx"> self.past.setHHMMSS(0, 0, 0)
</span><span class="cx"> self.past.offsetDay(-past)
</span><del>- self.now = PyCalendarDateTime.getNowUTC()
</del><ins>+ self.now = DateTime.getNowUTC()
</ins><span class="cx"> self.now.setHHMMSS(0, 0, 0)
</span><span class="cx"> self.now.offsetDay(-1)
</span><span class="cx">
</span><span class="lines">@@ -91,7 +91,7 @@
</span><span class="cx"> @type ical: L{Component}
</span><span class="cx">
</span><span class="cx"> @return: recurrence-id of the split
</span><del>- @rtype: L{PyCalendarDateTime}
</del><ins>+ @rtype: L{DateTime}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Find the instance RECURRENCE-ID where a split is going to happen
</span><span class="lines">@@ -124,7 +124,7 @@
</span><span class="cx"> @type ical: L{Component}
</span><span class="cx">
</span><span class="cx"> @param rid: recurrence-id where the split should occur, or C{None} to determine it here
</span><del>- @type rid: L{PyCalendarDateTime} or C{None}
</del><ins>+ @type rid: L{DateTime} or C{None}
</ins><span class="cx">
</span><span class="cx"> @param olderUID: UID to use for the split off component, or C{None} to generate one here
</span><span class="cx"> @type olderUID: C{str} or C{None}
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingimipoutboundpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/outbound.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/outbound.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/outbound.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -26,8 +26,8 @@
</span><span class="cx"> from email.mime.multipart import MIMEMultipart
</span><span class="cx"> from email.mime.text import MIMEText
</span><span class="cx"> import email.utils
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
</ins><span class="cx"> from twext.enterprise.dal.record import fromTable
</span><span class="cx"> from twext.enterprise.queue import WorkItem
</span><span class="cx"> from twext.python.log import Logger
</span><span class="lines">@@ -320,8 +320,8 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> if onlyAfter is None:
</span><del>- duration = PyCalendarDuration(days=self.suppressionDays)
- onlyAfter = PyCalendarDateTime.getNowUTC() - duration
</del><ins>+ duration = Duration(days=self.suppressionDays)
+ onlyAfter = DateTime.getNowUTC() - duration
</ins><span class="cx">
</span><span class="cx"> icaluid = calendar.resourceUID()
</span><span class="cx"> method = calendar.propertyValue("METHOD")
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingimiptesttest_deliverypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_delivery.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_delivery.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_delivery.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -64,7 +64,7 @@
</span><span class="cx"> yield delivery.generateSchedulingResponses()
</span><span class="cx">
</span><span class="cx"> self.assertEqual(len(responses.responses), 1)
</span><del>- self.assertEqual(str(responses.responses[0].children[1]), iTIPRequestStatus.SERVICE_UNAVAILABLE)
</del><ins>+ self.assertEqual(str(responses.responses[0].reqstatus), iTIPRequestStatus.SERVICE_UNAVAILABLE)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingimiptesttest_inboundpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -300,7 +300,7 @@
</span><span class="cx"> yield txn.commit()
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> "1.2;Scheduling message has been delivered",
</span><del>- result.responses[0].children[1].toString()
</del><ins>+ result.responses[0].reqstatus.toString()
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -333,7 +333,7 @@
</span><span class="cx"> yield txn.commit()
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> "3.7;Invalid Calendar User",
</span><del>- result.responses[0].children[1].toString()
</del><ins>+ result.responses[0].reqstatus.toString()
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingimiptesttest_outboundpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_outbound.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_outbound.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/imip/test/test_outbound.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -17,7 +17,7 @@
</span><span class="cx">
</span><span class="cx"> from cStringIO import StringIO
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, succeed
</span><span class="cx"> from twisted.trial import unittest
</span><span class="lines">@@ -275,7 +275,7 @@
</span><span class="cx"> inputOriginator,
</span><span class="cx"> inputRecipient,
</span><span class="cx"> Component.fromString(inputCalendar.replace("\n", "\r\n")),
</span><del>- onlyAfter=PyCalendarDateTime(2010, 1, 1, 0, 0, 0)
</del><ins>+ onlyAfter=DateTime(2010, 1, 1, 0, 0, 0)
</ins><span class="cx"> )
</span><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="lines">@@ -319,7 +319,7 @@
</span><span class="cx"> inputOriginator,
</span><span class="cx"> inputRecipient,
</span><span class="cx"> Component.fromString(inputCalendar.replace("\n", "\r\n")),
</span><del>- onlyAfter=PyCalendarDateTime(2021, 1, 1, 0, 0, 0)
</del><ins>+ onlyAfter=DateTime(2021, 1, 1, 0, 0, 0)
</ins><span class="cx"> )
</span><span class="cx"> yield txn.commit()
</span><span class="cx"> self.assertFalse(self.sender.smtpSender.sendMessageCalled)
</span><span class="lines">@@ -381,7 +381,7 @@
</span><span class="cx"> txn = self.store.newTransaction()
</span><span class="cx"> yield self.sender.outbound(txn, inputOriginator, inputRecipient,
</span><span class="cx"> Component.fromString(inputCalendar.replace("\n", "\r\n")),
</span><del>- onlyAfter=PyCalendarDateTime(2010, 1, 1, 0, 0, 0))
</del><ins>+ onlyAfter=DateTime(2010, 1, 1, 0, 0, 0))
</ins><span class="cx">
</span><span class="cx"> # Verify we didn't create a new token...
</span><span class="cx"> txn = self.store.newTransaction()
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingimplicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/implicit.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/implicit.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/implicit.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -21,7 +21,6 @@
</span><span class="cx"> from twext.web2 import responsecode
</span><span class="cx"> from twext.web2.http import HTTPError
</span><span class="cx">
</span><del>-from twistedcaldav import caldavxml
</del><span class="cx"> from twistedcaldav.caldavxml import caldav_namespace
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.ical import Property
</span><span class="lines">@@ -29,7 +28,7 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling import addressmapping
</span><span class="cx"> from txdav.caldav.datastore.scheduling.caldav.scheduler import CalDAVScheduler
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import InvalidCalendarUser, \
</span><del>- LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser, \
</del><ins>+ LocalCalendarUser, OtherServerCalendarUser, \
</ins><span class="cx"> normalizeCUAddr
</span><span class="cx"> from txdav.caldav.datastore.scheduling.icaldiff import iCalDiff
</span><span class="cx"> from txdav.caldav.datastore.scheduling.itip import iTipGenerator, iTIPRequestStatus
</span><span class="lines">@@ -251,7 +250,7 @@
</span><span class="cx"> # to create new scheduling resources.
</span><span class="cx"> if self.action == "create":
</span><span class="cx"> if self.organizerPrincipal and not self.organizerPrincipal.enabledAsOrganizer():
</span><del>- log.error("ORGANIZER not allowed to be an Organizer: %s" % (self.organizer,))
</del><ins>+ log.error("ORGANIZER not allowed to be an Organizer: {organizer}", organizer=self.organizer)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "organizer-allowed"),
</span><span class="lines">@@ -427,7 +426,7 @@
</span><span class="cx"> self.organizer = self.calendar.validOrganizerForScheduling()
</span><span class="cx"> except ValueError:
</span><span class="cx"> # We have different ORGANIZERs in the same iCalendar object - this is an error
</span><del>- log.error("Only one ORGANIZER is allowed in an iCalendar object:\n%s" % (self.calendar,))
</del><ins>+ log.error("Only one ORGANIZER is allowed in an iCalendar object:\n{calendar}", calendar=self.calendar)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "single-organizer"),
</span><span class="lines">@@ -462,7 +461,7 @@
</span><span class="cx"> # Check for matching resource somewhere else in the home
</span><span class="cx"> foundElsewhere = (yield self.calendar_home.hasCalendarResourceUIDSomewhereElse(self.uid, check_resource, mode))
</span><span class="cx"> if foundElsewhere is not None:
</span><del>- log.debug("Implicit - found component with same UID in a different collection: %s" % (check_resource,))
</del><ins>+ log.debug("Implicit - found component with same UID in a different collection: {resource}", resource=check_resource)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "unique-scheduling-object-resource"),
</span><span class="lines">@@ -531,7 +530,7 @@
</span><span class="cx"> # Check for a delete
</span><span class="cx"> if self.action == "remove":
</span><span class="cx">
</span><del>- log.debug("Implicit - organizer '%s' is removing UID: '%s'" % (self.organizer, self.uid))
</del><ins>+ log.debug("Implicit - organizer '{organizer}' is removing UID: '{uid}'", organizer=self.organizer, uid=self.uid)
</ins><span class="cx"> self.oldcalendar = self.calendar
</span><span class="cx">
</span><span class="cx"> # Cancel all attendees
</span><span class="lines">@@ -557,16 +556,16 @@
</span><span class="cx"> no_change, self.changed_rids, self.needs_action_rids, reinvites, recurrence_reschedule = self.isOrganizerChangeInsignificant()
</span><span class="cx"> if no_change:
</span><span class="cx"> if reinvites:
</span><del>- log.debug("Implicit - organizer '%s' is re-inviting UID: '%s', attendees: %s" % (self.organizer, self.uid, ", ".join(reinvites)))
</del><ins>+ log.debug("Implicit - organizer '{organizer}' is re-inviting UID: '{uid}', attendees: {attendees}", organizer=self.organizer, uid=self.uid, attendees=", ".join(reinvites))
</ins><span class="cx"> self.reinvites = reinvites
</span><span class="cx"> else:
</span><span class="cx"> # Nothing to do
</span><del>- log.debug("Implicit - organizer '%s' is modifying UID: '%s' but change is not significant" % (self.organizer, self.uid))
</del><ins>+ log.debug("Implicit - organizer '{organizer}' is modifying UID: '{uid}' but change is not significant", organizer=self.organizer, uid=self.uid)
</ins><span class="cx"> returnValue(None)
</span><span class="cx"> else:
</span><span class="cx"> # Do not change PARTSTATs for a split operation
</span><span class="cx"> if self.split_details is None:
</span><del>- log.debug("Implicit - organizer '%s' is modifying UID: '%s'" % (self.organizer, self.uid))
</del><ins>+ log.debug("Implicit - organizer '{organizer}' is modifying UID: '{uid}'", organizer=self.organizer, uid=self.uid)
</ins><span class="cx">
</span><span class="cx"> for rid in self.needs_action_rids:
</span><span class="cx"> comp = self.calendar.overriddenComponent(rid)
</span><span class="lines">@@ -587,7 +586,7 @@
</span><span class="cx">
</span><span class="cx"> attendee.setParameter("PARTSTAT", "NEEDS-ACTION")
</span><span class="cx"> else:
</span><del>- log.debug("Implicit - organizer '%s' is splitting UID: '%s'" % (self.organizer, self.uid))
</del><ins>+ log.debug("Implicit - organizer '{organizer}' is splitting UID: '{uid}'", organizer=self.organizer, uid=self.uid)
</ins><span class="cx">
</span><span class="cx"> # Check for removed attendees
</span><span class="cx"> if not recurrence_reschedule:
</span><span class="lines">@@ -601,10 +600,10 @@
</span><span class="cx">
</span><span class="cx"> elif self.action == "create":
</span><span class="cx"> if self.split_details is None:
</span><del>- log.debug("Implicit - organizer '%s' is creating UID: '%s'" % (self.organizer, self.uid))
</del><ins>+ log.debug("Implicit - organizer '{organizer}' is creating UID: '{uid}'", organizer=self.organizer, uid=self.uid)
</ins><span class="cx"> self.coerceAttendeesPartstatOnCreate()
</span><span class="cx"> else:
</span><del>- log.debug("Implicit - organizer '%s' is creating a split UID: '%s'" % (self.organizer, self.uid))
</del><ins>+ log.debug("Implicit - organizer '{organizer}' is creating a split UID: '{uid}'", organizer=self.organizer, uid=self.uid)
</ins><span class="cx"> self.needs_sequence_change = False
</span><span class="cx">
</span><span class="cx"> # Always set RSVP=TRUE for any NEEDS-ACTION
</span><span class="lines">@@ -698,7 +697,7 @@
</span><span class="cx"> oldOrganizer = self.oldcalendar.getOrganizer()
</span><span class="cx"> newOrganizer = self.calendar.getOrganizer()
</span><span class="cx"> if oldOrganizer != newOrganizer:
</span><del>- log.error("Cannot change ORGANIZER: UID:%s" % (self.uid,))
</del><ins>+ log.error("Cannot change ORGANIZER: UID:{uid}", uid=self.uid)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "valid-organizer-change"),
</span><span class="lines">@@ -906,7 +905,7 @@
</span><span class="cx"> if cuaddr not in coerced:
</span><span class="cx"> attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(cuaddr)
</span><span class="cx"> attendeeAddress = (yield addressmapping.mapper.getCalendarUser(cuaddr, attendeePrincipal))
</span><del>- local_attendee = type(attendeeAddress) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,)
</del><ins>+ local_attendee = type(attendeeAddress) in (LocalCalendarUser, OtherServerCalendarUser,)
</ins><span class="cx"> coerced[cuaddr] = local_attendee
</span><span class="cx"> if coerced[cuaddr]:
</span><span class="cx"> attendee.removeParameter("SCHEDULE-AGENT")
</span><span class="lines">@@ -977,7 +976,7 @@
</span><span class="cx"> scheduler = self.makeScheduler()
</span><span class="cx">
</span><span class="cx"> # Do the PUT processing
</span><del>- log.info("Implicit CANCEL - organizer: '%s' to attendee: '%s', UID: '%s', RIDs: '%s'" % (self.organizer, attendee, self.uid, rids))
</del><ins>+ log.info("Implicit CANCEL - organizer: '{organizer}' to attendee: '{attendee}', UID: '{uid}', RIDs: '{rids}'", organizer=self.organizer, attendee=attendee, uid=self.uid, rids=rids)
</ins><span class="cx"> response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, internal_request=True, suppress_refresh=self.suppress_refresh))
</span><span class="cx"> self.handleSchedulingResponse(response, True)
</span><span class="cx">
</span><span class="lines">@@ -1034,7 +1033,7 @@
</span><span class="cx"> scheduler = self.makeScheduler()
</span><span class="cx">
</span><span class="cx"> # Do the PUT processing
</span><del>- log.info("Implicit REQUEST - organizer: '%s' to attendee: '%s', UID: '%s'" % (self.organizer, attendee, self.uid,))
</del><ins>+ log.info("Implicit REQUEST - organizer: '{organizer}' to attendee: '{attendee}', UID: '{uid}'", organizer=self.organizer, attendee=attendee, uid=self.uid)
</ins><span class="cx"> response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, internal_request=True, suppress_refresh=self.suppress_refresh))
</span><span class="cx"> self.handleSchedulingResponse(response, True)
</span><span class="cx">
</span><span class="lines">@@ -1047,17 +1046,17 @@
</span><span class="cx">
</span><span class="cx"> # Map each recipient in the response to a status code
</span><span class="cx"> responses = {}
</span><ins>+ propname = self.calendar.mainComponent().recipientPropertyName() if is_organizer else "ORGANIZER"
</ins><span class="cx"> for item in response.responses:
</span><del>- assert isinstance(item, caldavxml.Response), "Wrong element in response"
- recipient = str(item.children[0].children[0])
- status = str(item.children[1])
</del><ins>+ recipient = str(item.recipient.children[0])
+ status = str(item.reqstatus)
</ins><span class="cx"> responses[recipient] = status
</span><span class="cx">
</span><span class="cx"> # Now apply to each ATTENDEE/ORGANIZER in the original data
</span><span class="cx"> self.calendar.setParameterToValueForPropertyWithValue(
</span><span class="cx"> "SCHEDULE-STATUS",
</span><span class="cx"> status.split(";")[0],
</span><del>- "ATTENDEE" if is_organizer else "ORGANIZER",
</del><ins>+ propname,
</ins><span class="cx"> recipient)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1069,19 +1068,19 @@
</span><span class="cx">
</span><span class="cx"> if self.action == "remove":
</span><span class="cx"> if self.calendar.hasPropertyValueInAllComponents(Property("STATUS", "CANCELLED")):
</span><del>- log.debug("Implicit - attendee '%s' is removing cancelled UID: '%s'" % (self.attendee, self.uid))
</del><ins>+ log.debug("Implicit - attendee '{attendee}' is removing cancelled UID: '{uid}'", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> # Nothing else to do
</span><span class="cx"> elif doScheduling:
</span><span class="cx"> # If attendee is already marked as declined in all components - nothing to do
</span><span class="cx"> attendees = self.calendar.getAttendeeProperties((self.attendee,))
</span><span class="cx"> if all([attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") == "DECLINED" for attendee in attendees]):
</span><del>- log.debug("Implicit - attendee '%s' is removing fully declined UID: '%s'" % (self.attendee, self.uid))
</del><ins>+ log.debug("Implicit - attendee '{attendee}' is removing fully declined UID: '{uid}'", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> # Nothing else to do
</span><span class="cx"> else:
</span><del>- log.debug("Implicit - attendee '%s' is cancelling UID: '%s'" % (self.attendee, self.uid))
</del><ins>+ log.debug("Implicit - attendee '{attendee}' is cancelling UID: '{uid}'", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> yield self.scheduleCancelWithOrganizer()
</span><span class="cx"> else:
</span><del>- log.debug("Implicit - attendee '%s' is removing UID without server scheduling: '%s'" % (self.attendee, self.uid))
</del><ins>+ log.debug("Implicit - attendee '{attendee}' is removing UID without server scheduling: '{uid}'", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> # Nothing else to do
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="lines">@@ -1092,7 +1091,7 @@
</span><span class="cx"> oldOrganizer = self.oldcalendar.getOrganizer()
</span><span class="cx"> newOrganizer = self.calendar.getOrganizer()
</span><span class="cx"> if oldOrganizer != newOrganizer:
</span><del>- log.error("Cannot change ORGANIZER: UID:%s" % (self.uid,))
</del><ins>+ log.error("Cannot change ORGANIZER: UID:{uid}", uid=self.uid)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "valid-attendee-change"),
</span><span class="lines">@@ -1107,7 +1106,7 @@
</span><span class="cx">
</span><span class="cx"> # If Organizer copy exists we cannot allow SCHEDULE-AGENT=CLIENT or NONE
</span><span class="cx"> if not doScheduling:
</span><del>- log.error("Attendee '%s' is not allowed to change SCHEDULE-AGENT on organizer: UID:%s" % (self.attendeePrincipal, self.uid,))
</del><ins>+ log.error("Attendee '{attendee}' is not allowed to change SCHEDULE-AGENT on organizer: UID:{uid}", attendee=self.attendeePrincipal, uid=self.uid)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "valid-attendee-change"),
</span><span class="lines">@@ -1121,11 +1120,11 @@
</span><span class="cx">
</span><span class="cx"> if not changeAllowed:
</span><span class="cx"> if self.calendar.hasPropertyValueInAllComponents(Property("STATUS", "CANCELLED")):
</span><del>- log.debug("Attendee '%s' is creating CANCELLED event for mismatched UID: '%s' - removing entire event" % (self.attendee, self.uid,))
</del><ins>+ log.debug("Attendee '{attendee}' is creating CANCELLED event for mismatched UID: '{uid}' - removing entire event", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> self.return_status = ImplicitScheduler.STATUS_ORPHANED_EVENT
</span><span class="cx"> returnValue(None)
</span><span class="cx"> else:
</span><del>- log.error("Attendee '%s' is not allowed to make an unauthorized change to an organized event: UID:%s" % (self.attendeePrincipal, self.uid,))
</del><ins>+ log.error("Attendee '{attendee}' is not allowed to make an unauthorized change to an organized event: UID:{uid}", attendee=self.attendeePrincipal, uid=self.uid)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "valid-attendee-change"),
</span><span class="lines">@@ -1135,21 +1134,21 @@
</span><span class="cx"> # Check that the return calendar actually has any components left - this can happen if a cancelled
</span><span class="cx"> # component is removed and replaced by another cancelled or invalid one
</span><span class="cx"> if self.calendar.mainType() is None:
</span><del>- log.debug("Attendee '%s' is replacing CANCELLED event: '%s' - removing entire event" % (self.attendee, self.uid,))
</del><ins>+ log.debug("Attendee '{attendee}' is replacing CANCELLED event: '{uid}' - removing entire event", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> self.return_status = ImplicitScheduler.STATUS_ORPHANED_EVENT
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="cx"> if not doITipReply:
</span><del>- log.debug("Implicit - attendee '%s' is updating UID: '%s' but change is not significant" % (self.attendee, self.uid))
</del><ins>+ log.debug("Implicit - attendee '{attendee}' is updating UID: '{uid}' but change is not significant", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> returnValue(self.return_calendar)
</span><del>- log.debug("Attendee '%s' is allowed to update UID: '%s' with local organizer '%s'" % (self.attendee, self.uid, self.organizer))
</del><ins>+ log.debug("Attendee '{attendee}' is allowed to update UID: '{uid}' with local organizer '{organizer}'", attendee=self.attendee, uid=self.uid, organizer=self.organizer)
</ins><span class="cx">
</span><span class="cx"> elif isinstance(self.organizerAddress, LocalCalendarUser):
</span><span class="cx"> # If Organizer copy does not exists we cannot allow SCHEDULE-AGENT=SERVER
</span><span class="cx"> if doScheduling:
</span><span class="cx"> # Check to see whether all instances are CANCELLED
</span><span class="cx"> if self.calendar.hasPropertyValueInAllComponents(Property("STATUS", "CANCELLED")):
</span><del>- log.debug("Attendee '%s' is creating CANCELLED event for missing UID: '%s' - removing entire event" % (self.attendee, self.uid,))
</del><ins>+ log.debug("Attendee '{attendee}' is creating CANCELLED event for missing UID: '{uid}' - removing entire event", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> self.return_status = ImplicitScheduler.STATUS_ORPHANED_CANCELLED_EVENT
</span><span class="cx"> returnValue(None)
</span><span class="cx"> else:
</span><span class="lines">@@ -1157,25 +1156,25 @@
</span><span class="cx"> if self.oldcalendar:
</span><span class="cx"> oldScheduling = self.oldcalendar.getOrganizerScheduleAgent()
</span><span class="cx"> if not oldScheduling:
</span><del>- log.error("Attendee '%s' is not allowed to set SCHEDULE-AGENT=SERVER on organizer: UID:%s" % (self.attendeePrincipal, self.uid,))
</del><ins>+ log.error("Attendee '{attendee}' is not allowed to set SCHEDULE-AGENT=SERVER on organizer: UID:{uid}", attendee=self.attendeePrincipal, uid=self.uid)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "valid-attendee-change"),
</span><span class="cx"> "Attendee cannot change organizer state",
</span><span class="cx"> ))
</span><span class="cx">
</span><del>- log.debug("Attendee '%s' is not allowed to update UID: '%s' - missing organizer copy - removing entire event" % (self.attendee, self.uid,))
</del><ins>+ log.debug("Attendee '{attendee}' is not allowed to update UID: '{uid}' - missing organizer copy - removing entire event", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> self.return_status = ImplicitScheduler.STATUS_ORPHANED_EVENT
</span><span class="cx"> returnValue(None)
</span><span class="cx"> else:
</span><del>- log.debug("Implicit - attendee '%s' is modifying UID without server scheduling: '%s'" % (self.attendee, self.uid))
</del><ins>+ log.debug("Implicit - attendee '{attendee}' is modifying UID without server scheduling: '{uid}'", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> # Nothing else to do
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="cx"> elif isinstance(self.organizerAddress, InvalidCalendarUser):
</span><span class="cx"> # We will allow the attendee to do anything in this case, but we will mark the organizer
</span><span class="cx"> # with an schedule-status error
</span><del>- log.debug("Attendee '%s' is allowed to update UID: '%s' with invalid organizer '%s'" % (self.attendee, self.uid, self.organizer))
</del><ins>+ log.debug("Attendee '{attendee}' is allowed to update UID: '{uid}' with invalid organizer '{organizer}'", attendee=self.attendee, uid=self.uid, organizer=self.organizer)
</ins><span class="cx"> if doScheduling:
</span><span class="cx"> self.calendar.setParameterToValueForPropertyWithValue(
</span><span class="cx"> "SCHEDULE-STATUS",
</span><span class="lines">@@ -1189,14 +1188,14 @@
</span><span class="cx"> # to make any change they like as we cannot verify what is reasonable. In reality
</span><span class="cx"> # we ought to be comparing the Attendee changes against the attendee's own copy
</span><span class="cx"> # and restrict changes based on that when the organizer's copy is not available.
</span><del>- log.debug("Attendee '%s' is allowed to update UID: '%s' with remote organizer '%s'" % (self.attendee, self.uid, self.organizer))
</del><ins>+ log.debug("Attendee '{attendee}' is allowed to update UID: '{uid}' with remote organizer '{organizer}'", attendee=self.attendee, uid=self.uid, organizer=self.organizer)
</ins><span class="cx"> changedRids = None
</span><span class="cx">
</span><span class="cx"> if doScheduling:
</span><del>- log.debug("Implicit - attendee '%s' is updating UID: '%s'" % (self.attendee, self.uid))
</del><ins>+ log.debug("Implicit - attendee '{attendee}' is updating UID: '{uid}'", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> yield self.scheduleWithOrganizer(changedRids)
</span><span class="cx"> else:
</span><del>- log.debug("Implicit - attendee '%s' is updating UID without server scheduling: '%s'" % (self.attendee, self.uid))
</del><ins>+ log.debug("Implicit - attendee '{attendee}' is updating UID without server scheduling: '{uid}'", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> # Nothing else to do
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1205,7 +1204,7 @@
</span><span class="cx">
</span><span class="cx"> if self.action == "remove":
</span><span class="cx"> # Nothing else to do
</span><del>- log.debug("Implicit - missing attendee is removing UID without server scheduling: '%s'" % (self.uid,))
</del><ins>+ log.debug("Implicit - missing attendee is removing UID without server scheduling: '{uid}'", uid=self.uid)
</ins><span class="cx">
</span><span class="cx"> else:
</span><span class="cx"> # Make sure ORGANIZER is not changed if originally SCHEDULE-AGENT=SERVER
</span><span class="lines">@@ -1214,7 +1213,7 @@
</span><span class="cx"> oldOrganizer = self.oldcalendar.getOrganizer()
</span><span class="cx"> newOrganizer = self.calendar.getOrganizer()
</span><span class="cx"> if oldOrganizer != newOrganizer and self.oldcalendar.getOrganizerScheduleAgent():
</span><del>- log.error("Cannot change ORGANIZER: UID:%s" % (self.uid,))
</del><ins>+ log.error("Cannot change ORGANIZER: UID:{uid}", uid=self.uid)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "valid-attendee-change"),
</span><span class="lines">@@ -1223,7 +1222,7 @@
</span><span class="cx">
</span><span class="cx"> # Never allow a missing attendee with a locally hosted organizer
</span><span class="cx"> if isinstance(self.organizerAddress, LocalCalendarUser):
</span><del>- log.error("Cannot remove ATTENDEE: UID:%s" % (self.uid,))
</del><ins>+ log.error("Cannot remove ATTENDEE: UID:{uid}", uid=self.uid)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "valid-attendee-change"),
</span><span class="lines">@@ -1232,7 +1231,7 @@
</span><span class="cx">
</span><span class="cx"> # We will allow the attendee to do anything in this case, but we will mark the organizer
</span><span class="cx"> # with an schedule-status error and schedule-agent none
</span><del>- log.debug("Missing attendee is allowed to update UID: '%s' with invalid organizer '%s'" % (self.uid, self.organizer))
</del><ins>+ log.debug("Missing attendee is allowed to update UID: '{uid}' with invalid organizer '{organizer}'", uid=self.uid, organizer=self.organizer)
</ins><span class="cx">
</span><span class="cx"> # Check SCHEDULE-AGENT and coerce SERVER to NONE
</span><span class="cx"> if self.calendar.getOrganizerScheduleAgent():
</span><span class="lines">@@ -1243,14 +1242,14 @@
</span><span class="cx"> def checkOrganizerScheduleAgent(self):
</span><span class="cx">
</span><span class="cx"> is_server = self.calendar.getOrganizerScheduleAgent()
</span><del>- local_organizer = type(self.organizerAddress) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,)
</del><ins>+ local_organizer = type(self.organizerAddress) in (LocalCalendarUser, OtherServerCalendarUser,)
</ins><span class="cx">
</span><span class="cx"> if config.Scheduling.iMIP.Enabled and self.organizerAddress.cuaddr.lower().startswith("mailto:"):
</span><span class="cx"> return is_server
</span><span class="cx">
</span><span class="cx"> if not config.Scheduling.iSchedule.Enabled and not local_organizer and is_server:
</span><span class="cx"> # Coerce ORGANIZER to SCHEDULE-AGENT=NONE
</span><del>- log.debug("Attendee '%s' is not allowed to use SCHEDULE-AGENT=SERVER on organizer: UID:%s" % (self.attendeePrincipal, self.uid,))
</del><ins>+ log.debug("Attendee '{attendee}' is not allowed to use SCHEDULE-AGENT=SERVER on organizer: UID:{uid}", attendee=self.attendeePrincipal, uid=self.uid)
</ins><span class="cx"> self.calendar.setParameterToValueForPropertyWithValue("SCHEDULE-AGENT", "NONE", "ORGANIZER", None)
</span><span class="cx"> self.calendar.setParameterToValueForPropertyWithValue("SCHEDULE-STATUS", iTIPRequestStatus.NO_USER_SUPPORT_CODE, "ORGANIZER", None)
</span><span class="cx"> is_server = False
</span><span class="lines">@@ -1272,8 +1271,8 @@
</span><span class="cx"> calendar_resource = (yield getCalendarObjectForRecord(self.calendar_home.transaction(), self.organizerPrincipal, self.uid))
</span><span class="cx"> if calendar_resource is not None:
</span><span class="cx"> self.organizer_calendar = (yield calendar_resource.componentForUser())
</span><del>- elif type(self.organizerAddress) in (PartitionedCalendarUser, OtherServerCalendarUser,):
- # For partitioning where the organizer is on a different node, we will assume that the attendee's copy
</del><ins>+ elif type(self.organizerAddress) in (OtherServerCalendarUser,):
+ # For podding where the organizer is on a different node, we will assume that the attendee's copy
</ins><span class="cx"> # of the event is up to date and "authoritative". So we pretend that is the organizer copy
</span><span class="cx"> self.organizer_calendar = self.oldcalendar
</span><span class="cx">
</span><span class="lines">@@ -1290,7 +1289,7 @@
</span><span class="cx"> oldcalendar = self.organizer_calendar
</span><span class="cx"> oldcalendar.attendeesView((self.attendee,), onlyScheduleAgentServer=True)
</span><span class="cx"> if oldcalendar.mainType() is None:
</span><del>- log.debug("Implicit - attendee '%s' cannot use an event they are not an attendee of, UID: '%s'" % (self.attendee, self.uid))
</del><ins>+ log.debug("Implicit - attendee '{attendee}' cannot use an event they are not an attendee of, UID: '{uid}'", attendee=self.attendee, uid=self.uid)
</ins><span class="cx"> raise HTTPError(ErrorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> (caldav_namespace, "valid-attendee-change"),
</span><span class="lines">@@ -1339,7 +1338,7 @@
</span><span class="cx"> def _gotResponse(response):
</span><span class="cx"> self.handleSchedulingResponse(response, False)
</span><span class="cx">
</span><del>- log.info("Implicit %s - attendee: '%s' to organizer: '%s', UID: '%s'" % (action, self.attendee, self.organizer, self.uid,))
</del><ins>+ log.info("Implicit {action} - attendee: '{attendee}' to organizer: '{organizer}', UID: '{uid}'", action=action, attendee=self.attendee, organizer=self.organizer, uid=self.uid)
</ins><span class="cx"> d = scheduler.doSchedulingViaPUT(self.originator, (self.organizer,), itipmsg, internal_request=True)
</span><span class="cx"> d.addCallback(_gotResponse)
</span><span class="cx"> return d
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduledeliverypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/delivery.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/delivery.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/delivery.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -40,8 +40,7 @@
</span><span class="cx"> from twistedcaldav.ical import normalizeCUAddress, Component
</span><span class="cx"> from twistedcaldav.util import utf8String
</span><span class="cx">
</span><del>-from txdav.caldav.datastore.scheduling.cuaddress import PartitionedCalendarUser, RemoteCalendarUser, \
- OtherServerCalendarUser
</del><ins>+from txdav.caldav.datastore.scheduling.cuaddress import RemoteCalendarUser, OtherServerCalendarUser
</ins><span class="cx"> from txdav.caldav.datastore.scheduling.delivery import DeliveryService
</span><span class="cx"> from txdav.caldav.datastore.scheduling.ischedule.dkim import DKIMRequest, DKIMUtils
</span><span class="cx"> from txdav.caldav.datastore.scheduling.ischedule.remoteservers import IScheduleServerRecord
</span><span class="lines">@@ -58,7 +57,7 @@
</span><span class="cx">
</span><span class="cx"> """
</span><span class="cx"> Handles the sending of iSchedule scheduling messages. Used for both cross-domain scheduling,
</span><del>-as well as internal partitioning or podding.
</del><ins>+as well as internal podding.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> __all__ = [
</span><span class="lines">@@ -72,6 +71,7 @@
</span><span class="cx"> class ScheduleViaISchedule(DeliveryService):
</span><span class="cx">
</span><span class="cx"> domainServerMap = {}
</span><ins>+ servermgr = None
</ins><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> def serviceType(cls):
</span><span class="lines">@@ -82,9 +82,7 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def matchCalendarUserAddress(cls, cuaddr):
</span><span class="cx">
</span><del>- # TODO: here is where we would attempt service discovery based on the cuaddr.
-
- # Only handle mailtos:
</del><ins>+ # Handle mailtos:
</ins><span class="cx"> if cuaddr.lower().startswith("mailto:"):
</span><span class="cx"> domain = extractEmailDomain(cuaddr)
</span><span class="cx"> server = (yield cls.serverForDomain(domain))
</span><span class="lines">@@ -100,25 +98,30 @@
</span><span class="cx"> def serverForDomain(cls, domain):
</span><span class="cx"> if domain not in cls.domainServerMap:
</span><span class="cx">
</span><del>- # First check built-in list of remote servers
- servermgr = IScheduleServers()
- server = servermgr.mapDomain(domain)
- if server is not None:
- cls.domainServerMap[domain] = server
- else:
- # Lookup domain
- result = (yield lookupServerViaSRV(domain))
- if result is None:
</del><ins>+ if config.Scheduling.iSchedule.Enabled:
+
+ # First check built-in list of remote servers
+ if cls.servermgr is None:
+ cls.servermgr = IScheduleServers()
+ server = cls.servermgr.mapDomain(domain)
+ if server is not None:
+ cls.domainServerMap[domain] = server
+ else:
</ins><span class="cx"> # Lookup domain
</span><del>- result = (yield lookupServerViaSRV(domain, service="_ischedule"))
</del><ins>+ result = (yield lookupServerViaSRV(domain))
</ins><span class="cx"> if result is None:
</span><del>- cls.domainServerMap[domain] = None
</del><ins>+ # Lookup domain
+ result = (yield lookupServerViaSRV(domain, service="_ischedule"))
+ if result is None:
+ cls.domainServerMap[domain] = None
+ else:
+ # Create the iSchedule server record for this server
+ cls.domainServerMap[domain] = IScheduleServerRecord(uri="http://%s:%s/.well-known/ischedule" % result)
</ins><span class="cx"> else:
</span><span class="cx"> # Create the iSchedule server record for this server
</span><del>- cls.domainServerMap[domain] = IScheduleServerRecord(uri="http://%s:%s/.well-known/ischedule" % result)
- else:
- # Create the iSchedule server record for this server
- cls.domainServerMap[domain] = IScheduleServerRecord(uri="https://%s:%s/.well-known/ischedule" % result)
</del><ins>+ cls.domainServerMap[domain] = IScheduleServerRecord(uri="https://%s:%s/.well-known/ischedule" % result)
+ else:
+ cls.domainServerMap[domain] = None
</ins><span class="cx">
</span><span class="cx"> returnValue(cls.domainServerMap[domain])
</span><span class="cx">
</span><span class="lines">@@ -136,8 +139,6 @@
</span><span class="cx"> if isinstance(recipient, RemoteCalendarUser):
</span><span class="cx"> # Map the recipient's domain to a server
</span><span class="cx"> server = (yield self.serverForDomain(recipient.domain))
</span><del>- elif isinstance(recipient, PartitionedCalendarUser):
- server = self._getServerForPartitionedUser(recipient)
</del><span class="cx"> elif isinstance(recipient, OtherServerCalendarUser):
</span><span class="cx"> server = self._getServerForOtherServerUser(recipient)
</span><span class="cx"> else:
</span><span class="lines">@@ -182,20 +183,6 @@
</span><span class="cx"> yield DeferredList(deferreds)
</span><span class="cx">
</span><span class="cx">
</span><del>- def _getServerForPartitionedUser(self, recipient):
-
- if not hasattr(self, "partitionedServers"):
- self.partitionedServers = {}
-
- partition = recipient.principal.partitionURI()
- if partition not in self.partitionedServers:
- self.partitionedServers[partition] = IScheduleServerRecord(uri=joinURL(partition, "/ischedule"))
- self.partitionedServers[partition].unNormalizeAddresses = False
- self.partitionedServers[partition].moreHeaders.append(recipient.principal.server().secretHeader())
-
- return self.partitionedServers[partition]
-
-
</del><span class="cx"> def _getServerForOtherServerUser(self, recipient):
</span><span class="cx">
</span><span class="cx"> if not hasattr(self, "otherServers"):
</span><span class="lines">@@ -203,9 +190,12 @@
</span><span class="cx">
</span><span class="cx"> serverURI = recipient.principal.serverURI()
</span><span class="cx"> if serverURI not in self.otherServers:
</span><del>- self.otherServers[serverURI] = IScheduleServerRecord(uri=joinURL(serverURI, "/ischedule"))
- self.otherServers[serverURI].unNormalizeAddresses = not recipient.principal.server().isImplicit
- self.otherServers[serverURI].moreHeaders.append(recipient.principal.server().secretHeader())
</del><ins>+ self.otherServers[serverURI] = IScheduleServerRecord(
+ uri=joinURL(serverURI, config.Servers.InboxName),
+ unNormalizeAddresses=not recipient.principal.server().isImplicit,
+ moreHeaders=[recipient.principal.server().secretHeader(), ],
+ podding=True,
+ )
</ins><span class="cx">
</span><span class="cx"> return self.otherServers[serverURI]
</span><span class="cx">
</span><span class="lines">@@ -222,6 +212,7 @@
</span><span class="cx"> self.refreshOnly = refreshOnly
</span><span class="cx"> self.headers = None
</span><span class="cx"> self.data = None
</span><ins>+ self.original_organizer = None
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -365,7 +356,8 @@
</span><span class="cx">
</span><span class="cx"> # The Originator must be the ORGANIZER (for a request) or ATTENDEE (for a reply)
</span><span class="cx"> originator = self.scheduler.organizer.cuaddr if self.scheduler.isiTIPRequest else self.scheduler.attendee
</span><del>- originator = normalizeCUAddress(originator, normalizationLookup, self.scheduler.txn.directoryService().recordWithCalendarUserAddress, toUUID=False)
</del><ins>+ if self.server.unNormalizeAddresses:
+ originator = normalizeCUAddress(originator, normalizationLookup, self.scheduler.txn.directoryService().recordWithCalendarUserAddress, toUUID=False)
</ins><span class="cx"> self.headers.addRawHeader("Originator", utf8String(originator))
</span><span class="cx"> self.sign_headers.append("Originator")
</span><span class="cx">
</span><span class="lines">@@ -414,15 +406,15 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> if self.data is None:
</span><ins>+
</ins><span class="cx"> # Need to remap cuaddrs from urn:uuid
</span><del>- if self.server.unNormalizeAddresses and self.scheduler.method == "PUT":
- normalizedCalendar = self.scheduler.calendar.duplicate()
</del><ins>+ normalizedCalendar = self.scheduler.calendar.duplicate()
+ self.original_organizer = normalizedCalendar.getOrganizer()
+ if self.server.unNormalizeAddresses:
</ins><span class="cx"> normalizedCalendar.normalizeCalendarUserAddresses(
</span><span class="cx"> normalizationLookup,
</span><span class="cx"> self.scheduler.txn.directoryService().recordWithCalendarUserAddress,
</span><span class="cx"> toUUID=False)
</span><del>- else:
- normalizedCalendar = self.scheduler.calendar
</del><span class="cx">
</span><span class="cx"> # For VFREEBUSY we need to strip out ATTENDEEs that do not match the recipient list
</span><span class="cx"> if self.scheduler.isfreebusy:
</span><span class="lines">@@ -445,13 +437,12 @@
</span><span class="cx"> f = Factory()
</span><span class="cx"> f.protocol = HTTPClientProtocol
</span><span class="cx"> if ssl:
</span><del>- ep = GAIEndpoint(reactor, host, port,
- _configuredClientContextFactory())
</del><ins>+ ep = GAIEndpoint(reactor, host, port, _configuredClientContextFactory())
</ins><span class="cx"> else:
</span><span class="cx"> ep = GAIEndpoint(reactor, host, port)
</span><span class="cx"> proto = (yield ep.connect(f))
</span><span class="cx">
</span><del>- if config.Scheduling.iSchedule.DKIM.Enabled:
</del><ins>+ if not self.server.podding() and config.Scheduling.iSchedule.DKIM.Enabled:
</ins><span class="cx"> domain, selector, key_file, algorithm, useDNSKey, useHTTPKey, usePrivateExchangeKey, expire = DKIMUtils.getConfiguration(config)
</span><span class="cx"> request = DKIMRequest(
</span><span class="cx"> "POST",
</span><span class="lines">@@ -503,6 +494,14 @@
</span><span class="cx"> calendar_data = response.childOfType(CalendarData)
</span><span class="cx"> if calendar_data:
</span><span class="cx"> calendar_data = str(calendar_data)
</span><ins>+ if self.server.unNormalizeAddresses and self.original_organizer is not None:
+ # Need to restore original ORGANIZER value if it got unnormalized
+ calendar = Component.fromString(calendar_data)
+ organizers = calendar.getAllPropertiesInAnyComponent("ORGANIZER", depth=1)
+ for organizer in organizers:
+ organizer.setValue(self.original_organizer)
+ calendar_data = str(calendar)
+
</ins><span class="cx"> error = response.childOfType(Error)
</span><span class="cx"> if error:
</span><span class="cx"> error = error.children
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischedulelocalserverspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/localservers.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/localservers.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/localservers.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -31,18 +31,12 @@
</span><span class="cx"> the principals across the whole domain need to be able to directly schedule each other and know of each others
</span><span class="cx"> existence. A common scenario would be a production server and a development/test server.
</span><span class="cx">
</span><del>-Each server is identified by an id and url. The id is used when assigning principals to a specific server. Each
-server can also support multiple partitions, and each of those is identified by an id and url, with the id also
-being used to assign principals to a specific partition.
</del><ins>+Each server is identified by an id and url. The id is used when assigning principals to a specific server.
</ins><span class="cx">
</span><del>-These servers support the concept of "partitioning" and "podding".
</del><ins>+These servers support the concept of "podding".
</ins><span class="cx">
</span><del>-A "partitioned" service is one that spreads its
-users out across multiple stores and does reverse proxying of incoming requests to the appropriate partitioned host.
-All servers within the same partition have to be running the same version of the software etc.
-
</del><span class="cx"> A "podded" service is one where different groups of users are hosted on different servers, which may be of
</span><del>-different versions etc. A "pod" may itself be "partitioned", but the partitioning is "invisible" to the outside world.
</del><ins>+different versions etc.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> __all__ = [
</span><span class="lines">@@ -104,13 +98,31 @@
</span><span class="cx"> def getThisServer(self):
</span><span class="cx"> return self._thisServer
</span><span class="cx">
</span><ins>+
+ def installReverseProxies(self, maxClients):
+ """
+ Install a reverse proxy for each of the other servers in the "pod".
+
+ @param maxClients: maximum number of clients in the pool.
+ @type maxClients: C{int}
+ """
+
+ for server in self._servers.values():
+ if server.thisServer:
+ continue
+ installPool(
+ server.id,
+ server.uri,
+ maxClients,
+ )
+
</ins><span class="cx"> Servers = ServersDB() # Global server DB
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class Server(object):
</span><span class="cx"> """
</span><del>- Represents a server which may itself be partitioned.
</del><ins>+ Represents a server.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def __init__(self):
</span><span class="lines">@@ -120,8 +132,6 @@
</span><span class="cx"> self.ips = set()
</span><span class="cx"> self.allowed_from_ips = set()
</span><span class="cx"> self.shared_secret = None
</span><del>- self.partitions = {}
- self.partitions_ips = set()
</del><span class="cx"> self.isImplicit = True
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -164,25 +174,12 @@
</span><span class="cx"> actual_ips.add(item)
</span><span class="cx"> self.allowed_from_ips = actual_ips
</span><span class="cx">
</span><del>- for uri in self.partitions.values():
- parsed_uri = urlparse.urlparse(uri)
- try:
- ips = getIPsFromHost(parsed_uri.hostname)
- except socket.gaierror, e:
- msg = "Unable to lookup ip-addr for partition '%s': %s" % (parsed_uri.hostname, str(e))
- log.error(msg)
- if ignoreIPLookupFailures:
- ips = ()
- else:
- raise ValueError(msg)
- self.partitions_ips.update(ips)
</del><span class="cx">
</span><del>-
</del><span class="cx"> def checkThisIP(self, ip):
</span><span class="cx"> """
</span><del>- Check that the passed in IP address corresponds to this server or one of its partitions.
</del><ins>+ Check that the passed in IP address corresponds to this server.
</ins><span class="cx"> """
</span><del>- return (ip in self.ips) or (ip in self.partitions_ips)
</del><ins>+ return (ip in self.ips)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def hasAllowedFromIP(self):
</span><span class="lines">@@ -218,38 +215,13 @@
</span><span class="cx"> return (SERVER_SECRET_HEADER, self.shared_secret,)
</span><span class="cx">
</span><span class="cx">
</span><del>- def addPartition(self, id, uri):
- self.partitions[id] = uri
</del><span class="cx">
</span><del>-
- def getPartitionURIForId(self, id):
- return self.partitions.get(id)
-
-
- def isPartitioned(self):
- return len(self.partitions) != 0
-
-
- def installReverseProxies(self, ownUID, maxClients):
-
- for partition, url in self.partitions.iteritems():
- if partition != ownUID:
- installPool(
- partition,
- url,
- maxClients,
- )
-
-
-
</del><span class="cx"> ELEMENT_SERVERS = "servers"
</span><span class="cx"> ELEMENT_SERVER = "server"
</span><span class="cx"> ELEMENT_ID = "id"
</span><span class="cx"> ELEMENT_URI = "uri"
</span><span class="cx"> ELEMENT_ALLOWED_FROM = "allowed-from"
</span><span class="cx"> ELEMENT_SHARED_SECRET = "shared-secret"
</span><del>-ELEMENT_PARTITIONS = "partitions"
-ELEMENT_PARTITION = "partition"
</del><span class="cx"> ATTR_IMPLICIT = "implicit"
</span><span class="cx"> ATTR_VALUE_YES = "yes"
</span><span class="cx"> ATTR_VALUE_NO = "no"
</span><span class="lines">@@ -286,39 +258,13 @@
</span><span class="cx"> server.allowed_from_ips.add(node.text)
</span><span class="cx"> elif node.tag == ELEMENT_SHARED_SECRET:
</span><span class="cx"> server.shared_secret = node.text
</span><del>- elif node.tag == ELEMENT_PARTITIONS:
- ServersParser._parsePartition(xmlFile, node, server)
</del><span class="cx"> else:
</span><span class="cx"> raise RuntimeError("Invalid element '%s' in servers file: '%s'" % (node.tag, xmlFile,))
</span><span class="cx">
</span><span class="cx"> if server.id is None or server.uri is None:
</span><del>- raise RuntimeError("Invalid partition '%s' in servers file: '%s'" % (child.tag, xmlFile,))
</del><ins>+ raise RuntimeError("Invalid server '%s' in servers file: '%s'" % (child.tag, xmlFile,))
</ins><span class="cx">
</span><span class="cx"> server.check(ignoreIPLookupFailures=ignoreIPLookupFailures)
</span><span class="cx"> results[server.id] = server
</span><span class="cx">
</span><span class="cx"> return results
</span><del>-
-
- @staticmethod
- def _parsePartition(xmlFile, partitions, server):
-
- for child in partitions:
-
- if child.tag != ELEMENT_PARTITION:
- raise RuntimeError("Unknown partition type: '%s' in servers file: '%s'" % (child.tag, xmlFile,))
-
- id = None
- uri = None
- for node in child:
- if node.tag == ELEMENT_ID:
- id = node.text
- elif node.tag == ELEMENT_URI:
- uri = node.text
- else:
- raise RuntimeError("Invalid element '%s' in augment file: '%s'" % (node.tag, xmlFile,))
-
- if id is None or uri is None:
- raise RuntimeError("Invalid partition '%s' in servers file: '%s'" % (child.tag, xmlFile,))
-
- server.addPartition(id, uri)
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduleremoteserverspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -23,7 +23,7 @@
</span><span class="cx">
</span><span class="cx"> """
</span><span class="cx"> XML based iSchedule configuration file handling. This is for handling of remote servers. The localservers.py module
</span><del>-handles servers that are local (partitioned or podded).
</del><ins>+handles servers that are local (podded).
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> __all__ = [
</span><span class="lines">@@ -138,7 +138,7 @@
</span><span class="cx"> """
</span><span class="cx"> Contains server-to-server details.
</span><span class="cx"> """
</span><del>- def __init__(self, uri=None):
</del><ins>+ def __init__(self, uri=None, unNormalizeAddresses=True, moreHeaders=[], podding=False):
</ins><span class="cx"> """
</span><span class="cx"> @param recordType: record type for directory entry.
</span><span class="cx"> """
</span><span class="lines">@@ -148,8 +148,9 @@
</span><span class="cx"> self.allow_to = True
</span><span class="cx"> self.domains = []
</span><span class="cx"> self.client_hosts = []
</span><del>- self.unNormalizeAddresses = True
- self.moreHeaders = []
</del><ins>+ self.unNormalizeAddresses = unNormalizeAddresses
+ self.moreHeaders = moreHeaders
+ self._podding = podding
</ins><span class="cx">
</span><span class="cx"> if uri:
</span><span class="cx"> self.uri = uri
</span><span class="lines">@@ -160,6 +161,10 @@
</span><span class="cx"> return (self.ssl, self.host, self.port, self.path,)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def podding(self):
+ return self._podding
+
+
</ins><span class="cx"> def redirect(self, location):
</span><span class="cx"> """
</span><span class="cx"> Permanent redirect for the lifetime of this record.
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduleresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/resource.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/resource.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/resource.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,8 +14,8 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twext.web2 import responsecode
</span><span class="cx"> from twext.web2.dav.http import ErrorResponse
</span><span class="lines">@@ -52,7 +52,7 @@
</span><span class="cx"> Extends L{DAVResource} to provide iSchedule inbox functionality.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def __init__(self, parent, store):
</del><ins>+ def __init__(self, parent, store, podding=False):
</ins><span class="cx"> """
</span><span class="cx"> @param parent: the parent resource of this one.
</span><span class="cx"> """
</span><span class="lines">@@ -62,6 +62,7 @@
</span><span class="cx">
</span><span class="cx"> self.parent = parent
</span><span class="cx"> self._newStore = store
</span><ins>+ self._podding = podding
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def deadProperties(self):
</span><span class="lines">@@ -109,12 +110,12 @@
</span><span class="cx"> def render(self, request):
</span><span class="cx"> output = """<html>
</span><span class="cx"> <head>
</span><del>-<title>Server To Server Inbox Resource</title>
</del><ins>+<title>%(rtype)s Inbox Resource</title>
</ins><span class="cx"> </head>
</span><span class="cx"> <body>
</span><del>-<h1>Server To Server Inbox Resource.</h1>
</del><ins>+<h1>%(rtype)s Inbox Resource.</h1>
</ins><span class="cx"> </body
</span><del>-</html>"""
</del><ins>+</html>""" % {"rtype" : "Podding" if self._podding else "iSchedule", }
</ins><span class="cx">
</span><span class="cx"> response = Response(200, {}, output)
</span><span class="cx"> response.headers.setHeader("content-type", MimeType("text", "html"))
</span><span class="lines">@@ -126,7 +127,7 @@
</span><span class="cx"> The iSchedule GET method.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- if not request.args:
</del><ins>+ if not request.args or self._podding:
</ins><span class="cx"> # Do normal GET behavior
</span><span class="cx"> return self.render(request)
</span><span class="cx">
</span><span class="lines">@@ -157,12 +158,26 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Determine min/max date-time for iSchedule
</span><del>- now = PyCalendarDateTime.getNowUTC()
- minDateTime = PyCalendarDateTime(now.getYear(), 1, 1, 0, 0, 0, PyCalendarTimezone(utc=True))
</del><ins>+ now = DateTime.getNowUTC()
+ minDateTime = DateTime(now.getYear(), 1, 1, 0, 0, 0, Timezone(utc=True))
</ins><span class="cx"> minDateTime.offsetYear(-1)
</span><del>- maxDateTime = PyCalendarDateTime(now.getYear(), 1, 1, 0, 0, 0, PyCalendarTimezone(utc=True))
</del><ins>+ maxDateTime = DateTime(now.getYear(), 1, 1, 0, 0, 0, Timezone(utc=True))
</ins><span class="cx"> maxDateTime.offsetYear(10)
</span><span class="cx">
</span><ins>+ dataTypes = []
+ dataTypes.append(
+ ischedulexml.CalendarDataType(**{
+ "content-type": "text/calendar",
+ "version": "2.0",
+ })
+ )
+ if config.EnableJSONData:
+ dataTypes.append(
+ ischedulexml.CalendarDataType(**{
+ "content-type": "application/calendar+json",
+ "version": "2.0",
+ })
+ )
</ins><span class="cx"> result = ischedulexml.QueryResult(
</span><span class="cx">
</span><span class="cx"> ischedulexml.Capabilities(
</span><span class="lines">@@ -188,12 +203,7 @@
</span><span class="cx"> name="VFREEBUSY"
</span><span class="cx"> ),
</span><span class="cx"> ),
</span><del>- ischedulexml.CalendarDataTypes(
- ischedulexml.CalendarDataType(**{
- "content-type": "text/calendar",
- "version": "2.0",
- }),
- ),
</del><ins>+ ischedulexml.CalendarDataTypes(*dataTypes),
</ins><span class="cx"> ischedulexml.Attachments(
</span><span class="cx"> ischedulexml.External(),
</span><span class="cx"> ),
</span><span class="lines">@@ -220,26 +230,51 @@
</span><span class="cx"> txn = transactionFromRequest(request, self._newStore)
</span><span class="cx">
</span><span class="cx"> # This is a server-to-server scheduling operation.
</span><del>- scheduler = IScheduleScheduler(txn, None)
</del><ins>+ scheduler = IScheduleScheduler(txn, None, podding=self._podding)
</ins><span class="cx">
</span><ins>+ # Check content first
+ contentType = request.headers.getHeader("content-type")
+ format = self.determineType(contentType)
+
+ if format is None:
+ msg = "MIME type %s not allowed in iSchedule request" % (contentType,)
+ self.log.error(msg)
+ raise HTTPError(scheduler.errorResponse(
+ responsecode.FORBIDDEN,
+ (ischedule_namespace, "invalid-calendar-data-type"),
+ msg,
+ ))
+
</ins><span class="cx"> originator = self.loadOriginatorFromRequestHeaders(request)
</span><span class="cx"> recipients = self.loadRecipientsFromRequestHeaders(request)
</span><span class="cx"> body = (yield allDataFromStream(request.stream))
</span><ins>+ calendar = Component.fromString(body, format=format)
</ins><span class="cx">
</span><span class="cx"> # Do the POST processing treating this as a non-local schedule
</span><span class="cx"> try:
</span><del>- result = (yield scheduler.doSchedulingViaPOST(request.remoteAddr, request.headers, body, originator, recipients))
</del><ins>+ result = (yield scheduler.doSchedulingViaPOST(request.remoteAddr, request.headers, body, calendar, originator, recipients))
</ins><span class="cx"> except Exception:
</span><span class="cx"> ex = Failure()
</span><span class="cx"> yield txn.abort()
</span><span class="cx"> ex.raiseException()
</span><span class="cx"> else:
</span><span class="cx"> yield txn.commit()
</span><del>- response = result.response()
- response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
</del><ins>+ response = result.response(format=format)
+ if not self._podding:
+ response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
</ins><span class="cx"> returnValue(response)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def determineType(self, content_type):
+ """
+ Determine if the supplied content-type is valid for storing and return the matching PyCalendar type.
+ """
+ format = None
+ if content_type is not None:
+ format = "%s/%s" % (content_type.mediaType, content_type.mediaSubtype,)
+ return format if format in Component.allowedTypes() else None
+
+
</ins><span class="cx"> def loadOriginatorFromRequestHeaders(self, request):
</span><span class="cx"> # Must have Originator header
</span><span class="cx"> originator = request.headers.getRawHeaders("originator")
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduleschedulerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/scheduler.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/scheduler.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/scheduler.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -23,7 +23,7 @@
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.config import config
</span><del>-from twistedcaldav.ical import normalizeCUAddress, Component
</del><ins>+from twistedcaldav.ical import normalizeCUAddress
</ins><span class="cx">
</span><span class="cx"> from txdav.caldav.datastore.scheduling import addressmapping
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import RemoteCalendarUser
</span><span class="lines">@@ -119,6 +119,9 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class IScheduleScheduler(RemoteScheduler):
</span><ins>+ """
+ Handles iSchedule and podding requests.
+ """
</ins><span class="cx">
</span><span class="cx"> scheduleResponse = IScheduleResponseQueue
</span><span class="cx">
</span><span class="lines">@@ -138,8 +141,13 @@
</span><span class="cx"> "max-recipients": (ischedule_namespace, "max-recipients"),
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ def __init__(self, txn, originator_uid, logItems=None, noAttendeeRefresh=False, podding=False):
+ super(IScheduleScheduler, self).__init__(txn, originator_uid, logItems=logItems, noAttendeeRefresh=noAttendeeRefresh)
+ self._podding = podding
+
+
</ins><span class="cx"> @inlineCallbacks
</span><del>- def doSchedulingViaPOST(self, remoteAddr, headers, body, originator, recipients):
</del><ins>+ def doSchedulingViaPOST(self, remoteAddr, headers, body, calendar, originator, recipients):
</ins><span class="cx"> """
</span><span class="cx"> Carry out iSchedule specific processing.
</span><span class="cx"> """
</span><span class="lines">@@ -148,7 +156,7 @@
</span><span class="cx"> self.headers = headers
</span><span class="cx"> self.verified = False
</span><span class="cx">
</span><del>- if config.Scheduling.iSchedule.DKIM.Enabled:
</del><ins>+ if not self._podding and config.Scheduling.iSchedule.DKIM.Enabled:
</ins><span class="cx"> verifier = DKIMVerifier(self.headers, body, protocol_debug=config.Scheduling.iSchedule.DKIM.ProtocolDebug)
</span><span class="cx"> try:
</span><span class="cx"> yield verifier.verify()
</span><span class="lines">@@ -172,13 +180,16 @@
</span><span class="cx"> msg,
</span><span class="cx"> ))
</span><span class="cx">
</span><del>- calendar = Component.fromString(body)
-
- if self.headers.getRawHeaders('x-calendarserver-itip-refreshonly', ("F"))[0] == "T":
</del><ins>+ if self._podding and self.headers.getRawHeaders('x-calendarserver-itip-refreshonly', ("F"))[0] == "T":
</ins><span class="cx"> self.txn.doing_attendee_refresh = 1
</span><span class="cx">
</span><span class="cx"> # Normalize recipient addresses
</span><del>- recipients = [normalizeCUAddress(recipient, normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress) for recipient in recipients]
</del><ins>+ results = []
+ for recipient in recipients:
+ normalized = normalizeCUAddress(recipient, normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress)
+ self.recipientsNormalizationMap[normalized] = recipient
+ results.append(normalized)
+ recipients = results
</ins><span class="cx">
</span><span class="cx"> result = (yield super(IScheduleScheduler, self).doSchedulingViaPOST(originator, recipients, calendar))
</span><span class="cx"> returnValue(result)
</span><span class="lines">@@ -218,7 +229,7 @@
</span><span class="cx"> originatorPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.originator)
</span><span class="cx"> localUser = (yield addressmapping.mapper.isCalendarUserInMyDomain(self.originator))
</span><span class="cx"> if originatorPrincipal or localUser:
</span><del>- if originatorPrincipal.locallyHosted():
</del><ins>+ if originatorPrincipal.thisServer():
</ins><span class="cx"> log.error("Cannot use originator that is on this server: %s" % (self.originator,))
</span><span class="cx"> raise HTTPError(self.errorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="lines">@@ -296,23 +307,17 @@
</span><span class="cx">
</span><span class="cx"> def _validAlternateServer(self, principal):
</span><span class="cx"> """
</span><del>- Check the validity of the partitioned host.
</del><ins>+ Check the validity of the podded host.
</ins><span class="cx"> """
</span><span class="cx">
</span><del>- # Extract expected host/port. This will be the partitionURI, or if no partitions,
- # the serverURI
- expected_uri = principal.partitionURI()
- if expected_uri is None:
- expected_uri = principal.serverURI()
</del><ins>+ # Extract expected host/port. This will be the serverURI.
+ expected_uri = principal.serverURI()
</ins><span class="cx"> expected_uri = urlparse.urlparse(expected_uri)
</span><span class="cx">
</span><span class="cx"> # Get the request IP and map to hostname.
</span><span class="cx"> clientip = self.remoteAddr.host
</span><span class="cx">
</span><del>- # Check against this server (or any of its partitions). We need this because an external iTIP message
- # may be addressed to users on different partitions, and the node receiving the iTIP message will need to
- # forward it to the partition nodes, thus the client ip seen by the partitions will in fact be the initial
- # receiving node.
</del><ins>+ # Check against this server.
</ins><span class="cx"> matched = False
</span><span class="cx"> if Servers.getThisServer().checkThisIP(clientip):
</span><span class="cx"> matched = True
</span><span class="lines">@@ -364,7 +369,7 @@
</span><span class="cx"> if organizer:
</span><span class="cx"> organizerPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(organizer)
</span><span class="cx"> if organizerPrincipal:
</span><del>- if organizerPrincipal.locallyHosted():
</del><ins>+ if organizerPrincipal.thisServer():
</ins><span class="cx"> log.error("Invalid ORGANIZER in calendar data: %s" % (self.calendar,))
</span><span class="cx"> raise HTTPError(self.errorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="lines">@@ -372,7 +377,7 @@
</span><span class="cx"> "Organizer is not local to server",
</span><span class="cx"> ))
</span><span class="cx"> else:
</span><del>- # Check that the origin server is the correct partition
</del><ins>+ # Check that the origin server is the correct pod
</ins><span class="cx"> self.organizer = calendarUserFromPrincipal(organizer, organizerPrincipal)
</span><span class="cx"> self._validAlternateServer(self.organizer.principal)
</span><span class="cx"> else:
</span><span class="lines">@@ -405,7 +410,7 @@
</span><span class="cx"> # Attendee cannot be local.
</span><span class="cx"> attendeePrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.attendee)
</span><span class="cx"> if attendeePrincipal:
</span><del>- if attendeePrincipal.locallyHosted():
</del><ins>+ if attendeePrincipal.thisServer():
</ins><span class="cx"> log.error("Invalid ATTENDEE in calendar data: %s" % (self.calendar,))
</span><span class="cx"> raise HTTPError(self.errorResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduletesttest_deliverypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -42,6 +42,7 @@
</span><span class="cx"> Make sure we do an exact comparison on EmailDomain
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ self.patch(config.Scheduling.iSchedule, "Enabled", True)
</ins><span class="cx"> self.patch(config.Scheduling.iSchedule, "RemoteServers", "")
</span><span class="cx">
</span><span class="cx"> # Only mailtos:
</span><span class="lines">@@ -64,3 +65,9 @@
</span><span class="cx"> self.assertFalse(result)
</span><span class="cx"> result = yield ScheduleViaISchedule.matchCalendarUserAddress("mailto:user")
</span><span class="cx"> self.assertFalse(result)
</span><ins>+
+ # Test when not enabled
+ ScheduleViaISchedule.domainServerMap = {}
+ self.patch(config.Scheduling.iSchedule, "Enabled", False)
+ result = yield ScheduleViaISchedule.matchCalendarUserAddress("mailto:user@example.com")
+ self.assertFalse(result)
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduletesttest_localserverspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -37,16 +37,6 @@
</span><span class="cx"> <server>
</span><span class="cx"> <id>00002</id>
</span><span class="cx"> <uri>https://caldav2.example.com:8843</uri>
</span><del>- <partitions>
- <partition>
- <id>A</id>
- <uri>https://machine1.example.com:8443</uri>
- </partition>
- <partition>
- <id>B</id>
- <uri>https://machine2.example.com:8443</uri>
- </partition>
- </partitions>
</del><span class="cx"> </server>
</span><span class="cx"> </servers>
</span><span class="cx"> """
</span><span class="lines">@@ -62,16 +52,6 @@
</span><span class="cx"> <server>
</span><span class="cx"> <id>00002</id>
</span><span class="cx"> <uri>https://caldav2.example.com:8843</uri>
</span><del>- <partitions>
- <partition>
- <id>A</id>
- <uri>https://machine1.example.com:8443</uri>
- </partition>
- <partition>
- <id>B</id>
- <uri>https://machine2.example.com:8443</uri>
- </partition>
- </partitions>
</del><span class="cx"> </server>
</span><span class="cx"> </servers>
</span><span class="cx"> """
</span><span class="lines">@@ -103,13 +83,7 @@
</span><span class="cx"> self.assertEqual(servers.getServerById("00001").shared_secret, "foobar")
</span><span class="cx"> self.assertEqual(servers.getServerById("00002").shared_secret, None)
</span><span class="cx">
</span><del>- self.assertEqual(len(servers.getServerById("00001").partitions), 0)
- self.assertEqual(len(servers.getServerById("00002").partitions), 2)
</del><span class="cx">
</span><del>- self.assertEqual(servers.getServerById("00002").getPartitionURIForId("A"), "https://machine1.example.com:8443")
- self.assertEqual(servers.getServerById("00002").getPartitionURIForId("B"), "https://machine2.example.com:8443")
-
-
</del><span class="cx"> def test_this_server(self):
</span><span class="cx">
</span><span class="cx"> servers = self._setupServers()
</span><span class="lines">@@ -129,14 +103,6 @@
</span><span class="cx"> self.assertTrue(servers.getServerById("00002").thisServer)
</span><span class="cx">
</span><span class="cx">
</span><del>- def test_check_is_partitioned(self):
-
- servers = self._setupServers()
-
- self.assertFalse(servers.getServerById("00001").isPartitioned())
- self.assertTrue(servers.getServerById("00002").isPartitioned())
-
-
</del><span class="cx"> def test_check_this_ip(self):
</span><span class="cx">
</span><span class="cx"> servers = self._setupServers()
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingischeduletesttest_resourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -94,6 +94,7 @@
</span><span class="cx"> headers=http_headers.Headers(rawHeaders={
</span><span class="cx"> "Originator": ("mailto:wsanchez@example.com",),
</span><span class="cx"> "Recipient": ("mailto:cdaboo@example.com",),
</span><ins>+ "Content-Type": "text/calendar",
</ins><span class="cx"> }),
</span><span class="cx"> content="""BEGIN:VCALENDAR
</span><span class="cx"> CALSCALE:GREGORIAN
</span><span class="lines">@@ -144,6 +145,7 @@
</span><span class="cx"> headers=http_headers.Headers(rawHeaders={
</span><span class="cx"> "Originator": ("mailto:user01@example.org",),
</span><span class="cx"> "Recipient": ("mailto:user02@example.com",),
</span><ins>+ "Content-Type": ("text/calendar",)
</ins><span class="cx"> }),
</span><span class="cx"> content="""BEGIN:VCALENDAR
</span><span class="cx"> CALSCALE:GREGORIAN
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingitippy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/itip.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/itip.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/itip.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -33,7 +33,7 @@
</span><span class="cx"> from twistedcaldav.ical import Property, iCalendarProductID, Component, \
</span><span class="cx"> ignoredComponents
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -402,9 +402,14 @@
</span><span class="cx"> @staticmethod
</span><span class="cx"> def updateAttendeeData(from_component, to_component):
</span><span class="cx"> """
</span><ins>+ Called when processing a REPLY only.
+
</ins><span class="cx"> Copy the PARTSTAT of the Attendee in the from_component to the matching ATTENDEE
</span><span class="cx"> in the to_component. Ignore if no match found. Also update the private comments.
</span><span class="cx">
</span><ins>+ For VPOLL we need to copy POLL-ITEM-ID response values into the actual matching
+ polled sub-components as VOTER properties.
+
</ins><span class="cx"> @param from_component: component to copy from
</span><span class="cx"> @type from_component: L{Component}
</span><span class="cx"> @param to_component: component to copy to
</span><span class="lines">@@ -423,7 +428,7 @@
</span><span class="cx"> reqstatus = "2.0"
</span><span class="cx">
</span><span class="cx"> # Get attendee in from_component - there MUST be only one
</span><del>- attendees = tuple(from_component.properties("ATTENDEE"))
</del><ins>+ attendees = tuple(from_component.properties(from_component.recipientPropertyName()))
</ins><span class="cx"> if len(attendees) != 1:
</span><span class="cx"> log.error("There must be one and only one ATTENDEE property in a REPLY\n%s" % (str(from_component),))
</span><span class="cx"> return None, False, False
</span><span class="lines">@@ -471,16 +476,9 @@
</span><span class="cx"> pass
</span><span class="cx">
</span><span class="cx"> elif attendee_comment is None and private_comment is not None:
</span><del>- # Remove all property parameters
- private_comment.removeAllParameters()
</del><ins>+ # We now remove the private comment on the organizer's side if the attendee removed it
+ to_component.removeProperty(private_comment)
</ins><span class="cx">
</span><del>- # Add default parameters
- private_comment.setParameter("X-CALENDARSERVER-ATTENDEE-REF", attendee.value())
- private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", PyCalendarDateTime.getNowUTC().getText())
-
- # Set value empty
- private_comment.setValue("")
-
</del><span class="cx"> private_comment_changed = True
</span><span class="cx">
</span><span class="cx"> elif attendee_comment is not None and private_comment is None:
</span><span class="lines">@@ -491,7 +489,7 @@
</span><span class="cx"> attendee_comment.value(),
</span><span class="cx"> params={
</span><span class="cx"> "X-CALENDARSERVER-ATTENDEE-REF": attendee.value(),
</span><del>- "X-CALENDARSERVER-DTSTAMP": PyCalendarDateTime.getNowUTC().getText(),
</del><ins>+ "X-CALENDARSERVER-DTSTAMP": DateTime.getNowUTC().getText(),
</ins><span class="cx"> }
</span><span class="cx"> )
</span><span class="cx"> to_component.addProperty(private_comment)
</span><span class="lines">@@ -506,17 +504,60 @@
</span><span class="cx">
</span><span class="cx"> # Add default parameters
</span><span class="cx"> private_comment.setParameter("X-CALENDARSERVER-ATTENDEE-REF", attendee.value())
</span><del>- private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", PyCalendarDateTime.getNowUTC().getText())
</del><ins>+ private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", DateTime.getNowUTC().getText())
</ins><span class="cx">
</span><span class="cx"> # Set new value
</span><span class="cx"> private_comment.setValue(attendee_comment.value())
</span><span class="cx">
</span><span class="cx"> private_comment_changed = True
</span><span class="cx">
</span><ins>+ # Do VPOLL transfer
+ if from_component.name() == "VPOLL":
+ # TODO: figure out how to report changes back
+ iTipProcessing.updateVPOLLData(from_component, to_component, attendee)
+
</ins><span class="cx"> return attendee.value(), partstat_changed, private_comment_changed
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @staticmethod
</span><ins>+ def updateVPOLLData(from_component, to_component, attendee):
+ """
+ Update VPOLL sub-components with voter's response.
+
+ @param from_component: component to copy from
+ @type from_component: L{Component}
+ @param to_component: component to copy to
+ @type to_component: L{Component}
+ @param attendee: attendee being processed
+ @type attendee: L{Property}
+ """
+
+ responses = {}
+ for prop in from_component.properties("POLL-ITEM-ID"):
+ responses[prop.value()] = prop
+
+ for component in to_component.subcomponents():
+ if component.name() in ignoredComponents:
+ continue
+ poll_item_id = component.propertyValue("POLL-ITEM-ID")
+ if poll_item_id is None:
+ continue
+ voter = component.getVoterProperty((attendee.value(),))
+
+ # If no response - remove
+ if poll_item_id not in responses or not responses[poll_item_id].hasParameter("RESPONSE"):
+ if voter is not None:
+ component.removeProperty(voter)
+ continue
+
+ # Add or update voter
+ if voter is None:
+ voter = Property("VOTER", attendee.value())
+ component.addProperty(voter)
+ voter.setParameter("RESPONSE", responses[poll_item_id].parameterValue("RESPONSE"))
+
+
+ @staticmethod
</ins><span class="cx"> def transferItems(from_calendar, to_component, master_valarms, private_comments, transps, completeds, organizer_schedule_status, attendee_dtstamp, other_props, recipient, remove_matched=False):
</span><span class="cx"> """
</span><span class="cx"> Transfer properties from a calendar to a component by first trying to match the component in the original calendar and
</span><span class="lines">@@ -854,7 +895,7 @@
</span><span class="cx"> itip.filterComponents(changedRids)
</span><span class="cx">
</span><span class="cx"> # Force update to DTSTAMP everywhere so reply sequencing will work
</span><del>- itip.replacePropertyInAllComponents(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
</del><ins>+ itip.replacePropertyInAllComponents(Property("DTSTAMP", DateTime.getNowUTC()))
</ins><span class="cx">
</span><span class="cx"> # Remove all attendees except the one we want
</span><span class="cx"> itip.removeAllButOneAttendee(attendee)
</span><span class="lines">@@ -884,6 +925,7 @@
</span><span class="cx"> "EXDATE",
</span><span class="cx"> "ORGANIZER",
</span><span class="cx"> "ATTENDEE",
</span><ins>+ "VOTER",
</ins><span class="cx"> "X-CALENDARSERVER-PRIVATE-COMMENT",
</span><span class="cx"> "SUMMARY",
</span><span class="cx"> "LOCATION",
</span><span class="lines">@@ -903,10 +945,38 @@
</span><span class="cx"> # Strip out unwanted bits
</span><span class="cx"> iTipGenerator.prepareSchedulingMessage(itip, reply=True)
</span><span class="cx">
</span><ins>+ # Handle VPOLL behavior
+ for component in itip.subcomponents():
+ if component.name() == "VPOLL":
+ iTipGenerator.generateVPOLLReply(component, attendee)
+
</ins><span class="cx"> return itip
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @staticmethod
</span><ins>+ def generateVPOLLReply(vpoll, attendee):
+ """
+ Generate the proper poll response in a reply for each component being voted on.
+
+ @param vpoll: the VPOLL component to process
+ @type vpoll: L{Component}
+ @param attendee: calendar user address of attendee replying
+ @type attendee: C{str}
+ """
+
+ for component in tuple(vpoll.subcomponents()):
+ if component.name() in ignoredComponents:
+ continue
+ poll_item_id = component.propertyValue("POLL-ITEM-ID")
+ if poll_item_id is None:
+ continue
+ voter = component.getVoterProperty((attendee,))
+ if voter is not None and voter.hasParameter("RESPONSE"):
+ vpoll.addProperty(Property("POLL-ITEM-ID", poll_item_id, {"RESPONSE": voter.parameterValue("RESPONSE")}))
+ vpoll.removeComponent(component)
+
+
+ @staticmethod
</ins><span class="cx"> def prepareSchedulingMessage(itip, reply=False):
</span><span class="cx"> """
</span><span class="cx"> Remove properties and parameters that should not be sent in an iTIP message
</span><span class="lines">@@ -932,6 +1002,7 @@
</span><span class="cx">
</span><span class="cx"> # Property Parameters
</span><span class="cx"> itip.removePropertyParameters("ATTENDEE", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND", "X-CALENDARSERVER-DTSTAMP",))
</span><ins>+ itip.removePropertyParameters("VOTER", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND", "X-CALENDARSERVER-DTSTAMP",))
</ins><span class="cx"> itip.removePropertyParameters("ORGANIZER", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND",))
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingprocessingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/processing.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/processing.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/processing.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,9 +14,9 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from twext.web2.dav.method.report import NumberOfMatchesWithinLimits
</span><span class="lines">@@ -785,13 +785,13 @@
</span><span class="cx"> cuas = self.recipient.principal.calendarUserAddresses
</span><span class="cx">
</span><span class="cx"> # First expand current one to get instances (only go 1 year into the future)
</span><del>- default_future_expansion_duration = PyCalendarDuration(days=config.Scheduling.Options.AutoSchedule.FutureFreeBusyDays)
- expand_max = PyCalendarDateTime.getToday() + default_future_expansion_duration
</del><ins>+ default_future_expansion_duration = Duration(days=config.Scheduling.Options.AutoSchedule.FutureFreeBusyDays)
+ expand_max = DateTime.getToday() + default_future_expansion_duration
</ins><span class="cx"> instances = calendar.expandTimeRanges(expand_max, ignoreInvalidInstances=True)
</span><span class="cx">
</span><span class="cx"> # We are going to ignore auto-accept processing for anything more than a day old (actually use -2 days
</span><span class="cx"> # to add some slop to account for possible timezone offsets)
</span><del>- min_date = PyCalendarDateTime.getToday()
</del><ins>+ min_date = DateTime.getToday()
</ins><span class="cx"> min_date.offsetDay(-2)
</span><span class="cx"> allOld = True
</span><span class="cx">
</span><span class="lines">@@ -824,7 +824,7 @@
</span><span class="cx"> # Get the timezone property from the collection, and store in the query filter
</span><span class="cx"> # for use during the query itself.
</span><span class="cx"> tz = testcal.getTimezone()
</span><del>- tzinfo = tz.gettimezone() if tz is not None else PyCalendarTimezone(utc=True)
</del><ins>+ tzinfo = tz.gettimezone() if tz is not None else Timezone(utc=True)
</ins><span class="cx">
</span><span class="cx"> # Now do search for overlapping time-range and set instance.free based
</span><span class="cx"> # on whether there is an overlap or not
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingschedulerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/scheduler.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/scheduler.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/scheduler.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -38,11 +38,12 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import RemoteCalendarUser
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import EmailCalendarUser
</span><del>-from txdav.caldav.datastore.scheduling.cuaddress import PartitionedCalendarUser
</del><span class="cx"> from txdav.caldav.datastore.scheduling.imip.delivery import ScheduleViaIMip
</span><span class="cx"> from txdav.caldav.datastore.scheduling.ischedule.delivery import ScheduleViaISchedule
</span><span class="cx"> from txdav.caldav.datastore.scheduling.itip import iTIPRequestStatus
</span><ins>+
</ins><span class="cx"> import hashlib
</span><ins>+from collections import namedtuple
</ins><span class="cx">
</span><span class="cx"> """
</span><span class="cx"> CalDAV/Server-to-Server scheduling behavior.
</span><span class="lines">@@ -50,8 +51,8 @@
</span><span class="cx"> This module handles the delivery of scheduling messages to organizer and attendees. The basic idea is to first
</span><span class="cx"> confirm the integrity of the incoming scheduling message, check authorization. Appropriate L{DeliveryService}s
</span><span class="cx"> are then used to deliver the message to attendees or organizer. Delivery responses are processed and returned.
</span><del>-This takes into account partitioning and podding of users by detecting the appropriate host for a calendar
-user and then dispatching the delivery accordingly.
</del><ins>+This takes into account podding of users by detecting the appropriate host for a calendar user and then
+dispatching the delivery accordingly.
</ins><span class="cx">
</span><span class="cx"> The L{Scheduler} class defines the basic behavior for processing deliveries. Sub-classes are defined for the
</span><span class="cx"> different ways a deliver can be triggered.
</span><span class="lines">@@ -142,6 +143,7 @@
</span><span class="cx">
</span><span class="cx"> self.originator = None
</span><span class="cx"> self.recipients = None
</span><ins>+ self.recipientsNormalizationMap = {}
</ins><span class="cx"> self.calendar = None
</span><span class="cx"> self.organizer = None
</span><span class="cx"> self.attendee = None
</span><span class="lines">@@ -232,51 +234,6 @@
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><span class="cx">
</span><del>- @inlineCallbacks
- def loadFromRequestData(self):
- self.loadOriginatorFromRequestDetails()
- self.loadRecipientsFromCalendarData()
-
-
- def loadOriginatorFromRequestDetails(self):
- # Get the originator who is the authenticated user
- originatorPrincipal = self.txn.directoryService().recordWithUID(self.originator_uid)
-
- # Pick the canonical CUA:
- originator = originatorPrincipal.canonicalCalendarUserAddress() if originatorPrincipal else ""
-
- if not originator:
- log.error("%s request must have Originator" % (self.method,))
- raise HTTPError(self.errorResponse(
- responsecode.FORBIDDEN,
- self.errorElements["originator-missing"],
- "Missing originator",
- ))
- else:
- self.originator = originator
-
-
- def loadRecipientsFromCalendarData(self):
-
- # Get the ATTENDEEs
- attendees = list()
- unique_set = set()
- for attendee, _ignore in self.calendar.getAttendeesByInstance():
- if attendee not in unique_set:
- attendees.append(attendee)
- unique_set.add(attendee)
-
- if not attendees:
- log.error("%s request must have at least one Recipient" % (self.method,))
- raise HTTPError(self.errorResponse(
- responsecode.FORBIDDEN,
- self.errorElements["recipient-missing"],
- "Must have recipients",
- ))
- else:
- self.recipients = list(attendees)
-
-
</del><span class="cx"> def preProcessCalendarData(self):
</span><span class="cx"> """
</span><span class="cx"> After loading calendar data from the request, do some optional processing of it. This method will be
</span><span class="lines">@@ -474,11 +431,10 @@
</span><span class="cx"> freebusy = self.checkForFreeBusy()
</span><span class="cx">
</span><span class="cx"> # Prepare for multiple responses
</span><del>- responses = self.scheduleResponse(self.method, responsecode.OK)
</del><ins>+ responses = self.scheduleResponse(self.method, responsecode.OK, self.mapRecipientAddress)
</ins><span class="cx">
</span><span class="cx"> # Loop over each recipient and aggregate into lists by service types.
</span><span class="cx"> caldav_recipients = []
</span><del>- partitioned_recipients = []
</del><span class="cx"> otherserver_recipients = []
</span><span class="cx"> remote_recipients = []
</span><span class="cx"> imip_recipients = []
</span><span class="lines">@@ -500,9 +456,6 @@
</span><span class="cx"> elif isinstance(recipient, LocalCalendarUser):
</span><span class="cx"> caldav_recipients.append(recipient)
</span><span class="cx">
</span><del>- elif isinstance(recipient, PartitionedCalendarUser):
- partitioned_recipients.append(recipient)
-
</del><span class="cx"> elif isinstance(recipient, OtherServerCalendarUser):
</span><span class="cx"> otherserver_recipients.append(recipient)
</span><span class="cx">
</span><span class="lines">@@ -524,10 +477,6 @@
</span><span class="cx"> if caldav_recipients:
</span><span class="cx"> yield self.generateLocalSchedulingResponses(caldav_recipients, responses, freebusy)
</span><span class="cx">
</span><del>- # Now process partitioned recipients
- if partitioned_recipients:
- yield self.generateRemoteSchedulingResponses(partitioned_recipients, responses, freebusy, getattr(self.txn, 'doing_attendee_refresh', False))
-
</del><span class="cx"> # Now process other server recipients
</span><span class="cx"> if otherserver_recipients:
</span><span class="cx"> yield self.generateRemoteSchedulingResponses(otherserver_recipients, responses, freebusy, getattr(self.txn, 'doing_attendee_refresh', False))
</span><span class="lines">@@ -577,7 +526,11 @@
</span><span class="cx"> return requestor.generateSchedulingResponses()
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def mapRecipientAddress(self, cuaddr):
+ return self.recipientsNormalizationMap.get(cuaddr, cuaddr)
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class RemoteScheduler(Scheduler):
</span><span class="cx">
</span><span class="cx"> def checkOrganizer(self):
</span><span class="lines">@@ -612,8 +565,8 @@
</span><span class="cx"> else:
</span><span class="cx"> # Map recipient to their inbox
</span><span class="cx"> inbox = None
</span><del>- if principal.calendarsEnabled() and principal.thisServer():
- if principal.locallyHosted():
</del><ins>+ if principal.calendarsEnabled():
+ if principal.thisServer():
</ins><span class="cx"> recipient_home = yield self.txn.calendarHomeWithUID(principal.uid, create=True)
</span><span class="cx"> if recipient_home:
</span><span class="cx"> inbox = (yield recipient_home.calendarWithName("inbox"))
</span><span class="lines">@@ -703,7 +656,12 @@
</span><span class="cx"> response_description_element = davxml.ResponseDescription
</span><span class="cx"> calendar_data_element = caldavxml.CalendarData
</span><span class="cx">
</span><del>- def __init__(self, method, success_response):
</del><ins>+ ScheduleResonseDetails = namedtuple(
+ "ScheduleResonseDetails",
+ ["recipient", "reqstatus", "calendar", "error", "message", ]
+ )
+
+ def __init__(self, method, success_response, recipient_mapper=None):
</ins><span class="cx"> """
</span><span class="cx"> @param method: the name of the method generating the queue.
</span><span class="cx"> @param success_response: the response to return in lieu of a
</span><span class="lines">@@ -712,6 +670,7 @@
</span><span class="cx"> self.responses = []
</span><span class="cx"> self.method = method
</span><span class="cx"> self.success_response = success_response
</span><ins>+ self.recipient_mapper = recipient_mapper
</ins><span class="cx"> self.location = None
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -745,19 +704,20 @@
</span><span class="cx"> else:
</span><span class="cx"> raise AssertionError("Unknown data type: %r" % (what,))
</span><span class="cx">
</span><ins>+ if self.recipient_mapper is not None:
+ recipient = self.recipient_mapper(recipient)
+
</ins><span class="cx"> if not suppressErrorLog and code > 400: # Error codes only
</span><span class="cx"> self.log.error("Error during %s for %s: %s" % (self.method, recipient, message))
</span><span class="cx">
</span><del>- children = []
- children.append(self.recipient_element(davxml.HRef.fromString(recipient)) if self.recipient_uses_href else self.recipient_element.fromString(recipient))
- children.append(self.request_status_element(reqstatus))
- if calendar is not None:
- children.append(self.calendar_data_element.fromCalendar(calendar))
- if error is not None:
- children.append(error)
- if message is not None:
- children.append(self.response_description_element(message))
- self.responses.append(self.response_element(*children))
</del><ins>+ details = ScheduleResponseQueue.ScheduleResonseDetails(
+ self.recipient_element(davxml.HRef.fromString(recipient)) if self.recipient_uses_href else self.recipient_element.fromString(recipient),
+ self.request_status_element(reqstatus),
+ calendar,
+ error,
+ self.response_description_element(message) if message is not None else None,
+ )
+ self.responses.append(details)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def errorForFailure(self, failure):
</span><span class="lines">@@ -773,19 +733,17 @@
</span><span class="cx"> @param clone: the response to clone.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- children = []
- children.append(self.recipient_element(davxml.HRef.fromString(recipient)) if self.recipient_uses_href else self.recipient_element.fromString(recipient))
- children.append(self.request_status_element.fromString(request_status))
- if calendar_data is not None:
- children.append(self.calendar_data_element.fromCalendar(calendar_data))
- if error is not None:
- children.append(self.error_element(*error))
- if desc is not None:
- children.append(self.response_description_element.fromString(desc))
- self.responses.append(self.response_element(*children))
</del><ins>+ details = ScheduleResponseQueue.ScheduleResonseDetails(
+ self.recipient_element(davxml.HRef.fromString(recipient)) if self.recipient_uses_href else self.recipient_element.fromString(recipient),
+ self.request_status_element.fromString(request_status),
+ calendar_data,
+ self.error_element(*error) if error is not None else None,
+ self.response_description_element.fromString(desc) if desc is not None else None,
+ )
+ self.responses.append(details)
</ins><span class="cx">
</span><span class="cx">
</span><del>- def response(self):
</del><ins>+ def response(self, format=None):
</ins><span class="cx"> """
</span><span class="cx"> Generate a L{ScheduleResponseResponse} with the responses contained in the
</span><span class="cx"> queue or, if no such responses, return the C{success_response} provided
</span><span class="lines">@@ -793,6 +751,20 @@
</span><span class="cx"> @return: the response.
</span><span class="cx"> """
</span><span class="cx"> if self.responses:
</span><del>- return ScheduleResponseResponse(self.schedule_response_element, self.responses, self.location)
</del><ins>+ # Convert our queue to all XML elements
+ xml_responses = []
+ for response in self.responses:
+ children = []
+ children.append(response.recipient)
+ children.append(response.reqstatus)
+ if response.calendar is not None:
+ children.append(self.calendar_data_element.fromCalendar(response.calendar, format))
+ if response.error is not None:
+ children.append(response.error)
+ if response.message is not None:
+ children.append(response.message)
+ xml_responses.append(self.response_element(*children))
+
+ return ScheduleResponseResponse(self.schedule_response_element, xml_responses, self.location)
</ins><span class="cx"> else:
</span><span class="cx"> return self.success_response
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingtesttest_freebusypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_freebusy.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_freebusy.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_freebusy.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,8 +14,8 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.period import PyCalendarPeriod
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.period import Period
</ins><span class="cx">
</span><span class="cx"> from twext.python.clsprop import classproperty
</span><span class="cx">
</span><span class="lines">@@ -94,7 +94,7 @@
</span><span class="cx"> (
</span><span class="cx"> "#1.3 With single busy time",
</span><span class="cx"> [
</span><del>- [PyCalendarPeriod.parseText("20080601T120000Z/20080601T130000Z"), ],
</del><ins>+ [Period.parseText("20080601T120000Z/20080601T130000Z"), ],
</ins><span class="cx"> [],
</span><span class="cx"> [],
</span><span class="cx"> ],
</span><span class="lines">@@ -120,8 +120,8 @@
</span><span class="cx"> "#1.4 With multiple busy time",
</span><span class="cx"> [
</span><span class="cx"> [
</span><del>- PyCalendarPeriod.parseText("20080601T120000Z/20080601T130000Z"),
- PyCalendarPeriod.parseText("20080601T140000Z/20080601T150000Z"),
</del><ins>+ Period.parseText("20080601T120000Z/20080601T130000Z"),
+ Period.parseText("20080601T140000Z/20080601T150000Z"),
</ins><span class="cx"> ],
</span><span class="cx"> [],
</span><span class="cx"> [],
</span><span class="lines">@@ -148,10 +148,10 @@
</span><span class="cx"> "#1.5 With multiple busy time, some overlap",
</span><span class="cx"> [
</span><span class="cx"> [
</span><del>- PyCalendarPeriod.parseText("20080601T120000Z/20080601T130000Z"),
- PyCalendarPeriod.parseText("20080601T123000Z/20080601T133000Z"),
- PyCalendarPeriod.parseText("20080601T140000Z/20080601T150000Z"),
- PyCalendarPeriod.parseText("20080601T150000Z/20080601T160000Z"),
</del><ins>+ Period.parseText("20080601T120000Z/20080601T130000Z"),
+ Period.parseText("20080601T123000Z/20080601T133000Z"),
+ Period.parseText("20080601T140000Z/20080601T150000Z"),
+ Period.parseText("20080601T150000Z/20080601T160000Z"),
</ins><span class="cx"> ],
</span><span class="cx"> [],
</span><span class="cx"> [],
</span><span class="lines">@@ -178,14 +178,14 @@
</span><span class="cx"> "#1.6 With all busy time types",
</span><span class="cx"> [
</span><span class="cx"> [
</span><del>- PyCalendarPeriod.parseText("20080601T120000Z/20080601T130000Z"),
- PyCalendarPeriod.parseText("20080601T140000Z/20080601T150000Z"),
</del><ins>+ Period.parseText("20080601T120000Z/20080601T130000Z"),
+ Period.parseText("20080601T140000Z/20080601T150000Z"),
</ins><span class="cx"> ],
</span><span class="cx"> [
</span><del>- PyCalendarPeriod.parseText("20080601T140000Z/20080601T150000Z"),
</del><ins>+ Period.parseText("20080601T140000Z/20080601T150000Z"),
</ins><span class="cx"> ],
</span><span class="cx"> [
</span><del>- PyCalendarPeriod.parseText("20080601T160000Z/20080601T170000Z"),
</del><ins>+ Period.parseText("20080601T160000Z/20080601T170000Z"),
</ins><span class="cx"> ],
</span><span class="cx"> ],
</span><span class="cx"> "20080601T000000Z",
</span><span class="lines">@@ -211,7 +211,7 @@
</span><span class="cx"> (
</span><span class="cx"> "#1.7 With single busy time and event details",
</span><span class="cx"> [
</span><del>- [PyCalendarPeriod.parseText("20080601T120000Z/20080601T130000Z"), ],
</del><ins>+ [Period.parseText("20080601T120000Z/20080601T130000Z"), ],
</ins><span class="cx"> [],
</span><span class="cx"> [],
</span><span class="cx"> ],
</span><span class="lines">@@ -269,7 +269,7 @@
</span><span class="cx"> self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
</span><span class="cx"> yield self.populate()
</span><span class="cx">
</span><del>- self.now = PyCalendarDateTime.getNowUTC()
</del><ins>+ self.now = DateTime.getNowUTC()
</ins><span class="cx"> self.now.setHHMMSS(0, 0, 0)
</span><span class="cx">
</span><span class="cx"> self.now_12H = self.now.duplicate()
</span><span class="lines">@@ -368,7 +368,7 @@
</span><span class="cx"> timerange = caldavxml.TimeRange(start=self.now.getText(), end=self.now_1D.getText())
</span><span class="cx"> result = (yield generateFreeBusyInfo(calendar, fbinfo, timerange, matchtotal))
</span><span class="cx"> self.assertEqual(result, 1)
</span><del>- self.assertEqual(fbinfo[0], [PyCalendarPeriod.parseText("%s/%s" % (self.now_12H.getText(), self.now_13H.getText(),)), ])
</del><ins>+ self.assertEqual(fbinfo[0], [Period.parseText("%s/%s" % (self.now_12H.getText(), self.now_13H.getText(),)), ])
</ins><span class="cx"> self.assertEqual(len(fbinfo[1]), 0)
</span><span class="cx"> self.assertEqual(len(fbinfo[2]), 0)
</span><span class="cx">
</span><span class="lines">@@ -406,7 +406,7 @@
</span><span class="cx"> event_details=event_details
</span><span class="cx"> ))
</span><span class="cx"> self.assertEqual(result, 1)
</span><del>- self.assertEqual(fbinfo[0], [PyCalendarPeriod.parseText("%s/%s" % (self.now_12H.getText(), self.now_13H.getText(),)), ])
</del><ins>+ self.assertEqual(fbinfo[0], [Period.parseText("%s/%s" % (self.now_12H.getText(), self.now_13H.getText(),)), ])
</ins><span class="cx"> self.assertEqual(len(fbinfo[1]), 0)
</span><span class="cx"> self.assertEqual(len(fbinfo[2]), 0)
</span><span class="cx"> self.assertEqual(len(event_details), 1)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingtesttest_icalsplitterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,7 +14,7 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx"> from twisted.trial import unittest
</span><span class="cx"> from twistedcaldav.stdconfig import config
</span><span class="cx"> from twistedcaldav.ical import Component
</span><span class="lines">@@ -31,7 +31,7 @@
</span><span class="cx">
</span><span class="cx"> self.subs = {}
</span><span class="cx">
</span><del>- self.now = PyCalendarDateTime.getNowUTC()
</del><ins>+ self.now = DateTime.getNowUTC()
</ins><span class="cx"> self.now.setHHMMSS(0, 0, 0)
</span><span class="cx">
</span><span class="cx"> self.subs["now"] = self.now
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingtesttest_implicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_implicit.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_implicit.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_implicit.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -14,8 +14,8 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twext.python.clsprop import classproperty
</span><span class="cx"> from twext.web2 import responsecode
</span><span class="lines">@@ -248,9 +248,9 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- ("mailto:user1@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ ("mailto:user1@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -287,12 +287,12 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- ("mailto:user1@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user1@example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2@example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3@example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ ("mailto:user1@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user1@example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2@example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3@example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -330,15 +330,15 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- ("mailto:user1@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user1@example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2@example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3@example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user1@example.com", PyCalendarDateTime(2008, 12, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2@example.com", PyCalendarDateTime(2008, 12, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3@example.com", PyCalendarDateTime(2008, 12, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ ("mailto:user1@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user1@example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2@example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3@example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user1@example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2@example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3@example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -447,7 +447,7 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> ("mailto:user3@example.com", None),
</span><del>- ("mailto:user3@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ ("mailto:user3@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -502,7 +502,7 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- ("mailto:user3@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ ("mailto:user3@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -648,9 +648,9 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- ("mailto:user1@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user3@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ ("mailto:user1@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user3@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -705,7 +705,7 @@
</span><span class="cx"> """,
</span><span class="cx"> (
</span><span class="cx"> ("mailto:user3@example.com", None),
</span><del>- ("mailto:user4@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ ("mailto:user4@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -751,7 +751,7 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- ("mailto:user4@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ ("mailto:user4@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="lines">@@ -798,9 +798,9 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> (
</span><del>- ("mailto:user1@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user2@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
- ("mailto:user4@example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
</del><ins>+ ("mailto:user1@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user2@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+ ("mailto:user4@example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
</ins><span class="cx"> ),
</span><span class="cx"> ),
</span><span class="cx"> )
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingtesttest_itippy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_itip.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_itip.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_itip.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -15,8 +15,8 @@
</span><span class="cx"> ##
</span><span class="cx"> from __future__ import print_function
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twisted.trial import unittest
</span><span class="cx">
</span><span class="lines">@@ -2025,7 +2025,7 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> ("mailto:user2@example.com",),
</span><del>- (PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),),
</del><ins>+ (DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),),
</ins><span class="cx"> ),
</span><span class="cx">
</span><span class="cx"> # Recurring component with one instance, each with one attendee - cancel instance
</span><span class="lines">@@ -2066,7 +2066,7 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """,
</span><span class="cx"> ("mailto:user2@example.com",),
</span><del>- (PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),),
</del><ins>+ (DateTime(2008, 11, 14, 0, 0, 0, tzid=Timezone(utc=True)),),
</ins><span class="cx"> ),
</span><span class="cx">
</span><span class="cx"> # Recurring component with one instance, each with one attendee - cancel master
</span><span class="lines">@@ -2127,7 +2127,7 @@
</span><span class="cx"> """,
</span><span class="cx"> "",
</span><span class="cx"> ("mailto:user2@example.com",),
</span><del>- (PyCalendarDateTime(2008, 12, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),),
</del><ins>+ (DateTime(2008, 12, 14, 0, 0, 0, tzid=Timezone(utc=True)),),
</ins><span class="cx"> ),
</span><span class="cx">
</span><span class="cx"> )
</span><span class="lines">@@ -2229,7 +2229,8 @@
</span><span class="cx"> DTSTART;TZID=America/Los_Angeles:20101007T113000
</span><span class="cx"> DTEND;TZID=America/Los_Angeles:20101007T120000
</span><span class="cx"> ATTENDEE;CN=Missing Attendee;CUTYPE=INDIVIDUAL;EMAIL=missing@example.com;P
</span><del>- ARTSTAT=DECLINED;ROLE=OPT-PARTICIPANT;RSVP=TRUE:mailto:missing@example.com
</del><ins>+ ARTSTAT=DECLINED;ROLE=OPT-PARTICIPANT;RSVP=TRUE:mailto:missing@example.co
+ m
</ins><span class="cx"> ORGANIZER;CN=The Organizer:mailto:organizer@example.com
</span><span class="cx"> REQUEST-STATUS:2.0;Success
</span><span class="cx"> SEQUENCE:24
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingtesttest_utilspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_utils.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_utils.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/test/test_utils.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -18,7 +18,7 @@
</span><span class="cx"> Tests for calendarserver.tools.purge
</span><span class="cx"> """
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx"> from twisted.trial import unittest
</span><span class="lines">@@ -30,7 +30,7 @@
</span><span class="cx"> from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
</span><span class="cx">
</span><span class="cx">
</span><del>-now = PyCalendarDateTime.getToday().getYear()
</del><ins>+now = DateTime.getToday().getYear()
</ins><span class="cx">
</span><span class="cx"> ORGANIZER_ICS = """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreschedulingutilspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/utils.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/utils.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/scheduling/utils.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -29,7 +29,7 @@
</span><span class="cx"> one of them to avoid scheduling problems.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- if record and record.locallyHosted():
</del><ins>+ if record and record.thisServer():
</ins><span class="cx"> # Get record's calendar-home
</span><span class="cx"> calendar_home = yield txn.calendarHomeWithUID(record.uid)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/sql.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/sql.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/sql.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -47,7 +47,7 @@
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue, succeed
</span><span class="cx"> from twisted.python import hashlib
</span><span class="cx">
</span><del>-from twistedcaldav import caldavxml, customxml
</del><ins>+from twistedcaldav import caldavxml, customxml, ical
</ins><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
</span><span class="cx"> from twistedcaldav.dateops import normalizeForIndex, datetimeMktime, \
</span><span class="lines">@@ -89,11 +89,13 @@
</span><span class="cx"> InvalidUIDError, UIDExistsError, UIDExistsElsewhereError, \
</span><span class="cx"> InvalidResourceMove, InvalidComponentForStoreError
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.timezone import PyCalendarTimezone
-from pycalendar.value import PyCalendarValue
</del><ins>+from txdav.idav import ChangeCategory
</ins><span class="cx">
</span><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.timezone import Timezone
+from pycalendar.value import Value
+
</ins><span class="cx"> from zope.interface.declarations import implements
</span><span class="cx">
</span><span class="cx"> from urlparse import urlparse, urlunparse
</span><span class="lines">@@ -406,6 +408,26 @@
</span><span class="cx">
</span><span class="cx"> _cacher = Memcacher("SQL.calhome", pickle=True, key_normalization=False)
</span><span class="cx">
</span><ins>+ _componentCalendarName = {
+ "VEVENT": "calendar",
+ "VTODO": "tasks",
+ "VJOURNAL": "journals",
+ "VAVAILABILITY": "available",
+ "VPOLL": "polls",
+ }
+
+ _componentDefaultColumn = {
+ "VEVENT": schema.CALENDAR_HOME_METADATA.DEFAULT_EVENTS,
+ "VTODO": schema.CALENDAR_HOME_METADATA.DEFAULT_TASKS,
+ "VPOLL": schema.CALENDAR_HOME_METADATA.DEFAULT_POLLS,
+ }
+
+ _componentDefaultAttribute = {
+ "VEVENT": "_default_events",
+ "VTODO": "_default_tasks",
+ "VPOLL": "_default_polls",
+ }
+
</ins><span class="cx"> def __init__(self, transaction, ownerUID):
</span><span class="cx">
</span><span class="cx"> self._childClass = Calendar
</span><span class="lines">@@ -422,9 +444,8 @@
</span><span class="cx">
</span><span class="cx"> # Common behavior is to have created and modified
</span><span class="cx">
</span><del>- return (
- cls._homeMetaDataSchema.DEFAULT_EVENTS,
- cls._homeMetaDataSchema.DEFAULT_TASKS,
</del><ins>+ default_collections = tuple([cls._componentDefaultColumn[name] for name in sorted(cls._componentDefaultColumn.keys())])
+ return default_collections + (
</ins><span class="cx"> cls._homeMetaDataSchema.ALARM_VEVENT_TIMED,
</span><span class="cx"> cls._homeMetaDataSchema.ALARM_VEVENT_ALLDAY,
</span><span class="cx"> cls._homeMetaDataSchema.ALARM_VTODO_TIMED,
</span><span class="lines">@@ -445,9 +466,8 @@
</span><span class="cx">
</span><span class="cx"> # Common behavior is to have created and modified
</span><span class="cx">
</span><del>- return (
- "_default_events",
- "_default_tasks",
</del><ins>+ default_attributes = tuple([cls._componentDefaultAttribute[name] for name in sorted(cls._componentDefaultAttribute.keys())])
+ return default_attributes + (
</ins><span class="cx"> "_alarm_vevent_timed",
</span><span class="cx"> "_alarm_vevent_allday",
</span><span class="cx"> "_alarm_vtodo_timed",
</span><span class="lines">@@ -624,22 +644,18 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def createdHome(self):
</span><span class="cx">
</span><del>- # Default calendar
- defaultCal = yield self.createCalendarWithName("calendar")
-
</del><span class="cx"> # Check whether components type must be separate
</span><span class="cx"> if config.RestrictCalendarsToOneComponentType:
</span><del>- yield defaultCal.setSupportedComponents("VEVENT")
- yield self.setDefaultCalendar(defaultCal, False)
-
- # Default tasks
- defaultTasks = yield self.createCalendarWithName("tasks")
- yield defaultTasks.setSupportedComponents("VTODO")
- yield defaultTasks.setUsedForFreeBusy(False)
- yield self.setDefaultCalendar(defaultTasks, True)
</del><ins>+ for name in ical.allowedStoreComponents:
+ cal = yield self.createCalendarWithName(self._componentCalendarName[name])
+ yield cal.setSupportedComponents(name)
+ if name not in ("VEVENT", "VAVAILABILITY",):
+ yield cal.setUsedForFreeBusy(False)
+ yield self.setDefaultCalendar(cal, name)
</ins><span class="cx"> else:
</span><del>- yield self.setDefaultCalendar(defaultCal, False)
- yield self.setDefaultCalendar(defaultCal, True)
</del><ins>+ cal = yield self.createCalendarWithName("calendar")
+ for name in ical.allowedStoreComponents:
+ yield self.setDefaultCalendar(cal, name)
</ins><span class="cx">
</span><span class="cx"> inbox = yield self.createCalendarWithName("inbox")
</span><span class="cx"> yield inbox.setUsedForFreeBusy(False)
</span><span class="lines">@@ -693,55 +709,13 @@
</span><span class="cx"> newcal = yield self.createCalendarWithName(newname)
</span><span class="cx"> yield newcal.setSupportedComponents(support_component)
</span><span class="cx">
</span><del>- yield _requireCalendarWithType("VEVENT", "calendar")
- yield _requireCalendarWithType("VTODO", "tasks")
</del><ins>+ for name in ical.allowedStoreComponents:
+ yield _requireCalendarWithType(name, self._componentCalendarName[name])
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def pickNewDefaultCalendar(self, tasks=False):
</del><ins>+ def setDefaultCalendar(self, calendar, componentType):
</ins><span class="cx"> """
</span><del>- First see if default provisioned calendar exists in the calendar home and pick that. Otherwise
- pick another from the calendar home.
- """
-
- componentType = "VTODO" if tasks else "VEVENT"
- test_name = "tasks" if tasks else "calendar"
-
- defaultCalendar = (yield self.calendarWithName(test_name))
- if defaultCalendar is None or not defaultCalendar.owned():
-
- @inlineCallbacks
- def _findDefault():
- for calendarName in (yield self.listCalendars()):
- calendar = (yield self.calendarWithName(calendarName))
- if calendar.isInbox():
- continue
- if not calendar.owned():
- continue
- if not calendar.isSupportedComponent(componentType):
- continue
- break
- else:
- calendar = None
- returnValue(calendar)
-
- defaultCalendar = yield _findDefault()
- if defaultCalendar is None:
- # Create a default and try and get its name again
- yield self.ensureDefaultCalendarsExist()
- defaultCalendar = yield _findDefault()
- if defaultCalendar is None:
- # Failed to even create a default - bad news...
- raise RuntimeError("No valid calendars to use as a default %s calendar." % (componentType,))
-
- yield self.setDefaultCalendar(defaultCalendar, tasks)
-
- returnValue(defaultCalendar)
-
-
- @inlineCallbacks
- def setDefaultCalendar(self, calendar, tasks=False):
- """
</del><span class="cx"> Set the default calendar for a particular type of component.
</span><span class="cx">
</span><span class="cx"> @param calendar: the calendar being set as the default
</span><span class="lines">@@ -749,10 +723,15 @@
</span><span class="cx"> @param tasks: C{True} for VTODO, C{False} for VEVENT
</span><span class="cx"> @type componentType: C{bool}
</span><span class="cx"> """
</span><ins>+
+ # We only support VEVENT and VTOTO right now
+ componentType = componentType.upper()
+ if componentType not in self._componentDefaultAttribute:
+ returnValue(None)
+
</ins><span class="cx"> chm = self._homeMetaDataSchema
</span><del>- componentType = "VTODO" if tasks else "VEVENT"
- attribute_to_test = "_default_tasks" if tasks else "_default_events"
- column_to_set = chm.DEFAULT_TASKS if tasks else chm.DEFAULT_EVENTS
</del><ins>+ attribute_to_test = self._componentDefaultAttribute[componentType]
+ column_to_set = self._componentDefaultColumn[componentType]
</ins><span class="cx">
</span><span class="cx"> # Check validity of the default
</span><span class="cx"> if calendar.isInbox():
</span><span class="lines">@@ -794,8 +773,13 @@
</span><span class="cx"> @rtype: L{Calendar} or C{None}
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ # We only support VEVENT and VTOTO right now
+ componentType = componentType.upper()
+ if componentType not in self._componentDefaultAttribute:
+ returnValue(None)
+
</ins><span class="cx"> # Check any default calendar property first - this will create if none exists
</span><del>- attribute_to_test = "_default_tasks" if componentType == "VTODO" else "_default_events"
</del><ins>+ attribute_to_test = self._componentDefaultAttribute[componentType]
</ins><span class="cx"> defaultID = getattr(self, attribute_to_test)
</span><span class="cx"> if defaultID:
</span><span class="cx"> default = (yield self.childWithID(defaultID))
</span><span class="lines">@@ -816,7 +800,8 @@
</span><span class="cx">
</span><span class="cx"> # Try to find a calendar supporting the required component type. If there are multiple, pick
</span><span class="cx"> # the one with the oldest created timestamp as that will likely be the initial provision.
</span><del>- for calendarName in (yield self.listCalendars()):
</del><ins>+ existing_names = (yield self.listCalendars())
+ for calendarName in existing_names:
</ins><span class="cx"> calendar = (yield self.calendarWithName(calendarName))
</span><span class="cx"> if calendar.isInbox():
</span><span class="cx"> continue
</span><span class="lines">@@ -832,12 +817,15 @@
</span><span class="cx"> if not create:
</span><span class="cx"> returnValue(None)
</span><span class="cx"> else:
</span><del>- new_name = "%ss" % (componentType.lower()[1:],)
</del><ins>+ # Try a default name mapping first, else use a UUID
+ new_name = self._componentCalendarName[componentType]
+ if new_name in existing_names:
+ new_name = str(uuid.uuid4())
</ins><span class="cx"> default = yield self.createCalendarWithName(new_name)
</span><del>- yield default.setSupportedComponents(componentType.upper())
</del><ins>+ yield default.setSupportedComponents(componentType)
</ins><span class="cx">
</span><span class="cx"> # Update the metadata
</span><del>- yield self.setDefaultCalendar(default, componentType == "VTODO")
</del><ins>+ yield self.setDefaultCalendar(default, componentType)
</ins><span class="cx">
</span><span class="cx"> returnValue(default)
</span><span class="cx">
</span><span class="lines">@@ -847,7 +835,10 @@
</span><span class="cx"> Is the supplied calendar one of the possible default calendars.
</span><span class="cx"> """
</span><span class="cx"> # Not allowed to delete the default calendar
</span><del>- return calendar._resourceID in (self._default_events, self._default_tasks)
</del><ins>+ for attr in self._componentDefaultAttribute.values():
+ if calendar._resourceID == getattr(self, attr):
+ return True
+ return False
</ins><span class="cx">
</span><span class="cx"> ALARM_DETAILS = {
</span><span class="cx"> (True, True): (_homeMetaDataSchema.ALARM_VEVENT_TIMED, "_alarm_vevent_timed"),
</span><span class="lines">@@ -1736,20 +1727,23 @@
</span><span class="cx">
</span><span class="cx"> NB Do this before implicit scheduling as we don't want old clients to trigger scheduling when
</span><span class="cx"> the X- property is missing.
</span><ins>+
+ We now only preserve the "X-CALENDARSERVER-ATTENDEE-COMMENT" property. We will now allow clients
+ to delete the "X-CALENDARSERVER-PRIVATE-COMMENT" and treat that as a removal of the attendee
+ comment (which will trigger scheduling with the organizer to remove the comment on the organizer's
+ side).
</ins><span class="cx"> """
</span><span class="cx"> if config.Scheduling.CalDAV.get("EnablePrivateComments", True):
</span><span class="cx"> old_has_private_comments = not inserting and self.hasPrivateComment
</span><span class="cx"> new_has_private_comments = component.hasPropertyInAnyComponent((
</span><del>- "X-CALENDARSERVER-PRIVATE-COMMENT",
</del><span class="cx"> "X-CALENDARSERVER-ATTENDEE-COMMENT",
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="cx"> if old_has_private_comments and not new_has_private_comments:
</span><span class="cx"> # Transfer old comments to new calendar
</span><del>- log.debug("Private Comments properties were entirely removed by the client. Restoring existing properties.")
</del><ins>+ log.debug("Organizer private comment properties were entirely removed by the client. Restoring existing properties.")
</ins><span class="cx"> old_calendar = (yield self.componentForUser())
</span><span class="cx"> component.transferProperties(old_calendar, (
</span><del>- "X-CALENDARSERVER-PRIVATE-COMMENT",
</del><span class="cx"> "X-CALENDARSERVER-ATTENDEE-COMMENT",
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="lines">@@ -2200,8 +2194,18 @@
</span><span class="cx"> else:
</span><span class="cx"> yield self._calendar._updateRevision(self._name)
</span><span class="cx">
</span><del>- yield self._calendar.notifyChanged()
</del><ins>+ # Determine change category
+ category = ChangeCategory.default
+ if internal_state == ComponentUpdateState.INBOX:
+ category = ChangeCategory.inbox
+ elif internal_state == ComponentUpdateState.ORGANIZER_ITIP_UPDATE:
+ category = ChangeCategory.organizerITIPUpdate
+ elif (internal_state == ComponentUpdateState.ATTENDEE_ITIP_UPDATE and
+ hasattr(self._txn, "doing_attende_refresh")):
+ category = ChangeCategory.attendeeITIPUpdate
</ins><span class="cx">
</span><ins>+ yield self._calendar.notifyChanged(category=category)
+
</ins><span class="cx"> # Finally check if a split is needed
</span><span class="cx"> if internal_state not in (ComponentUpdateState.SPLIT_OWNER, ComponentUpdateState.SPLIT_ATTENDEE,) and schedule_state == "organizer":
</span><span class="cx"> yield self.checkSplit()
</span><span class="lines">@@ -2248,7 +2252,7 @@
</span><span class="cx"> # When there is no master we have a set of overridden components -
</span><span class="cx"> # index them all.
</span><span class="cx"> # When there is one instance - index it.
</span><del>- expand = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ expand = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> doInstanceIndexing = True
</span><span class="cx"> else:
</span><span class="cx">
</span><span class="lines">@@ -2260,8 +2264,8 @@
</span><span class="cx"> # by default. This is a caching parameter which affects the size of the index;
</span><span class="cx"> # it does not affect search results beyond this period, but it may affect
</span><span class="cx"> # performance of such a search.
</span><del>- expand = (PyCalendarDateTime.getToday() +
- PyCalendarDuration(days=config.FreeBusyIndexExpandAheadDays))
</del><ins>+ expand = (DateTime.getToday() +
+ Duration(days=config.FreeBusyIndexExpandAheadDays))
</ins><span class="cx">
</span><span class="cx"> if expand_until and expand_until > expand:
</span><span class="cx"> expand = expand_until
</span><span class="lines">@@ -2278,12 +2282,12 @@
</span><span class="cx"> # occurrences into some obscenely far-in-the-future date, so we cap the caching
</span><span class="cx"> # period. Searches beyond this period will always be relatively expensive for
</span><span class="cx"> # resources with occurrences beyond this period.
</span><del>- if expand > (PyCalendarDateTime.getToday() +
- PyCalendarDuration(days=config.FreeBusyIndexExpandMaxDays)):
</del><ins>+ if expand > (DateTime.getToday() +
+ Duration(days=config.FreeBusyIndexExpandMaxDays)):
</ins><span class="cx"> raise IndexedSearchException
</span><span class="cx">
</span><span class="cx"> if config.FreeBusyIndexLowerLimitDays:
</span><del>- truncateLowerLimit = PyCalendarDateTime.getToday()
</del><ins>+ truncateLowerLimit = DateTime.getToday()
</ins><span class="cx"> truncateLowerLimit.offsetDay(-config.FreeBusyIndexLowerLimitDays)
</span><span class="cx"> else:
</span><span class="cx"> truncateLowerLimit = None
</span><span class="lines">@@ -2310,7 +2314,7 @@
</span><span class="cx"> if not doInstanceIndexing:
</span><span class="cx"> instances = None
</span><span class="cx"> recurrenceLowerLimit = None
</span><del>- recurrenceLimit = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ recurrenceLimit = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx">
</span><span class="cx"> co = schema.CALENDAR_OBJECT
</span><span class="cx"> tr = schema.TIME_RANGE
</span><span class="lines">@@ -2429,7 +2433,7 @@
</span><span class="cx"> @param instances: the set of instances to add
</span><span class="cx"> @type instances: L{InstanceList}
</span><span class="cx"> @param truncateLowerLimit: the lower limit for instances
</span><del>- @type truncateLowerLimit: L{PyCalendarDateTime}
</del><ins>+ @type truncateLowerLimit: L{DateTime}
</ins><span class="cx"> @param isInboxItem: indicates if an inbox item
</span><span class="cx"> @type isInboxItem: C{bool}
</span><span class="cx"> @param txn: transaction to use
</span><span class="lines">@@ -2458,8 +2462,8 @@
</span><span class="cx"> # For truncated items we insert a tomb stone lower bound so that a time-range
</span><span class="cx"> # query with just an end bound will match
</span><span class="cx"> if lowerLimitApplied or instances.lowerLimit and len(instances.instances) == 0:
</span><del>- start = PyCalendarDateTime(1901, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- end = PyCalendarDateTime(1901, 1, 1, 1, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ start = DateTime(1901, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ end = DateTime(1901, 1, 1, 1, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> yield self._addInstanceDetails(component, None, start, end, False, True, "UNKNOWN", isInboxItem, txn)
</span><span class="cx">
</span><span class="cx"> # Special - for unbounded recurrence we insert a value for "infinity"
</span><span class="lines">@@ -2467,8 +2471,8 @@
</span><span class="cx"> # We also need to add the "infinity" value if the event was bounded but
</span><span class="cx"> # starts after the future expansion cut-off limit.
</span><span class="cx"> if component.isRecurringUnbounded() or instances.limit and len(instances.instances) == 0:
</span><del>- start = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- end = PyCalendarDateTime(2100, 1, 1, 1, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ start = DateTime(2100, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ end = DateTime(2100, 1, 1, 1, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> yield self._addInstanceDetails(component, None, start, end, False, True, "UNKNOWN", isInboxItem, txn)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -2655,7 +2659,7 @@
</span><span class="cx"> Get the RECURRANCE_MIN, RECURRANCE_MAX value from the database. Occasionally we might need to do an
</span><span class="cx"> update to time-range data via a separate transaction, so we allow that to be passed in.
</span><span class="cx">
</span><del>- @return: L{PyCalendarDateTime} result
</del><ins>+ @return: L{DateTime} result
</ins><span class="cx"> """
</span><span class="cx"> # Setup appropriate txn
</span><span class="cx"> txn = txn if txn is not None else self._txn
</span><span class="lines">@@ -4277,7 +4281,7 @@
</span><span class="cx"> """
</span><span class="cx"> Return an iCalendar ATTACH property for this attachment.
</span><span class="cx"> """
</span><del>- attach = Property("ATTACH", "", valuetype=PyCalendarValue.VALUETYPE_URI)
</del><ins>+ attach = Property("ATTACH", "", valuetype=Value.VALUETYPE_URI)
</ins><span class="cx"> location = (yield self.updateProperty(attach))
</span><span class="cx"> returnValue((attach, location,))
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretestcommonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/common.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/common.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/common.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -53,7 +53,9 @@
</span><span class="cx"> from txdav.common.icommondatastore import ConcurrentModification
</span><span class="cx"> from twistedcaldav.ical import Component
</span><span class="cx"> from twistedcaldav.config import config
</span><ins>+from calendarserver.push.util import PushPriority
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> storePath = FilePath(__file__).parent().child("calendar_store")
</span><span class="cx">
</span><span class="cx"> homeRoot = storePath.child("ho").child("me").child("home1")
</span><span class="lines">@@ -456,8 +458,8 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/notification/",
</del><ins>+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/notification/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx"> yield self.commit()
</span><span class="lines">@@ -474,8 +476,8 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/notification/",
</del><ins>+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/notification/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx"> yield self.commit()
</span><span class="lines">@@ -698,7 +700,7 @@
</span><span class="cx"> calendarProperties = (yield home.calendarWithName(name)).properties()
</span><span class="cx"> self.assertEqual(len(calendarProperties), 0)
</span><span class="cx"> # notify is called prior to commit
</span><del>- self.assertTrue("/CalDAV/example.com/home1/" in self.notifierFactory.history)
</del><ins>+ self.assertTrue(("/CalDAV/example.com/home1/", PushPriority.high) in self.notifierFactory.history)
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx"> # Make sure it's available in a new transaction; i.e. test the commit.
</span><span class="lines">@@ -741,10 +743,10 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
- "/CalDAV/example.com/home1/calendar_2/",
- "/CalDAV/example.com/home1/calendar_empty/",
</del><ins>+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_2/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_empty/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -918,8 +920,8 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
</del><ins>+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx"> yield self.commit()
</span><span class="lines">@@ -1337,7 +1339,7 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_iCalendarText(self):
</span><span class="cx"> """
</span><del>- L{ICalendarObject.iCalendarText} returns a C{str} describing the same
</del><ins>+ L{ICalendarObject._text} returns a C{str} describing the same
</ins><span class="cx"> data provided by L{ICalendarObject.component}.
</span><span class="cx"> """
</span><span class="cx"> text = yield (yield self.calendarObjectUnderTest())._text()
</span><span class="lines">@@ -1474,8 +1476,8 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
</del><ins>+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx"> yield self.commit()
</span><span class="lines">@@ -1593,8 +1595,8 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
</del><ins>+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx"> yield self.commit()
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_attachmentspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_attachments.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_attachments.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_attachments.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -16,8 +16,8 @@
</span><span class="cx">
</span><span class="cx"> from calendarserver.tap.util import directoryFromConfig
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.value import PyCalendarValue
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.value import Value
</ins><span class="cx">
</span><span class="cx"> from twext.enterprise.dal.syntax import Delete
</span><span class="cx"> from twext.python.clsprop import classproperty
</span><span class="lines">@@ -1249,7 +1249,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-now = PyCalendarDateTime.getToday().getYear()
</del><ins>+now = DateTime.getToday().getYear()
</ins><span class="cx">
</span><span class="cx"> PLAIN_ICS = """BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="lines">@@ -1454,7 +1454,7 @@
</span><span class="cx"> cal.mainComponent().addProperty(Property(
</span><span class="cx"> "ATTACH",
</span><span class="cx"> "http://localhost/calendars/users/%s/dropbox/%s.dropbox/%s" % (home.name(), dropboxid, name,),
</span><del>- valuetype=PyCalendarValue.VALUETYPE_URI
</del><ins>+ valuetype=Value.VALUETYPE_URI
</ins><span class="cx"> ))
</span><span class="cx"> yield event.setComponent(cal)
</span><span class="cx"> yield txn.commit()
</span><span class="lines">@@ -1477,7 +1477,7 @@
</span><span class="cx"> cal.mainComponent().addProperty(Property(
</span><span class="cx"> "ATTACH",
</span><span class="cx"> "http://localhost/calendars/users/%s/dropbox/%s.dropbox/%s" % (owner_home, dropboxid, name,),
</span><del>- valuetype=PyCalendarValue.VALUETYPE_URI
</del><ins>+ valuetype=Value.VALUETYPE_URI
</ins><span class="cx"> ))
</span><span class="cx"> yield event.setComponent(cal)
</span><span class="cx"> yield txn.commit()
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_filepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_file.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_file.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_file.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx"> from txdav.caldav.datastore.test.common import (
</span><span class="cx"> CommonTests, test_event_text, event1modified_text)
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> storePath = FilePath(__file__).parent().child("calendar_store")
</span><span class="cx">
</span><span class="lines">@@ -68,7 +68,7 @@
</span><span class="cx"> storePath.copyTo(calendarPath)
</span><span class="cx">
</span><span class="cx"> # Set year values to current year
</span><del>- nowYear = PyCalendarDateTime.getToday().getYear()
</del><ins>+ nowYear = DateTime.getToday().getYear()
</ins><span class="cx"> for home in calendarPath.child("ho").child("me").children():
</span><span class="cx"> if not home.basename().startswith("."):
</span><span class="cx"> for calendar in home.children():
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_implicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_implicit.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_implicit.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_implicit.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -478,9 +478,9 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def test_validation_preservePrivateComments(self):
</del><ins>+ def test_validation_noPreservePrivateComments(self):
</ins><span class="cx"> """
</span><del>- Test that resource private comments are restored.
</del><ins>+ Test that attendee private comments are no longer restored.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> data1 = """BEGIN:VCALENDAR
</span><span class="lines">@@ -524,12 +524,65 @@
</span><span class="cx"> calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
</span><span class="cx"> calendar1 = (yield calendar_resource.component())
</span><span class="cx"> calendar1 = str(calendar1).replace("\r\n ", "")
</span><del>- self.assertTrue("X-CALENDARSERVER-PRIVATE-COMMENT:My Comment" in calendar1)
</del><ins>+ self.assertFalse("X-CALENDARSERVER-PRIVATE-COMMENT:My Comment" in calendar1)
</ins><span class="cx"> self.assertTrue("SUMMARY:Changed" in calendar1)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><ins>+ def test_validation_preserveOrganizerPrivateComments(self):
+ """
+ Test that organizer private comments are restored.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-organizer
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="urn:uuid:user01";
+ X-CALENDARSERVER-DTSTAMP=20131101T100000Z:Someone else's comment
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-organizer
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data2)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
+ yield calendar_resource.setComponent(calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF=\"urn:uuid:user01\";X-CALENDARSERVER-DTSTAMP=20131101T100000Z:Someone else's comment" in calendar1)
+ self.assertTrue("SUMMARY:Changed" in calendar1)
+ yield self.commit()
+
+
+ @inlineCallbacks
</ins><span class="cx"> def test_validation_replaceMissingToDoProperties_OrganizerAttendee(self):
</span><span class="cx"> """
</span><span class="cx"> Test that missing scheduling properties in VTODOs are recovered.
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_index_filepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_index_file.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_index_file.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_index_file.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -30,7 +30,7 @@
</span><span class="cx"> from twistedcaldav.test.util import InMemoryMemcacheProtocol
</span><span class="cx"> import twistedcaldav.test.util
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> import os
</span><span class="cx">
</span><span class="lines">@@ -311,7 +311,7 @@
</span><span class="cx"> else:
</span><span class="cx"> self.assertFalse(self.db.resourceExists(name), msg=description)
</span><span class="cx">
</span><del>- self.db.testAndUpdateIndex(PyCalendarDateTime(2020, 1, 1))
</del><ins>+ self.db.testAndUpdateIndex(DateTime(2020, 1, 1))
</ins><span class="cx"> for description, name, calendar_txt, reCreate, ok in data:
</span><span class="cx"> if ok:
</span><span class="cx"> self.assertTrue(self.db.resourceExists(name), msg=description)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_sqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_sql.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_sql.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_sql.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -27,8 +27,8 @@
</span><span class="cx"> L{txdav.caldav.datastore.test.common}.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
</ins><span class="cx">
</span><span class="cx"> from twext.enterprise.dal.syntax import Select, Parameter, Insert, Delete, \
</span><span class="cx"> Update
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx"> from twisted.internet.task import deferLater
</span><span class="cx"> from twisted.trial import unittest
</span><span class="cx">
</span><del>-from twistedcaldav import caldavxml
</del><ins>+from twistedcaldav import caldavxml, ical
</ins><span class="cx"> from twistedcaldav.caldavxml import CalendarDescription
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.dateops import datetimeMktime
</span><span class="lines">@@ -78,7 +78,7 @@
</span><span class="cx"> self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
</span><span class="cx"> yield self.populate()
</span><span class="cx">
</span><del>- self.nowYear = {"now": PyCalendarDateTime.getToday().getYear()}
</del><ins>+ self.nowYear = {"now": DateTime.getToday().getYear()}
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -474,14 +474,14 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> supported_components = set()
</span><del>- self.assertEqual(len(toCalendars), 4)
</del><ins>+ self.assertEqual(len(toCalendars), 2 + len(ical.allowedStoreComponents))
</ins><span class="cx"> for calendar in toCalendars:
</span><span class="cx"> if calendar.name() == "inbox":
</span><span class="cx"> continue
</span><span class="cx"> result = yield calendar.getSupportedComponents()
</span><span class="cx"> supported_components.add(result)
</span><span class="cx">
</span><del>- self.assertEqual(supported_components, set(("VEVENT", "VTODO",)))
</del><ins>+ self.assertEqual(supported_components, set(ical.allowedStoreComponents))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -509,7 +509,7 @@
</span><span class="cx"> result = yield calendar.getSupportedComponents()
</span><span class="cx"> supported_components.add(result)
</span><span class="cx">
</span><del>- self.assertEqual(supported_components, set(("VEVENT", "VTODO",)))
</del><ins>+ self.assertEqual(supported_components, set(ical.allowedStoreComponents))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_calendarHomeVersion(self):
</span><span class="lines">@@ -1124,7 +1124,7 @@
</span><span class="cx"> result = yield calendar.getSupportedComponents()
</span><span class="cx"> supported_components.add(result)
</span><span class="cx">
</span><del>- self.assertEqual(supported_components, set(("VEVENT", "VTODO",)))
</del><ins>+ self.assertEqual(supported_components, set(ical.allowedStoreComponents))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -1190,7 +1190,7 @@
</span><span class="cx"> self.assertEqual(home._default_events, None)
</span><span class="cx"> self.assertEqual(home._default_tasks, None)
</span><span class="cx"> calendar1 = yield home.calendarWithName("calendar_1")
</span><del>- yield home.setDefaultCalendar(calendar1, False)
</del><ins>+ yield home.setDefaultCalendar(calendar1, "VEVENT")
</ins><span class="cx"> self.assertEqual(home._default_events, calendar1._resourceID)
</span><span class="cx"> self.assertEqual(home._default_tasks, None)
</span><span class="cx"> yield self.commit()
</span><span class="lines">@@ -1198,7 +1198,7 @@
</span><span class="cx"> home = yield self.homeUnderTest(name="home_defaults")
</span><span class="cx"> calendar1 = yield home.calendarWithName("calendar_1")
</span><span class="cx"> calendar2 = yield home.calendarWithName("calendar_1-vtodo")
</span><del>- yield self.failUnlessFailure(home.setDefaultCalendar(calendar2, False), InvalidDefaultCalendar)
</del><ins>+ yield self.failUnlessFailure(home.setDefaultCalendar(calendar2, "VEVENT"), InvalidDefaultCalendar)
</ins><span class="cx"> self.assertEqual(home._default_events, calendar1._resourceID)
</span><span class="cx"> self.assertEqual(home._default_tasks, None)
</span><span class="cx"> yield self.commit()
</span><span class="lines">@@ -1206,20 +1206,20 @@
</span><span class="cx"> home = yield self.homeUnderTest(name="home_defaults")
</span><span class="cx"> calendar1 = yield home.calendarWithName("calendar_1")
</span><span class="cx"> calendar2 = yield home.calendarWithName("calendar_1-vtodo")
</span><del>- yield home.setDefaultCalendar(calendar2, True)
</del><ins>+ yield home.setDefaultCalendar(calendar2, "VTODO")
</ins><span class="cx"> self.assertEqual(home._default_events, calendar1._resourceID)
</span><span class="cx"> self.assertEqual(home._default_tasks, calendar2._resourceID)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx"> home = yield self.homeUnderTest(name="home_defaults")
</span><span class="cx"> calendar1 = yield home.calendarWithName("inbox")
</span><del>- yield self.failUnlessFailure(home.setDefaultCalendar(calendar1, False), InvalidDefaultCalendar)
</del><ins>+ yield self.failUnlessFailure(home.setDefaultCalendar(calendar1, "VEVENT"), InvalidDefaultCalendar)
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx"> home = yield self.homeUnderTest(name="home_defaults")
</span><span class="cx"> home_other = yield self.homeUnderTest(name="home_splits")
</span><span class="cx"> calendar1 = yield home_other.calendarWithName("calendar_1")
</span><del>- yield self.failUnlessFailure(home.setDefaultCalendar(calendar1, False), InvalidDefaultCalendar)
</del><ins>+ yield self.failUnlessFailure(home.setDefaultCalendar(calendar1, "VEVENT"), InvalidDefaultCalendar)
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1377,38 +1377,38 @@
</span><span class="cx"> self.assertEqual(rmax.getYear(), nowYear + 1)
</span><span class="cx">
</span><span class="cx"> # Fully within range
</span><del>- testMin = PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- testMax = PyCalendarDateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ testMin = DateTime(nowYear, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ testMax = DateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> result = yield index.notExpandedWithin(testMin, testMax)
</span><span class="cx"> self.assertEqual(result, [])
</span><span class="cx">
</span><span class="cx"> # Upper bound exceeded
</span><del>- testMin = PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- testMax = PyCalendarDateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ testMin = DateTime(nowYear, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ testMax = DateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> result = yield index.notExpandedWithin(testMin, testMax)
</span><span class="cx"> self.assertEqual(result, ["indexing.ics"])
</span><span class="cx">
</span><span class="cx"> # Lower bound exceeded
</span><del>- testMin = PyCalendarDateTime(nowYear - 5, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- testMax = PyCalendarDateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ testMin = DateTime(nowYear - 5, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ testMax = DateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> result = yield index.notExpandedWithin(testMin, testMax)
</span><span class="cx"> self.assertEqual(result, ["indexing.ics"])
</span><span class="cx">
</span><span class="cx"> # Lower and upper bounds exceeded
</span><del>- testMin = PyCalendarDateTime(nowYear - 5, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
- testMax = PyCalendarDateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ testMin = DateTime(nowYear - 5, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
+ testMax = DateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> result = yield index.notExpandedWithin(testMin, testMax)
</span><span class="cx"> self.assertEqual(result, ["indexing.ics"])
</span><span class="cx">
</span><span class="cx"> # Lower none within range
</span><span class="cx"> testMin = None
</span><del>- testMax = PyCalendarDateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ testMax = DateTime(nowYear + 1, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> result = yield index.notExpandedWithin(testMin, testMax)
</span><span class="cx"> self.assertEqual(result, [])
</span><span class="cx">
</span><span class="cx"> # Lower none and upper bounds exceeded
</span><span class="cx"> testMin = None
</span><del>- testMax = PyCalendarDateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
</del><ins>+ testMax = DateTime(nowYear + 5, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
</ins><span class="cx"> result = yield index.notExpandedWithin(testMin, testMax)
</span><span class="cx"> self.assertEqual(result, ["indexing.ics"])
</span><span class="cx">
</span><span class="lines">@@ -2154,7 +2154,7 @@
</span><span class="cx">
</span><span class="cx"> self.subs = {}
</span><span class="cx">
</span><del>- self.now = PyCalendarDateTime.getNowUTC()
</del><ins>+ self.now = DateTime.getNowUTC()
</ins><span class="cx"> self.now.setHHMMSS(0, 0, 0)
</span><span class="cx">
</span><span class="cx"> self.subs["now"] = self.now
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretesttest_utilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_util.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_util.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/test_util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -349,7 +349,8 @@
</span><span class="cx"> "inbox": {},
</span><span class="cx"> # XXX: implementation is configuration-sensitive regarding the
</span><span class="cx"> # 'tasks' calendar and it shouldn't be.
</span><del>- "tasks": {}
</del><ins>+ "tasks": {},
+ "polls": {},
</ins><span class="cx"> }
</span><span class="cx"> }, self.storeUnderTest())
</span><span class="cx"> txn = self.transactionUnderTest()
</span><span class="lines">@@ -412,7 +413,7 @@
</span><span class="cx"> c1 = {"1.ics": self.sampleEvent("uid1")}
</span><span class="cx"> if c2 is None:
</span><span class="cx"> c2 = {"2.ics": self.sampleEvent("uid2")}
</span><del>- defaults = {"calendar": {}, "inbox": {}, "tasks": {}}
</del><ins>+ defaults = {"calendar": {}, "inbox": {}, "tasks": {}, "polls": {}}
</ins><span class="cx"> def conflicted(caldata):
</span><span class="cx"> d = defaults.copy()
</span><span class="cx"> d.update(conflicted=caldata)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoretestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/util.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/util.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/test/util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -61,7 +61,6 @@
</span><span class="cx"> fullName,
</span><span class="cx"> calendarUserAddresses,
</span><span class="cx"> cutype="INDIVIDUAL",
</span><del>- locallyHosted=True,
</del><span class="cx"> thisServer=True,
</span><span class="cx"> ):
</span><span class="cx">
</span><span class="lines">@@ -72,7 +71,6 @@
</span><span class="cx"> self.displayName = self.fullName if self.fullName else self.shortNames[0]
</span><span class="cx"> self.calendarUserAddresses = calendarUserAddresses
</span><span class="cx"> self.cutype = cutype
</span><del>- self._locallyHosted = locallyHosted
</del><span class="cx"> self._thisServer = thisServer
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -92,10 +90,6 @@
</span><span class="cx"> return cua
</span><span class="cx">
</span><span class="cx">
</span><del>- def locallyHosted(self):
- return self._locallyHosted
-
-
</del><span class="cx"> def thisServer(self):
</span><span class="cx"> return self._thisServer
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavdatastoreutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/util.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/util.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/datastore/util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> from twext.python.vcomponent import InvalidICalendarDataError
</span><span class="cx"> from twext.python.vcomponent import VComponent
</span><span class="cx">
</span><ins>+from twistedcaldav import ical
</ins><span class="cx"> from twistedcaldav.datafilters.hiddeninstance import HiddenInstanceFilter
</span><span class="cx"> from twistedcaldav.datafilters.privateevents import PrivateEventFilter
</span><span class="cx"> from twistedcaldav.ical import PERUSER_UID
</span><span class="lines">@@ -380,9 +381,11 @@
</span><span class="cx"> """
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> if not merge:
</span><del>- yield outHome.removeCalendarWithName("calendar")
</del><span class="cx"> if config.RestrictCalendarsToOneComponentType:
</span><del>- yield outHome.removeCalendarWithName("tasks")
</del><ins>+ for name in ical.allowedStoreComponents:
+ yield outHome.removeCalendarWithName(outHome._componentCalendarName[name])
+ else:
+ yield outHome.removeCalendarWithName("calendar")
</ins><span class="cx"> yield outHome.removeCalendarWithName("inbox")
</span><span class="cx">
</span><span class="cx"> outHome.properties().update(inHome.properties())
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavicalendardirectoryservicepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendardirectoryservice.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendardirectoryservice.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendardirectoryservice.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -60,18 +60,9 @@
</span><span class="cx"> @rtype: C{str}
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def locallyHosted(): #@NoSelf
- """
- Indicates whether the record is host on this specific server "pod".
-
- @return: C{True} if locally hosted.
- @rtype: C{bool}
- """
-
</del><span class="cx"> def thisServer(): #@NoSelf
</span><span class="cx"> """
</span><del>- Indicates whether the record is hosted on this server or another "pod"
- that hosts the same directory service.
</del><ins>+ Indicates whether the record is hosted on this server "pod".
</ins><span class="cx">
</span><span class="cx"> @return: C{True} if hosted by this service.
</span><span class="cx"> @rtype: C{bool}
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcaldavicalendarstorepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendarstore.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendarstore.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/caldav/icalendarstore.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -363,9 +363,9 @@
</span><span class="cx"> instances that occur within the time range that begins at
</span><span class="cx"> C{start} and ends at C{end}.
</span><span class="cx">
</span><del>- @param start: a L{PyCalendarDateTime}.
- @param end: a L{PyCalendarDateTime}.
- @param timeZone: a L{PyCalendarTimezone}.
</del><ins>+ @param start: a L{DateTime}.
+ @param end: a L{DateTime}.
+ @param timeZone: a L{Timezone}.
</ins><span class="cx"> @return: an iterable of L{ICalendarObject}s.
</span><span class="cx"> """
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcarddavdatastoretestcommonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/common.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/common.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/carddav/datastore/test/common.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -39,7 +39,9 @@
</span><span class="cx"> from txdav.common.icommondatastore import ObjectResourceNameAlreadyExistsError
</span><span class="cx"> from txdav.idav import IPropertyStore, IDataStore
</span><span class="cx"> from txdav.xml.element import WebDAVUnknownElement
</span><ins>+from calendarserver.push.util import PushPriority
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> storePath = FilePath(__file__).parent().child("addressbook_store")
</span><span class="cx">
</span><span class="cx"> home1Root = storePath.child("ho").child("me").child("home1")
</span><span class="lines">@@ -372,7 +374,7 @@
</span><span class="cx"> yield home.removeAddressBookWithName(name)
</span><span class="cx"> self.assertNotIdentical((yield home.addressbookWithName(name)), None)
</span><span class="cx"> # notify is called prior to commit
</span><del>- self.assertTrue("/CardDAV/example.com/home1/" in self.notifierFactory.history)
</del><ins>+ self.assertTrue(("/CardDAV/example.com/home1/", PushPriority.high) in self.notifierFactory.history)
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx"> # Make sure it's available in a new transaction; i.e. test the commit.
</span><span class="lines">@@ -399,8 +401,8 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
</del><ins>+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -532,8 +534,8 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
</del><ins>+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -693,8 +695,8 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
</del><ins>+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -809,8 +811,8 @@
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set(self.notifierFactory.history),
</span><span class="cx"> set([
</span><del>- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
</del><ins>+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
</ins><span class="cx"> ])
</span><span class="cx"> )
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -27,7 +27,7 @@
</span><span class="cx">
</span><span class="cx"> from cStringIO import StringIO
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from twext.enterprise.dal.syntax import (
</span><span class="cx"> Delete, utcNowSQL, Union, Insert, Len, Max, Parameter, SavepointAction,
</span><span class="lines">@@ -74,6 +74,7 @@
</span><span class="cx"> from txdav.common.inotifications import INotificationCollection, \
</span><span class="cx"> INotificationObject
</span><span class="cx"> from txdav.xml.parser import WebDAVDocument
</span><ins>+from txdav.idav import ChangeCategory
</ins><span class="cx">
</span><span class="cx"> from uuid import uuid4, UUID
</span><span class="cx">
</span><span class="lines">@@ -1107,7 +1108,7 @@
</span><span class="cx"> def eventsOlderThan(self, cutoff, batchSize=None):
</span><span class="cx"> """
</span><span class="cx"> Return up to the oldest batchSize events which exist completely earlier
</span><del>- than "cutoff" (PyCalendarDateTime)
</del><ins>+ than "cutoff" (DateTime)
</ins><span class="cx">
</span><span class="cx"> Returns a deferred to a list of (uid, calendarName, eventName, maxDate)
</span><span class="cx"> tuples.
</span><span class="lines">@@ -1115,7 +1116,7 @@
</span><span class="cx">
</span><span class="cx"> # Make sure cut off is after any lower limit truncation in the DB
</span><span class="cx"> if config.FreeBusyIndexLowerLimitDays:
</span><del>- truncateLowerLimit = PyCalendarDateTime.getToday()
</del><ins>+ truncateLowerLimit = DateTime.getToday()
</ins><span class="cx"> truncateLowerLimit.offsetDay(-config.FreeBusyIndexLowerLimitDays)
</span><span class="cx"> if cutoff < truncateLowerLimit:
</span><span class="cx"> raise ValueError("Cannot query events older than %s" % (truncateLowerLimit.getText(),))
</span><span class="lines">@@ -1133,7 +1134,7 @@
</span><span class="cx">
</span><span class="cx"> # Make sure cut off is after any lower limit truncation in the DB
</span><span class="cx"> if config.FreeBusyIndexLowerLimitDays:
</span><del>- truncateLowerLimit = PyCalendarDateTime.getToday()
</del><ins>+ truncateLowerLimit = DateTime.getToday()
</ins><span class="cx"> truncateLowerLimit.offsetDay(-config.FreeBusyIndexLowerLimitDays)
</span><span class="cx"> if cutoff < truncateLowerLimit:
</span><span class="cx"> raise ValueError("Cannot query events older than %s" % (truncateLowerLimit.getText(),))
</span><span class="lines">@@ -2203,7 +2204,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def notifyChanged(self):
</del><ins>+ def notifyChanged(self, category=ChangeCategory.default):
</ins><span class="cx"> """
</span><span class="cx"> Send notifications, change sync token and bump last modified because
</span><span class="cx"> the resource has changed. We ensure we only do this once per object
</span><span class="lines">@@ -2227,7 +2228,7 @@
</span><span class="cx"> # push notifiers add their work items immediately
</span><span class="cx"> notifier = self._notifiers.get("push", None)
</span><span class="cx"> if notifier:
</span><del>- yield notifier.notify(self._txn)
</del><ins>+ yield notifier.notify(self._txn, priority=category.value)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classproperty
</span><span class="lines">@@ -4335,11 +4336,11 @@
</span><span class="cx"> return self.ownerHome().notifierID()
</span><span class="cx">
</span><span class="cx">
</span><del>- def notifyChanged(self):
</del><ins>+ def notifyChanged(self, category=ChangeCategory.default):
</ins><span class="cx"> """
</span><span class="cx"> Send notifications when a child resource is changed.
</span><span class="cx"> """
</span><del>- return self._notifyChanged(property_change=False)
</del><ins>+ return self._notifyChanged(property_change=False, category=category)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def notifyPropertyChanged(self):
</span><span class="lines">@@ -4350,7 +4351,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def _notifyChanged(self, property_change=False):
</del><ins>+ def _notifyChanged(self, property_change=False,
+ category=ChangeCategory.default):
</ins><span class="cx"> """
</span><span class="cx"> Send notifications, change sync token and bump last modified because
</span><span class="cx"> the resource has changed. We ensure we only do this once per object
</span><span class="lines">@@ -4386,7 +4388,7 @@
</span><span class="cx"> # push notifiers add their work items immediately
</span><span class="cx"> notifier = self._notifiers.get("push", None)
</span><span class="cx"> if notifier:
</span><del>- yield notifier.notify(self._txn)
</del><ins>+ yield notifier.notify(self._txn, priority=category.value)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classproperty
</span><span class="lines">@@ -5200,7 +5202,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def notifyChanged(self):
</del><ins>+ def notifyChanged(self, category=ChangeCategory.default):
</ins><span class="cx"> """
</span><span class="cx"> Send notifications, change sync token and bump last modified because
</span><span class="cx"> the resource has changed. We ensure we only do this once per object
</span><span class="lines">@@ -5219,7 +5221,7 @@
</span><span class="cx"> # push notifiers add their work items immediately
</span><span class="cx"> notifier = self._notifiers.get("push", None)
</span><span class="cx"> if notifier:
</span><del>- yield notifier.notify(self._txn)
</del><ins>+ yield notifier.notify(self._txn, priority=category.value)
</ins><span class="cx">
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_legacypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_legacy.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_legacy.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_legacy.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -43,8 +43,8 @@
</span><span class="cx"> from twext.python.clsprop import classproperty
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
</del><ins>+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -566,7 +566,7 @@
</span><span class="cx"> )
</span><span class="cx"> if qualifiers is not None:
</span><span class="cx">
</span><del>- today = PyCalendarDateTime.getToday()
</del><ins>+ today = DateTime.getToday()
</ins><span class="cx">
</span><span class="cx"> # Determine how far we need to extend the current expansion of
</span><span class="cx"> # events. If we have an open-ended time-range we will expand
</span><span class="lines">@@ -578,11 +578,11 @@
</span><span class="cx"> maxDate = maxDate.duplicate()
</span><span class="cx"> maxDate.offsetDay(1)
</span><span class="cx"> maxDate.setDateOnly(True)
</span><del>- upperLimit = today + PyCalendarDuration(days=config.FreeBusyIndexExpandMaxDays)
</del><ins>+ upperLimit = today + Duration(days=config.FreeBusyIndexExpandMaxDays)
</ins><span class="cx"> if maxDate > upperLimit:
</span><span class="cx"> raise TimeRangeUpperLimit(upperLimit)
</span><span class="cx"> if isStartDate:
</span><del>- maxDate += PyCalendarDuration(days=365)
</del><ins>+ maxDate += Duration(days=365)
</ins><span class="cx">
</span><span class="cx"> # Determine if the start date is too early for the restricted range we
</span><span class="cx"> # are applying. If it is today or later we don't need to worry about truncation
</span><span class="lines">@@ -591,7 +591,7 @@
</span><span class="cx"> if minDate >= today:
</span><span class="cx"> minDate = None
</span><span class="cx"> if minDate is not None and config.FreeBusyIndexLowerLimitDays:
</span><del>- truncateLowerLimit = today - PyCalendarDuration(days=config.FreeBusyIndexLowerLimitDays)
</del><ins>+ truncateLowerLimit = today - Duration(days=config.FreeBusyIndexLowerLimitDays)
</ins><span class="cx"> if minDate < truncateLowerLimit:
</span><span class="cx"> raise TimeRangeLowerLimit(truncateLowerLimit)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemacurrentoracledialectsql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current-oracle-dialect.sql (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current-oracle-dialect.sql        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current-oracle-dialect.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx"> "QUOTA_USED_BYTES" integer default 0 not null,
</span><span class="cx"> "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
</span><span class="cx"> "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
</span><ins>+ "DEFAULT_POLLS" integer default null references CALENDAR on delete set null,
</ins><span class="cx"> "ALARM_VEVENT_TIMED" nclob default null,
</span><span class="cx"> "ALARM_VEVENT_ALLDAY" nclob default null,
</span><span class="cx"> "ALARM_VTODO_TIMED" nclob default null,
</span><span class="lines">@@ -349,7 +350,8 @@
</span><span class="cx"> create table PUSH_NOTIFICATION_WORK (
</span><span class="cx"> "WORK_ID" integer primary key not null,
</span><span class="cx"> "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
</span><del>- "PUSH_ID" nvarchar2(255)
</del><ins>+ "PUSH_ID" nvarchar2(255),
+ "PRIORITY" integer not null
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create table GROUP_CACHER_POLLING_WORK (
</span><span class="lines">@@ -368,7 +370,7 @@
</span><span class="cx"> "VALUE" nvarchar2(255)
</span><span class="cx"> );
</span><span class="cx">
</span><del>-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '26');
</del><ins>+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '29');
</ins><span class="cx"> insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
</span><span class="cx"> insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
</span><span class="cx"> create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
</span><span class="lines">@@ -379,6 +381,10 @@
</span><span class="cx"> DEFAULT_TASKS
</span><span class="cx"> );
</span><span class="cx">
</span><ins>+create index CALENDAR_HOME_METADAT_910264ce on CALENDAR_HOME_METADATA (
+ DEFAULT_POLLS
+);
+
</ins><span class="cx"> create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
</span><span class="cx"> NOTIFICATION_HOME_RESOURCE_ID
</span><span class="cx"> );
</span><span class="lines">@@ -450,9 +456,11 @@
</span><span class="cx"> CALENDAR_RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx">
</span><del>-create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
</del><ins>+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
</ins><span class="cx"> CALENDAR_RESOURCE_ID,
</span><del>- RESOURCE_NAME
</del><ins>+ RESOURCE_NAME,
+ DELETED,
+ REVISION
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
</span><span class="lines">@@ -465,9 +473,11 @@
</span><span class="cx"> OWNER_HOME_RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx">
</span><del>-create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
</del><ins>+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
</ins><span class="cx"> OWNER_HOME_RESOURCE_ID,
</span><del>- RESOURCE_NAME
</del><ins>+ RESOURCE_NAME,
+ DELETED,
+ REVISION
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemacurrentsql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/current.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -74,6 +74,7 @@
</span><span class="cx"> QUOTA_USED_BYTES integer default 0 not null,
</span><span class="cx"> DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
</span><span class="cx"> DEFAULT_TASKS integer default null references CALENDAR on delete set null,
</span><ins>+ DEFAULT_POLLS integer default null references CALENDAR on delete set null,
</ins><span class="cx"> ALARM_VEVENT_TIMED text default null,
</span><span class="cx"> ALARM_VEVENT_ALLDAY text default null,
</span><span class="cx"> ALARM_VTODO_TIMED text default null,
</span><span class="lines">@@ -87,6 +88,8 @@
</span><span class="cx">         CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
</span><span class="cx"> create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
</span><span class="cx">         CALENDAR_HOME_METADATA(DEFAULT_TASKS);
</span><ins>+create index CALENDAR_HOME_METADATA_DEFAULT_POLLS on
+        CALENDAR_HOME_METADATA(DEFAULT_POLLS);
</ins><span class="cx">
</span><span class="cx"> -----------------------
</span><span class="cx"> -- Calendar Metadata --
</span><span class="lines">@@ -504,7 +507,7 @@
</span><span class="cx"> MESSAGE                 text, -- FIXME: xml?
</span><span class="cx">
</span><span class="cx"> primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
</span><del>- unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
</del><ins>+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_GROUP_BIND_RESOURCE_ID on
</span><span class="lines">@@ -534,8 +537,8 @@
</span><span class="cx"> create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
</span><span class="cx"> on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
</span><span class="cx">
</span><del>-create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
- on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
</del><ins>+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
</ins><span class="cx">
</span><span class="cx"> create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
</span><span class="cx"> on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
</span><span class="lines">@@ -558,8 +561,8 @@
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
</span><span class="cx"> on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
</span><span class="cx">
</span><del>-create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
- on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME);
</del><ins>+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
</ins><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
</span><span class="cx"> on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
</span><span class="lines">@@ -669,7 +672,8 @@
</span><span class="cx"> create table PUSH_NOTIFICATION_WORK (
</span><span class="cx"> WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
</span><span class="cx"> NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
</span><del>- PUSH_ID varchar(255) not null
</del><ins>+ PUSH_ID varchar(255) not null,
+ PRIORITY integer not null -- 1:low 5:medium 10:high
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> -----------------
</span><span class="lines">@@ -704,6 +708,6 @@
</span><span class="cx"> VALUE varchar(255)
</span><span class="cx"> );
</span><span class="cx">
</span><del>-insert into CALENDARSERVER values ('VERSION', '26');
</del><ins>+insert into CALENDARSERVER values ('VERSION', '29');
</ins><span class="cx"> insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
</span><span class="cx"> insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldoracledialectv24sql"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,491 +0,0 @@
</span><del>-create sequence RESOURCE_ID_SEQ;
-create sequence INSTANCE_ID_SEQ;
-create sequence ATTACHMENT_ID_SEQ;
-create sequence REVISION_SEQ;
-create sequence WORKITEM_SEQ;
-create table NODE_INFO (
- "HOSTNAME" nvarchar2(255),
- "PID" integer not null,
- "PORT" integer not null,
- "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
- primary key("HOSTNAME", "PORT")
-);
-
-create table NAMED_LOCK (
- "LOCK_NAME" nvarchar2(255) primary key
-);
-
-create table CALENDAR_HOME (
- "RESOURCE_ID" integer primary key,
- "OWNER_UID" nvarchar2(255) unique,
- "DATAVERSION" integer default 0 not null
-);
-
-create table CALENDAR (
- "RESOURCE_ID" integer primary key
-);
-
-create table CALENDAR_HOME_METADATA (
- "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
- "QUOTA_USED_BYTES" integer default 0 not null,
- "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
- "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
- "ALARM_VEVENT_TIMED" nclob default null,
- "ALARM_VEVENT_ALLDAY" nclob default null,
- "ALARM_VTODO_TIMED" nclob default null,
- "ALARM_VTODO_ALLDAY" nclob default null,
- "AVAILABILITY" nclob default null,
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
-);
-
-create table CALENDAR_METADATA (
- "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
- "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
-);
-
-create table NOTIFICATION_HOME (
- "RESOURCE_ID" integer primary key,
- "OWNER_UID" nvarchar2(255) unique
-);
-
-create table NOTIFICATION (
- "RESOURCE_ID" integer primary key,
- "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
- "NOTIFICATION_UID" nvarchar2(255),
- "XML_TYPE" nvarchar2(255),
- "XML_DATA" nclob,
- "MD5" nchar(32),
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
-);
-
-create table CALENDAR_BIND (
- "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
- "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
- "CALENDAR_RESOURCE_NAME" nvarchar2(255),
- "BIND_MODE" integer not null,
- "BIND_STATUS" integer not null,
- "BIND_REVISION" integer default 0 not null,
- "MESSAGE" nclob,
- "TRANSP" integer default 0 not null,
- "ALARM_VEVENT_TIMED" nclob default null,
- "ALARM_VEVENT_ALLDAY" nclob default null,
- "ALARM_VTODO_TIMED" nclob default null,
- "ALARM_VTODO_ALLDAY" nclob default null,
- "TIMEZONE" nclob default null,
- primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
- unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
-);
-
-create table CALENDAR_BIND_MODE (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
-insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
-insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
-insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
-create table CALENDAR_BIND_STATUS (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
-insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
-insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
-insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
-create table CALENDAR_TRANSP (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
-insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
-create table CALENDAR_OBJECT (
- "RESOURCE_ID" integer primary key,
- "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
- "RESOURCE_NAME" nvarchar2(255),
- "ICALENDAR_TEXT" nclob,
- "ICALENDAR_UID" nvarchar2(255),
- "ICALENDAR_TYPE" nvarchar2(255),
- "ATTACHMENTS_MODE" integer default 0 not null,
- "DROPBOX_ID" nvarchar2(255),
- "ORGANIZER" nvarchar2(255),
- "RECURRANCE_MIN" date,
- "RECURRANCE_MAX" date,
- "ACCESS" integer default 0 not null,
- "SCHEDULE_OBJECT" integer default 0,
- "SCHEDULE_TAG" nvarchar2(36) default null,
- "SCHEDULE_ETAGS" nclob default null,
- "PRIVATE_COMMENTS" integer default 0 not null,
- "MD5" nchar(32),
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
-);
-
-create table CALENDAR_OBJECT_ATTACHMENTS_MO (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
-insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
-insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
-create table CALENDAR_ACCESS_TYPE (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(32) unique
-);
-
-insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
-insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
-insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
-insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
-insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
-create table TIME_RANGE (
- "INSTANCE_ID" integer primary key,
- "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
- "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
- "FLOATING" integer not null,
- "START_DATE" timestamp not null,
- "END_DATE" timestamp not null,
- "FBTYPE" integer not null,
- "TRANSPARENT" integer not null
-);
-
-create table FREE_BUSY_TYPE (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
-insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
-insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
-insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
-insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
-create table TRANSPARENCY (
- "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
- "USER_ID" nvarchar2(255),
- "TRANSPARENT" integer not null
-);
-
-create table ATTACHMENT (
- "ATTACHMENT_ID" integer primary key,
- "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
- "DROPBOX_ID" nvarchar2(255),
- "CONTENT_TYPE" nvarchar2(255),
- "SIZE" integer not null,
- "MD5" nchar(32),
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "PATH" nvarchar2(1024)
-);
-
-create table ATTACHMENT_CALENDAR_OBJECT (
- "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
- "MANAGED_ID" nvarchar2(255),
- "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
- primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
- unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
-);
-
-create table RESOURCE_PROPERTY (
- "RESOURCE_ID" integer not null,
- "NAME" nvarchar2(255),
- "VALUE" nclob,
- "VIEWER_UID" nvarchar2(255),
- primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
-);
-
-create table ADDRESSBOOK_HOME (
- "RESOURCE_ID" integer primary key,
- "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
- "OWNER_UID" nvarchar2(255) unique,
- "DATAVERSION" integer default 0 not null
-);
-
-create table ADDRESSBOOK_HOME_METADATA (
- "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
- "QUOTA_USED_BYTES" integer default 0 not null,
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
-);
-
-create table SHARED_ADDRESSBOOK_BIND (
- "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
- "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
- "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
- "BIND_MODE" integer not null,
- "BIND_STATUS" integer not null,
- "BIND_REVISION" integer default 0 not null,
- "MESSAGE" nclob,
- primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
-);
-
-create table ADDRESSBOOK_OBJECT (
- "RESOURCE_ID" integer primary key,
- "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
- "RESOURCE_NAME" nvarchar2(255),
- "VCARD_TEXT" nclob,
- "VCARD_UID" nvarchar2(255),
- "KIND" integer not null,
- "MD5" nchar(32),
- "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
-);
-
-create table ADDRESSBOOK_OBJECT_KIND (
- "ID" integer primary key,
- "DESCRIPTION" nvarchar2(16) unique
-);
-
-insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
-insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
-insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
-insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
-create table ABO_MEMBERS (
- "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
- "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
- "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
- primary key("GROUP_ID", "MEMBER_ID")
-);
-
-create table ABO_FOREIGN_MEMBERS (
- "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
- "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
- "MEMBER_ADDRESS" nvarchar2(255),
- primary key("GROUP_ID", "MEMBER_ADDRESS")
-);
-
-create table SHARED_GROUP_BIND (
- "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
- "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
- "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
- "BIND_MODE" integer not null,
- "BIND_STATUS" integer not null,
- "BIND_REVISION" integer default 0 not null,
- "MESSAGE" nclob,
- primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
-);
-
-create table CALENDAR_OBJECT_REVISIONS (
- "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
- "CALENDAR_RESOURCE_ID" integer references CALENDAR,
- "CALENDAR_NAME" nvarchar2(255) default null,
- "RESOURCE_NAME" nvarchar2(255),
- "REVISION" integer not null,
- "DELETED" integer not null
-);
-
-create table ADDRESSBOOK_OBJECT_REVISIONS (
- "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
- "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
- "ADDRESSBOOK_NAME" nvarchar2(255) default null,
- "RESOURCE_NAME" nvarchar2(255),
- "REVISION" integer not null,
- "DELETED" integer not null
-);
-
-create table NOTIFICATION_OBJECT_REVISIONS (
- "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
- "RESOURCE_NAME" nvarchar2(255),
- "REVISION" integer not null,
- "DELETED" integer not null,
- unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
-);
-
-create table APN_SUBSCRIPTIONS (
- "TOKEN" nvarchar2(255),
- "RESOURCE_KEY" nvarchar2(255),
- "MODIFIED" integer not null,
- "SUBSCRIBER_GUID" nvarchar2(255),
- "USER_AGENT" nvarchar2(255) default null,
- "IP_ADDR" nvarchar2(255) default null,
- primary key("TOKEN", "RESOURCE_KEY")
-);
-
-create table IMIP_TOKENS (
- "TOKEN" nvarchar2(255),
- "ORGANIZER" nvarchar2(255),
- "ATTENDEE" nvarchar2(255),
- "ICALUID" nvarchar2(255),
- "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- primary key("ORGANIZER", "ATTENDEE", "ICALUID")
-);
-
-create table IMIP_INVITATION_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "FROM_ADDR" nvarchar2(255),
- "TO_ADDR" nvarchar2(255),
- "ICALENDAR_TEXT" nclob
-);
-
-create table IMIP_POLLING_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
-);
-
-create table IMIP_REPLY_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "ORGANIZER" nvarchar2(255),
- "ATTENDEE" nvarchar2(255),
- "ICALENDAR_TEXT" nclob
-);
-
-create table PUSH_NOTIFICATION_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "PUSH_ID" nvarchar2(255)
-);
-
-create table GROUP_CACHER_POLLING_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
-);
-
-create table CALENDAR_OBJECT_SPLITTER_WORK (
- "WORK_ID" integer primary key not null,
- "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
-);
-
-create table CALENDARSERVER (
- "NAME" nvarchar2(255) primary key,
- "VALUE" nvarchar2(255)
-);
-
-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '24');
-insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
-insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
-create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
- DEFAULT_EVENTS
-);
-
-create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
- DEFAULT_TASKS
-);
-
-create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
- NOTIFICATION_HOME_RESOURCE_ID
-);
-
-create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
- CALENDAR_RESOURCE_ID
-);
-
-create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
- CALENDAR_RESOURCE_ID,
- ICALENDAR_UID
-);
-
-create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
- CALENDAR_RESOURCE_ID,
- RECURRANCE_MAX
-);
-
-create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
- ICALENDAR_UID
-);
-
-create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
- DROPBOX_ID
-);
-
-create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
- CALENDAR_RESOURCE_ID
-);
-
-create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
- CALENDAR_OBJECT_RESOURCE_ID
-);
-
-create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
- TIME_RANGE_INSTANCE_ID
-);
-
-create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
- CALENDAR_HOME_RESOURCE_ID
-);
-
-create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
- CALENDAR_OBJECT_RESOURCE_ID
-);
-
-create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
- OWNER_HOME_RESOURCE_ID
-);
-
-create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
- ADDRESSBOOK_ID
-);
-
-create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
- MEMBER_ID
-);
-
-create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
- ADDRESSBOOK_ID
-);
-
-create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
- GROUP_RESOURCE_ID
-);
-
-create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
- CALENDAR_HOME_RESOURCE_ID,
- CALENDAR_RESOURCE_ID
-);
-
-create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
- CALENDAR_RESOURCE_ID,
- RESOURCE_NAME
-);
-
-create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
- CALENDAR_RESOURCE_ID,
- REVISION
-);
-
-create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
- ADDRESSBOOK_HOME_RESOURCE_ID,
- OWNER_HOME_RESOURCE_ID
-);
-
-create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
- OWNER_HOME_RESOURCE_ID,
- RESOURCE_NAME
-);
-
-create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
- OWNER_HOME_RESOURCE_ID,
- REVISION
-);
-
-create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
- NOTIFICATION_HOME_RESOURCE_ID,
- REVISION
-);
-
-create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
- RESOURCE_KEY
-);
-
-create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
- TOKEN
-);
-
-create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
- RESOURCE_ID
-);
-
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldoracledialectv24sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaoldoracledialectv24sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,491 @@
</span><ins>+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '24');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
+ DEFAULT_EVENTS
+);
+
+create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
+ DEFAULT_TASKS
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
+ MEMBER_ID
+);
+
+create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+ RESOURCE_ID
+);
+
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldoracledialectv26sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaoldoracledialectv26sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,495 @@
</span><ins>+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '26');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
+ DEFAULT_EVENTS
+);
+
+create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
+ DEFAULT_TASKS
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
+ MEMBER_ID
+);
+
+create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+ RESOURCE_ID
+);
+
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldoracledialectv27sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaoldoracledialectv27sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v27.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v27.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v27.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v27.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,500 @@
</span><ins>+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_POLLS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '27');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
+ DEFAULT_EVENTS
+);
+
+create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
+ DEFAULT_TASKS
+);
+
+create index CALENDAR_HOME_METADAT_910264ce on CALENDAR_HOME_METADATA (
+ DEFAULT_POLLS
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
+ MEMBER_ID
+);
+
+create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+ RESOURCE_ID
+);
+
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldoracledialectv28sql"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v28.sql (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v28.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/oracle-dialect/v28.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,501 @@
</span><ins>+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_POLLS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255),
+ "PRIORITY" integer not null
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '28');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
+ DEFAULT_EVENTS
+);
+
+create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
+ DEFAULT_TASKS
+);
+
+create index CALENDAR_HOME_METADAT_910264ce on CALENDAR_HOME_METADATA (
+ DEFAULT_POLLS
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
+ MEMBER_ID
+);
+
+create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
+ ADDRESSBOOK_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+ RESOURCE_ID
+);
+
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldpostgresdialectv24sql"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,700 +0,0 @@
</span><del>--- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
-
-----
--- Copyright (c) 2010-2013 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.
-----
-
-
------------------
--- Resource ID --
------------------
-
-create sequence RESOURCE_ID_SEQ;
-
-
--------------------------
--- Cluster Bookkeeping --
--------------------------
-
--- Information about a process connected to this database.
-
--- Note that this must match the node info schema in twext.enterprise.queue.
-create table NODE_INFO (
- HOSTNAME varchar(255) not null,
- PID integer not null,
- PORT integer not null,
- TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
-
- primary key (HOSTNAME, PORT)
-);
-
--- Unique named locks. This table should always be empty, but rows are
--- temporarily created in order to prevent undesirable concurrency.
-create table NAMED_LOCK (
- LOCK_NAME varchar(255) primary key
-);
-
-
--------------------
--- Calendar Home --
--------------------
-
-create table CALENDAR_HOME (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- OWNER_UID varchar(255) not null unique, -- implicit index
- DATAVERSION integer default 0 not null
-);
-
---------------
--- Calendar --
---------------
-
-create table CALENDAR (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
-);
-
-----------------------------
--- Calendar Home Metadata --
-----------------------------
-
-create table CALENDAR_HOME_METADATA (
- RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
- QUOTA_USED_BYTES integer default 0 not null,
- DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
- DEFAULT_TASKS integer default null references CALENDAR on delete set null,
- ALARM_VEVENT_TIMED text default null,
- ALARM_VEVENT_ALLDAY text default null,
- ALARM_VTODO_TIMED text default null,
- ALARM_VTODO_ALLDAY text default null,
- AVAILABILITY text default null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
-);
-
-create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
-        CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
-create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
-        CALENDAR_HOME_METADATA(DEFAULT_TASKS);
-
------------------------
--- Calendar Metadata --
------------------------
-
-create table CALENDAR_METADATA (
- RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
- SUPPORTED_COMPONENTS varchar(255) default null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
-);
-
-
----------------------------
--- Sharing Notifications --
----------------------------
-
-create table NOTIFICATION_HOME (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- OWNER_UID varchar(255) not null unique -- implicit index
-);
-
-create table NOTIFICATION (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
- NOTIFICATION_UID varchar(255) not null,
- XML_TYPE varchar(255) not null,
- XML_DATA text not null,
- MD5 char(32) not null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
-
- unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
-);
-
-create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
-        NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
-
-
--------------------
--- Calendar Bind --
--------------------
-
--- Joins CALENDAR_HOME and CALENDAR
-
-create table CALENDAR_BIND (
- CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
- CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
- CALENDAR_RESOURCE_NAME varchar(255) not null,
- BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
- BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
- BIND_REVISION                                integer default 0 not null,
- MESSAGE text,
- TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
- ALARM_VEVENT_TIMED text default null,
- ALARM_VEVENT_ALLDAY text default null,
- ALARM_VTODO_TIMED text default null,
- ALARM_VTODO_ALLDAY text default null,
- TIMEZONE text default null,
-
- primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
- unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
-);
-
-create index CALENDAR_BIND_RESOURCE_ID on
-        CALENDAR_BIND(CALENDAR_RESOURCE_ID);
-
--- Enumeration of calendar bind modes
-
-create table CALENDAR_BIND_MODE (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into CALENDAR_BIND_MODE values (0, 'own' );
-insert into CALENDAR_BIND_MODE values (1, 'read' );
-insert into CALENDAR_BIND_MODE values (2, 'write');
-insert into CALENDAR_BIND_MODE values (3, 'direct');
-
--- Enumeration of statuses
-
-create table CALENDAR_BIND_STATUS (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into CALENDAR_BIND_STATUS values (0, 'invited' );
-insert into CALENDAR_BIND_STATUS values (1, 'accepted');
-insert into CALENDAR_BIND_STATUS values (2, 'declined');
-insert into CALENDAR_BIND_STATUS values (3, 'invalid');
-
-
--- Enumeration of transparency
-
-create table CALENDAR_TRANSP (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into CALENDAR_TRANSP values (0, 'opaque' );
-insert into CALENDAR_TRANSP values (1, 'transparent');
-
-
----------------------
--- Calendar Object --
----------------------
-
-create table CALENDAR_OBJECT (
- RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
- RESOURCE_NAME varchar(255) not null,
- ICALENDAR_TEXT text not null,
- ICALENDAR_UID varchar(255) not null,
- ICALENDAR_TYPE varchar(255) not null,
- ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
- DROPBOX_ID varchar(255),
- ORGANIZER varchar(255),
- RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
- RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
- ACCESS integer default 0 not null,
- SCHEDULE_OBJECT boolean default false,
- SCHEDULE_TAG varchar(36) default null,
- SCHEDULE_ETAGS text default null,
- PRIVATE_COMMENTS boolean default false not null,
- MD5 char(32) not null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
-
- unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
-
- -- since the 'inbox' is a 'calendar resource' for the purpose of storing
- -- calendar objects, this constraint has to be selectively enforced by the
- -- application layer.
-
- -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
-);
-
-create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
- CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
-
-create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
- CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
-
-create index CALENDAR_OBJECT_ICALENDAR_UID on
- CALENDAR_OBJECT(ICALENDAR_UID);
-
-create index CALENDAR_OBJECT_DROPBOX_ID on
- CALENDAR_OBJECT(DROPBOX_ID);
-
--- Enumeration of attachment modes
-
-create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
-insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
-insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
-
-
--- Enumeration of calendar access types
-
-create table CALENDAR_ACCESS_TYPE (
- ID integer primary key,
- DESCRIPTION varchar(32) not null unique
-);
-
-insert into CALENDAR_ACCESS_TYPE values (0, '' );
-insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
-insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
-insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
-insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
-
-
------------------
--- Instance ID --
------------------
-
-create sequence INSTANCE_ID_SEQ;
-
-
-----------------
--- Time Range --
-----------------
-
-create table TIME_RANGE (
- INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
- CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
- CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
- FLOATING boolean not null,
- START_DATE timestamp not null,
- END_DATE timestamp not null,
- FBTYPE integer not null,
- TRANSPARENT boolean not null
-);
-
-create index TIME_RANGE_CALENDAR_RESOURCE_ID on
- TIME_RANGE(CALENDAR_RESOURCE_ID);
-create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
- TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
-
-
--- Enumeration of free/busy types
-
-create table FREE_BUSY_TYPE (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into FREE_BUSY_TYPE values (0, 'unknown' );
-insert into FREE_BUSY_TYPE values (1, 'free' );
-insert into FREE_BUSY_TYPE values (2, 'busy' );
-insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
-insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
-
-
-------------------
--- Transparency --
-------------------
-
-create table TRANSPARENCY (
- TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
- USER_ID varchar(255) not null,
- TRANSPARENT boolean not null
-);
-
-create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
- TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
-
-
-----------------
--- Attachment --
-----------------
-
-create sequence ATTACHMENT_ID_SEQ;
-
-create table ATTACHMENT (
- ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
- CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
- DROPBOX_ID varchar(255),
- CONTENT_TYPE varchar(255) not null,
- SIZE integer not null,
- MD5 char(32) not null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- PATH varchar(1024) not null
-);
-
-create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
- ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
-
--- Many-to-many relationship between attachments and calendar objects
-create table ATTACHMENT_CALENDAR_OBJECT (
- ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
- MANAGED_ID varchar(255) not null,
- CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
-
- primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
- unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
-);
-
-create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
-        ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
-
------------------------
--- Resource Property --
------------------------
-
-create table RESOURCE_PROPERTY (
- RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
- NAME varchar(255) not null,
- VALUE text not null, -- FIXME: xml?
- VIEWER_UID varchar(255),
-
- primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
-);
-
-
-----------------------
--- AddressBook Home --
-----------------------
-
-create table ADDRESSBOOK_HOME (
- RESOURCE_ID                                 integer                        primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- ADDRESSBOOK_PROPERTY_STORE_ID        integer         default nextval('RESOURCE_ID_SEQ') not null,         -- implicit index
- OWNER_UID                                 varchar(255)         not null unique, -- implicit index
- DATAVERSION                                 integer         default 0 not null
-);
-
-
--------------------------------
--- AddressBook Home Metadata --
--------------------------------
-
-create table ADDRESSBOOK_HOME_METADATA (
- RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
- QUOTA_USED_BYTES integer default 0 not null,
- CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
-);
-
-
------------------------------
--- Shared AddressBook Bind --
------------------------------
-
--- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
-
-create table SHARED_ADDRESSBOOK_BIND (
- ADDRESSBOOK_HOME_RESOURCE_ID                        integer                        not null references ADDRESSBOOK_HOME,
- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer         not null references ADDRESSBOOK_HOME on delete cascade,
- ADDRESSBOOK_RESOURCE_NAME                         varchar(255)         not null,
- BIND_MODE                         integer         not null,        -- enum CALENDAR_BIND_MODE
- BIND_STATUS                         integer         not null,        -- enum CALENDAR_BIND_STATUS
- BIND_REVISION                                                         integer         default 0 not null,
- MESSAGE                         text,                 -- FIXME: xml?
-
- primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
- unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
-);
-
-create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
- SHARED_ADDRESSBOOK_BIND(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
-
-
-------------------------
--- AddressBook Object --
-------------------------
-
-create table ADDRESSBOOK_OBJECT (
- RESOURCE_ID                 integer                 primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- ADDRESSBOOK_HOME_RESOURCE_ID         integer         not null references ADDRESSBOOK_HOME on delete cascade,
- RESOURCE_NAME                 varchar(255)         not null,
- VCARD_TEXT                 text         not null,
- VCARD_UID                 varchar(255)         not null,
- KIND                                                           integer         not null, -- enum ADDRESSBOOK_OBJECT_KIND
- MD5                 char(32)         not null,
- CREATED                 timestamp         default timezone('UTC', CURRENT_TIMESTAMP),
- MODIFIED                 timestamp         default timezone('UTC', CURRENT_TIMESTAMP),
-
- unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
- unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
-);
-
-
------------------------------
--- AddressBook Object kind --
------------------------------
-
-create table ADDRESSBOOK_OBJECT_KIND (
- ID integer primary key,
- DESCRIPTION varchar(16) not null unique
-);
-
-insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
-insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
-insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
-insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
-
-
----------------------------------
--- Address Book Object Members --
----------------------------------
-
-create table ABO_MEMBERS (
- GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
-         ADDRESSBOOK_ID                 integer not null references ADDRESSBOOK_HOME on delete cascade,
- MEMBER_ID integer not null references ADDRESSBOOK_OBJECT,                                                -- member AddressBook Object's RESOURCE_ID
-
- primary key (GROUP_ID, MEMBER_ID) -- implicit index
-);
-
-create index ABO_MEMBERS_ADDRESSBOOK_ID on
-        ABO_MEMBERS(ADDRESSBOOK_ID);
-create index ABO_MEMBERS_MEMBER_ID on
-        ABO_MEMBERS(MEMBER_ID);
-
-------------------------------------------
--- Address Book Object Foreign Members --
-------------------------------------------
-
-create table ABO_FOREIGN_MEMBERS (
- GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
-         ADDRESSBOOK_ID                 integer not null references ADDRESSBOOK_HOME on delete cascade,
- MEMBER_ADDRESS          varchar(255) not null,                                                                                                         -- member AddressBook Object's 'calendar' address
-
- primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
-);
-
-create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
-        ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
-
------------------------
--- Shared Group Bind --
------------------------
-
--- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
-
-create table SHARED_GROUP_BIND (        
- ADDRESSBOOK_HOME_RESOURCE_ID                 integer not null references ADDRESSBOOK_HOME,
- GROUP_RESOURCE_ID                         integer not null references ADDRESSBOOK_OBJECT on delete cascade,
- GROUP_ADDRESSBOOK_RESOURCE_NAME        varchar(255) not null,
- BIND_MODE                 integer not null, -- enum CALENDAR_BIND_MODE
- BIND_STATUS                 integer not null, -- enum CALENDAR_BIND_STATUS
- BIND_REVISION                                                 integer default 0 not null,
- MESSAGE                 text, -- FIXME: xml?
-
- primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
- unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_NAME) -- implicit index
-);
-
-create index SHARED_GROUP_BIND_RESOURCE_ID on
- SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
-
-
----------------
--- Revisions --
----------------
-
-create sequence REVISION_SEQ;
-
-
--------------------------------
--- Calendar Object Revisions --
--------------------------------
-
-create table CALENDAR_OBJECT_REVISIONS (
- CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
- CALENDAR_RESOURCE_ID integer references CALENDAR,
- CALENDAR_NAME varchar(255) default null,
- RESOURCE_NAME varchar(255),
- REVISION integer default nextval('REVISION_SEQ') not null,
- DELETED boolean not null
-);
-
-create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
- on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
-
-create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
- on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
-
-create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
- on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
-
-
-----------------------------------
--- AddressBook Object Revisions --
-----------------------------------
-
-create table ADDRESSBOOK_OBJECT_REVISIONS (
- ADDRESSBOOK_HOME_RESOURCE_ID                         integer                        not null references ADDRESSBOOK_HOME,
- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer         references ADDRESSBOOK_HOME,
- ADDRESSBOOK_NAME                         varchar(255)         default null,
- RESOURCE_NAME                         varchar(255),
- REVISION                         integer         default nextval('REVISION_SEQ') not null,
- DELETED                         boolean         not null
-);
-
-create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
- on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
-
-create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
- on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME);
-
-create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
- on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, REVISION);
-
-
------------------------------------
--- Notification Object Revisions --
------------------------------------
-
-create table NOTIFICATION_OBJECT_REVISIONS (
- NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
- RESOURCE_NAME varchar(255),
- REVISION integer default nextval('REVISION_SEQ') not null,
- DELETED boolean not null,
-
- unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
-);
-
-create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
- on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
-
-
--------------------------------------------
--- Apple Push Notification Subscriptions --
--------------------------------------------
-
-create table APN_SUBSCRIPTIONS (
- TOKEN varchar(255) not null,
- RESOURCE_KEY varchar(255) not null,
- MODIFIED integer not null,
- SUBSCRIBER_GUID varchar(255) not null,
- USER_AGENT varchar(255) default null,
- IP_ADDR varchar(255) default null,
-
- primary key (TOKEN, RESOURCE_KEY) -- implicit index
-);
-
-create index APN_SUBSCRIPTIONS_RESOURCE_KEY
- on APN_SUBSCRIPTIONS(RESOURCE_KEY);
-
-
------------------
--- IMIP Tokens --
------------------
-
-create table IMIP_TOKENS (
- TOKEN varchar(255) not null,
- ORGANIZER varchar(255) not null,
- ATTENDEE varchar(255) not null,
- ICALUID varchar(255) not null,
- ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
-
- primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
-);
-
-create index IMIP_TOKENS_TOKEN
- on IMIP_TOKENS(TOKEN);
-
-
-----------------
--- Work Items --
-----------------
-
-create sequence WORKITEM_SEQ;
-
-
----------------------------
--- IMIP Inivitation Work --
----------------------------
-
-create table IMIP_INVITATION_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- FROM_ADDR varchar(255) not null,
- TO_ADDR varchar(255) not null,
- ICALENDAR_TEXT text not null
-);
-
-
------------------------
--- IMIP Polling Work --
------------------------
-
-create table IMIP_POLLING_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
-);
-
-
----------------------
--- IMIP Reply Work --
----------------------
-
-create table IMIP_REPLY_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- ORGANIZER varchar(255) not null,
- ATTENDEE varchar(255) not null,
- ICALENDAR_TEXT text not null
-);
-
-
-------------------------
--- Push Notifications --
-------------------------
-
-create table PUSH_NOTIFICATION_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- PUSH_ID varchar(255) not null
-);
-
------------------
--- GroupCacher --
------------------
-
-create table GROUP_CACHER_POLLING_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
-);
-
-
---------------------------
--- Object Splitter Work --
---------------------------
-
-create table CALENDAR_OBJECT_SPLITTER_WORK (
- WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
- NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
-);
-
-create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
-        CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
-
---------------------
--- Schema Version --
---------------------
-
-create table CALENDARSERVER (
- NAME varchar(255) primary key, -- implicit index
- VALUE varchar(255)
-);
-
-insert into CALENDARSERVER values ('VERSION', '24');
-insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
-insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldpostgresdialectv24sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaoldpostgresdialectv24sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,700 @@
</span><ins>+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ AVAILABILITY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
+        CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
+create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
+        CALENDAR_HOME_METADATA(DEFAULT_TASKS);
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+        NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ TIMEZONE text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on
+        CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
+        ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID                                 integer                        primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID        integer         default nextval('RESOURCE_ID_SEQ') not null,         -- implicit index
+ OWNER_UID                                 varchar(255)         not null unique, -- implicit index
+ DATAVERSION                                 integer         default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID                        integer                        not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer         not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME                         varchar(255)         not null,
+ BIND_MODE                         integer         not null,        -- enum CALENDAR_BIND_MODE
+ BIND_STATUS                         integer         not null,        -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                                         integer         default 0 not null,
+ MESSAGE                         text,                 -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID                 integer                 primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID         integer         not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME                 varchar(255)         not null,
+ VCARD_TEXT                 text         not null,
+ VCARD_UID                 varchar(255)         not null,
+ KIND                                                           integer         not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5                 char(32)         not null,
+ CREATED                 timestamp         default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED                 timestamp         default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
+         ADDRESSBOOK_ID                 integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT,                                                -- member AddressBook Object's RESOURCE_ID
+
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+create index ABO_MEMBERS_ADDRESSBOOK_ID on
+        ABO_MEMBERS(ADDRESSBOOK_ID);
+create index ABO_MEMBERS_MEMBER_ID on
+        ABO_MEMBERS(MEMBER_ID);
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
+         ADDRESSBOOK_ID                 integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS          varchar(255) not null,                                                                                                         -- member AddressBook Object's 'calendar' address
+
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
+        ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (        
+ ADDRESSBOOK_HOME_RESOURCE_ID                 integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID                         integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_RESOURCE_NAME        varchar(255) not null,
+ BIND_MODE                 integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS                 integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                                 integer default 0 not null,
+ MESSAGE                 text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+-------------------------------
+-- Calendar Object Revisions --
+-------------------------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID                         integer                        not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer         references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME                         varchar(255)         default null,
+ RESOURCE_NAME                         varchar(255),
+ REVISION                         integer         default nextval('REVISION_SEQ') not null,
+ DELETED                         boolean         not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
+        CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '24');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldpostgresdialectv26sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaoldpostgresdialectv26sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,700 @@
</span><ins>+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ AVAILABILITY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
+        CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
+create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
+        CALENDAR_HOME_METADATA(DEFAULT_TASKS);
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+        NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ TIMEZONE text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on
+        CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
+        ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID                                 integer                        primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID        integer         default nextval('RESOURCE_ID_SEQ') not null,         -- implicit index
+ OWNER_UID                                 varchar(255)         not null unique, -- implicit index
+ DATAVERSION                                 integer         default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID                        integer                        not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID                         integer         not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME                         varchar(255)         not null,
+ BIND_MODE                         integer         not null,        -- enum CALENDAR_BIND_MODE
+ BIND_STATUS                         integer         not null,        -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                                         integer         default 0 not null,
+ MESSAGE                         text,                 -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID                 integer                 primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID         integer         not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME                 varchar(255)         not null,
+ VCARD_TEXT                 text         not null,
+ VCARD_UID                 varchar(255)         not null,
+ KIND                                                           integer         not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5                 char(32)         not null,
+ CREATED                 timestamp         default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED                 timestamp         default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
+         ADDRESSBOOK_ID                 integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT,                                                -- member AddressBook Object's RESOURCE_ID
+
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+create index ABO_MEMBERS_ADDRESSBOOK_ID on
+        ABO_MEMBERS(ADDRESSBOOK_ID);
+create index ABO_MEMBERS_MEMBER_ID on
+        ABO_MEMBERS(MEMBER_ID);
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
+         ADDRESSBOOK_ID                 integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS          varchar(255) not null,                                                                                                         -- member AddressBook Object's 'calendar' address
+
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
+        ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (        
+ ADDRESSBOOK_HOME_RESOURCE_ID                 integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID                         integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_NAME                        varchar(255) not null,
+ BIND_MODE                 integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS                 integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                                 integer default 0 not null,
+ MESSAGE                 text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+-------------------------------
+-- Calendar Object Revisions --
+-------------------------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID                         integer                        not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID                         integer         references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME                         varchar(255)         default null,
+ RESOURCE_NAME                         varchar(255),
+ REVISION                         integer         default nextval('REVISION_SEQ') not null,
+ DELETED                         boolean         not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
+        CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '26');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldpostgresdialectv27sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaoldpostgresdialectv27sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v27.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v27.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v27.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v27.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,703 @@
</span><ins>+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ DEFAULT_POLLS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ AVAILABILITY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
+        CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
+create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
+        CALENDAR_HOME_METADATA(DEFAULT_TASKS);
+create index CALENDAR_HOME_METADATA_DEFAULT_POLLS on
+        CALENDAR_HOME_METADATA(DEFAULT_POLLS);
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+        NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ TIMEZONE text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on
+        CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
+        ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID                                 integer                        primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID        integer         default nextval('RESOURCE_ID_SEQ') not null,         -- implicit index
+ OWNER_UID                                 varchar(255)         not null unique, -- implicit index
+ DATAVERSION                                 integer         default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID                        integer                        not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID                         integer         not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME                         varchar(255)         not null,
+ BIND_MODE                         integer         not null,        -- enum CALENDAR_BIND_MODE
+ BIND_STATUS                         integer         not null,        -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                                         integer         default 0 not null,
+ MESSAGE                         text,                 -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID                 integer                 primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID         integer         not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME                 varchar(255)         not null,
+ VCARD_TEXT                 text         not null,
+ VCARD_UID                 varchar(255)         not null,
+ KIND                                                           integer         not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5                 char(32)         not null,
+ CREATED                 timestamp         default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED                 timestamp         default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
+         ADDRESSBOOK_ID                 integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT,                                                -- member AddressBook Object's RESOURCE_ID
+
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+create index ABO_MEMBERS_ADDRESSBOOK_ID on
+        ABO_MEMBERS(ADDRESSBOOK_ID);
+create index ABO_MEMBERS_MEMBER_ID on
+        ABO_MEMBERS(MEMBER_ID);
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
+         ADDRESSBOOK_ID                 integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS          varchar(255) not null,                                                                                                         -- member AddressBook Object's 'calendar' address
+
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
+        ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (        
+ ADDRESSBOOK_HOME_RESOURCE_ID                 integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID                         integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_NAME                        varchar(255) not null,
+ BIND_MODE                 integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS                 integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                                 integer default 0 not null,
+ MESSAGE                 text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+-------------------------------
+-- Calendar Object Revisions --
+-------------------------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID                         integer                        not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID                         integer         references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME                         varchar(255)         default null,
+ RESOURCE_NAME                         varchar(255),
+ REVISION                         integer         default nextval('REVISION_SEQ') not null,
+ DELETED                         boolean         not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
+        CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '27');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaoldpostgresdialectv28sql"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v28.sql (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v28.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/old/postgres-dialect/v28.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,704 @@
</span><ins>+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 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.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ DEFAULT_POLLS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ AVAILABILITY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
+        CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
+create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
+        CALENDAR_HOME_METADATA(DEFAULT_TASKS);
+create index CALENDAR_HOME_METADATA_DEFAULT_POLLS on
+        CALENDAR_HOME_METADATA(DEFAULT_POLLS);
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+        NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ TIMEZONE text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on
+        CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
+        ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID                                 integer                        primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID        integer         default nextval('RESOURCE_ID_SEQ') not null,         -- implicit index
+ OWNER_UID                                 varchar(255)         not null unique, -- implicit index
+ DATAVERSION                                 integer         default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID                        integer                        not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID                         integer         not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME                         varchar(255)         not null,
+ BIND_MODE                         integer         not null,        -- enum CALENDAR_BIND_MODE
+ BIND_STATUS                         integer         not null,        -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                                         integer         default 0 not null,
+ MESSAGE                         text,                 -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID                 integer                 primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID         integer         not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME                 varchar(255)         not null,
+ VCARD_TEXT                 text         not null,
+ VCARD_UID                 varchar(255)         not null,
+ KIND                                                           integer         not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5                 char(32)         not null,
+ CREATED                 timestamp         default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED                 timestamp         default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
+         ADDRESSBOOK_ID                 integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT,                                                -- member AddressBook Object's RESOURCE_ID
+
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+create index ABO_MEMBERS_ADDRESSBOOK_ID on
+        ABO_MEMBERS(ADDRESSBOOK_ID);
+create index ABO_MEMBERS_MEMBER_ID on
+        ABO_MEMBERS(MEMBER_ID);
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
+         ADDRESSBOOK_ID                 integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS          varchar(255) not null,                                                                                                         -- member AddressBook Object's 'calendar' address
+
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
+        ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (        
+ ADDRESSBOOK_HOME_RESOURCE_ID                 integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID                         integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_NAME                        varchar(255) not null,
+ BIND_MODE                 integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS                 integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION                                                 integer default 0 not null,
+ MESSAGE                 text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+-------------------------------
+-- Calendar Object Revisions --
+-------------------------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID                         integer                        not null references ADDRESSBOOK_HOME,
+ OWNER_HOME_RESOURCE_ID                         integer         references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME                         varchar(255)         default null,
+ RESOURCE_NAME                         varchar(255),
+ REVISION                         integer         default nextval('REVISION_SEQ') not null,
+ DELETED                         boolean         not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null,
+ PRIORITY integer not null -- 1:low 5:medium 10:high
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
+        CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '28');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_24_to_25sql"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,26 +0,0 @@
</span><del>-----
--- Copyright (c) 2012-2013 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.
-----
-
----------------------------------------------------
--- Upgrade database schema from VERSION 24 to 25 --
----------------------------------------------------
-
--- This is actually a noop for Oracle as we had some invalid names in the v20 schema that
--- were corrected in v20 (but not corrected in postgres which is being updated for v25).
-
--- Now update the version
--- No data upgrades
-update CALENDARSERVER set VALUE = '25' where NAME = 'VERSION';
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_24_to_25sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_24_to_25sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,26 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 24 to 25 --
+---------------------------------------------------
+
+-- This is actually a noop for Oracle as we had some invalid names in the v20 schema that
+-- were corrected in v20 (but not corrected in postgres which is being updated for v25).
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '25' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_25_to_26sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -15,35 +15,29 @@
</span><span class="cx"> ----
</span><span class="cx">
</span><span class="cx"> ---------------------------------------------------
</span><del>--- Upgrade database schema from VERSION 24 to 25 --
</del><ins>+-- Upgrade database schema from VERSION 25 to 26 --
</ins><span class="cx"> ---------------------------------------------------
</span><span class="cx">
</span><del>-----------------------------------------
--- Change Address Book Object Members --
-----------------------------------------
</del><ins>+-- Replace index
</ins><span class="cx">
</span><del>-alter table ABO_MEMBERS
-        drop ("abo_members_member_id_fkey");
-alter table ABO_MEMBERS
-        drop ("abo_members_group_id_fkey");
-alter table ABO_MEMBERS
-        add ("REVISION" integer default nextval('REVISION_SEQ') not null);
-alter table ABO_MEMBERS
-        add ("REMOVED" boolean default false not null);
-alter table ABO_MEMBERS
-         drop ("abo_members_pkey");
-alter table ABO_MEMBERS
-         add ("abo_members_pkey" primary key ("GROUP_ID", "MEMBER_ID", "REVISION"));
</del><ins>+drop index CALENDAR_OBJECT_REVIS_2643d556;
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
</ins><span class="cx">
</span><del>-------------------------------------------
--- Change Address Book Object Revisions --
-------------------------------------------
-        
-alter table ADDRESSBOOK_OBJECT_REVISIONS
-        add ("OBJECT_RESOURCE_ID" integer default 0);
</del><span class="cx">
</span><del>---------------------
--- Update version --
---------------------
</del><ins>+drop index ADDRESSBOOK_OBJECT_RE_980b9872;
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
</ins><span class="cx">
</span><ins>+
+-- Now update the version
+-- No data upgrades
</ins><span class="cx"> update CALENDARSERVER set VALUE = '26' where NAME = 'VERSION';
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_26_to_27sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_26_to_27sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 26 to 27 --
+---------------------------------------------------
+
+-- Calendar home related updates
+
+alter table CALENDAR_HOME_METADATA
+ add ("DEFAULT_POLLS" integer default null references CALENDAR on delete set null);
+
+create index CALENDAR_HOME_METADAT_910264ce on CALENDAR_HOME_METADATA (
+ DEFAULT_POLLS
+);
+
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '27' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_27_to_28sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_27_to_28sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_27_to_28.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_27_to_28.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_27_to_28.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_27_to_28.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,29 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 27 to 28 --
+---------------------------------------------------
+
+-- Push notification work related updates
+
+alter table PUSH_NOTIFICATION_WORK
+ add ("PRIORITY" integer default 10 not null);
+
+update PUSH_NOTIFICATION_WORK set PRIORITY = 10;
+
+-- Now update the version
+update CALENDARSERVER set VALUE = '28' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_28_to_29sql"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_28_to_29.sql (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_28_to_29.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_28_to_29.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 28 to 29 --
+---------------------------------------------------
+
+----------------------------------------
+-- Change Address Book Object Members --
+----------------------------------------
+
+alter table ABO_MEMBERS
+        drop ("abo_members_member_id_fkey");
+alter table ABO_MEMBERS
+        drop ("abo_members_group_id_fkey");
+alter table ABO_MEMBERS
+        add ("REVISION" integer default nextval('REVISION_SEQ') not null);
+alter table ABO_MEMBERS
+        add ("REMOVED" boolean default false not null);
+alter table ABO_MEMBERS
+         drop ("abo_members_pkey");
+alter table ABO_MEMBERS
+         add ("abo_members_pkey" primary key ("GROUP_ID", "MEMBER_ID", "REVISION"));
+
+------------------------------------------
+-- Change Address Book Object Revisions --
+------------------------------------------
+        
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+        add ("OBJECT_RESOURCE_ID" integer default 0);
+
+--------------------
+-- Update version --
+--------------------
+
+update CALENDARSERVER set VALUE = '29' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_24_to_25sql"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -1,35 +0,0 @@
</span><del>-----
--- Copyright (c) 2012-2013 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.
-----
-
----------------------------------------------------
--- Upgrade database schema from VERSION 24 to 25 --
----------------------------------------------------
-
--- Rename columns and indexes
-alter table SHARED_ADDRESSBOOK_BIND
-        rename column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID to OWNER_HOME_RESOURCE_ID;
-
-alter table SHARED_GROUP_BIND
-        rename column GROUP_ADDRESSBOOK_RESOURCE_NAME to GROUP_ADDRESSBOOK_NAME;
-
-alter table ADDRESSBOOK_OBJECT_REVISIONS
-        rename column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID to OWNER_HOME_RESOURCE_ID;
-
-alter index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID rename to ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID;
-
--- Now update the version
--- No data upgrades
-update CALENDARSERVER set VALUE = '25' where NAME = 'VERSION';
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_24_to_25sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_24_to_25sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,35 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 24 to 25 --
+---------------------------------------------------
+
+-- Rename columns and indexes
+alter table SHARED_ADDRESSBOOK_BIND
+        rename column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID to OWNER_HOME_RESOURCE_ID;
+
+alter table SHARED_GROUP_BIND
+        rename column GROUP_ADDRESSBOOK_RESOURCE_NAME to GROUP_ADDRESSBOOK_NAME;
+
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+        rename column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID to OWNER_HOME_RESOURCE_ID;
+
+alter index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID rename to ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID;
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '25' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_25_to_26sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -15,30 +15,18 @@
</span><span class="cx"> ----
</span><span class="cx">
</span><span class="cx"> ---------------------------------------------------
</span><del>--- Upgrade database schema from VERSION 24 to 25 --
</del><ins>+-- Upgrade database schema from VERSION 25 to 26 --
</ins><span class="cx"> ---------------------------------------------------
</span><span class="cx">
</span><del>-----------------------------------------
--- Change Address Book Object Members --
-----------------------------------------
</del><ins>+-- Replace index
+drop index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME;
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
</ins><span class="cx">
</span><del>-alter table ABO_MEMBERS
-        drop constraint        abo_members_member_id_fkey,
-        drop constraint        abo_members_group_id_fkey,
-        add column        REVISION                integer default nextval('REVISION_SEQ') not null,
-        add column        REMOVED boolean default false not null,
-        drop constraint abo_members_pkey,
-        add constraint abo_members_pkey primary key(GROUP_ID, MEMBER_ID, REVISION);
</del><ins>+drop index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME;
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
</ins><span class="cx">
</span><del>-------------------------------------------
--- Change Address Book Object Revisions --
-------------------------------------------
-        
-alter table ADDRESSBOOK_OBJECT_REVISIONS
-        add column OBJECT_RESOURCE_ID integer default 0;
-        
---------------------
--- Update version --
---------------------
-
</del><ins>+-- Now update the version
+-- No data upgrades
</ins><span class="cx"> update CALENDARSERVER set VALUE = '26' where NAME = 'VERSION';
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_26_to_27sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_26_to_27sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 26 to 27 --
+---------------------------------------------------
+
+-- Calendar home related updates
+
+alter table CALENDAR_HOME_METADATA
+ add column DEFAULT_POLLS integer default null references CALENDAR on delete set null;
+
+create index CALENDAR_HOME_METADATA_DEFAULT_POLLS on
+        CALENDAR_HOME_METADATA(DEFAULT_POLLS);
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '27' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_27_to_28sqlfromrev12016CalendarServertrunktxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_27_to_28sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_27_to_28.sql (from rev 12016, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_27_to_28.sql) (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_27_to_28.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_27_to_28.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,29 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 27 to 28 --
+---------------------------------------------------
+
+-- Push notification work related updates
+
+alter table PUSH_NOTIFICATION_WORK
+ add column PRIORITY integer default 10 not null;
+
+update PUSH_NOTIFICATION_WORK set PRIORITY = 10;
+
+-- Now update the version
+update CALENDARSERVER set VALUE = '28' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_28_to_29sql"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_28_to_29.sql (0 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_28_to_29.sql         (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_28_to_29.sql        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -0,0 +1,44 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 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.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 28 to 29 --
+---------------------------------------------------
+
+----------------------------------------
+-- Change Address Book Object Members --
+----------------------------------------
+
+alter table ABO_MEMBERS
+        drop constraint        abo_members_member_id_fkey,
+        drop constraint        abo_members_group_id_fkey,
+        add column        REVISION                integer default nextval('REVISION_SEQ') not null,
+        add column        REMOVED boolean default false not null,
+        drop constraint abo_members_pkey,
+        add constraint abo_members_pkey primary key(GROUP_ID, MEMBER_ID, REVISION);
+
+------------------------------------------
+-- Change Address Book Object Revisions --
+------------------------------------------
+        
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+        add column OBJECT_RESOURCE_ID integer default 0;
+        
+--------------------
+-- Update version --
+--------------------
+
+update CALENDARSERVER set VALUE = '29' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoretestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/test/util.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/test/util.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/test/util.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -32,7 +32,7 @@
</span><span class="cx">
</span><span class="cx"> from hashlib import md5
</span><span class="cx">
</span><del>-from pycalendar.datetime import PyCalendarDateTime
</del><ins>+from pycalendar.datetime import DateTime
</ins><span class="cx">
</span><span class="cx"> from random import Random
</span><span class="cx">
</span><span class="lines">@@ -50,6 +50,7 @@
</span><span class="cx"> from twisted.internet.task import deferLater
</span><span class="cx"> from twisted.trial.unittest import TestCase
</span><span class="cx">
</span><ins>+from twistedcaldav import ical
</ins><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.stdconfig import DEFAULT_CONFIG
</span><span class="cx"> from twistedcaldav.vcard import Component as ABComponent
</span><span class="lines">@@ -447,12 +448,11 @@
</span><span class="cx"> # We don't want the default calendar or inbox to appear unless it's
</span><span class="cx"> # explicitly listed.
</span><span class="cx"> try:
</span><del>- yield home.removeCalendarWithName("calendar")
- # FIXME: this should be an argument to the function, not a
- # global configuration variable. Related: this needs
- # independent tests.
</del><span class="cx"> if config.RestrictCalendarsToOneComponentType:
</span><del>- yield home.removeCalendarWithName("tasks")
</del><ins>+ for name in ical.allowedStoreComponents:
+ yield home.removeCalendarWithName(home._componentCalendarName[name])
+ else:
+ yield home.removeCalendarWithName("calendar")
</ins><span class="cx"> yield home.removeCalendarWithName("inbox")
</span><span class="cx"> except NoSuchHomeChildError:
</span><span class="cx"> pass
</span><span class="lines">@@ -480,7 +480,7 @@
</span><span class="cx"> Update the supplied iCalendar data so that all dates are updated to the current year.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- nowYear = PyCalendarDateTime.getToday().getYear()
</del><ins>+ nowYear = DateTime.getToday().getYear()
</ins><span class="cx"> return data % {"now": nowYear}
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -726,8 +726,8 @@
</span><span class="cx"> return "/%s/%s/%s/" % (prefix, self.hostname, id)
</span><span class="cx">
</span><span class="cx">
</span><del>- def send(self, prefix, id, txn):
- self.history.append(self.pushKeyForId(prefix, id))
</del><ins>+ def send(self, prefix, id, txn, priority):
+ self.history.append((self.pushKeyForId(prefix, id), priority))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def reset(self):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_3_to_4py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -94,27 +94,32 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> inbox = (yield home.calendarWithName("inbox"))
</span><del>- prop = inbox.properties().get(PropertyName.fromElement(propname))
- if prop is not None:
- defaultCalendar = str(prop.children[0])
- parts = defaultCalendar.split("/")
- if len(parts) == 5:
</del><ins>+ if inbox is not None:
+ prop = inbox.properties().get(PropertyName.fromElement(propname))
+ if prop is not None:
+ defaultCalendar = str(prop.children[0])
+ parts = defaultCalendar.split("/")
+ if len(parts) == 5:
</ins><span class="cx">
</span><del>- calendarName = parts[-1]
- calendarHomeUID = parts[-2]
- if calendarHomeUID == home.uid():
</del><ins>+ calendarName = parts[-1]
+ calendarHomeUID = parts[-2]
+ if calendarHomeUID == home.uid():
</ins><span class="cx">
</span><del>- calendar = (yield home.calendarWithName(calendarName))
- if calendar is not None:
- try:
- yield home.setDefaultCalendar(
- calendar, tasks=(propname == customxml.ScheduleDefaultTasksURL)
- )
- except InvalidDefaultCalendar:
- # Ignore these - the server will recover
- pass
</del><ins>+ calendar = (yield home.calendarWithName(calendarName))
+ if calendar is not None:
+ try:
+ if propname == caldavxml.ScheduleDefaultCalendarURL:
+ ctype = "VEVENT"
+ elif propname == customxml.ScheduleDefaultTasksURL:
+ ctype = "VTODO"
+ yield home.setDefaultCalendar(
+ calendar, ctype
+ )
+ except InvalidDefaultCalendar:
+ # Ignore these - the server will recover
+ pass
</ins><span class="cx">
</span><del>- del inbox.properties()[PropertyName.fromElement(propname)]
</del><ins>+ del inbox.properties()[PropertyName.fromElement(propname)]
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -130,15 +135,13 @@
</span><span class="cx"> calendars = (yield home.loadChildren())
</span><span class="cx"> for calendar in calendars:
</span><span class="cx"> if calendar.isInbox():
</span><del>- continue
</del><ins>+ prop = calendar.properties().get(PropertyName.fromElement(caldavxml.CalendarFreeBusySet))
+ if prop is not None:
+ del calendar.properties()[PropertyName.fromElement(caldavxml.CalendarFreeBusySet)]
</ins><span class="cx"> prop = calendar.properties().get(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp))
</span><span class="cx"> if prop is not None:
</span><span class="cx"> yield calendar.setUsedForFreeBusy(prop == caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
</span><span class="cx"> del calendar.properties()[PropertyName.fromElement(caldavxml.ScheduleCalendarTransp)]
</span><del>- inbox = (yield home.calendarWithName("inbox"))
- prop = inbox.properties().get(PropertyName.fromElement(caldavxml.CalendarFreeBusySet))
- if prop is not None:
- del inbox.properties()[PropertyName.fromElement(caldavxml.CalendarFreeBusySet)]
</del><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_4_to_5py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -102,10 +102,11 @@
</span><span class="cx"> the new value from the XML property.
</span><span class="cx"> """
</span><span class="cx"> inbox = (yield home.calendarWithName("inbox"))
</span><del>- prop = inbox.properties().get(PropertyName.fromElement(customxml.CalendarAvailability))
- if prop is not None:
- yield home.setAvailability(prop.calendar())
- del inbox.properties()[customxml.CalendarAvailability]
</del><ins>+ if inbox is not None:
+ prop = inbox.properties().get(PropertyName.fromElement(customxml.CalendarAvailability))
+ if prop is not None:
+ yield home.setAvailability(prop.calendar())
+ del inbox.properties()[PropertyName.fromElement(customxml.CalendarAvailability)]
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_4_to_5py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -161,7 +161,10 @@
</span><span class="cx"> self.assertEqual(version, 4)
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name=calname, home=user))
</span><span class="cx"> self.assertEqual(calendar.getTimezone(), None)
</span><del>- self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) in calendar.properties())
</del><ins>+ if tz:
+ self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) in calendar.properties())
+ else:
+ self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) not in calendar.properties())
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -272,7 +275,10 @@
</span><span class="cx"> self.assertEqual(version, 4)
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="inbox", home=user))
</span><span class="cx"> self.assertEqual(home.getAvailability(), None)
</span><del>- self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) in calendar.properties())
</del><ins>+ if av:
+ self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) in calendar.properties())
+ else:
+ self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) not in calendar.properties())
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayasharedgroupfixestxdavidavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/idav.py (12109 => 12110)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/idav.py        2013-12-14 04:45:59 UTC (rev 12109)
+++ CalendarServer/branches/users/gaya/sharedgroupfixes/txdav/idav.py        2013-12-14 06:28:16 UTC (rev 12110)
</span><span class="lines">@@ -34,6 +34,9 @@
</span><span class="cx"> from zope.interface import Attribute, Interface
</span><span class="cx"> from zope.interface.common.mapping import IMapping
</span><span class="cx">
</span><ins>+from twisted.python.constants import Values, ValueConstant
+from calendarserver.push.util import PushPriority
+
</ins><span class="cx"> #
</span><span class="cx"> # Exceptions
</span><span class="cx"> #
</span><span class="lines">@@ -231,6 +234,19 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><ins>+class ChangeCategory(Values):
+ """
+ Constants to use for notifyChanged's category parameter. Maps
+ types of changes to the appropriate push priority level.
+ TODO: make these values configurable in plist perhaps.
+ """
+ default = ValueConstant(PushPriority.high)
+ inbox = ValueConstant(PushPriority.medium)
+ attendeeITIPUpdate = ValueConstant(PushPriority.medium)
+ organizerITIPUpdate = ValueConstant(PushPriority.medium)
+
+
+
</ins><span class="cx"> class INotifier(Interface):
</span><span class="cx"> """
</span><span class="cx"> Interface for an object that can send change notifications. Notifiers are associated with specific notifier factories
</span><span class="lines">@@ -260,9 +276,12 @@
</span><span class="cx"> @rtype: L{IStoreNotifier} or C{None}
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def notifyChanged(): #@NoSelf
</del><ins>+ def notifyChanged(category): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> Send a change notification to any notifiers assigned to the object.
</span><ins>+
+ @param category: the kind of change triggering this notification
+ @type: L{ChangeCategory}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def notifierID(): #@NoSelf
</span></span></pre>
</div>
</div>
</body>
</html>