<!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>[13732] CalendarServer/branches/users/gaya/groupsharee2</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/13732">13732</a></dd>
<dt>Author</dt> <dd>gaya@apple.com</dd>
<dt>Date</dt> <dd>2014-07-08 00:05:29 -0700 (Tue, 08 Jul 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>merge in trunk to <a href="http://trac.calendarserver.org//changeset/13731">r13731</a>; fix problem where group membership cache is out of sync when first added</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2bintest">CalendarServer/branches/users/gaya/groupsharee2/bin/test</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarserverprovisionrootpy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/provision/root.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarserverprovisiontesttest_rootpy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/provision/test/test_root.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarserverpushnotifierpy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/push/notifier.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertapcaldavpy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tap/caldav.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolsanonymizepy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/anonymize.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolscalverifypy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/calverify.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolsdashboardpy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/dashboard.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolsdbinspectpy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/dbinspect.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolsgatewaypy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/gateway.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolsprincipalspy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/principals.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolspurgepy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/purge.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolsshellcmdpy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/cmd.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolsshelldirectorypy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/directory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolsshellvfspy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/vfs.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolstestdeprovisioncaldavdplist">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/deprovision/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolstestgatewaycaldavdplist">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/gateway/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolstestprincipalscaldavdplist">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/principals/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolstesttest_calverifypy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/test_calverify.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarservertoolsutilpy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2confauthgenerate_test_accountspy">CalendarServer/branches/users/gaya/groupsharee2/conf/auth/generate_test_accounts.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2confcaldavdtestplist">CalendarServer/branches/users/gaya/groupsharee2/conf/caldavd-test.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2confresourcescaldavdresourcesplist">CalendarServer/branches/users/gaya/groupsharee2/conf/resources/caldavd-resources.plist</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2contribperformanceloadtesticalpy">CalendarServer/branches/users/gaya/groupsharee2/contrib/performance/loadtest/ical.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2requirementsdevtxt">CalendarServer/branches/users/gaya/groupsharee2/requirements-dev.txt</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2requirementsstabletxt">CalendarServer/branches/users/gaya/groupsharee2/requirements-stable.txt</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2setuppy">CalendarServer/branches/users/gaya/groupsharee2/setup.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavdatafiltershiddeninstancepy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/datafilters/hiddeninstance.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectoryaugmentpy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/augment.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectorycalendaruserproxypy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/calendaruserproxy.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectorydigestpy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/digest.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectoryprincipalpy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/principal.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectorytesttest_principalpy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/test/test_principal.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectoryutilpy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavicalpy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/ical.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavresourcepy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavscheduling_storecaldavresourcepy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/scheduling_store/caldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavstdconfigpy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavstorebridgepy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/storebridge.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavtesttest_icalendarpy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/test/test_icalendar.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavtesttest_upgradepy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/test/test_upgrade.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2twistedcaldavupgradepy">CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/upgrade.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulepy">CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/schedule.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingimplicitpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/implicit.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingitippy">CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/itip.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingprocessingpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/processing.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingtesttest_implicitpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/test/test_implicit.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingworkpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/work.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoresqlpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoretesttest_sqlpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/test_sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcarddavdatastoresqlpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/carddav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcarddavdatastoretesttest_sqlpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/carddav/datastore/test/test_sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresqlpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemacurrentoracledialectextrassql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current-oracle-dialect-extras.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemacurrentoracledialectsql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current-oracle-dialect.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemacurrentsql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_43_to_44sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_43_to_44.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_tablespy">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_tables.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavdpsclientpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/dps/client.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavdpsserverpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/dps/server.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavwhoaugmentpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/who/augment.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavwhodelegatespy">CalendarServer/branches/users/gaya/groupsharee2/txdav/who/delegates.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavwhodirectorypy">CalendarServer/branches/users/gaya/groupsharee2/txdav/who/directory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavwhogroupspy">CalendarServer/branches/users/gaya/groupsharee2/txdav/who/groups.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavwhoopendirectorypy">CalendarServer/branches/users/gaya/groupsharee2/txdav/who/opendirectory.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavwhotesttest_delegatespy">CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_delegates.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavwhotesttest_group_attendeespy">CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_group_attendees.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavwhotesttest_groupspy">CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_groups.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavwhoutilpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/who/util.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavwhowikipy">CalendarServer/branches/users/gaya/groupsharee2/txdav/who/wiki.py</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarserverplatformdarwinsaclpy">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/platform/darwin/sacl.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingtesttest_workpy">CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/test/test_work.py</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaoldoracledialectv44sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/oracle-dialect/v44.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaoldoracledialectv45sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/oracle-dialect/v45.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaoldpostgresdialectv44sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/postgres-dialect/v44.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaoldpostgresdialectv45sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/postgres-dialect/v45.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_44_to_45sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_44_to_45.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_45_to_46sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_45_to_46.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_43_to_44sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_43_to_44.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_44_to_45sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_44_to_45.sql</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_45_to_46sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_45_to_46.sql</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2calendarserverplatformdarwin_saclc">CalendarServer/branches/users/gaya/groupsharee2/calendarserver/platform/darwin/_sacl.c</a></li>
<li><a href="#CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_43_to_44sql">CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_43_to_44.sql</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesusersgayagroupsharee2bintest"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/bin/test (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/bin/test        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/bin/test        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -83,15 +83,7 @@
</span><span class="cx"> done;
</span><span class="cx"> shift $((${OPTIND} - 1));
</span><span class="cx"> 
</span><del>-if [ $# -eq 0 ]; then
-  lint=&quot;true&quot;;
-  set - calendarserver twistedcaldav txdav txweb2 contrib twext;
-else
-  lint=&quot;false&quot;;
-fi;
</del><span class="cx"> 
</span><del>-
-
</del><span class="cx"> #
</span><span class="cx"> # Dependencies
</span><span class="cx"> #
</span><span class="lines">@@ -112,11 +104,10 @@
</span><span class="cx"> # Unit tests
</span><span class="cx"> #
</span><span class="cx"> 
</span><del>-for module in &quot;$@&quot;; do
-  if [ -f &quot;${module}&quot; ]; then
-    module=&quot;--testmodule=${module}&quot;;
-  fi;
-
</del><ins>+if [ $# -eq 0 ]; then
+  lint=&quot;true&quot;;
+  # Test these modules with a single invocation of trial
+  set - calendarserver twistedcaldav txdav txweb2 contrib
</ins><span class="cx">   cd &quot;${wd}&quot; &amp;&amp;                                       \
</span><span class="cx">     &quot;${wd}/bin/trial&quot;                                 \
</span><span class="cx">     --temp-directory=&quot;${dev_home}/trial&quot;              \
</span><span class="lines">@@ -127,10 +118,30 @@
</span><span class="cx">     ${no_color}                                       \
</span><span class="cx">     ${coverage}                                       \
</span><span class="cx">     ${numjobs}                                        \
</span><del>-    &quot;${module}&quot;;
-done;
</del><ins>+    &quot;$@&quot;;
+else
+  lint=&quot;false&quot;;
+  # Loop over the passed-in modules
+  for module in &quot;$@&quot;; do
+    if [ -f &quot;${module}&quot; ]; then
+      module=&quot;--testmodule=${module}&quot;;
+    fi;
+    cd &quot;${wd}&quot; &amp;&amp;                                       \
+      &quot;${wd}/bin/trial&quot;                                 \
+      --temp-directory=&quot;${dev_home}/trial&quot;              \
+      --rterrors                                        \
+      ${reactor}                                        \
+      ${random}                                         \
+      ${until_fail}                                     \
+      ${no_color}                                       \
+      ${coverage}                                       \
+      ${numjobs}                                        \
+      &quot;${module}&quot;;
+  done;
+fi;
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> if ! &quot;${lint}&quot;; then
</span><span class="cx">   exit 0;
</span><span class="cx"> fi;
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarserverplatformdarwin_saclc"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/platform/darwin/_sacl.c (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/platform/darwin/_sacl.c        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/platform/darwin/_sacl.c        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -1,96 +0,0 @@
</span><del>-/*
- * Copyright (c) 2006-2014 Apple Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
- * 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 &quot;AS IS&quot; 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.
- */
-
-#include &quot;Python.h&quot;
-#include &lt;Security/Security.h&gt;
-#include &lt;membership.h&gt;
-
-int mbr_check_service_membership(const uuid_t user, const char* servicename, int* ismember);
-int mbr_user_name_to_uuid(const char* name, uuid_t uu);
-int mbr_group_name_to_uuid(const char* name, uuid_t uu);
-
-/*
-    CheckSACL(userOrGroupName, service)
-    Checks user or group membership in a service.
-*/
-static PyObject *appleauth_CheckSACL(PyObject *self, PyObject *args) {
-    char *username;
-    int usernameSize;
-    char *serviceName;
-    int serviceNameSize;
-
-    char *prefix = &quot;com.apple.access_&quot;;
-    char groupName[256];
-    uuid_t group_uu;
-
-    // get the args
-    if (!PyArg_ParseTuple(args, &quot;s#s#&quot;, &amp;username,
-                          &amp;usernameSize, &amp;serviceName, &amp;serviceNameSize)) {
-        return NULL;
-    }
-
-    // If the username is empty, see if there is a com.apple.access_&lt;service&gt;
-    // group
-    if ( usernameSize == 0 ) {
-        if ( strlen(serviceName) &gt; 255 - strlen(prefix) ) {
-            return Py_BuildValue(&quot;i&quot;, (-3));
-        }
-        memcpy(groupName, prefix, strlen(prefix));
-        strcpy(groupName + strlen(prefix), serviceName);
-        if ( mbr_group_name_to_uuid(groupName, group_uu) == 0 ) {
-            // com.apple.access_&lt;serviceName&gt; group does exist, so
-            // unauthenticated users are not allowed
-            return Py_BuildValue(&quot;i&quot;, (-1));
-        } else {
-            // com.apple.access_&lt;serviceName&gt; group doesn't exist, so
-            // unauthenticated users are allowed
-            return Py_BuildValue(&quot;i&quot;, 0);
-        }
-    }
-
-    // get a uuid for the user
-    uuid_t user;
-    int result = mbr_user_name_to_uuid(username, user);
-    int isMember = 0;
-
-    if ( result != 0 ) {
-        // no uuid for the user, we might be a group.
-        result = mbr_group_name_to_uuid(username, user);
-    }
-
-    if ( result != 0 ) {
-        return Py_BuildValue(&quot;i&quot;, (-1));
-    }
-
-    result = mbr_check_service_membership(user, serviceName, &amp;isMember);
-
-    if ( ( result == 0 &amp;&amp; isMember == 1 ) || ( result == ENOENT ) ) {
-        // passed
-        return Py_BuildValue(&quot;i&quot;, 0);
-    }
-
-    return Py_BuildValue(&quot;i&quot;, (-2));
-}
-
-/* Method definitions. */
-static struct PyMethodDef _sacl_methods[] = {
-    {&quot;CheckSACL&quot;, appleauth_CheckSACL},
-    {NULL, NULL} /* Sentinel */
-};
-
-void init_sacl(void) {
-    Py_InitModule(&quot;_sacl&quot;, _sacl_methods);
-}
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarserverplatformdarwinsaclpyfromrev13731CalendarServertrunkcalendarserverplatformdarwinsaclpy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/platform/darwin/sacl.py (from rev 13731, CalendarServer/trunk/calendarserver/platform/darwin/sacl.py) (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/platform/darwin/sacl.py                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/platform/darwin/sacl.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,85 @@
</span><ins>+##
+# Copyright (c) 2005-2014 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; 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 __future__ import print_function
+
+__all__ = [
+    &quot;checkSACL&quot;
+]
+
+from cffi import FFI, VerificationError
+
+ffi = FFI()
+
+definitions = &quot;&quot;&quot;
+    typedef unsigned char uuid_t[16];
+    int mbr_check_service_membership(const uuid_t user, const char* servicename, int* ismember);
+    int mbr_user_name_to_uuid(const char* name, uuid_t uu);
+    int mbr_group_name_to_uuid(const char* name, uuid_t uu);
+&quot;&quot;&quot;
+
+ffi.cdef(definitions)
+
+try:
+    lib = ffi.verify(definitions, libraries=[])
+except VerificationError as ve:
+    raise ImportError(ve)
+
+
+def checkSACL(userOrGroupName, serviceName):
+    &quot;&quot;&quot;
+    Check to see if a given user or group is a member of an OS X Server
+    service's access group.  If userOrGroupName is an empty string, we
+    want to know if unauthenticated access is allowed for the given service.
+
+    @param userOrGroupName: the name of the user or group
+    @type userOrGroupName: C{unicode}
+
+    @param serviceName: the name of the service (e.g. calendar, addressbook)
+    @type serviceName: C{str}
+
+    @return: True if the user or group is allowed access to service
+    @rtype: C{bool}
+    &quot;&quot;&quot;
+
+    userOrGroupName = userOrGroupName.encode(&quot;utf-8&quot;)
+    prefix = &quot;com.apple.access_&quot;
+    uu = ffi.new(&quot;uuid_t&quot;)
+
+    # See if the access group exists.  If it does not, then there are no
+    # restrictions
+    groupName = prefix + serviceName
+    groupMissing = lib.mbr_group_name_to_uuid(groupName, uu)
+    if groupMissing:
+        return True
+
+    # See if userOrGroupName matches a user
+    result = lib.mbr_user_name_to_uuid(userOrGroupName, uu)
+    if result:
+        # Not a user, try looking up a group of that name
+        result = lib.mbr_group_name_to_uuid(userOrGroupName, uu)
+
+    if result:
+        # Neither a user nor a group matches the name
+        return False
+
+    # See if the uuid is a member of the service access group
+    isMember = ffi.new(&quot;int *&quot;)
+    result = lib.mbr_check_service_membership(uu, serviceName, isMember)
+    if not result and isMember[0]:
+        return True
+
+    return False
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarserverprovisionrootpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/provision/root.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/provision/root.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/provision/root.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -19,6 +19,12 @@
</span><span class="cx">     &quot;RootResource&quot;,
</span><span class="cx"> ]
</span><span class="cx"> 
</span><ins>+try:
+    from calendarserver.platform.darwin.sacl import checkSACL
+except ImportError:
+    # OS X Server SACLs not supported on this system, make SACL check a no-op
+    checkSACL = lambda *ignored: True
+
</ins><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from twisted.cred.error import LoginFailed, UnauthorizedLogin
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue, succeed
</span><span class="lines">@@ -75,10 +81,7 @@
</span><span class="cx">         super(RootResource, self).__init__(path, *args, **kwargs)
</span><span class="cx"> 
</span><span class="cx">         if config.EnableSACLs:
</span><del>-            if RootResource.CheckSACL:
-                self.useSacls = True
-            else:
-                log.warn(&quot;SACLs are enabled, but SACLs are not supported.&quot;)
</del><ins>+            self.useSacls = True
</ins><span class="cx"> 
</span><span class="cx">         self.contentFilters = []
</span><span class="cx"> 
</span><span class="lines">@@ -122,7 +125,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def checkSacl(self, request):
</del><ins>+    def checkSACL(self, request):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Check SACLs against the current request
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -147,7 +150,7 @@
</span><span class="cx">         # with an empty string.
</span><span class="cx">         if authzUser is None:
</span><span class="cx">             for saclService in saclServices:
</span><del>-                if RootResource.CheckSACL(&quot;&quot;, saclService) == 0:
</del><ins>+                if checkSACL(&quot;&quot;, saclService):
</ins><span class="cx">                     # No group actually exists for this SACL, so allow
</span><span class="cx">                     # unauthenticated access
</span><span class="cx">                     returnValue(True)
</span><span class="lines">@@ -169,7 +172,7 @@
</span><span class="cx"> 
</span><span class="cx">         access = False
</span><span class="cx">         for saclService in saclServices:
</span><del>-            if RootResource.CheckSACL(username, saclService) == 0:
</del><ins>+            if checkSACL(username, saclService):
</ins><span class="cx">                 # Access is allowed
</span><span class="cx">                 access = True
</span><span class="cx">                 break
</span><span class="lines">@@ -346,7 +349,7 @@
</span><span class="cx">             self.useSacls and
</span><span class="cx">             not hasattr(request, &quot;checkedSACL&quot;)
</span><span class="cx">         ):
</span><del>-            yield self.checkSacl(request)
</del><ins>+            yield self.checkSACL(request)
</ins><span class="cx"> 
</span><span class="cx">         if config.RejectClients:
</span><span class="cx">             #
</span><span class="lines">@@ -432,11 +435,3 @@
</span><span class="cx"> 
</span><span class="cx">     def http_DELETE(self, request):
</span><span class="cx">         return responsecode.FORBIDDEN
</span><del>-
-# So CheckSACL will be parameterized
-# We do this after RootResource is defined
-try:
-    from calendarserver.platform.darwin._sacl import CheckSACL
-    RootResource.CheckSACL = CheckSACL
-except ImportError:
-    RootResource.CheckSACL = None
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarserverprovisiontesttest_rootpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/provision/test/test_root.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/provision/test/test_root.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/provision/test/test_root.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -26,22 +26,27 @@
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase, SimpleStoreRequest
</span><span class="cx"> from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
</span><span class="cx"> 
</span><del>-from calendarserver.provision.root import RootResource
</del><ins>+import calendarserver.provision.root  # for patching checkSACL
</ins><span class="cx"> 
</span><span class="cx"> 
</span><del>-class FakeCheckSACL(object):
-    def __init__(self, sacls=None):
-        self.sacls = sacls or {}
</del><ins>+TEST_SACLS = {
+    &quot;calendar&quot;: [
+        &quot;dreid&quot;
+    ],
+    &quot;addressbook&quot;: [
+        &quot;dreid&quot;
+    ],
+}
</ins><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def __call__(self, username, service):
-        if service not in self.sacls:
-            return 1
</del><ins>+def stubCheckSACL(username, service):
+    if service not in TEST_SACLS:
+        return True
</ins><span class="cx"> 
</span><del>-        if username in self.sacls[service]:
-            return 0
</del><ins>+    if username in TEST_SACLS[service]:
+        return True
</ins><span class="cx"> 
</span><del>-        return 1
</del><ins>+    return False
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -51,7 +56,7 @@
</span><span class="cx">     def setUp(self):
</span><span class="cx">         yield super(RootTests, self).setUp()
</span><span class="cx"> 
</span><del>-        RootResource.CheckSACL = FakeCheckSACL(sacls={&quot;calendar&quot;: [&quot;dreid&quot;]})
</del><ins>+        self.patch(calendarserver.provision.root, &quot;checkSACL&quot;, stubCheckSACL)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -337,7 +342,7 @@
</span><span class="cx">             &quot;PROPFIND&quot;,
</span><span class="cx">             &quot;/principals/users/dreid/&quot;,
</span><span class="cx">             headers=http_headers.Headers({
</span><del>-                    'Depth': '1',
</del><ins>+                'Depth': '1',
</ins><span class="cx">             }),
</span><span class="cx">             authPrincipal=principal,
</span><span class="cx">             content=body
</span><span class="lines">@@ -353,7 +358,7 @@
</span><span class="cx">             &quot;PROPFIND&quot;,
</span><span class="cx">             &quot;/principals/users/dreid/&quot;,
</span><span class="cx">             headers=http_headers.Headers({
</span><del>-                    'Depth': '1',
</del><ins>+                'Depth': '1',
</ins><span class="cx">             }),
</span><span class="cx">             authPrincipal=principal,
</span><span class="cx">             content=body
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarserverpushnotifierpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/push/notifier.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/push/notifier.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/push/notifier.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -20,7 +20,7 @@
</span><span class="cx"> 
</span><span class="cx"> from twext.enterprise.dal.record import fromTable
</span><span class="cx"> from twext.enterprise.dal.syntax import Delete, Select, Parameter
</span><del>-from twext.enterprise.jobqueue import WorkItem, WORK_PRIORITY_HIGH, \
</del><ins>+from twext.enterprise.jobqueue import JobItem, WorkItem, WORK_PRIORITY_HIGH, \
</ins><span class="cx">     WORK_WEIGHT_1
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> 
</span><span class="lines">@@ -50,7 +50,7 @@
</span><span class="cx"> 
</span><span class="cx">         # Find all work items with the same push ID and find the highest
</span><span class="cx">         # priority.  Delete matching work items.
</span><del>-        results = (yield Select([self.table.WORK_ID, self.table.PUSH_PRIORITY],
</del><ins>+        results = (yield Select([self.table.WORK_ID, self.table.JOB_ID, self.table.PUSH_PRIORITY],
</ins><span class="cx">             From=self.table, Where=self.table.PUSH_ID == self.pushID).on(
</span><span class="cx">             self.transaction))
</span><span class="cx"> 
</span><span class="lines">@@ -59,17 +59,20 @@
</span><span class="cx">         # If there are other enqueued work items for this push ID, find the
</span><span class="cx">         # highest priority one and use that value
</span><span class="cx">         if results:
</span><del>-            workIDs = []
-            for workID, priority in results:
-                if priority &gt; maxPriority:
-                    maxPriority = priority
-                workIDs.append(workID)
</del><ins>+            workIDs, jobIDs, priorities = zip(*results)
+            maxPriority = max(priorities)
</ins><span class="cx"> 
</span><del>-            # Delete the work items we selected
</del><ins>+            # Delete the work items and jobs we selected - deleting the job will ensure that there are no
+            # orphaned&quot; jobs left in the job queue which would otherwise get to run at some later point,
+            # though not do anything because there is no related work item.
</ins><span class="cx">             yield Delete(
</span><span class="cx">                 From=self.table,
</span><span class="cx">                 Where=self.table.WORK_ID.In(Parameter(&quot;workIDs&quot;, len(workIDs)))
</span><span class="cx">             ).on(self.transaction, workIDs=workIDs)
</span><ins>+            yield Delete(
+                From=JobItem.table, #@UndefinedVariable
+                Where=JobItem.jobID.In(Parameter(&quot;jobIDs&quot;, len(jobIDs))) #@UndefinedVariable
+            ).on(self.transaction, jobIDs=jobIDs)
</ins><span class="cx"> 
</span><span class="cx">         pushDistributor = self.transaction._pushDistributor
</span><span class="cx">         if pushDistributor is not None:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertapcaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tap/caldav.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tap/caldav.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tap/caldav.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -67,7 +67,7 @@
</span><span class="cx"> from twext.enterprise.adbapi2 import ConnectionPool
</span><span class="cx"> from twext.enterprise.ienterprise import ORACLE_DIALECT
</span><span class="cx"> from twext.enterprise.ienterprise import POSTGRES_DIALECT
</span><del>-from twext.enterprise.jobqueue import NonPerformingQueuer
</del><ins>+from twext.enterprise.jobqueue import NonPerformingQueuer, JobItem
</ins><span class="cx"> from twext.enterprise.jobqueue import PeerConnectionPool
</span><span class="cx"> from twext.enterprise.jobqueue import WorkerFactory as QueueWorkerFactory
</span><span class="cx"> from twext.application.service import ReExecService
</span><span class="lines">@@ -881,6 +881,8 @@
</span><span class="cx">         if pool is not None:
</span><span class="cx">             pool.setServiceParent(result)
</span><span class="cx"> 
</span><ins>+        self._initJobQueue(None)
+
</ins><span class="cx">         if config.ControlSocket:
</span><span class="cx">             id = config.ControlSocket
</span><span class="cx">             self.log.info(&quot;Control via AF_UNIX: {id}&quot;, id=id)
</span><span class="lines">@@ -1281,6 +1283,7 @@
</span><span class="cx">             pool = PeerConnectionPool(
</span><span class="cx">                 reactor, store.newTransaction, config.WorkQueue.ampPort
</span><span class="cx">             )
</span><ins>+            self._initJobQueue(pool)
</ins><span class="cx">             store.queuer = store.queuer.transferProposalCallbacks(pool)
</span><span class="cx">             pool.setServiceParent(result)
</span><span class="cx"> 
</span><span class="lines">@@ -1869,6 +1872,7 @@
</span><span class="cx">             pool = PeerConnectionPool(
</span><span class="cx">                 reactor, store.newTransaction, config.WorkQueue.ampPort
</span><span class="cx">             )
</span><ins>+            self._initJobQueue(pool)
</ins><span class="cx">             store.queuer = store.queuer.transferProposalCallbacks(pool)
</span><span class="cx">             controlSocket.addFactory(
</span><span class="cx">                 _QUEUE_ROUTE, pool.workerListenerFactory()
</span><span class="lines">@@ -1991,7 +1995,34 @@
</span><span class="cx">                         remove(checkSocket)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _initJobQueue(self, pool):
+        &quot;&quot;&quot;
+        Common job queue initialization
</ins><span class="cx"> 
</span><ins>+        @param pool: the connection pool to init or L{None}
+        @type pool: L{PeerConnectionPool} or C{None}
+        &quot;&quot;&quot;
+
+        # Initialize queue polling parameters from config settings
+        if pool is not None:
+            for attr in (
+                &quot;queuePollInterval&quot;,
+                &quot;queueOverdueTimeout&quot;,
+                &quot;overloadLevel&quot;,
+                &quot;highPriorityLevel&quot;,
+                &quot;mediumPriorityLevel&quot;,
+            ):
+                setattr(pool, attr, getattr(config.WorkQueue, attr))
+
+        # Initialize job parameters from config settings
+        for attr in (
+            &quot;failureRescheduleInterval&quot;,
+            &quot;lockRescheduleInterval&quot;,
+        ):
+            setattr(JobItem, attr, getattr(config.WorkQueue, attr))
+
+
+
</ins><span class="cx"> class TwistdSlaveProcess(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     A L{TwistdSlaveProcess} is information about how to start a slave process
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolsanonymizepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/anonymize.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/anonymize.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/anonymize.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -342,7 +342,7 @@
</span><span class="cx">             except KeyError:
</span><span class="cx">                 pass
</span><span class="cx"> 
</span><del>-    return pyobj.getText(includeTimezones=True)
</del><ins>+    return pyobj.getText(includeTimezones=Calendar.ALL_TIMEZONES)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolscalverifypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/calverify.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/calverify.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/calverify.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -255,7 +255,6 @@
</span><span class="cx"> 
</span><span class="cx"> Options for --ical:
</span><span class="cx"> 
</span><del>---badcua   : only look for bad calendar user addresses.
</del><span class="cx"> --uuid     : only scan specified calendar homes. Can be a partial GUID
</span><span class="cx">              to scan all GUIDs with that as a prefix.
</span><span class="cx"> --uid      : scan only calendar data with the specific iCalendar UID.
</span><span class="lines">@@ -2719,7 +2718,7 @@
</span><span class="cx">             return NukeService(store, options, output, reactor, config)
</span><span class="cx">         elif options[&quot;missing&quot;]:
</span><span class="cx">             return OrphansService(store, options, output, reactor, config)
</span><del>-        elif options[&quot;ical&quot;] or options[&quot;badcua&quot;]:
</del><ins>+        elif options[&quot;ical&quot;]:
</ins><span class="cx">             return BadDataService(store, options, output, reactor, config)
</span><span class="cx">         elif options[&quot;mismatch&quot;]:
</span><span class="cx">             return SchedulingMismatchService(store, options, output, reactor, config)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolsdashboardpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/dashboard.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/dashboard.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/dashboard.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -545,8 +545,8 @@
</span><span class="cx">             &quot;Total:&quot;,
</span><span class="cx">             total_queued,
</span><span class="cx">             total_assigned,
</span><ins>+            total_late,
</ins><span class="cx">             total_failed,
</span><del>-            total_late,
</del><span class="cx">             total_completed,
</span><span class="cx">             safeDivision(total_time, total_completed, 1000.0)
</span><span class="cx">         )
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolsdbinspectpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/dbinspect.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/dbinspect.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/dbinspect.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -96,26 +96,27 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def UserNameFromUID(txn, uid):
</span><del>-    record = txn.directoryService().recordWithGUID(uid)
-    return record.shortNames[0] if record else &quot;(%s)&quot; % (uid,)
</del><ins>+    record = yield txn.directoryService().recordWithGUID(uid)
+    returnValue(record.shortNames[0] if record else &quot;(%s)&quot; % (uid,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><ins>+@inlineCallbacks
</ins><span class="cx"> def UIDFromInput(txn, value):
</span><span class="cx">     try:
</span><del>-        return str(UUID(value)).upper()
</del><ins>+        returnValue(str(UUID(value)).upper())
</ins><span class="cx">     except (ValueError, TypeError):
</span><span class="cx">         pass
</span><span class="cx"> 
</span><del>-    record = txn.directoryService().recordWithShortName(RecordType.user, value)
</del><ins>+    record = yield txn.directoryService().recordWithShortName(RecordType.user, value)
</ins><span class="cx">     if record is None:
</span><del>-        record = txn.directoryService().recordWithShortName(CalRecordType.location, value)
</del><ins>+        record = yield txn.directoryService().recordWithShortName(CalRecordType.location, value)
</ins><span class="cx">     if record is None:
</span><del>-        record = txn.directoryService().recordWithShortName(CalRecordType.resource, value)
</del><ins>+        record = yield txn.directoryService().recordWithShortName(CalRecordType.resource, value)
</ins><span class="cx">     if record is None:
</span><del>-        record = txn.directoryService().recordWithShortName(RecordType.group, value)
-    return record.guid if record else None
</del><ins>+        record = yield txn.directoryService().recordWithShortName(RecordType.group, value)
+    returnValue(record.guid if record else None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -184,7 +185,7 @@
</span><span class="cx">         table = tables.Table()
</span><span class="cx">         table.addHeader((&quot;Owner UID&quot;, &quot;Short Name&quot;))
</span><span class="cx">         for uid in sorted(uids):
</span><del>-            shortname = UserNameFromUID(txn, uid)
</del><ins>+            shortname = yield UserNameFromUID(txn, uid)
</ins><span class="cx">             if shortname.startswith(&quot;(&quot;):
</span><span class="cx">                 missing += 1
</span><span class="cx">             table.addRow((
</span><span class="lines">@@ -230,7 +231,7 @@
</span><span class="cx">         table.addHeader((&quot;Owner UID&quot;, &quot;Short Name&quot;, &quot;Calendars&quot;, &quot;Resources&quot;))
</span><span class="cx">         totals = [0, 0, 0]
</span><span class="cx">         for uid in sorted(results.keys()):
</span><del>-            shortname = UserNameFromUID(txn, uid)
</del><ins>+            shortname = yield UserNameFromUID(txn, uid)
</ins><span class="cx">             table.addRow((
</span><span class="cx">                 uid,
</span><span class="cx">                 shortname,
</span><span class="lines">@@ -287,7 +288,7 @@
</span><span class="cx">         table = tables.Table()
</span><span class="cx">         table.addHeader((&quot;Owner UID&quot;, &quot;Short Name&quot;, &quot;Calendar&quot;, &quot;Resources&quot;))
</span><span class="cx">         for uid, calname, count in sorted(uids, key=lambda x: (x[0], x[1])):
</span><del>-            shortname = UserNameFromUID(txn, uid)
</del><ins>+            shortname = yield UserNameFromUID(txn, uid)
</ins><span class="cx">             table.addRow((
</span><span class="cx">                 uid,
</span><span class="cx">                 shortname,
</span><span class="lines">@@ -329,7 +330,7 @@
</span><span class="cx">     def doIt(self, txn):
</span><span class="cx"> 
</span><span class="cx">         uid = raw_input(&quot;Owner UID/Name: &quot;)
</span><del>-        uid = UIDFromInput(txn, uid)
</del><ins>+        uid = yield UIDFromInput(txn, uid)
</ins><span class="cx">         uids = yield self.getCalendars(txn, uid)
</span><span class="cx"> 
</span><span class="cx">         # Print table of results
</span><span class="lines">@@ -337,7 +338,7 @@
</span><span class="cx">         table.addHeader((&quot;Owner UID&quot;, &quot;Short Name&quot;, &quot;Calendars&quot;, &quot;ID&quot;, &quot;Resources&quot;))
</span><span class="cx">         totals = [0, 0, ]
</span><span class="cx">         for uid, calname, resid, count in sorted(uids, key=lambda x: x[1]):
</span><del>-            shortname = UserNameFromUID(txn, uid)
</del><ins>+            shortname = yield UserNameFromUID(txn, uid)
</ins><span class="cx">             table.addRow((
</span><span class="cx">                 uid if totals[0] == 0 else &quot;&quot;,
</span><span class="cx">                 shortname if totals[0] == 0 else &quot;&quot;,
</span><span class="lines">@@ -390,7 +391,7 @@
</span><span class="cx">         table = tables.Table()
</span><span class="cx">         table.addHeader((&quot;Owner UID&quot;, &quot;Short Name&quot;, &quot;Calendar&quot;, &quot;ID&quot;, &quot;Type&quot;, &quot;UID&quot;))
</span><span class="cx">         for uid, calname, id, caltype, caluid in sorted(uids, key=lambda x: (x[0], x[1])):
</span><del>-            shortname = UserNameFromUID(txn, uid)
</del><ins>+            shortname = yield UserNameFromUID(txn, uid)
</ins><span class="cx">             table.addRow((
</span><span class="cx">                 uid,
</span><span class="cx">                 shortname,
</span><span class="lines">@@ -484,9 +485,10 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Base class for common event details commands.
</span><span class="cx">     &quot;&quot;&quot;
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def printEventDetails(self, txn, details):
</span><span class="cx">         owner, calendar, resource_id, resource, created, modified, data = details
</span><del>-        shortname = UserNameFromUID(txn, owner)
</del><ins>+        shortname = yield UserNameFromUID(txn, owner)
</ins><span class="cx">         table = tables.Table()
</span><span class="cx">         table.addRow((&quot;Owner UID:&quot;, owner,))
</span><span class="cx">         table.addRow((&quot;User Name:&quot;, shortname,))
</span><span class="lines">@@ -540,7 +542,7 @@
</span><span class="cx">             returnValue(None)
</span><span class="cx">         result = yield self.getData(txn, rid)
</span><span class="cx">         if result:
</span><del>-            self.printEventDetails(txn, result[0])
</del><ins>+            yield self.printEventDetails(txn, result[0])
</ins><span class="cx">         else:
</span><span class="cx">             print(&quot;Could not find resource&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -562,7 +564,7 @@
</span><span class="cx">         rows = yield self.getData(txn, uid)
</span><span class="cx">         if rows:
</span><span class="cx">             for result in rows:
</span><del>-                self.printEventDetails(txn, result)
</del><ins>+                yield self.printEventDetails(txn, result)
</ins><span class="cx">         else:
</span><span class="cx">             print(&quot;Could not find icalendar data&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -584,7 +586,7 @@
</span><span class="cx">         rows = yield self.getData(txn, name)
</span><span class="cx">         if rows:
</span><span class="cx">             for result in rows:
</span><del>-                self.printEventDetails(txn, result)
</del><ins>+                yield self.printEventDetails(txn, result)
</ins><span class="cx">         else:
</span><span class="cx">             print(&quot;Could not find icalendar data&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -603,11 +605,11 @@
</span><span class="cx">     def doIt(self, txn):
</span><span class="cx"> 
</span><span class="cx">         uid = raw_input(&quot;Owner UID/Name: &quot;)
</span><del>-        uid = UIDFromInput(txn, uid)
</del><ins>+        uid = yield UIDFromInput(txn, uid)
</ins><span class="cx">         rows = yield self.getData(txn, uid)
</span><span class="cx">         if rows:
</span><span class="cx">             for result in rows:
</span><del>-                self.printEventDetails(txn, result)
</del><ins>+                yield self.printEventDetails(txn, result)
</ins><span class="cx">         else:
</span><span class="cx">             print(&quot;Could not find icalendar data&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -626,12 +628,12 @@
</span><span class="cx">     def doIt(self, txn):
</span><span class="cx"> 
</span><span class="cx">         uid = raw_input(&quot;Owner UID/Name: &quot;)
</span><del>-        uid = UIDFromInput(txn, uid)
</del><ins>+        uid = yield UIDFromInput(txn, uid)
</ins><span class="cx">         name = raw_input(&quot;Calendar resource name: &quot;)
</span><span class="cx">         rows = yield self.getData(txn, uid, name)
</span><span class="cx">         if rows:
</span><span class="cx">             for result in rows:
</span><del>-                self.printEventDetails(txn, result)
</del><ins>+                yield self.printEventDetails(txn, result)
</ins><span class="cx">         else:
</span><span class="cx">             print(&quot;Could not find icalendar data&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -661,7 +663,7 @@
</span><span class="cx">         rows = yield self.getData(txn, homeName, calendarName, resourceName)
</span><span class="cx">         if rows:
</span><span class="cx">             for result in rows:
</span><del>-                self.printEventDetails(txn, result)
</del><ins>+                yield self.printEventDetails(txn, result)
</ins><span class="cx">         else:
</span><span class="cx">             print(&quot;Could not find icalendar data&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -693,7 +695,7 @@
</span><span class="cx">         rows = yield self.getData(txn, uid)
</span><span class="cx">         if rows:
</span><span class="cx">             for result in rows:
</span><del>-                self.printEventDetails(txn, result)
</del><ins>+                yield self.printEventDetails(txn, result)
</ins><span class="cx">         else:
</span><span class="cx">             print(&quot;Could not find icalendar data&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -772,7 +774,7 @@
</span><span class="cx">             event = yield calendar.calendarObjectWithName(name)
</span><span class="cx">             ical_data = yield event.component()
</span><span class="cx">             ical_data = PerUserDataFilter(uid).filter(ical_data)
</span><del>-            ical_data.stripKnownTimezones()
</del><ins>+            ical_data.stripStandardTimezones()
</ins><span class="cx"> 
</span><span class="cx">             table = tables.Table()
</span><span class="cx">             table.addRow((&quot;Calendar:&quot;, calendar.name(),))
</span><span class="lines">@@ -782,7 +784,7 @@
</span><span class="cx">             table.addRow((&quot;Modified&quot;, event.modified()))
</span><span class="cx">             print(&quot;\n&quot;)
</span><span class="cx">             table.printTable()
</span><del>-            print(ical_data.getTextWithTimezones(includeTimezones=False))
</del><ins>+            print(ical_data.getTextWithoutTimezones())
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolsgatewaypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/gateway.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/gateway.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/gateway.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -380,7 +380,7 @@
</span><span class="cx">             self.respondWithError(&quot;Principal not found: %s&quot; % (uid,))
</span><span class="cx">             return
</span><span class="cx">         recordDict = recordToDict(record)
</span><del>-        # recordDict['AutoSchedule'] = principal.getAutoSchedule()
</del><ins>+        # recordDict['AutoSchedule'] = yield principal.getAutoSchedule()
</ins><span class="cx">         try:
</span><span class="cx">             recordDict['AutoAcceptGroup'] = record.autoAcceptGroup
</span><span class="cx">         except AttributeError:
</span><span class="lines">@@ -398,7 +398,7 @@
</span><span class="cx">     # Resources
</span><span class="cx"> 
</span><span class="cx">     def command_getResourceList(self, command):
</span><del>-        self.respondWithRecordsOfTypes(self.dir, command, [&quot;resources&quot;])
</del><ins>+        return self.respondWithRecordsOfTypes(self.dir, command, [&quot;resources&quot;])
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     # deferred
</span><span class="lines">@@ -416,7 +416,7 @@
</span><span class="cx">     def _delete(self, typeName, command):
</span><span class="cx">         uid = command['GeneratedUID']
</span><span class="cx">         yield self.dir.removeRecords([uid])
</span><del>-        self.respondWithRecordsOfTypes(self.dir, command, [typeName])
</del><ins>+        yield self.respondWithRecordsOfTypes(self.dir, command, [typeName])
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolsprincipalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/principals.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/principals.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/principals.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -409,12 +409,15 @@
</span><span class="cx">                 )
</span><span class="cx">             )
</span><span class="cx">             print(&quot;   UID: {u}&quot;.format(u=record.uid,))
</span><del>-            print(
-                &quot;   Record name{plural}: {names}&quot;.format(
-                    plural=(&quot;s&quot; if len(record.shortNames) &gt; 1 else &quot;&quot;),
-                    names=(&quot;, &quot;.join(record.shortNames))
</del><ins>+            try:
+                print(
+                    &quot;   Record name{plural}: {names}&quot;.format(
+                        plural=(&quot;s&quot; if len(record.shortNames) &gt; 1 else &quot;&quot;),
+                        names=(&quot;, &quot;.join(record.shortNames))
+                    )
</ins><span class="cx">                 )
</span><del>-            )
</del><ins>+            except AttributeError:
+                pass
</ins><span class="cx">             try:
</span><span class="cx">                 if record.emailAddresses:
</span><span class="cx">                     print(
</span><span class="lines">@@ -787,7 +790,9 @@
</span><span class="cx">     txn = store.newTransaction()
</span><span class="cx">     groupUIDs = yield txn.allGroupDelegates()
</span><span class="cx">     for groupUID in groupUIDs:
</span><del>-        groupID, name, _ignore_membershipHash, modified = yield txn.groupByUID(
</del><ins>+        (
+            groupID, name, _ignore_membershipHash, modified, extant
+        ) = yield txn.groupByUID(
</ins><span class="cx">             groupUID
</span><span class="cx">         )
</span><span class="cx">         print(&quot;Group: \&quot;{name}\&quot; ({uid})&quot;.format(name=name, uid=groupUID))
</span><span class="lines">@@ -803,7 +808,7 @@
</span><span class="cx">                 )
</span><span class="cx"> 
</span><span class="cx">         print(&quot;Group members:&quot;)
</span><del>-        memberUIDs = yield txn.membersOfGroup(groupID)
</del><ins>+        memberUIDs = yield txn.groupMemberUIDs(groupID)
</ins><span class="cx">         for memberUID in memberUIDs:
</span><span class="cx">             record = yield directory.recordWithUID(memberUID)
</span><span class="cx">             print(prettyRecord(record))
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolspurgepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/purge.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/purge.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/purge.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -234,6 +234,7 @@
</span><span class="cx"> 
</span><span class="cx">         # Check for pending scheduling operations
</span><span class="cx">         sow = schema.SCHEDULE_ORGANIZER_WORK
</span><ins>+        sosw = schema.SCHEDULE_ORGANIZER_SEND_WORK
</ins><span class="cx">         srw = schema.SCHEDULE_REPLY_WORK
</span><span class="cx">         srcw = schema.SCHEDULE_REPLY_CANCEL_WORK
</span><span class="cx">         rows = yield Select(
</span><span class="lines">@@ -242,14 +243,21 @@
</span><span class="cx">             Where=(sow.HOME_RESOURCE_ID == self.homeResourceID),
</span><span class="cx">             SetExpression=Union(
</span><span class="cx">                 Select(
</span><del>-                    [srw.HOME_RESOURCE_ID],
-                    From=srw,
-                    Where=(srw.HOME_RESOURCE_ID == self.homeResourceID),
</del><ins>+                    [sosw.HOME_RESOURCE_ID],
+                    From=sosw,
+                    Where=(sosw.HOME_RESOURCE_ID == self.homeResourceID),
</ins><span class="cx">                     SetExpression=Union(
</span><span class="cx">                         Select(
</span><del>-                            [srcw.HOME_RESOURCE_ID],
-                            From=srcw,
-                            Where=(srcw.HOME_RESOURCE_ID == self.homeResourceID),
</del><ins>+                            [srw.HOME_RESOURCE_ID],
+                            From=srw,
+                            Where=(srw.HOME_RESOURCE_ID == self.homeResourceID),
+                            SetExpression=Union(
+                                Select(
+                                    [srcw.HOME_RESOURCE_ID],
+                                    From=srcw,
+                                    Where=(srcw.HOME_RESOURCE_ID == self.homeResourceID),
+                                )
+                            ),
</ins><span class="cx">                         )
</span><span class="cx">                     ),
</span><span class="cx">                 )
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolsshellcmdpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/cmd.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/cmd.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/cmd.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -163,6 +163,7 @@
</span><span class="cx">                 returnValue(())
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def directoryRecordWithID(self, id):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Obtains a directory record corresponding to the given C{id}.
</span><span class="lines">@@ -173,7 +174,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         directory = self.protocol.service.directory
</span><span class="cx"> 
</span><del>-        record = directory.recordWithUID(id)
</del><ins>+        record = yield directory.recordWithUID(id)
</ins><span class="cx"> 
</span><span class="cx">         if not record:
</span><span class="cx">             # Try type:name form
</span><span class="lines">@@ -182,9 +183,9 @@
</span><span class="cx">             except ValueError:
</span><span class="cx">                 pass
</span><span class="cx">             else:
</span><del>-                record = directory.recordWithShortName(recordType, shortName)
</del><ins>+                record = yield directory.recordWithShortName(recordType, shortName)
</ins><span class="cx"> 
</span><del>-        return record
</del><ins>+        returnValue(record)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def commands(self, showHidden=False):
</span><span class="lines">@@ -628,7 +629,7 @@
</span><span class="cx"> 
</span><span class="cx">         directory = self.protocol.service.directory
</span><span class="cx"> 
</span><del>-        record = self.directoryRecordWithID(id)
</del><ins>+        record = yield self.directoryRecordWithID(id)
</ins><span class="cx"> 
</span><span class="cx">         if record:
</span><span class="cx">             self.terminal.write((yield recordInfo(directory, record)))
</span><span class="lines">@@ -657,7 +658,7 @@
</span><span class="cx"> 
</span><span class="cx">         records = []
</span><span class="cx">         for id in tokens:
</span><del>-            record = self.directoryRecordWithID(id)
</del><ins>+            record = yield self.directoryRecordWithID(id)
</ins><span class="cx">             records.append(record)
</span><span class="cx"> 
</span><span class="cx">             if not record:
</span><span class="lines">@@ -701,6 +702,7 @@
</span><span class="cx">     # Sharing
</span><span class="cx">     #
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def cmd_share(self, tokens):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Share a resource with a principal.
</span><span class="lines">@@ -715,12 +717,12 @@
</span><span class="cx">         mode = tokens.pop(0)
</span><span class="cx">         principalID = tokens.pop(0)
</span><span class="cx"> 
</span><del>-        record = self.directoryRecordWithID(principalID)
</del><ins>+        record = yield self.directoryRecordWithID(principalID)
</ins><span class="cx"> 
</span><span class="cx">         if not record:
</span><span class="cx">             self.terminal.write(&quot;Principal not found: %s\n&quot; % (principalID,))
</span><span class="cx"> 
</span><del>-        targets = self.getTargets(tokens)
</del><ins>+        targets = yield self.getTargets(tokens)
</ins><span class="cx"> 
</span><span class="cx">         if mode == &quot;r&quot;:
</span><span class="cx">             mode = None
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolsshelldirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/directory.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/directory.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/directory.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -155,7 +155,7 @@
</span><span class="cx">             # I don't know how to get DirectoryRecord objects for the proxyUID here, so, let's cheat for now.
</span><span class="cx">             proxyUID, proxyType = proxyUID.split(&quot;#&quot;)
</span><span class="cx">             if (proxyUID, proxyType) not in proxyInfoSeen:
</span><del>-                proxyRecord = directory.recordWithUID(proxyUID)
</del><ins>+                proxyRecord = yield directory.recordWithUID(proxyUID)
</ins><span class="cx">                 rows.append((proxyUID, proxyRecord.recordType, proxyRecord.shortNames[0], proxyRecord.fullName, proxyType))
</span><span class="cx">                 proxyInfoSeen.add((proxyUID, proxyType))
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolsshellvfspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/vfs.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/vfs.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/shell/vfs.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -290,10 +290,11 @@
</span><span class="cx">         # FIXME: Merge in directory UIDs also?
</span><span class="cx">         # FIXME: Add directory info (eg. name) to list entry
</span><span class="cx"> 
</span><ins>+        @inlineCallbacks
</ins><span class="cx">         def addResult(ignoredTxn, home):
</span><span class="cx">             uid = home.uid()
</span><span class="cx"> 
</span><del>-            record = self.service.directory.recordWithUID(uid)
</del><ins>+            record = yield self.service.directory.recordWithUID(uid)
</ins><span class="cx">             if record:
</span><span class="cx">                 info = {
</span><span class="cx">                     &quot;Record Type&quot;: record.recordType,
</span><span class="lines">@@ -331,12 +332,13 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def list(self):
</span><span class="cx">         names = set()
</span><span class="cx"> 
</span><del>-        for record in self.service.directory.recordsWithRecordType(
</del><ins>+        for record in (yield self.service.directory.recordsWithRecordType(
</ins><span class="cx">             self.recordType
</span><del>-        ):
</del><ins>+        )):
</ins><span class="cx">             for shortName in record.shortNames:
</span><span class="cx">                 if shortName in names:
</span><span class="cx">                     continue
</span><span class="lines">@@ -395,6 +397,7 @@
</span><span class="cx">         Folder.__init__(self, service, path)
</span><span class="cx"> 
</span><span class="cx">         if record is None:
</span><ins>+            # FIXME: recordWithUID returns a Deferred but we cannot return or yield it in an __init__ method
</ins><span class="cx">             record = self.service.directory.recordWithUID(uid)
</span><span class="cx"> 
</span><span class="cx">         if record is not None:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolstestdeprovisioncaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/deprovision/caldavd.plist (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/deprovision/caldavd.plist        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/deprovision/caldavd.plist        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -115,12 +115,6 @@
</span><span class="cx">     &lt;key&gt;MaxAttendeesPerInstance&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;100&lt;/integer&gt;
</span><span class="cx"> 
</span><del>-    &lt;!-- Maximum number of instances allowed for a single RRULE --&gt;
-    &lt;!-- 0 for no limit --&gt;
-    &lt;key&gt;MaxInstancesForRRULE&lt;/key&gt;
-    &lt;integer&gt;400&lt;/integer&gt;
-
-
</del><span class="cx">     &lt;!--
</span><span class="cx">         Directory service
</span><span class="cx"> 
</span><span class="lines">@@ -135,7 +129,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFile&lt;/key&gt;
</span><span class="lines">@@ -155,7 +149,7 @@
</span><span class="cx">       &lt;true/&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFile&lt;/key&gt;
</span><span class="lines">@@ -167,14 +161,14 @@
</span><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- Open Directory Service (Mac OS X) --&gt;
</span><span class="cx">     &lt;!--
</span><span class="cx">     &lt;key&gt;DirectoryService&lt;/key&gt;
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.appleopendirectory.OpenDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;node&lt;/key&gt;
</span><span class="lines">@@ -198,7 +192,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentXMLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFiles&lt;/key&gt;
</span><span class="lines">@@ -207,14 +201,14 @@
</span><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- Sqlite Augment Service --&gt;
</span><span class="cx">     &lt;!--
</span><span class="cx">     &lt;key&gt;AugmentService&lt;/key&gt;
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentSqliteDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;dbpath&lt;/key&gt;
</span><span class="lines">@@ -229,7 +223,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentPostgreSQLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;host&lt;/key&gt;
</span><span class="lines">@@ -245,7 +239,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.calendaruserproxy.ProxySqliteDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;dbpath&lt;/key&gt;
</span><span class="lines">@@ -259,7 +253,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;host&lt;/key&gt;
</span><span class="lines">@@ -687,7 +681,7 @@
</span><span class="cx">     &lt;!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 --&gt;
</span><span class="cx">     &lt;key&gt;ResponseCompression&lt;/key&gt;
</span><span class="cx">     &lt;false/&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- The retry-after value (in seconds) to return with a 503 error. --&gt;
</span><span class="cx">     &lt;key&gt;HTTPRetryAfter&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;180&lt;/integer&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolstestgatewaycaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/gateway/caldavd.plist (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/gateway/caldavd.plist        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/gateway/caldavd.plist        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -127,12 +127,6 @@
</span><span class="cx">     &lt;key&gt;MaxAttendeesPerInstance&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;100&lt;/integer&gt;
</span><span class="cx"> 
</span><del>-    &lt;!-- Maximum number of instances allowed for a single RRULE --&gt;
-    &lt;!-- 0 for no limit --&gt;
-    &lt;key&gt;MaxInstancesForRRULE&lt;/key&gt;
-    &lt;integer&gt;400&lt;/integer&gt;
-
-
</del><span class="cx">     &lt;!--
</span><span class="cx">         Directory service
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolstestprincipalscaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/principals/caldavd.plist (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/principals/caldavd.plist        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/principals/caldavd.plist        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -119,12 +119,6 @@
</span><span class="cx">     &lt;key&gt;MaxAttendeesPerInstance&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;100&lt;/integer&gt;
</span><span class="cx"> 
</span><del>-    &lt;!-- Maximum number of instances allowed for a single RRULE --&gt;
-    &lt;!-- 0 for no limit --&gt;
-    &lt;key&gt;MaxInstancesForRRULE&lt;/key&gt;
-    &lt;integer&gt;400&lt;/integer&gt;
-
-
</del><span class="cx">     &lt;!--
</span><span class="cx">         Directory service
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolstesttest_calverifypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/test_calverify.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/test_calverify.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/test/test_calverify.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -2541,9 +2541,9 @@
</span><span class="cx"> UID:INVITE_VALID_ORGANIZER_ICS
</span><span class="cx"> DTSTART:%(now_fwd11)s
</span><span class="cx"> DURATION:PT1H
</span><del>-ATTENDEE;CN=Example User1;EMAIL=example1@example.com:urn:x-uid:%(uuid1)s
-ATTENDEE;CN=Example User2;EMAIL=example2@example.com:urn:x-uid:%(uuid2)s
-ORGANIZER;CN=Example User1;EMAIL=example1@example.com:urn:x-uid:%(uuid1)s
</del><ins>+ATTENDEE:urn:x-uid:%(uuid1)s
+ATTENDEE:urn:x-uid:%(uuid2)s
+ORGANIZER:urn:x-uid:%(uuid1)s
</ins><span class="cx"> RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
</span><span class="cx"> RRULE:FREQ=DAILY
</span><span class="cx"> SEQUENCE:1
</span><span class="lines">@@ -2560,9 +2560,9 @@
</span><span class="cx"> UID:INVITE_VALID_ORGANIZER_ICS
</span><span class="cx"> DTSTART:%(now_fwd11)s
</span><span class="cx"> DURATION:PT1H
</span><del>-ATTENDEE;CN=Example User1;EMAIL=example1@example.com:urn:x-uid:%(uuid1)s
-ATTENDEE;CN=Example User2;EMAIL=example2@example.com:urn:x-uid:%(uuid2)s
-ORGANIZER;CN=Example User1;EMAIL=example1@example.com:urn:x-uid:%(uuid1)s
</del><ins>+ATTENDEE:urn:x-uid:%(uuid1)s
+ATTENDEE:urn:x-uid:%(uuid2)s
+ORGANIZER:urn:x-uid:%(uuid1)s
</ins><span class="cx"> RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
</span><span class="cx"> RRULE:FREQ=DAILY
</span><span class="cx"> SEQUENCE:1
</span><span class="lines">@@ -2579,9 +2579,9 @@
</span><span class="cx"> UID:%(uid)s
</span><span class="cx"> DTSTART:%(now)s
</span><span class="cx"> DURATION:PT1H
</span><del>-ATTENDEE;CN=Example User1;EMAIL=example1@example.com:urn:x-uid:%(uuid1)s
-ATTENDEE;CN=Example User2;EMAIL=example2@example.com:urn:x-uid:%(uuid2)s
-ORGANIZER;CN=Example User1;EMAIL=example1@example.com:urn:x-uid:%(uuid1)s
</del><ins>+ATTENDEE:urn:x-uid:%(uuid1)s
+ATTENDEE:urn:x-uid:%(uuid2)s
+ORGANIZER:urn:x-uid:%(uuid1)s
</ins><span class="cx"> RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
</span><span class="cx"> RRULE:FREQ=DAILY;UNTIL=%(now_fwd11_1)s
</span><span class="cx"> SEQUENCE:1
</span><span class="lines">@@ -2598,9 +2598,9 @@
</span><span class="cx"> UID:%(uid)s
</span><span class="cx"> DTSTART:%(now)s
</span><span class="cx"> DURATION:PT1H
</span><del>-ATTENDEE;CN=Example User1;EMAIL=example1@example.com:urn:x-uid:%(uuid1)s
-ATTENDEE;CN=Example User2;EMAIL=example2@example.com:urn:x-uid:%(uuid2)s
-ORGANIZER;CN=Example User1;EMAIL=example1@example.com:urn:x-uid:%(uuid1)s
</del><ins>+ATTENDEE:urn:x-uid:%(uuid1)s
+ATTENDEE:urn:x-uid:%(uuid2)s
+ORGANIZER:urn:x-uid:%(uuid1)s
</ins><span class="cx"> RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
</span><span class="cx"> RRULE:FREQ=DAILY;UNTIL=%(now_fwd11_1)s
</span><span class="cx"> SEQUENCE:1
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2calendarservertoolsutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/util.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/util.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/calendarserver/tools/util.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -560,11 +560,15 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> def prettyRecord(record):
</span><ins>+    try:
+        shortNames = record.shortNames
+    except AttributeError:
+        shortNames = []
</ins><span class="cx">     return &quot;\&quot;{d}\&quot; {uid} ({rt}) {sn}&quot;.format(
</span><span class="cx">         d=record.displayName,
</span><span class="cx">         rt=record.recordType.name,
</span><span class="cx">         uid=record.uid,
</span><del>-        sn=(&quot;, &quot;.join(record.shortNames))
</del><ins>+        sn=(&quot;, &quot;.join(shortNames))
</ins><span class="cx">     )
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2confauthgenerate_test_accountspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/conf/auth/generate_test_accounts.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/conf/auth/generate_test_accounts.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/conf/auth/generate_test_accounts.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -24,6 +24,8 @@
</span><span class="cx"> 
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+EXTRA_GROUPS = False
+
</ins><span class="cx"> # The uids and guids for CDT test accounts are the same
</span><span class="cx"> # The short name is of the form userNN
</span><span class="cx"> USERGUIDS = &quot;10000000-0000-0000-0000-000000000%03d&quot;
</span><span class="lines">@@ -58,7 +60,7 @@
</span><span class="cx"> &quot;&quot;&quot;.format(uid=uid, guid=guid, fullName=fullName))
</span><span class="cx"> 
</span><span class="cx"> # user01-100
</span><del>-for i in xrange(1, 101):
</del><ins>+for i in xrange(1, 501 if EXTRA_GROUPS else 101):
</ins><span class="cx">     out.write(&quot;&quot;&quot;&lt;record type=&quot;user&quot;&gt;
</span><span class="cx">     &lt;uid&gt;{guid}&lt;/uid&gt;
</span><span class="cx">     &lt;guid&gt;{guid}&lt;/guid&gt;
</span><span class="lines">@@ -92,6 +94,621 @@
</span><span class="cx">     GROUPGUIDS % 7: (USERGUIDS % 22, USERGUIDS % 23, USERGUIDS % 24),
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+if EXTRA_GROUPS:
+    members.update({
+    GROUPGUIDS % 8: (
+        USERGUIDS % 1,
+        USERGUIDS % 2,
+        USERGUIDS % 3,
+        USERGUIDS % 4,
+        USERGUIDS % 5,
+    ),
+    GROUPGUIDS % 9: (
+        USERGUIDS % 1,
+        USERGUIDS % 2,
+        USERGUIDS % 3,
+        USERGUIDS % 4,
+        USERGUIDS % 5,
+        USERGUIDS % 6,
+        USERGUIDS % 7,
+        USERGUIDS % 8,
+        USERGUIDS % 9,
+        USERGUIDS % 10,
+        USERGUIDS % 11,
+        USERGUIDS % 12,
+        USERGUIDS % 13,
+        USERGUIDS % 14,
+        USERGUIDS % 15,
+        USERGUIDS % 16,
+        USERGUIDS % 17,
+        USERGUIDS % 18,
+        USERGUIDS % 19,
+        USERGUIDS % 20,
+        USERGUIDS % 21,
+        USERGUIDS % 22,
+        USERGUIDS % 23,
+        USERGUIDS % 24,
+        USERGUIDS % 25,
+        USERGUIDS % 26,
+        USERGUIDS % 27,
+        USERGUIDS % 28,
+        USERGUIDS % 29,
+        USERGUIDS % 30,
+        USERGUIDS % 31,
+        USERGUIDS % 32,
+        USERGUIDS % 33,
+        USERGUIDS % 34,
+        USERGUIDS % 35,
+        USERGUIDS % 36,
+        USERGUIDS % 37,
+        USERGUIDS % 38,
+        USERGUIDS % 39,
+        USERGUIDS % 40,
+        USERGUIDS % 41,
+        USERGUIDS % 42,
+        USERGUIDS % 43,
+        USERGUIDS % 44,
+        USERGUIDS % 45,
+        USERGUIDS % 46,
+        USERGUIDS % 47,
+        USERGUIDS % 48,
+        USERGUIDS % 49,
+        USERGUIDS % 50,
+        USERGUIDS % 51,
+        USERGUIDS % 52,
+        USERGUIDS % 53,
+        USERGUIDS % 54,
+        USERGUIDS % 55,
+        USERGUIDS % 56,
+        USERGUIDS % 57,
+        USERGUIDS % 58,
+        USERGUIDS % 59,
+        USERGUIDS % 60,
+        USERGUIDS % 61,
+        USERGUIDS % 62,
+        USERGUIDS % 63,
+        USERGUIDS % 64,
+        USERGUIDS % 65,
+        USERGUIDS % 66,
+        USERGUIDS % 67,
+        USERGUIDS % 68,
+        USERGUIDS % 69,
+        USERGUIDS % 70,
+        USERGUIDS % 71,
+        USERGUIDS % 72,
+        USERGUIDS % 73,
+        USERGUIDS % 74,
+        USERGUIDS % 75,
+        USERGUIDS % 76,
+        USERGUIDS % 77,
+        USERGUIDS % 78,
+        USERGUIDS % 79,
+        USERGUIDS % 80,
+        USERGUIDS % 81,
+        USERGUIDS % 82,
+        USERGUIDS % 83,
+        USERGUIDS % 84,
+        USERGUIDS % 85,
+        USERGUIDS % 86,
+        USERGUIDS % 87,
+        USERGUIDS % 88,
+        USERGUIDS % 89,
+        USERGUIDS % 90,
+        USERGUIDS % 91,
+        USERGUIDS % 92,
+        USERGUIDS % 93,
+        USERGUIDS % 94,
+        USERGUIDS % 95,
+        USERGUIDS % 96,
+        USERGUIDS % 97,
+        USERGUIDS % 98,
+        USERGUIDS % 99,
+        USERGUIDS % 100,
+    ),
+    GROUPGUIDS % 10: (
+        USERGUIDS % 1,
+        USERGUIDS % 2,
+        USERGUIDS % 3,
+        USERGUIDS % 4,
+        USERGUIDS % 5,
+        USERGUIDS % 6,
+        USERGUIDS % 7,
+        USERGUIDS % 8,
+        USERGUIDS % 9,
+        USERGUIDS % 10,
+        USERGUIDS % 11,
+        USERGUIDS % 12,
+        USERGUIDS % 13,
+        USERGUIDS % 14,
+        USERGUIDS % 15,
+        USERGUIDS % 16,
+        USERGUIDS % 17,
+        USERGUIDS % 18,
+        USERGUIDS % 19,
+        USERGUIDS % 20,
+        USERGUIDS % 21,
+        USERGUIDS % 22,
+        USERGUIDS % 23,
+        USERGUIDS % 24,
+        USERGUIDS % 25,
+        USERGUIDS % 26,
+        USERGUIDS % 27,
+        USERGUIDS % 28,
+        USERGUIDS % 29,
+        USERGUIDS % 30,
+        USERGUIDS % 31,
+        USERGUIDS % 32,
+        USERGUIDS % 33,
+        USERGUIDS % 34,
+        USERGUIDS % 35,
+        USERGUIDS % 36,
+        USERGUIDS % 37,
+        USERGUIDS % 38,
+        USERGUIDS % 39,
+        USERGUIDS % 40,
+        USERGUIDS % 41,
+        USERGUIDS % 42,
+        USERGUIDS % 43,
+        USERGUIDS % 44,
+        USERGUIDS % 45,
+        USERGUIDS % 46,
+        USERGUIDS % 47,
+        USERGUIDS % 48,
+        USERGUIDS % 49,
+        USERGUIDS % 50,
+        USERGUIDS % 51,
+        USERGUIDS % 52,
+        USERGUIDS % 53,
+        USERGUIDS % 54,
+        USERGUIDS % 55,
+        USERGUIDS % 56,
+        USERGUIDS % 57,
+        USERGUIDS % 58,
+        USERGUIDS % 59,
+        USERGUIDS % 60,
+        USERGUIDS % 61,
+        USERGUIDS % 62,
+        USERGUIDS % 63,
+        USERGUIDS % 64,
+        USERGUIDS % 65,
+        USERGUIDS % 66,
+        USERGUIDS % 67,
+        USERGUIDS % 68,
+        USERGUIDS % 69,
+        USERGUIDS % 70,
+        USERGUIDS % 71,
+        USERGUIDS % 72,
+        USERGUIDS % 73,
+        USERGUIDS % 74,
+        USERGUIDS % 75,
+        USERGUIDS % 76,
+        USERGUIDS % 77,
+        USERGUIDS % 78,
+        USERGUIDS % 79,
+        USERGUIDS % 80,
+        USERGUIDS % 81,
+        USERGUIDS % 82,
+        USERGUIDS % 83,
+        USERGUIDS % 84,
+        USERGUIDS % 85,
+        USERGUIDS % 86,
+        USERGUIDS % 87,
+        USERGUIDS % 88,
+        USERGUIDS % 89,
+        USERGUIDS % 90,
+        USERGUIDS % 91,
+        USERGUIDS % 92,
+        USERGUIDS % 93,
+        USERGUIDS % 94,
+        USERGUIDS % 95,
+        USERGUIDS % 96,
+        USERGUIDS % 97,
+        USERGUIDS % 98,
+        USERGUIDS % 99,
+        USERGUIDS % 100,
+        USERGUIDS % 101,
+        USERGUIDS % 102,
+        USERGUIDS % 103,
+        USERGUIDS % 104,
+        USERGUIDS % 105,
+        USERGUIDS % 106,
+        USERGUIDS % 107,
+        USERGUIDS % 108,
+        USERGUIDS % 109,
+        USERGUIDS % 110,
+        USERGUIDS % 111,
+        USERGUIDS % 112,
+        USERGUIDS % 113,
+        USERGUIDS % 114,
+        USERGUIDS % 115,
+        USERGUIDS % 116,
+        USERGUIDS % 117,
+        USERGUIDS % 118,
+        USERGUIDS % 119,
+        USERGUIDS % 120,
+        USERGUIDS % 121,
+        USERGUIDS % 122,
+        USERGUIDS % 123,
+        USERGUIDS % 124,
+        USERGUIDS % 125,
+        USERGUIDS % 126,
+        USERGUIDS % 127,
+        USERGUIDS % 128,
+        USERGUIDS % 129,
+        USERGUIDS % 130,
+        USERGUIDS % 131,
+        USERGUIDS % 132,
+        USERGUIDS % 133,
+        USERGUIDS % 134,
+        USERGUIDS % 135,
+        USERGUIDS % 136,
+        USERGUIDS % 137,
+        USERGUIDS % 138,
+        USERGUIDS % 139,
+        USERGUIDS % 140,
+        USERGUIDS % 141,
+        USERGUIDS % 142,
+        USERGUIDS % 143,
+        USERGUIDS % 144,
+        USERGUIDS % 145,
+        USERGUIDS % 146,
+        USERGUIDS % 147,
+        USERGUIDS % 148,
+        USERGUIDS % 149,
+        USERGUIDS % 150,
+        USERGUIDS % 151,
+        USERGUIDS % 152,
+        USERGUIDS % 153,
+        USERGUIDS % 154,
+        USERGUIDS % 155,
+        USERGUIDS % 156,
+        USERGUIDS % 157,
+        USERGUIDS % 158,
+        USERGUIDS % 159,
+        USERGUIDS % 160,
+        USERGUIDS % 161,
+        USERGUIDS % 162,
+        USERGUIDS % 163,
+        USERGUIDS % 164,
+        USERGUIDS % 165,
+        USERGUIDS % 166,
+        USERGUIDS % 167,
+        USERGUIDS % 168,
+        USERGUIDS % 169,
+        USERGUIDS % 170,
+        USERGUIDS % 171,
+        USERGUIDS % 172,
+        USERGUIDS % 173,
+        USERGUIDS % 174,
+        USERGUIDS % 175,
+        USERGUIDS % 176,
+        USERGUIDS % 177,
+        USERGUIDS % 178,
+        USERGUIDS % 179,
+        USERGUIDS % 180,
+        USERGUIDS % 181,
+        USERGUIDS % 182,
+        USERGUIDS % 183,
+        USERGUIDS % 184,
+        USERGUIDS % 185,
+        USERGUIDS % 186,
+        USERGUIDS % 187,
+        USERGUIDS % 188,
+        USERGUIDS % 189,
+        USERGUIDS % 190,
+        USERGUIDS % 191,
+        USERGUIDS % 192,
+        USERGUIDS % 193,
+        USERGUIDS % 194,
+        USERGUIDS % 195,
+        USERGUIDS % 196,
+        USERGUIDS % 197,
+        USERGUIDS % 198,
+        USERGUIDS % 199,
+        USERGUIDS % 200,
+        USERGUIDS % 201,
+        USERGUIDS % 202,
+        USERGUIDS % 203,
+        USERGUIDS % 204,
+        USERGUIDS % 205,
+        USERGUIDS % 206,
+        USERGUIDS % 207,
+        USERGUIDS % 208,
+        USERGUIDS % 209,
+        USERGUIDS % 210,
+        USERGUIDS % 211,
+        USERGUIDS % 212,
+        USERGUIDS % 213,
+        USERGUIDS % 214,
+        USERGUIDS % 215,
+        USERGUIDS % 216,
+        USERGUIDS % 217,
+        USERGUIDS % 218,
+        USERGUIDS % 219,
+        USERGUIDS % 220,
+        USERGUIDS % 221,
+        USERGUIDS % 222,
+        USERGUIDS % 223,
+        USERGUIDS % 224,
+        USERGUIDS % 225,
+        USERGUIDS % 226,
+        USERGUIDS % 227,
+        USERGUIDS % 228,
+        USERGUIDS % 229,
+        USERGUIDS % 230,
+        USERGUIDS % 231,
+        USERGUIDS % 232,
+        USERGUIDS % 233,
+        USERGUIDS % 234,
+        USERGUIDS % 235,
+        USERGUIDS % 236,
+        USERGUIDS % 237,
+        USERGUIDS % 238,
+        USERGUIDS % 239,
+        USERGUIDS % 240,
+        USERGUIDS % 241,
+        USERGUIDS % 242,
+        USERGUIDS % 243,
+        USERGUIDS % 244,
+        USERGUIDS % 245,
+        USERGUIDS % 246,
+        USERGUIDS % 247,
+        USERGUIDS % 248,
+        USERGUIDS % 249,
+        USERGUIDS % 250,
+        USERGUIDS % 251,
+        USERGUIDS % 252,
+        USERGUIDS % 253,
+        USERGUIDS % 254,
+        USERGUIDS % 255,
+        USERGUIDS % 256,
+        USERGUIDS % 257,
+        USERGUIDS % 258,
+        USERGUIDS % 259,
+        USERGUIDS % 260,
+        USERGUIDS % 261,
+        USERGUIDS % 262,
+        USERGUIDS % 263,
+        USERGUIDS % 264,
+        USERGUIDS % 265,
+        USERGUIDS % 266,
+        USERGUIDS % 267,
+        USERGUIDS % 268,
+        USERGUIDS % 269,
+        USERGUIDS % 270,
+        USERGUIDS % 271,
+        USERGUIDS % 272,
+        USERGUIDS % 273,
+        USERGUIDS % 274,
+        USERGUIDS % 275,
+        USERGUIDS % 276,
+        USERGUIDS % 277,
+        USERGUIDS % 278,
+        USERGUIDS % 279,
+        USERGUIDS % 280,
+        USERGUIDS % 281,
+        USERGUIDS % 282,
+        USERGUIDS % 283,
+        USERGUIDS % 284,
+        USERGUIDS % 285,
+        USERGUIDS % 286,
+        USERGUIDS % 287,
+        USERGUIDS % 288,
+        USERGUIDS % 289,
+        USERGUIDS % 290,
+        USERGUIDS % 291,
+        USERGUIDS % 292,
+        USERGUIDS % 293,
+        USERGUIDS % 294,
+        USERGUIDS % 295,
+        USERGUIDS % 296,
+        USERGUIDS % 297,
+        USERGUIDS % 298,
+        USERGUIDS % 299,
+        USERGUIDS % 300,
+        USERGUIDS % 301,
+        USERGUIDS % 302,
+        USERGUIDS % 303,
+        USERGUIDS % 304,
+        USERGUIDS % 305,
+        USERGUIDS % 306,
+        USERGUIDS % 307,
+        USERGUIDS % 308,
+        USERGUIDS % 309,
+        USERGUIDS % 310,
+        USERGUIDS % 311,
+        USERGUIDS % 312,
+        USERGUIDS % 313,
+        USERGUIDS % 314,
+        USERGUIDS % 315,
+        USERGUIDS % 316,
+        USERGUIDS % 317,
+        USERGUIDS % 318,
+        USERGUIDS % 319,
+        USERGUIDS % 320,
+        USERGUIDS % 321,
+        USERGUIDS % 322,
+        USERGUIDS % 323,
+        USERGUIDS % 324,
+        USERGUIDS % 325,
+        USERGUIDS % 326,
+        USERGUIDS % 327,
+        USERGUIDS % 328,
+        USERGUIDS % 329,
+        USERGUIDS % 330,
+        USERGUIDS % 331,
+        USERGUIDS % 332,
+        USERGUIDS % 333,
+        USERGUIDS % 334,
+        USERGUIDS % 335,
+        USERGUIDS % 336,
+        USERGUIDS % 337,
+        USERGUIDS % 338,
+        USERGUIDS % 339,
+        USERGUIDS % 340,
+        USERGUIDS % 341,
+        USERGUIDS % 342,
+        USERGUIDS % 343,
+        USERGUIDS % 344,
+        USERGUIDS % 345,
+        USERGUIDS % 346,
+        USERGUIDS % 347,
+        USERGUIDS % 348,
+        USERGUIDS % 349,
+        USERGUIDS % 350,
+        USERGUIDS % 351,
+        USERGUIDS % 352,
+        USERGUIDS % 353,
+        USERGUIDS % 354,
+        USERGUIDS % 355,
+        USERGUIDS % 356,
+        USERGUIDS % 357,
+        USERGUIDS % 358,
+        USERGUIDS % 359,
+        USERGUIDS % 360,
+        USERGUIDS % 361,
+        USERGUIDS % 362,
+        USERGUIDS % 363,
+        USERGUIDS % 364,
+        USERGUIDS % 365,
+        USERGUIDS % 366,
+        USERGUIDS % 367,
+        USERGUIDS % 368,
+        USERGUIDS % 369,
+        USERGUIDS % 370,
+        USERGUIDS % 371,
+        USERGUIDS % 372,
+        USERGUIDS % 373,
+        USERGUIDS % 374,
+        USERGUIDS % 375,
+        USERGUIDS % 376,
+        USERGUIDS % 377,
+        USERGUIDS % 378,
+        USERGUIDS % 379,
+        USERGUIDS % 380,
+        USERGUIDS % 381,
+        USERGUIDS % 382,
+        USERGUIDS % 383,
+        USERGUIDS % 384,
+        USERGUIDS % 385,
+        USERGUIDS % 386,
+        USERGUIDS % 387,
+        USERGUIDS % 388,
+        USERGUIDS % 389,
+        USERGUIDS % 390,
+        USERGUIDS % 391,
+        USERGUIDS % 392,
+        USERGUIDS % 393,
+        USERGUIDS % 394,
+        USERGUIDS % 395,
+        USERGUIDS % 396,
+        USERGUIDS % 397,
+        USERGUIDS % 398,
+        USERGUIDS % 399,
+        USERGUIDS % 400,
+        USERGUIDS % 401,
+        USERGUIDS % 402,
+        USERGUIDS % 403,
+        USERGUIDS % 404,
+        USERGUIDS % 405,
+        USERGUIDS % 406,
+        USERGUIDS % 407,
+        USERGUIDS % 408,
+        USERGUIDS % 409,
+        USERGUIDS % 410,
+        USERGUIDS % 411,
+        USERGUIDS % 412,
+        USERGUIDS % 413,
+        USERGUIDS % 414,
+        USERGUIDS % 415,
+        USERGUIDS % 416,
+        USERGUIDS % 417,
+        USERGUIDS % 418,
+        USERGUIDS % 419,
+        USERGUIDS % 420,
+        USERGUIDS % 421,
+        USERGUIDS % 422,
+        USERGUIDS % 423,
+        USERGUIDS % 424,
+        USERGUIDS % 425,
+        USERGUIDS % 426,
+        USERGUIDS % 427,
+        USERGUIDS % 428,
+        USERGUIDS % 429,
+        USERGUIDS % 430,
+        USERGUIDS % 431,
+        USERGUIDS % 432,
+        USERGUIDS % 433,
+        USERGUIDS % 434,
+        USERGUIDS % 435,
+        USERGUIDS % 436,
+        USERGUIDS % 437,
+        USERGUIDS % 438,
+        USERGUIDS % 439,
+        USERGUIDS % 440,
+        USERGUIDS % 441,
+        USERGUIDS % 442,
+        USERGUIDS % 443,
+        USERGUIDS % 444,
+        USERGUIDS % 445,
+        USERGUIDS % 446,
+        USERGUIDS % 447,
+        USERGUIDS % 448,
+        USERGUIDS % 449,
+        USERGUIDS % 450,
+        USERGUIDS % 451,
+        USERGUIDS % 452,
+        USERGUIDS % 453,
+        USERGUIDS % 454,
+        USERGUIDS % 455,
+        USERGUIDS % 456,
+        USERGUIDS % 457,
+        USERGUIDS % 458,
+        USERGUIDS % 459,
+        USERGUIDS % 460,
+        USERGUIDS % 461,
+        USERGUIDS % 462,
+        USERGUIDS % 463,
+        USERGUIDS % 464,
+        USERGUIDS % 465,
+        USERGUIDS % 466,
+        USERGUIDS % 467,
+        USERGUIDS % 468,
+        USERGUIDS % 469,
+        USERGUIDS % 470,
+        USERGUIDS % 471,
+        USERGUIDS % 472,
+        USERGUIDS % 473,
+        USERGUIDS % 474,
+        USERGUIDS % 475,
+        USERGUIDS % 476,
+        USERGUIDS % 477,
+        USERGUIDS % 478,
+        USERGUIDS % 479,
+        USERGUIDS % 480,
+        USERGUIDS % 481,
+        USERGUIDS % 482,
+        USERGUIDS % 483,
+        USERGUIDS % 484,
+        USERGUIDS % 485,
+        USERGUIDS % 486,
+        USERGUIDS % 487,
+        USERGUIDS % 488,
+        USERGUIDS % 489,
+        USERGUIDS % 490,
+        USERGUIDS % 491,
+        USERGUIDS % 492,
+        USERGUIDS % 493,
+        USERGUIDS % 494,
+        USERGUIDS % 495,
+        USERGUIDS % 496,
+        USERGUIDS % 497,
+        USERGUIDS % 498,
+        USERGUIDS % 499,
+        USERGUIDS % 500,
+    ),
+})
+
</ins><span class="cx"> for i in xrange(1, 101):
</span><span class="cx"> 
</span><span class="cx">     memberElements = []
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2confcaldavdtestplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/conf/caldavd-test.plist (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/conf/caldavd-test.plist        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/conf/caldavd-test.plist        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -816,6 +816,8 @@
</span><span class="cx">                 &lt;dict&gt;
</span><span class="cx">                         &lt;key&gt;Enabled&lt;/key&gt;
</span><span class="cx">                         &lt;true/&gt;
</span><ins>+                        
+                        &lt;!-- Make these short for testing --&gt;
</ins><span class="cx">             &lt;key&gt;RequestDelaySeconds&lt;/key&gt;
</span><span class="cx">             &lt;real&gt;0.1&lt;/real&gt;
</span><span class="cx">             &lt;key&gt;ReplyDelaySeconds&lt;/key&gt;
</span><span class="lines">@@ -830,6 +832,14 @@
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><span class="cx"> 
</span><ins>+    &lt;key&gt;WorkQueue&lt;/key&gt;
+    &lt;dict&gt;
+          &lt;!-- Make these short for testing --&gt;
+      &lt;key&gt;failureRescheduleInterval&lt;/key&gt;
+      &lt;integer&gt;1&lt;/integer&gt;
+      &lt;key&gt;lockRescheduleInterval&lt;/key&gt;
+      &lt;integer&gt;1&lt;/integer&gt;
+    &lt;/dict&gt;
</ins><span class="cx"> 
</span><span class="cx">     &lt;!--
</span><span class="cx">         Free-busy URL protocol
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2confresourcescaldavdresourcesplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/conf/resources/caldavd-resources.plist (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/conf/resources/caldavd-resources.plist        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/conf/resources/caldavd-resources.plist        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -99,12 +99,6 @@
</span><span class="cx">     &lt;key&gt;MaxAttendeesPerInstance&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;100&lt;/integer&gt;
</span><span class="cx"> 
</span><del>-    &lt;!-- Maximum number of instances allowed for a single RRULE --&gt;
-    &lt;!-- 0 for no limit --&gt;
-    &lt;key&gt;MaxInstancesForRRULE&lt;/key&gt;
-    &lt;integer&gt;400&lt;/integer&gt;
-
-
</del><span class="cx">     &lt;!--
</span><span class="cx">         Directory service
</span><span class="cx"> 
</span><span class="lines">@@ -119,7 +113,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFile&lt;/key&gt;
</span><span class="lines">@@ -131,14 +125,14 @@
</span><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;key&gt;ResourceService&lt;/key&gt;
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;Enabled&lt;/key&gt;
</span><span class="cx">       &lt;true/&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFile&lt;/key&gt;
</span><span class="lines">@@ -150,14 +144,14 @@
</span><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- Open Directory Service (Mac OS X) --&gt;
</span><span class="cx">     &lt;!--
</span><span class="cx">     &lt;key&gt;DirectoryService&lt;/key&gt;
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.appleopendirectory.OpenDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;node&lt;/key&gt;
</span><span class="lines">@@ -181,7 +175,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentXMLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFiles&lt;/key&gt;
</span><span class="lines">@@ -190,14 +184,14 @@
</span><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- Sqlite Augment Service --&gt;
</span><span class="cx">     &lt;!--
</span><span class="cx">     &lt;key&gt;AugmentService&lt;/key&gt;
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentSqliteDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;dbpath&lt;/key&gt;
</span><span class="lines">@@ -212,7 +206,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentPostgreSQLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;host&lt;/key&gt;
</span><span class="lines">@@ -228,7 +222,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.calendaruserproxy.ProxySqliteDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;dbpath&lt;/key&gt;
</span><span class="lines">@@ -242,7 +236,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;host&lt;/key&gt;
</span><span class="lines">@@ -672,7 +666,7 @@
</span><span class="cx">     &lt;!-- Support for Content-Encoding compression --&gt;
</span><span class="cx">     &lt;key&gt;ResponseCompression&lt;/key&gt;
</span><span class="cx">     &lt;false/&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- The retry-after value (in seconds) to return with a 503 error. --&gt;
</span><span class="cx">     &lt;key&gt;HTTPRetryAfter&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;180&lt;/integer&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2contribperformanceloadtesticalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/contrib/performance/loadtest/ical.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/contrib/performance/loadtest/ical.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/contrib/performance/loadtest/ical.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -1477,7 +1477,7 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         # Finally, re-retrieve the event to update the etag
</span><del>-        self._updateEvent(response, href)
</del><ins>+        yield self._updateEvent(response, href)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2requirementsdevtxt"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/requirements-dev.txt (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/requirements-dev.txt        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/requirements-dev.txt        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -7,4 +7,4 @@
</span><span class="cx"> mockldap
</span><span class="cx"> q
</span><span class="cx"> --editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVClientLibrary/trunk@13420#egg=CalDAVClientLibrary
</span><del>---editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVTester/trunk@13670#egg=CalDAVTester
</del><ins>+--editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVTester/trunk@13716#egg=CalDAVTester
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2requirementsstabletxt"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/requirements-stable.txt (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/requirements-stable.txt        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/requirements-stable.txt        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -5,9 +5,9 @@
</span><span class="cx"> # For CalendarServer development, don't try to get these projects from PyPI; use svn.
</span><span class="cx"> 
</span><span class="cx"> -e .
</span><del>--e svn+http://svn.calendarserver.org/repository/calendarserver/twext/trunk@13660#egg=twextpy
</del><ins>+-e svn+http://svn.calendarserver.org/repository/calendarserver/twext/trunk@13724#egg=twextpy
</ins><span class="cx"> -e svn+http://svn.calendarserver.org/repository/calendarserver/PyKerberos/trunk@13420#egg=kerberos
</span><del>--e svn+http://svn.calendarserver.org/repository/calendarserver/PyCalendar/trunk@13621#egg=pycalendar
</del><ins>+-e svn+http://svn.calendarserver.org/repository/calendarserver/PyCalendar/trunk@13711#egg=pycalendar
</ins><span class="cx"> 
</span><span class="cx"> # Specify specific versions of our dependencies so that we're all testing the same config.
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2setuppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/setup.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/setup.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/setup.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -256,17 +256,8 @@
</span><span class="cx"> 
</span><span class="cx"> extensions = []
</span><span class="cx"> 
</span><del>-# if sys.platform == &quot;darwin&quot;:
-#     extensions.append(
-#         Extension(
-#             &quot;calendarserver.platform.darwin._sacl&quot;,
-#             extra_link_args=[&quot;-framework&quot;, &quot;Security&quot;],
-#             sources=[&quot;calendarserver/platform/darwin/_sacl.c&quot;]
-#         )
-#     )
</del><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> #
</span><span class="cx"> # Run setup
</span><span class="cx"> #
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavdatafiltershiddeninstancepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/datafilters/hiddeninstance.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/datafilters/hiddeninstance.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/datafilters/hiddeninstance.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -15,7 +15,7 @@
</span><span class="cx"> ##
</span><span class="cx"> 
</span><span class="cx"> from twistedcaldav.datafilters.filter import CalendarFilter
</span><del>-from twistedcaldav.ical import Component, ignoredComponents, Property
</del><ins>+from twistedcaldav.ical import Component, ignoredComponents
</ins><span class="cx"> 
</span><span class="cx"> __all__ = [
</span><span class="cx">     &quot;HiddenInstanceFilter&quot;,
</span><span class="lines">@@ -50,10 +50,7 @@
</span><span class="cx"> 
</span><span class="cx">                 # Add EXDATE and try to preserve same timezone as DTSTART
</span><span class="cx">                 if master is not None:
</span><del>-                    dtstart = master.getProperty(&quot;DTSTART&quot;)
-                    if dtstart is not None and not dtstart.value().isDateOnly() and dtstart.value().local():
-                        rid.adjustTimezone(dtstart.value().getTimezone())
-                    master.addProperty(Property(&quot;EXDATE&quot;, [rid, ]))
</del><ins>+                    master.addExdate(rid)
</ins><span class="cx"> 
</span><span class="cx">         return ical
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectoryaugmentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/augment.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/augment.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/augment.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -504,8 +504,7 @@
</span><span class="cx">         Remove all records.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        self.removeAugmentRecords(self.db.keys())
-        return succeed(None)
</del><ins>+        return self.removeAugmentRecords(self.db.keys())
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def _shouldReparse(self, xmlFiles):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectorycalendaruserproxypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/calendaruserproxy.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/calendaruserproxy.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/calendaruserproxy.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -858,7 +858,7 @@
</span><span class="cx">             yield self.open()
</span><span class="cx"> 
</span><span class="cx">         for group in [row[0] for row in (yield self.query(&quot;select GROUPNAME from GROUPS&quot;))]:
</span><del>-            self.removeGroup(group)
</del><ins>+            yield self.removeGroup(group)
</ins><span class="cx"> 
</span><span class="cx">         yield super(ProxyDB, self).clean()
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectorydigestpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/digest.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/digest.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/digest.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -167,8 +167,7 @@
</span><span class="cx">             header.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        challenge = yield (super(QopDigestCredentialFactory, self)
-                           .getChallenge(peer))
</del><ins>+        challenge = yield (super(QopDigestCredentialFactory, self).getChallenge(peer))
</ins><span class="cx">         c = challenge['nonce']
</span><span class="cx"> 
</span><span class="cx">         # Make sure it is not a duplicate
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectoryprincipalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/principal.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/principal.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/principal.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -290,15 +290,17 @@
</span><span class="cx">         #
</span><span class="cx">         # Create children
</span><span class="cx">         #
</span><del>-
-        self.supportedChildTypes = (
</del><ins>+        self.supportedChildTypes = [
</ins><span class="cx">             self.directory.recordType.user,
</span><span class="cx">             self.directory.recordType.group,
</span><span class="cx">             self.directory.recordType.location,
</span><span class="cx">             self.directory.recordType.resource,
</span><span class="cx">             self.directory.recordType.address,
</span><del>-            self.directory.recordType.macOSXServerWiki,
-        )
</del><ins>+        ]
+        if config.Authentication.Wiki.Enabled:
+            self.supportedChildTypes.append(
+                self.directory.recordType.macOSXServerWiki
+            )
</ins><span class="cx"> 
</span><span class="cx">         for name, recordType in [
</span><span class="cx">             (self.directory.recordTypeToOldName(r), r)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectorytesttest_principalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/test/test_principal.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/test/test_principal.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/test/test_principal.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -97,7 +97,9 @@
</span><span class="cx">                         self.directory.recordType.location,
</span><span class="cx">                         self.directory.recordType.resource,
</span><span class="cx">                         self.directory.recordType.address,
</span><del>-                        self.directory.recordType.macOSXServerWiki,
</del><ins>+                        # FIXME: add a test where wikis are enabled, then this
+                        # resource should appear:
+                        # self.directory.recordType.macOSXServerWiki,
</ins><span class="cx">                     )
</span><span class="cx">                 ]
</span><span class="cx">             )
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavdirectoryutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/util.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/util.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/directory/util.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -39,6 +39,7 @@
</span><span class="cx"> 
</span><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def uuidFromName(namespace, name):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Generate a version 5 (SHA-1) UUID from a namespace UUID and a name.
</span><span class="lines">@@ -69,6 +70,7 @@
</span><span class="cx"> 
</span><span class="cx"> TRANSACTION_KEY = '_newStoreTransaction'
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def transactionFromRequest(request, newStore):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Return the associated transaction from the given HTTP request, creating a
</span><span class="lines">@@ -97,6 +99,7 @@
</span><span class="cx">         else:
</span><span class="cx">             authz_uid = None
</span><span class="cx">         transaction = newStore.newTransaction(repr(request), authz_uid=authz_uid)
</span><ins>+
</ins><span class="cx">         def abortIfUncommitted(request, response):
</span><span class="cx">             try:
</span><span class="cx">                 # TODO: missing 'yield' here.  For formal correctness as per
</span><span class="lines">@@ -186,7 +189,11 @@
</span><span class="cx">                 record = principal.parent.record
</span><span class="cx">             except:
</span><span class="cx">                 return None
</span><del>-        return (record.recordType, record.shortNames[0])
</del><ins>+        try:
+            shortName = record.shortNames[0]
+        except AttributeError:
+            shortName = u&quot;&quot;
+        return (record.recordType, shortName)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def describe(principal):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavicalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/ical.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/ical.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/ical.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -45,7 +45,7 @@
</span><span class="cx"> from twistedcaldav.dateops import timeRangesOverlap, normalizeForIndex, differenceDateTime, \
</span><span class="cx">     normalizeForExpand
</span><span class="cx"> from twistedcaldav.instance import InstanceList, InvalidOverriddenInstanceError
</span><del>-from twistedcaldav.timezones import hasTZ, TimezoneException
</del><ins>+from twistedcaldav.timezones import hasTZ
</ins><span class="cx"> 
</span><span class="cx"> from txdav.caldav.datastore.scheduling.utils import normalizeCUAddr
</span><span class="cx"> 
</span><span class="lines">@@ -602,13 +602,31 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def getText(self, format=None):
</span><del>-        return self.getTextWithTimezones(False, format)
</del><ins>+        &quot;&quot;&quot;
+        Return text representation and include non-standard timezones.
+        &quot;&quot;&quot;
+        return self._getTextWithTimezones(includeTimezones=Calendar.NONSTD_TIMEZONES, format=format)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def getTextWithTimezones(self, includeTimezones, format=None):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Return text representation and include timezones if the option is on.
</span><span class="cx">         &quot;&quot;&quot;
</span><ins>+        includeTimezones = Calendar.ALL_TIMEZONES if includeTimezones else Calendar.NONSTD_TIMEZONES
+        return self._getTextWithTimezones(includeTimezones=includeTimezones, format=format)
+
+
+    def getTextWithoutTimezones(self, format=None):
+        &quot;&quot;&quot;
+        Return text representation without including timezones.
+        &quot;&quot;&quot;
+        return self._getTextWithTimezones(includeTimezones=Calendar.NO_TIMEZONES, format=format)
+
+
+    def _getTextWithTimezones(self, includeTimezones, format=None):
+        &quot;&quot;&quot;
+        Return text representation and include timezones if the option is on.
+        &quot;&quot;&quot;
</ins><span class="cx">         assert self.name() == &quot;VCALENDAR&quot;, &quot;Must be a VCALENDAR: {0!r}&quot;.format(self,)
</span><span class="cx"> 
</span><span class="cx">         result = self._pycalendar.getText(includeTimezones=includeTimezones, format=format)
</span><span class="lines">@@ -1708,6 +1726,21 @@
</span><span class="cx">         return rid in new_rids
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def addExdate(self, exdate):
+        &quot;&quot;&quot;
+        Add an EXDATE to a master recurring component and ensure the value type, TZID
+        etc match the DTSTART of the master. This method assumes that L{self} is the
+        master component - no checking of that will be done.
+
+        @param exdate: the exdate to add
+        @type exdate: L{DateTime}
+        &quot;&quot;&quot;
+        dtstart = self.getProperty(&quot;DTSTART&quot;)
+        if dtstart is not None and not dtstart.value().isDateOnly() and dtstart.value().local():
+            exdate.adjustTimezone(dtstart.value().getTimezone())
+        self.addProperty(Property(&quot;EXDATE&quot;, [exdate, ]))
+
+
</ins><span class="cx">     def resourceUID(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @return: the UID of the subcomponents in this component.
</span><span class="lines">@@ -1766,28 +1799,13 @@
</span><span class="cx">         return self._resource_type
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def stripKnownTimezones(self):
</del><ins>+    def stripStandardTimezones(self):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Remove timezones that this server knows about
</span><span class="cx">         &quot;&quot;&quot;
</span><ins>+        return self._pycalendar.stripStandardTimezones()
</ins><span class="cx"> 
</span><del>-        changed = False
-        for subcomponent in tuple(self.subcomponents()):
-            if subcomponent.name() == &quot;VTIMEZONE&quot;:
-                tzid = subcomponent.propertyValue(&quot;TZID&quot;)
-                try:
-                    hasTZ(tzid)
-                except TimezoneException:
-                    # tzid not available - do not strip
-                    pass
-                else:
-                    # tzid known - strip component out
-                    self.removeComponent(subcomponent)
-                    changed = True
</del><span class="cx"> 
</span><del>-        return changed
-
-
</del><span class="cx">     def validCalendarData(self, doFix=True, doRaise=True, validateRecurrences=False):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @return: tuple of fixed, unfixed issues
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/resource.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/resource.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/resource.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -886,7 +886,7 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def ownerPrincipal(self, request):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Return the DAV:owner property value (MUST be a DAV:href or None).
</del><ins>+        Return the principal resource for the owner of this resource.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if hasattr(self, &quot;_newStoreObject&quot;):
</span><span class="cx">             if not hasattr(self._newStoreObject, &quot;ownerHome&quot;):
</span><span class="lines">@@ -906,7 +906,7 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def resourceOwnerPrincipal(self, request):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        This is the owner of the resource based on the URI used to access it. For a shared
</del><ins>+        This is the principal resource of the owner of the resource based on the URI used to access it. For a shared
</ins><span class="cx">         collection it will be the sharee, otherwise it will be the regular the ownerPrincipal.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         parent = (yield self.locateParent(
</span><span class="lines">@@ -1884,7 +1884,7 @@
</span><span class="cx">                 ))
</span><span class="cx"> 
</span><span class="cx">             # elif name == &quot;auto-schedule&quot; and self.calendarsEnabled():
</span><del>-            #     autoSchedule = self.getAutoSchedule()
</del><ins>+            #     autoSchedule = yield self.getAutoSchedule()
</ins><span class="cx">             #     returnValue(customxml.AutoSchedule(&quot;true&quot; if autoSchedule else &quot;false&quot;))
</span><span class="cx"> 
</span><span class="cx">             elif name == &quot;auto-schedule-mode&quot; and self.calendarsEnabled():
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavscheduling_storecaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/scheduling_store/caldav/resource.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/scheduling_store/caldav/resource.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/scheduling_store/caldav/resource.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -471,17 +471,13 @@
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def loadOriginatorFromRequestDetails(self, request):
</span><del>-        # Get the originator who is the authenticated user
-        originatorPrincipal = None
</del><ins>+        # The originator is the owner of the Outbox. We will have checked prior to this
+        # that the authenticated user has privileges to schedule as the owner.
</ins><span class="cx">         originator = &quot;&quot;
</span><del>-        authz_principal = self.currentPrincipal(request).children[0]
-        if isinstance(authz_principal, davxml.HRef):
-            originatorPrincipalURL = str(authz_principal)
-            if originatorPrincipalURL:
-                originatorPrincipal = (yield request.locateResource(originatorPrincipalURL))
-                if originatorPrincipal:
-                    # Pick the canonical CUA:
-                    originator = originatorPrincipal.canonicalCalendarUserAddress()
</del><ins>+        originatorPrincipal = (yield self.ownerPrincipal(request))
+        if originatorPrincipal:
+            # Pick the canonical CUA:
+            originator = originatorPrincipal.canonicalCalendarUserAddress()
</ins><span class="cx"> 
</span><span class="cx">         if not originator:
</span><span class="cx">             self.log.error(&quot;%s request must have Originator&quot; % (self.method,))
</span><span class="lines">@@ -545,7 +541,8 @@
</span><span class="cx">                 )
</span><span class="cx">             )
</span><span class="cx">         else:
</span><del>-            returnValue(super(ScheduleOutboxResource, self).defaultAccessControlList())
</del><ins>+            result = yield super(ScheduleOutboxResource, self).defaultAccessControlList()
+            returnValue(result)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def report_urn_ietf_params_xml_ns_caldav_calendar_query(self, request, calendar_query):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/stdconfig.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/stdconfig.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/stdconfig.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -323,7 +323,17 @@
</span><span class="cx">     # Work queue configuration information
</span><span class="cx">     #
</span><span class="cx">     &quot;WorkQueue&quot;: {
</span><del>-        &quot;ampPort&quot;: 7654,            # Port used for hosts in a cluster to take to each other
</del><ins>+        &quot;ampPort&quot;: 7654,            # Port used for hosts in a cluster to talk to each other
+
+        &quot;queuePollInterval&quot;: 0.1,   # Interval in seconds for job queue polling
+        &quot;queueOverdueTimeout&quot;: 300, # Number of seconds before an assigned job is considered overdue
+
+        &quot;overloadLevel&quot;: 95,        # Queue capacity (percentage) which causes job processing to halt
+        &quot;highPriorityLevel&quot;: 80,    # Queue capacity (percentage) at which only high priority items are run
+        &quot;mediumPriorityLevel&quot;: 50,  # Queue capacity (percentage) at which only high/medium priority items are run
+
+        &quot;failureRescheduleInterval&quot;: 60,    # When a job fails, reschedule it this number of seconds in the future
+        &quot;lockRescheduleInterval&quot;: 60,       # When a job can't run because of a lock, reschedule it this number of seconds in the future
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     #
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavstorebridgepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/storebridge.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/storebridge.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/storebridge.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -2888,7 +2888,7 @@
</span><span class="cx">             except ResourceDeletedError:
</span><span class="cx">                 # This is OK - it just means the server deleted the resource during the PUT. We make it look
</span><span class="cx">                 # like the PUT succeeded.
</span><del>-                response = responsecode.CREATED if self.exists() else responsecode.NO_CONTENT
</del><ins>+                response = responsecode.NO_CONTENT if self.exists() else responsecode.CREATED
</ins><span class="cx"> 
</span><span class="cx">                 # Re-initialize to get stuff setup again now we have no object
</span><span class="cx">                 self._initializeWithObject(None, self._newStoreParent)
</span><span class="lines">@@ -3622,7 +3622,7 @@
</span><span class="cx">             except ResourceDeletedError:
</span><span class="cx">                 # This is OK - it just means the server deleted the resource during the PUT. We make it look
</span><span class="cx">                 # like the PUT succeeded.
</span><del>-                response = responsecode.CREATED if self.exists() else responsecode.NO_CONTENT
</del><ins>+                response = responsecode.NO_CONTENT if self.exists() else responsecode.CREATED
</ins><span class="cx"> 
</span><span class="cx">                 # Re-initialize to get stuff setup again now we have no object
</span><span class="cx">                 self._initializeWithObject(None, self._newStoreParent)
</span><span class="lines">@@ -3804,7 +3804,7 @@
</span><span class="cx">         @return: a sequence of the names of all known children of this resource.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         children = set(self.putChildren.keys())
</span><del>-        children.update(self._newStoreNotifications.listNotificationObjects())
</del><ins>+        children.update((yield self._newStoreNotifications.listNotificationObjects()))
</ins><span class="cx">         returnValue(children)
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavtesttest_icalendarpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/test/test_icalendar.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/test/test_icalendar.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/test/test_icalendar.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -5673,6 +5673,93 @@
</span><span class="cx">                 self.fail(&quot;Valid calendar should validate&quot;)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
+
+    def test_add_exdate(self):
+        data = ((
+            &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Apple Inc.//iCal 3.0//EN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:FB81D520-ED27-4DBA-8894-45B7612A7621
+DTSTART;TZID=US/Pacific:20090705T100000
+DTEND;TZID=US/Pacific:20090730T103000
+CREATED:20090604T225706Z
+DTSTAMP:20090604T230500Z
+RRULE:FREQ=DAILY
+SEQUENCE:1
+SUMMARY:TEST
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;,
+            (
+                DateTime(2009, 7, 6, 17, 0, 0, tzid=Timezone(utc=True)),
+                DateTime(2009, 7, 6, 10, 0, 0, tzid=Timezone(tzid=&quot;US/Pacific&quot;)),
+            ),
+
+            &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Apple Inc.//iCal 3.0//EN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:FB81D520-ED27-4DBA-8894-45B7612A7621
+DTSTART;TZID=US/Pacific:20090705T100000
+DTEND;TZID=US/Pacific:20090730T103000
+CREATED:20090604T225706Z
+DTSTAMP:20090604T230500Z
+EXDATE;TZID=US/Pacific:20090706T100000
+RRULE:FREQ=DAILY
+SEQUENCE:1
+SUMMARY:TEST
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;,
+        ))
+
+        for exdate in data[1]:
+            calendar = Component.fromString(data[0])
+            result = Component.fromString(data[2])
+            calendar.masterComponent().addExdate(exdate)
+            self.assertEqual(normalize_iCalStr(calendar), normalize_iCalStr(result), &quot;Failed exdate add: {}&quot;.format(exdate))
+
+
</ins><span class="cx">     def test_allperuseruids(self):
</span><span class="cx">         data = &quot;&quot;&quot;BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavtesttest_upgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/test/test_upgrade.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/test/test_upgrade.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/test/test_upgrade.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -14,26 +14,26 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx"> 
</span><ins>+import cPickle
</ins><span class="cx"> import hashlib
</span><span class="cx"> import os
</span><span class="cx"> import zlib
</span><del>-import cPickle
</del><span class="cx"> 
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, succeed
</span><del>-
-from txdav.xml.parser import WebDAVDocument
-from txdav.caldav.datastore.index_file import db_basename
-
</del><ins>+from twisted.python.filepath import FilePath
</ins><span class="cx"> from twistedcaldav.config import config
</span><del>-from txdav.caldav.datastore.scheduling.imip.mailgateway import MailGatewayTokensDatabase
</del><ins>+from twistedcaldav.directory.calendaruserproxy import ProxySqliteDB
+from twistedcaldav.test.util import StoreTestCase
</ins><span class="cx"> from twistedcaldav.upgrade import (
</span><span class="cx">     xattrname, upgradeData, updateFreeBusySet,
</span><span class="cx">     removeIllegalCharacters, normalizeCUAddrs,
</span><del>-    loadDelegatesFromXML, migrateDelegatesToStore
</del><ins>+    loadDelegatesFromXML, migrateDelegatesToStore,
+    upgradeResourcesXML
</ins><span class="cx"> )
</span><del>-from twistedcaldav.test.util import StoreTestCase
</del><ins>+from txdav.caldav.datastore.index_file import db_basename
+from txdav.caldav.datastore.scheduling.imip.mailgateway import MailGatewayTokensDatabase
</ins><span class="cx"> from txdav.who.delegates import delegatesOf
</span><del>-from twistedcaldav.directory.calendaruserproxy import ProxySqliteDB
</del><ins>+from txdav.xml.parser import WebDAVDocument
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1503,7 +1503,34 @@
</span><span class="cx">         yield txn.commit()
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def test_resourcesXML(self):
+        &quot;&quot;&quot;
+        Verify conversion of old resources.xml format to twext.who.xml format
+        &quot;&quot;&quot;
+        fileName = self.mktemp()
+        fp = FilePath(fileName)
+        fp.setContent(oldResourcesFormat)
+        upgradeResourcesXML(fp)
+        self.assertEquals(fp.getContent(), newResourcesFormat)
</ins><span class="cx"> 
</span><ins>+
+oldResourcesFormat = &quot;&quot;&quot;&lt;accounts realm=&quot;/Search&quot;&gt;
+  &lt;location&gt;
+    &lt;uid&gt;location1&lt;/uid&gt;
+    &lt;guid&gt;C4F46062-9094-4D34-8591-61A42D993FAA&lt;/guid&gt;
+    &lt;name&gt;location name&lt;/name&gt;
+  &lt;/location&gt;
+  &lt;resource&gt;
+    &lt;uid&gt;resource1&lt;/uid&gt;
+    &lt;guid&gt;60B771CC-D727-4453-ACE0-0FE13CD7445A&lt;/guid&gt;
+    &lt;name&gt;resource name&lt;/name&gt;
+  &lt;/resource&gt;
+&lt;/accounts&gt;
+&quot;&quot;&quot;
+
+newResourcesFormat = &quot;&quot;&quot;&lt;directory realm=&quot;/Search&quot;&gt;&lt;record type=&quot;location&quot;&gt;&lt;short-name&gt;location1&lt;/short-name&gt;&lt;guid&gt;C4F46062-9094-4D34-8591-61A42D993FAA&lt;/guid&gt;&lt;uid&gt;C4F46062-9094-4D34-8591-61A42D993FAA&lt;/uid&gt;&lt;full-name&gt;location name&lt;/full-name&gt;&lt;/record&gt;&lt;record type=&quot;resource&quot;&gt;&lt;short-name&gt;resource1&lt;/short-name&gt;&lt;guid&gt;60B771CC-D727-4453-ACE0-0FE13CD7445A&lt;/guid&gt;&lt;uid&gt;60B771CC-D727-4453-ACE0-0FE13CD7445A&lt;/uid&gt;&lt;full-name&gt;resource name&lt;/full-name&gt;&lt;/record&gt;&lt;/directory&gt;&quot;&quot;&quot;
+
+
</ins><span class="cx"> normalizeEvent = &quot;&quot;&quot;BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> BEGIN:VEVENT
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2twistedcaldavupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/upgrade.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/upgrade.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/twistedcaldav/upgrade.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -30,6 +30,10 @@
</span><span class="cx"> from zlib import compress
</span><span class="cx"> from cPickle import loads as unpickle, UnpicklingError
</span><span class="cx"> 
</span><ins>+from xml.etree.ElementTree import (
+    parse as parseXML, ParseError as XMLParseError,
+    tostring as etreeToString, Element as XMLElement
+)
</ins><span class="cx"> 
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from txdav.xml import element
</span><span class="lines">@@ -48,6 +52,7 @@
</span><span class="cx"> from twisted.internet.defer import (
</span><span class="cx">     inlineCallbacks, succeed, returnValue
</span><span class="cx"> )
</span><ins>+from twisted.python.filepath import FilePath
</ins><span class="cx"> from twisted.python.reflect import namedAny
</span><span class="cx"> from twisted.python.reflect import namedClass
</span><span class="cx"> 
</span><span class="lines">@@ -646,7 +651,54 @@
</span><span class="cx">         raise UpgradeError(&quot;Data upgrade failed, see error.log for details&quot;)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+def upgradeResourcesXML(resourcesFilePath):
+    &quot;&quot;&quot;
+    Convert the old XML format to the twext.who.xml format
</ins><span class="cx"> 
</span><ins>+    @param resourcesFilePath: the file to convert
+    @type resourcesFilePath: L{FilePath}
+    &quot;&quot;&quot;
+    try:
+        with resourcesFilePath.open() as fh:
+            try:
+                etree = parseXML(fh)
+            except XMLParseError:
+                log.error(&quot;Cannot parse {path}&quot;, path=resourcesFilePath.path)
+                return
+    except (OSError, IOError):
+        # Can't read the file
+        log.error(&quot;Cannot read {path}&quot;, path=resourcesFilePath.path)
+        return
+
+    accountsNode = etree.getroot()
+    if accountsNode.tag != &quot;accounts&quot;:
+        return
+
+    tagMap = {
+        &quot;uid&quot;: (&quot;short-name&quot;,),
+        &quot;guid&quot;: (&quot;guid&quot;, &quot;uid&quot;),
+        &quot;name&quot;: (&quot;full-name&quot;,),
+    }
+    log.info(&quot;Converting resources.xml&quot;)
+    directoryNode = XMLElement(&quot;directory&quot;)
+    directoryNode.set(&quot;realm&quot;, accountsNode.get(&quot;realm&quot;))
+    for sourceNode in accountsNode:
+        recordType = sourceNode.tag
+        destNode = XMLElement(&quot;record&quot;)
+        destNode.set(&quot;type&quot;, recordType)
+        for sourceFieldNode in sourceNode:
+            tags = tagMap.get(sourceFieldNode.tag, None)
+            if tags:
+                for tag in tags:
+                    destFieldNode = XMLElement(tag)
+                    destFieldNode.text = sourceFieldNode.text
+                    destNode.append(destFieldNode)
+
+        directoryNode.append(destNode)
+
+    resourcesFilePath.setContent(etreeToString(directoryNode, &quot;utf-8&quot;))
+
+
</ins><span class="cx"> # The on-disk version number (which defaults to zero if .calendarserver_version
</span><span class="cx"> # doesn't exist), is compared with each of the numbers in the upgradeMethods
</span><span class="cx"> # array.  If it is less than the number, the associated method is called.
</span><span class="lines">@@ -660,6 +712,14 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def upgradeData(config, directory):
</span><span class="cx"> 
</span><ins>+    if config.ResourceService.Enabled:
+        resourcesFileName = config.ResourceService.params.xmlFile
+        if resourcesFileName[0] not in (&quot;/&quot;, &quot;.&quot;):
+            resourcesFileName = os.path.join(config.DataRoot, resourcesFileName)
+        resourcesFilePath = FilePath(resourcesFileName)
+        if resourcesFilePath.exists():
+            upgradeResourcesXML(resourcesFilePath)
+
</ins><span class="cx">     triggerPath = os.path.join(config.ServerRoot, TRIGGER_FILE)
</span><span class="cx">     if os.path.exists(triggerPath):
</span><span class="cx">         try:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/schedule.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/schedule.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/schedule.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -43,8 +43,7 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def calendarHomeWithUID(self, uid, create=False):
</span><span class="cx">         # FIXME: 'create' flag
</span><del>-        newHome = yield super(ImplicitTransaction, self
-            ).calendarHomeWithUID(uid, create)
</del><ins>+        newHome = yield super(ImplicitTransaction, self).calendarHomeWithUID(uid, create)
</ins><span class="cx"> #        return ImplicitCalendarHome(newHome, self)
</span><span class="cx">         if newHome is None:
</span><span class="cx">             returnValue(None)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingimplicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/implicit.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/implicit.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/implicit.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -30,12 +30,13 @@
</span><span class="cx">     LocalCalendarUser, OtherServerCalendarUser, \
</span><span class="cx">     calendarUserFromCalendarUserAddress, \
</span><span class="cx">     calendarUserFromCalendarUserUID
</span><del>-from txdav.caldav.datastore.scheduling.utils import normalizeCUAddr
</del><ins>+from txdav.caldav.datastore.scheduling.utils import normalizeCUAddr,\
+    uidFromCalendarUserAddress
</ins><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="cx"> from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForRecord
</span><span class="cx"> from txdav.caldav.datastore.scheduling.work import ScheduleReplyWork, \
</span><del>-    ScheduleReplyCancelWork, ScheduleOrganizerWork
</del><ins>+    ScheduleReplyCancelWork, ScheduleOrganizerWork, ScheduleOrganizerSendWork
</ins><span class="cx"> 
</span><span class="cx"> import collections
</span><span class="cx"> 
</span><span class="lines">@@ -468,6 +469,38 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><ins>+    def queuedOrganizerSending(self, txn, action, home, resource, uid, organizer, attendee, itipmsg, no_refresh):
+        &quot;&quot;&quot;
+        Process an organizer scheduling work queue item. The basic goal here is to setup the ImplicitScheduler as if
+        this operation were the equivalent of the PUT that enqueued the work, and then do the actual work.
+        &quot;&quot;&quot;
+
+        self.txn = txn
+        self.action = action
+        self.state = &quot;organizer&quot;
+        self.calendar_home = home
+        self.resource = resource
+        self.queuedResponses = []
+        self.suppress_refresh = no_refresh
+        self.uid = uid
+        self.calendar = None
+        self.oldcalendar = None
+
+        self.organizer = organizer
+        self.attendees = None
+        self.organizerAddress = None
+
+        # Originator is the organizer in this case
+        self.originator = self.organizer
+
+        self.except_attendees = ()
+        self.only_refresh_attendees = None
+        self.split_details = None
+
+        yield self.processSend(attendee, itipmsg, jobqueue=False)
+
+
+    @inlineCallbacks
</ins><span class="cx">     def sendAttendeeReply(self, txn, resource):
</span><span class="cx"> 
</span><span class="cx">         self.txn = txn
</span><span class="lines">@@ -591,8 +624,24 @@
</span><span class="cx">         if not self.organizer:
</span><span class="cx">             returnValue(False)
</span><span class="cx"> 
</span><del>-        # Check to see whether any attendee is the owner
</del><ins>+        # Performance optimization: calling L{calendarUserFromCalendarUserAddress} results
+        # in a directory lookup which may be expensive and we may end up doing it for every
+        # attendee. However, all we need is the uid from the cu-address, so do one loop first
+        # just using L{uidFromCalendarUserAddress} which is super fast, and if that does not
+        # match, then do the slower loop
+
+        # Fast loop: Check to see whether any attendee is the owner
</ins><span class="cx">         for attendee in self.attendees:
</span><ins>+            uid = uidFromCalendarUserAddress(attendee)
+            if uid is not None and uid == self.calendar_home.uid():
+                attendeeAddress = yield calendarUserFromCalendarUserAddress(attendee, self.txn)
+                if attendeeAddress.hosted() and attendeeAddress.record.uid == self.calendar_home.uid():
+                    self.attendee = attendee
+                    self.attendeeAddress = attendeeAddress
+                    returnValue(True)
+
+        # Slow Loop: Check to see whether any attendee is the owner
+        for attendee in self.attendees:
</ins><span class="cx">             attendeeAddress = yield calendarUserFromCalendarUserAddress(attendee, self.txn)
</span><span class="cx">             if attendeeAddress.hosted() and attendeeAddress.record.uid == self.calendar_home.uid():
</span><span class="cx">                 self.attendee = attendee
</span><span class="lines">@@ -1226,7 +1275,7 @@
</span><span class="cx"> 
</span><span class="cx">         # Process regular requests next
</span><span class="cx">         if self.action in (&quot;create&quot;, &quot;modify&quot;,):
</span><del>-            total += (yield self.processRequests())
</del><ins>+            total += (yield self.processRequests(total))
</ins><span class="cx"> 
</span><span class="cx">         if self.logItems is not None:
</span><span class="cx">             self.logItems[&quot;itip.requests&quot;] = total
</span><span class="lines">@@ -1280,21 +1329,15 @@
</span><span class="cx">                     itipmsg.addProperty(Property(&quot;X-CALENDARSERVER-SPLIT-RID&quot;, rid))
</span><span class="cx">                     itipmsg.addProperty(Property(&quot;X-CALENDARSERVER-SPLIT-OLDER-UID&quot; if newer_piece else &quot;X-CALENDARSERVER-SPLIT-NEWER-UID&quot;, uid))
</span><span class="cx"> 
</span><del>-                # This is a local CALDAV scheduling operation.
-                scheduler = self.makeScheduler()
</del><ins>+                yield self.processSend(attendee, itipmsg, count=count)
</ins><span class="cx"> 
</span><del>-                # Do the PUT processing
-                log.info(&quot;Implicit CANCEL - organizer: '{organizer}' to attendee: '{attendee}', UID: '{uid}', RIDs: '{rids}'&quot;, organizer=self.organizer, attendee=attendee, uid=self.uid, rids=rids)
-                response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, internal_request=True, suppress_refresh=self.suppress_refresh))
-                self.handleSchedulingResponse(response, True)
-
</del><span class="cx">                 count += 1
</span><span class="cx"> 
</span><span class="cx">         returnValue(count)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def processRequests(self):
</del><ins>+    def processRequests(self, cancel_count=0):
</ins><span class="cx"> 
</span><span class="cx">         # TODO: a better policy here is to aggregate by attendees with the same set of instances
</span><span class="cx">         # being requested, but for now we will do one scheduling message per attendee.
</span><span class="lines">@@ -1341,19 +1384,57 @@
</span><span class="cx">                     itipmsg.addProperty(Property(&quot;X-CALENDARSERVER-SPLIT-RID&quot;, rid))
</span><span class="cx">                     itipmsg.addProperty(Property(&quot;X-CALENDARSERVER-SPLIT-OLDER-UID&quot; if newer_piece else &quot;X-CALENDARSERVER-SPLIT-NEWER-UID&quot;, uid))
</span><span class="cx"> 
</span><del>-                # This is a local CALDAV scheduling operation.
-                scheduler = self.makeScheduler()
</del><ins>+                yield self.processSend(attendee, itipmsg, count=count + cancel_count)
</ins><span class="cx"> 
</span><del>-                # Do the PUT processing
-                log.info(&quot;Implicit REQUEST - organizer: '{organizer}' to attendee: '{attendee}', UID: '{uid}'&quot;, organizer=self.organizer, attendee=attendee, uid=self.uid)
-                response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, internal_request=True, suppress_refresh=self.suppress_refresh))
-                self.handleSchedulingResponse(response, True)
-
</del><span class="cx">                 count += 1
</span><span class="cx"> 
</span><span class="cx">         returnValue(count)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def processSend(self, attendee, itipmsg, jobqueue=True, count=0):
+        &quot;&quot;&quot;
+        Send an iTIP message to an attendee. This might send it directly, or it might create job to
+        send it later.
+
+        @param attendee: the calendar user address of the attendee to send the message to
+        @type attendee: L{str}
+        @param itipmsg: the iTIP message to send
+        @type itipmsg: L{Component}
+        @param jobqueue: if allowed, queue up a job to do the actual work
+        @type jobqueue: L{bool}
+        &quot;&quot;&quot;
+
+        # Attendee refreshes are already executed in a job (in batches) so don't create more
+        if jobqueue and config.Scheduling.Options.WorkQueues.Enabled and not hasattr(self.txn, &quot;doing_attendee_refresh&quot;):
+            # Create job for the work
+            yield ScheduleOrganizerSendWork.schedule(
+                self.txn,
+                self.action,
+                self.calendar_home,
+                self.resource,
+                self.organizerAddress.record.canonicalCalendarUserAddress(),
+                attendee,
+                itipmsg,
+                self.suppress_refresh,
+                count,
+            )
+        else:
+            # Execute the work right now
+            scheduler = self.makeScheduler()
+
+            # Do the PUT processing
+            log.info(
+                &quot;Implicit {method} - organizer: '{organizer}' to attendee: '{attendee}', UID: '{uid}'&quot;,
+                method=itipmsg.propertyValue(&quot;METHOD&quot;),
+                organizer=self.organizer,
+                attendee=attendee,
+                uid=self.uid,
+            )
+            response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, internal_request=True, suppress_refresh=self.suppress_refresh))
+            self.handleSchedulingResponse(response, True)
+
+
</ins><span class="cx">     def handleSchedulingResponse(self, response, is_organizer):
</span><span class="cx"> 
</span><span class="cx">         # For a queued operation we stash the response away for the work item to deal with
</span><span class="lines">@@ -1475,9 +1556,13 @@
</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(&quot;STATUS&quot;, &quot;CANCELLED&quot;)):
</span><del>-                        log.debug(&quot;Attendee '{attendee}' is creating CANCELLED event for missing UID: '{uid}' - removing entire event&quot;, attendee=self.attendee, uid=self.uid)
-                        self.return_status = ImplicitScheduler.STATUS_ORPHANED_CANCELLED_EVENT
-                        returnValue(None)
</del><ins>+                        if self.action == &quot;create&quot;:
+                            log.debug(&quot;Attendee '{attendee}' is creating CANCELLED event for missing UID: '{uid}' - removing entire event&quot;, attendee=self.attendee, uid=self.uid)
+                            self.return_status = ImplicitScheduler.STATUS_ORPHANED_CANCELLED_EVENT
+                            returnValue(None)
+                        else:
+                            log.debug(&quot;Attendee '{attendee}' is modifying CANCELLED event for missing UID: '{uid}'&quot;, attendee=self.attendee, uid=self.uid)
+                            returnValue(None)
</ins><span class="cx">                     else:
</span><span class="cx">                         # Check to see whether existing event is SCHEDULE-AGENT=CLIENT/NONE
</span><span class="cx">                         if self.oldcalendar:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingitippy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/itip.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/itip.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/itip.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -174,10 +174,17 @@
</span><span class="cx">                         hidden = component.hasProperty(Component.HIDDEN_INSTANCE_PROPERTY)
</span><span class="cx">                         new_component = new_calendar.deriveInstance(rid, allowCancelled=allowCancelled and not hidden)
</span><span class="cx">                         if new_component is not None:
</span><del>-                            new_calendar.addComponent(new_component)
-                            iTipProcessing.transferItems(calendar, new_component, needs_action_rids, reschedule, valarms, private_comments, transps, completeds, organizer_schedule_status, attendee, attendee_dtstamp, other_props, recipient)
-                            if hidden:
-                                new_component.addProperty(Property(Component.HIDDEN_INSTANCE_PROPERTY, &quot;T&quot;))
</del><ins>+                            # If the new component is not CANCELLED then add the one derived from the new master and
+                            # sync over attendee properties from the existing attendee data. However, if the new
+                            # component is cancelled, we need to preserve the original state of the attendee's
+                            # version as it may differ from the one derived from the new master.
+                            if allowCancelled:
+                                new_calendar.addComponent(component.duplicate())
+                            else:
+                                new_calendar.addComponent(new_component)
+                                iTipProcessing.transferItems(calendar, new_component, needs_action_rids, reschedule, valarms, private_comments, transps, completeds, organizer_schedule_status, attendee, attendee_dtstamp, other_props, recipient)
+                                if hidden:
+                                    new_component.addProperty(Property(Component.HIDDEN_INSTANCE_PROPERTY, &quot;T&quot;))
</ins><span class="cx"> 
</span><span class="cx">             iTipProcessing.addTranspForNeedsAction(new_calendar.subcomponents(), recipient)
</span><span class="cx"> 
</span><span class="lines">@@ -298,11 +305,12 @@
</span><span class="cx">                         overridden.replaceProperty(Property(&quot;STATUS&quot;, &quot;CANCELLED&quot;))
</span><span class="cx">                         calendar.addComponent(overridden)
</span><span class="cx">                         newseq = component.propertyValue(&quot;SEQUENCE&quot;)
</span><del>-                        overridden.replacePropertyInAllComponents(Property(&quot;SEQUENCE&quot;, newseq))
</del><ins>+                        overridden.replaceProperty(Property(&quot;SEQUENCE&quot;, newseq))
</ins><span class="cx"> 
</span><span class="cx">         # If we have any EXDATEs lets add them to the existing calendar object.
</span><span class="cx">         if exdates and calendar_master:
</span><del>-            calendar_master.addProperty(Property(&quot;EXDATE&quot;, exdates))
</del><ins>+            for exdate in exdates:
+                calendar_master.addExdate(exdate)
</ins><span class="cx"> 
</span><span class="cx">         # See if there are still components in the calendar - we might have deleted the last overridden instance
</span><span class="cx">         # in which case the calendar object is empty (except for VTIMEZONEs) or has only hidden components.
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingprocessingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/processing.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/processing.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/processing.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -463,7 +463,7 @@
</span><span class="cx">             if send_reply:
</span><span class="cx">                 # Track outstanding auto-reply processing
</span><span class="cx">                 log.debug(&quot;ImplicitProcessing - recipient '%s' processing UID: '%s' - auto-reply queued&quot; % (self.recipient.cuaddr, self.uid,))
</span><del>-                ScheduleAutoReplyWork.autoReply(self.txn, new_resource, partstat)
</del><ins>+                yield ScheduleAutoReplyWork.autoReply(self.txn, new_resource, partstat)
</ins><span class="cx"> 
</span><span class="cx">             # Build the schedule-changes XML element
</span><span class="cx">             changes = customxml.ScheduleChanges(
</span><span class="lines">@@ -511,7 +511,7 @@
</span><span class="cx">                 if send_reply:
</span><span class="cx">                     # Track outstanding auto-reply processing
</span><span class="cx">                     log.debug(&quot;ImplicitProcessing - recipient '%s' processing UID: '%s' - auto-reply queued&quot; % (self.recipient.cuaddr, self.uid,))
</span><del>-                    ScheduleAutoReplyWork.autoReply(self.txn, new_resource, partstat)
</del><ins>+                    yield ScheduleAutoReplyWork.autoReply(self.txn, new_resource, partstat)
</ins><span class="cx"> 
</span><span class="cx">                 # Build the schedule-changes XML element
</span><span class="cx">                 update_details = []
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingtesttest_implicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/test/test_implicit.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/test/test_implicit.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/test/test_implicit.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling.scheduler import ScheduleResponseQueue
</span><span class="cx"> from txdav.caldav.icalendarstore import AttendeeAllowedError, \
</span><span class="cx">     ComponentUpdateState
</span><ins>+from txdav.caldav.datastore.sql import CalendarObject
</ins><span class="cx"> from txdav.common.datastore.test.util import CommonCommonTests, populateCalendarsFrom
</span><span class="cx"> 
</span><span class="cx"> from twext.enterprise.jobqueue import JobItem
</span><span class="lines">@@ -1275,7 +1276,10 @@
</span><span class="cx">         yield self._createCalendarObject(data1, &quot;user01&quot;, &quot;test.ics&quot;)
</span><span class="cx"> 
</span><span class="cx">         cobj = yield self.calendarObjectUnderTest(home=&quot;user01&quot;, name=&quot;test.ics&quot;)
</span><ins>+        actualVersion = CalendarObject._currentDataVersion
+        self.patch(CalendarObject, &quot;_currentDataVersion&quot;, 0)
</ins><span class="cx">         yield cobj._setComponentInternal(Component.fromString(data1), internal_state=ComponentUpdateState.RAW)
</span><ins>+        CalendarObject._currentDataVersion = actualVersion
</ins><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><span class="cx">         cobj = yield self.calendarObjectUnderTest(home=&quot;user01&quot;, name=&quot;test.ics&quot;)
</span><span class="lines">@@ -1285,7 +1289,10 @@
</span><span class="cx">         self.assertFalse(comp.getOrganizerScheduleAgent())
</span><span class="cx"> 
</span><span class="cx">         cobj = yield self.calendarObjectUnderTest(home=&quot;user01&quot;, name=&quot;test.ics&quot;)
</span><ins>+        actualVersion = CalendarObject._currentDataVersion
+        self.patch(CalendarObject, &quot;_currentDataVersion&quot;, 0)
</ins><span class="cx">         yield cobj.setComponent(Component.fromString(data2))
</span><ins>+        CalendarObject._currentDataVersion = actualVersion
</ins><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><span class="cx">         cobj = yield self.calendarObjectUnderTest(home=&quot;user01&quot;, name=&quot;test.ics&quot;)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingtesttest_workpyfromrev13731CalendarServertrunktxdavcaldavdatastoreschedulingtesttest_workpy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/test/test_work.py (from rev 13731, CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_work.py) (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/test/test_work.py                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/test/test_work.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,267 @@
</span><ins>+##
+# Copyright (c) 2013-2014 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# 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 &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+from twistedcaldav.ical import Component
+from twext.enterprise.jobqueue import JobItem, WorkItem
+from txdav.common.datastore.sql_tables import scheduleActionFromSQL
+from twisted.internet import reactor
+
+&quot;&quot;&quot;
+Tests for txdav.caldav.datastore.utils
+&quot;&quot;&quot;
+
+from twisted.internet.defer import inlineCallbacks
+from twisted.trial import unittest
+
+from txdav.caldav.datastore.scheduling.work import ScheduleOrganizerWork, \
+    ScheduleWorkMixin, ScheduleWork, ScheduleOrganizerSendWork
+from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
+
+
+
+class BaseWorkTests(CommonCommonTests, unittest.TestCase):
+    &quot;&quot;&quot;
+    Tests for scheduling work.
+    &quot;&quot;&quot;
+    @inlineCallbacks
+    def setUp(self):
+
+        yield super(BaseWorkTests, self).setUp()
+        yield self.buildStoreAndDirectory()
+        yield self.populate()
+
+
+    @inlineCallbacks
+    def populate(self):
+        yield populateCalendarsFrom(self.requirements, self.storeUnderTest())
+        self.notifierFactory.reset()
+
+    requirements = {
+        &quot;user01&quot;: {
+            &quot;calendar&quot;: {
+            },
+            &quot;inbox&quot;: {
+            },
+        },
+        &quot;user02&quot;: {
+            &quot;calendar&quot;: {
+            },
+            &quot;inbox&quot;: {
+            },
+        },
+        &quot;user03&quot;: {
+            &quot;calendar&quot;: {
+            },
+            &quot;inbox&quot;: {
+            },
+        },
+    }
+
+
+    def storeUnderTest(self):
+        &quot;&quot;&quot;
+        Create and return a L{CalendarStore} for testing.
+        &quot;&quot;&quot;
+        return self._sqlCalendarStore
+
+
+
+class TestScheduleOrganizerWork(BaseWorkTests):
+    &quot;&quot;&quot;
+    Test creation of L{ScheduleOrganizerWork} items.
+    &quot;&quot;&quot;
+
+    calendar_old = Component.fromString(&quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+ORGANIZER:urn:uuid:user01
+ATTENDEE:urn:uuid:user01
+ATTENDEE:urn:uuid:user02
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;)
+
+    calendar_new = Component.fromString(&quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T130000Z
+DURATION:PT1H
+ORGANIZER:urn:uuid:user01
+ATTENDEE:urn:uuid:user01
+ATTENDEE:urn:uuid:user02
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;)
+
+
+    @inlineCallbacks
+    def test_create(self):
+        &quot;&quot;&quot;
+        Test that jobs associated with L{txdav.caldav.datastore.scheduling.work.ScheduleOrganizerWork}
+        can be created and correctly removed.
+        &quot;&quot;&quot;
+
+        ScheduleWorkMixin._queued = 0
+        txn = self.transactionUnderTest()
+        home = yield self.homeUnderTest(name=&quot;user01&quot;)
+        yield ScheduleOrganizerWork.schedule(
+            txn,
+            &quot;12345-67890&quot;,
+            &quot;create&quot;,
+            home,
+            None,
+            None,
+            self.calendar_new,
+            &quot;urn:uuid:user01&quot;,
+            2,
+            True,
+        )
+        yield self.commit()
+        self.assertEqual(ScheduleWorkMixin._queued, 1)
+
+        jobs = yield JobItem.all(self.transactionUnderTest())
+        self.assertEqual(len(jobs), 1)
+
+        work = yield jobs[0].workItem()
+        self.assertTrue(isinstance(work, ScheduleOrganizerWork))
+        self.assertEqual(work.icalendarUid, &quot;12345-67890&quot;)
+        self.assertEqual(scheduleActionFromSQL[work.scheduleAction], &quot;create&quot;)
+
+        yield work.delete()
+        yield jobs[0].delete()
+        yield self.commit()
+
+        jobs = yield JobItem.all(self.transactionUnderTest())
+        self.assertEqual(len(jobs), 0)
+        work = yield ScheduleOrganizerWork.all(self.transactionUnderTest())
+        self.assertEqual(len(work), 0)
+        baseWork = yield ScheduleWork.all(self.transactionUnderTest())
+        self.assertEqual(len(baseWork), 0)
+
+
+    @inlineCallbacks
+    def test_cascade_delete_cleanup(self):
+        &quot;&quot;&quot;
+        Test that when work associated with L{txdav.caldav.datastore.scheduling.work.ScheduleWork}
+        is removed with the L{ScheduleWork} item being removed, the associated L{JobItem} runs and
+        removes itself and the L{ScheduleWork}.
+        &quot;&quot;&quot;
+
+        ScheduleWorkMixin._queued = 0
+        txn = self.transactionUnderTest()
+        home = yield self.homeUnderTest(name=&quot;user01&quot;)
+        yield ScheduleOrganizerWork.schedule(
+            txn,
+            &quot;12345-67890&quot;,
+            &quot;create&quot;,
+            home,
+            None,
+            None,
+            self.calendar_new,
+            &quot;urn:uuid:user01&quot;,
+            2,
+            True,
+        )
+        yield self.commit()
+        self.assertEqual(ScheduleWorkMixin._queued, 1)
+
+        jobs = yield JobItem.all(self.transactionUnderTest())
+        work = yield jobs[0].workItem()
+        yield WorkItem.delete(work)
+        yield self.commit()
+
+        jobs = yield JobItem.all(self.transactionUnderTest())
+        self.assertEqual(len(jobs), 1)
+        baseWork = yield ScheduleWork.all(self.transactionUnderTest())
+        self.assertEqual(len(baseWork), 1)
+        self.assertEqual(baseWork[0].jobID, jobs[0].jobID)
+
+        work = yield jobs[0].workItem()
+        self.assertTrue(work is None)
+        yield self.commit()
+
+        yield JobItem.waitEmpty(self.storeUnderTest().newTransaction, reactor, 60)
+
+        jobs = yield JobItem.all(self.transactionUnderTest())
+        self.assertEqual(len(jobs), 0)
+        work = yield ScheduleOrganizerWork.all(self.transactionUnderTest())
+        self.assertEqual(len(work), 0)
+        baseWork = yield ScheduleWork.all(self.transactionUnderTest())
+        self.assertEqual(len(baseWork), 0)
+
+
+
+class TestScheduleOrganizerSendWork(BaseWorkTests):
+    &quot;&quot;&quot;
+    Test creation of L{ScheduleOrganizerSendWork} items.
+    &quot;&quot;&quot;
+
+    itip_new = Component.fromString(&quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T130000Z
+DURATION:PT1H
+ORGANIZER:urn:x-uid:user01
+{attendees}
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;.format(attendees=&quot;\n&quot;.join([&quot;ATTENDEE:urn:x-uid:user%02d&quot; % i for i in range(1, 100)]))
+)
+
+
+    @inlineCallbacks
+    def test_create(self):
+        &quot;&quot;&quot;
+        Test that jobs associated with L{txdav.caldav.datastore.scheduling.work.ScheduleOrganizerSendWork}
+        can be created and correctly removed.
+        &quot;&quot;&quot;
+
+        txn = self.transactionUnderTest()
+        home = yield self.homeUnderTest(name=&quot;user01&quot;)
+        yield ScheduleOrganizerSendWork.schedule(
+            txn,
+            &quot;create&quot;,
+            home,
+            None,
+            &quot;urn:x-uid:user01&quot;,
+            &quot;urn:x-uid:user02&quot;,
+            self.itip_new,
+            True,
+            1000,
+        )
+
+        jobs = yield JobItem.all(self.transactionUnderTest())
+        self.assertEqual(len(jobs), 1)
+
+        work = yield jobs[0].workItem()
+        yield work.doWork()
+
+        home2 = yield self.calendarUnderTest(home=&quot;user02&quot;, name=&quot;calendar&quot;)
+        cobjs = yield home2.calendarObjects()
+        self.assertEqual(len(cobjs), 1)
+        #cal2 = yield cobjs[0].component()
+
+        yield work.delete()
+        yield jobs[0].delete()
+        yield self.commit()
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoreschedulingworkpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/work.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/work.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/scheduling/work.py        2014-07-08 07:05:29 UTC (rev 13732)
</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 twext.enterprise.dal.record import fromTable
</del><ins>+from twext.enterprise.dal.record import fromTable, Record
</ins><span class="cx"> from twext.enterprise.dal.syntax import Select, Insert, Delete, Parameter
</span><span class="cx"> from twext.enterprise.locking import NamedLock
</span><del>-from twext.enterprise.jobqueue import WorkItem, WORK_PRIORITY_MEDIUM
</del><ins>+from twext.enterprise.jobqueue import WorkItem, WORK_PRIORITY_MEDIUM, JobItem
</ins><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> 
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue, Deferred
</span><span class="lines">@@ -51,7 +51,10 @@
</span><span class="cx"> 
</span><span class="cx"> class ScheduleWorkMixin(WorkItem):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    Base class for common schedule work item behavior.
</del><ins>+    Base class for common schedule work item behavior. Sub-classes have their own class specific data
+    stored in per-class tables. This class manages a SCHEDULE_WORK table that contains the work id, job id
+    and iCalendar UID. That table is used for locking all scheduling items with the same UID, as well as
+    allow smart re-scheduling/ordering etc of items with the same UID.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     # Track when all work is complete (needed for unit tests)
</span><span class="lines">@@ -59,12 +62,132 @@
</span><span class="cx">     _queued = 0
</span><span class="cx"> 
</span><span class="cx">     # Schedule work is grouped based on calendar object UID
</span><del>-    group = property(lambda self: (self.table.ICALENDAR_UID == self.icalendarUid))
</del><span class="cx">     default_priority = WORK_PRIORITY_MEDIUM
</span><span class="cx">     default_weight = 5
</span><span class="cx"> 
</span><ins>+    @classmethod
+    @inlineCallbacks
+    def create(cls, transaction, **kwargs):
+        &quot;&quot;&quot;
+        A new work item needs to be created. First we create a SCHEDULE_WORK record, then
+        we create the actual work item.
</ins><span class="cx"> 
</span><ins>+        @param transaction: the transaction to use
+        @type transaction: L{IAsyncTransaction}
+        &quot;&quot;&quot;
+
+        baseargs = {
+            &quot;jobID&quot;: kwargs.pop(&quot;jobID&quot;),
+            &quot;icalendarUid&quot;: kwargs.pop(&quot;icalendarUid&quot;),
+            &quot;workType&quot;: cls.workType()
+        }
+
+        baseWork = yield ScheduleWork.create(transaction, **baseargs)
+
+        kwargs[&quot;workID&quot;] = baseWork.workID
+        work = yield super(ScheduleWorkMixin, cls).create(transaction, **kwargs)
+        work.addBaseWork(baseWork)
+        returnValue(work)
+
+
</ins><span class="cx">     @classmethod
</span><ins>+    @inlineCallbacks
+    def loadForJob(cls, txn, jobID):
+        baseItems = yield ScheduleWork.query(txn, (ScheduleWork.jobID == jobID))
+        workItems = []
+        for baseItem in baseItems:
+            workItem = yield cls.query(txn, (cls.workID == baseItem.workID))
+            if len(workItem) == 0:
+                # This can happen if a cascade delete is done on the actual work item - that will not
+                # remove the corresponding L{JobItem} or L{ScheduleWork}
+                yield baseItem.delete()
+                continue
+            workItem[0].addBaseWork(baseItem)
+            workItems.append(workItem[0])
+        returnValue(workItems)
+
+
+    @inlineCallbacks
+    def runlock(self):
+        &quot;&quot;&quot;
+        Lock the &quot;group&quot; which is all the base items with the same UID. Also make sure
+        to lock this item after.
+
+        @return: an L{Deferred} that fires with L{True} if the L{WorkItem} was locked,
+            L{False} if not.
+        @rtype: L{Deferred}
+        &quot;&quot;&quot;
+
+        # Do the group lock first since this can impact multiple rows and thus could
+        # cause deadlocks if done in the wrong order
+
+        # Row level lock on this item
+        locked = yield self.baseWork.trylock(ScheduleWork.icalendarUid == self.icalendarUid)
+        if locked:
+            yield self.trylock()
+        returnValue(locked)
+
+
+    def addBaseWork(self, baseWork):
+        &quot;&quot;&quot;
+        Add the base work fields into the sub-classes as non-record attributes.
+
+        @param baseWork: the base work item to add
+        @type baseWork: L{ScheduleWork}
+        &quot;&quot;&quot;
+        self.__dict__[&quot;baseWork&quot;] = baseWork
+        self.__dict__[&quot;jobID&quot;] = baseWork.jobID
+        self.__dict__[&quot;icalendarUid&quot;] = baseWork.icalendarUid
+
+
+    def delete(self):
+        &quot;&quot;&quot;
+        Delete the base work item which will delete this one via cascade.
+
+        @return: a L{Deferred} which fires with C{None} when the underlying row
+            has been deleted, or fails with L{NoSuchRecord} if the underlying
+            row was already deleted.
+        &quot;&quot;&quot;
+        return self.baseWork.delete()
+
+
+    @classmethod
+    @inlineCallbacks
+    def hasWork(cls, txn):
+        sch = cls.table
+        rows = (yield Select(
+            (sch.WORK_ID,),
+            From=sch,
+        ).on(txn))
+        returnValue(len(rows) &gt; 0)
+
+
+    @inlineCallbacks
+    def afterWork(self):
+        &quot;&quot;&quot;
+        A hook that gets called after the L{WorkItem} does its real work. This can be used
+        for common clean-up behaviors. The base implementation does nothing.
+        &quot;&quot;&quot;
+        yield super(ScheduleWorkMixin, self).afterWork()
+
+        # Find the next item and schedule to run immediately after this.
+        # We only coalesce ScheduleOrganizerSendWork.
+        if self.workType() == ScheduleOrganizerSendWork.workType():
+            all = yield self.baseWork.query(
+                self.transaction,
+                (ScheduleWork.icalendarUid == self.icalendarUid).And(ScheduleWork.workID != self.workID),
+                order=ScheduleWork.workID,
+                limit=1,
+            )
+            if all:
+                work = all[0]
+                if work.workType == self.workType():
+                    job = yield JobItem.load(self.transaction, work.jobID)
+                    yield job.update(notBefore=datetime.datetime.utcnow())
+                    log.debug(&quot;ScheduleOrganizerSendWork - promoted job: {id}, UID: '{uid}'&quot;, id=work.workID, uid=self.icalendarUid)
+
+
+    @classmethod
</ins><span class="cx">     def allDone(cls):
</span><span class="cx">         d = Deferred()
</span><span class="cx">         cls._allDoneCallback = d.callback
</span><span class="lines">@@ -94,6 +217,36 @@
</span><span class="cx">                 self.transaction.postCommit(_post)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def extractSchedulingResponse(self, queuedResponses):
+        &quot;&quot;&quot;
+        Extract a list of (recipient, status) pairs from a scheduling response, returning that list
+        and an indicator of whether any have a schedule status other than delivered.
+
+        @param queuedResponses: the scheduling response object
+        @type queuedResponses: L{list} of L{caldavxml.ScheduleResponse}
+
+        @return: a L{tuple} of the list and the status state
+        @rtype: L{tuple} of (L{list}, L{bool})
+        &quot;&quot;&quot;
+
+        # Map each recipient in the response to a status code
+        results = []
+        all_delivered = True
+        for response in queuedResponses:
+            for item in response.responses:
+                recipient = str(item.recipient.children[0])
+                status = str(item.reqstatus)
+                statusCode = status.split(&quot;;&quot;)[0]
+
+                results.append((recipient, statusCode,))
+
+                # Now apply to each ATTENDEE/ORGANIZER in the original data only if not 1.2
+                if statusCode != iTIPRequestStatus.MESSAGE_DELIVERED_CODE:
+                    all_delivered = False
+
+        return results, all_delivered
+
+
</ins><span class="cx">     def handleSchedulingResponse(self, response, calendar, is_organizer):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Update a user's calendar object resource based on the results of a queued scheduling
</span><span class="lines">@@ -101,8 +254,8 @@
</span><span class="cx">         as we will already have updated the calendar object resource to make it look like scheduling
</span><span class="cx">         worked prior to the work queue item being enqueued.
</span><span class="cx"> 
</span><del>-        @param response: the scheduling response object
-        @type response: L{caldavxml.ScheduleResponse}
</del><ins>+        @param response: the scheduling response summary data
+        @type response: L{list} of L{tuple} of (L{str} - recipient, L{str} - status)
</ins><span class="cx">         @param calendar: original calendar component
</span><span class="cx">         @type calendar: L{Component}
</span><span class="cx">         @param is_organizer: whether or not iTIP message was sent by the organizer
</span><span class="lines">@@ -112,11 +265,7 @@
</span><span class="cx">         # Map each recipient in the response to a status code
</span><span class="cx">         changed = False
</span><span class="cx">         propname = calendar.mainComponent().recipientPropertyName() if is_organizer else &quot;ORGANIZER&quot;
</span><del>-        for item in response.responses:
-            recipient = str(item.recipient.children[0])
-            status = str(item.reqstatus)
-            statusCode = status.split(&quot;;&quot;)[0]
-
</del><ins>+        for recipient, statusCode in response:
</ins><span class="cx">             # Now apply to each ATTENDEE/ORGANIZER in the original data only if not 1.2
</span><span class="cx">             if statusCode != iTIPRequestStatus.MESSAGE_DELIVERED_CODE:
</span><span class="cx">                 calendar.setParameterToValueForPropertyWithValue(
</span><span class="lines">@@ -131,12 +280,22 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+class ScheduleWork(Record, fromTable(schema.SCHEDULE_WORK)):
+    &quot;&quot;&quot;
+    A L{Record} based table whose rows are used for locking scheduling work by iCalendar UID value.
+    as well as helping to determine the next work for a particular UID.
+    &quot;&quot;&quot;
+    pass
+
+
+
</ins><span class="cx"> class ScheduleOrganizerWork(ScheduleWorkMixin, fromTable(schema.SCHEDULE_ORGANIZER_WORK)):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     The associated work item table is SCHEDULE_ORGANIZER_WORK.
</span><span class="cx"> 
</span><del>-    This work item is used to send a iTIP request and cancel messages when an organizer changes
-    their calendar object resource.
</del><ins>+    This work item is used to generate a set of L{ScheduleOrganizerSendWork} work items for
+    each set of iTIP messages that need to be sent as the result of an organizer changing
+    their copy of the event.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     @classmethod
</span><span class="lines">@@ -177,18 +336,7 @@
</span><span class="cx">         log.debug(&quot;ScheduleOrganizerWork - enqueued for ID: {id}, UID: {uid}, organizer: {org}&quot;, id=proposal.workItem.workID, uid=uid, org=organizer)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    @classmethod
</del><span class="cx">     @inlineCallbacks
</span><del>-    def hasWork(cls, txn):
-        srw = schema.SCHEDULE_ORGANIZER_WORK
-        rows = (yield Select(
-            (srw.WORK_ID,),
-            From=srw,
-        ).on(txn))
-        returnValue(len(rows) &gt; 0)
-
-
-    @inlineCallbacks
</del><span class="cx">     def doWork(self):
</span><span class="cx"> 
</span><span class="cx">         try:
</span><span class="lines">@@ -217,31 +365,142 @@
</span><span class="cx">                 self.smartMerge
</span><span class="cx">             )
</span><span class="cx"> 
</span><ins>+            self._dequeued()
+
+        except Exception, e:
+            log.debug(&quot;ScheduleOrganizerWork - exception ID: {id}, UID: '{uid}', {err}&quot;, id=self.workID, uid=self.icalendarUid, err=str(e))
+            log.debug(traceback.format_exc())
+            raise
+        except:
+            log.debug(&quot;ScheduleOrganizerWork - bare exception ID: {id}, UID: '{uid}'&quot;, id=self.workID, uid=self.icalendarUid)
+            log.debug(traceback.format_exc())
+            raise
+
+        log.debug(&quot;ScheduleOrganizerWork - done for ID: {id}, UID: {uid}, organizer: {org}&quot;, id=self.workID, uid=self.icalendarUid, org=organizer)
+
+
+
+class ScheduleOrganizerSendWork(ScheduleWorkMixin, fromTable(schema.SCHEDULE_ORGANIZER_SEND_WORK)):
+    &quot;&quot;&quot;
+    The associated work item table is SCHEDULE_ORGANIZER_SEND_WORK.
+
+    This work item is used to send iTIP request and cancel messages when an organizer changes
+    their calendar object resource. One of these will be created for each iTIP message that
+    L{ScheduleOrganizerWork} needs to have sent.
+    &quot;&quot;&quot;
+
+    @classmethod
+    @inlineCallbacks
+    def schedule(cls, txn, action, home, resource, organizer, attendee, itipmsg, no_refresh, stagger):
+        &quot;&quot;&quot;
+        Create the work item. Because there may be lots of these dumped onto the server in one go, we will
+        stagger them via notBefore. However, we are using a &quot;chained&quot; work item so when one completes, it
+        will reschedule the next one to run immediately after it, so if work is being done quickly, the
+        stagger interval is effectively ignored.
+
+        @param txn: the transaction to use
+        @type txn: L{CommonStoreTransaction}
+        @param organizer: the calendar user address of the organizer
+        @type organizer: L{str}
+        @param attendee: the calendar user address of the attendee to send the message to
+        @type attendee: L{str}
+        @param itipmsg: the iTIP message to send
+        @type itipmsg: L{Component}
+        @param no_refresh: whether or not refreshes are allowed
+        @type no_refresh: L{bool}
+        @param stagger: number of seconds into the future for notBefore
+        @type stagger: L{int}
+        &quot;&quot;&quot;
+
+        # Always queue up new work - coalescing happens when work is executed
+        notBefore = datetime.datetime.utcnow() + datetime.timedelta(seconds=config.Scheduling.Options.WorkQueues.RequestDelaySeconds + stagger)
+        uid = itipmsg.resourceUID()
+        proposal = (yield txn.enqueue(
+            cls,
+            notBefore=notBefore,
+            icalendarUid=uid,
+            scheduleAction=scheduleActionToSQL[action],
+            homeResourceID=home.id(),
+            resourceID=resource.id() if resource else None,
+            attendee=attendee,
+            itipMsg=itipmsg.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference),
+            noRefresh=no_refresh,
+        ))
+        cls._enqueued()
+        log.debug(
+            &quot;ScheduleOrganizerSendWork - enqueued for ID: {id}, UID: {uid}, organizer: {org}, attendee: {att}&quot;,
+            id=proposal.workItem.workID,
+            uid=uid,
+            org=organizer,
+            att=attendee
+        )
+
+
+    @inlineCallbacks
+    def doWork(self):
+
+        try:
+            home = (yield self.transaction.calendarHomeWithResourceID(self.homeResourceID))
+            resource = (yield home.objectResourceWithID(self.resourceID))
+            itipmsg = Component.fromString(self.itipMsg)
+
+            organizerAddress = yield calendarUserFromCalendarUserUID(home.uid(), self.transaction)
+            organizer = organizerAddress.record.canonicalCalendarUserAddress()
+            log.debug(
+                &quot;ScheduleOrganizerSendWork - running for ID: {id}, UID: {uid}, organizer: {org}, attendee: {att}&quot;,
+                id=self.workID,
+                uid=self.icalendarUid,
+                org=organizer,
+                att=self.attendee
+            )
+
+            # We need to get the UID lock for implicit processing.
+            yield NamedLock.acquire(self.transaction, &quot;ImplicitUIDLock:%s&quot; % (hashlib.md5(self.icalendarUid).hexdigest(),))
+
+            from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler
+            scheduler = ImplicitScheduler()
+            yield scheduler.queuedOrganizerSending(
+                self.transaction,
+                scheduleActionFromSQL[self.scheduleAction],
+                home,
+                resource,
+                self.icalendarUid,
+                organizer,
+                self.attendee,
+                itipmsg,
+                self.noRefresh
+            )
+
</ins><span class="cx">             # Handle responses - update the actual resource in the store. Note that for a create the resource did not previously
</span><span class="cx">             # exist and is stored as None for the work item, but the scheduler will attempt to find the new resources and use
</span><span class="cx">             # that. We need to grab the scheduler's resource for further processing.
</span><span class="cx">             resource = scheduler.resource
</span><span class="cx">             if resource is not None:
</span><del>-                changed = False
-                calendar = (yield resource.componentForUser())
-                for response in scheduler.queuedResponses:
-                    changed |= yield self.handleSchedulingResponse(response, calendar, True)
</del><ins>+                responses, all_delivered = self.extractSchedulingResponse(scheduler.queuedResponses)
+                if not all_delivered:
+                    calendar = (yield resource.componentForUser())
+                    changed = self.handleSchedulingResponse(responses, calendar, True)
+                    if changed:
+                        yield resource._setComponentInternal(calendar, internal_state=ComponentUpdateState.ORGANIZER_ITIP_UPDATE)
</ins><span class="cx"> 
</span><del>-                if changed:
-                    yield resource._setComponentInternal(calendar, internal_state=ComponentUpdateState.ORGANIZER_ITIP_UPDATE)
-
</del><span class="cx">             self._dequeued()
</span><span class="cx"> 
</span><span class="cx">         except Exception, e:
</span><del>-            log.debug(&quot;ScheduleOrganizerWork - exception ID: {id}, UID: '{uid}', {err}&quot;, id=self.workID, uid=self.icalendarUid, err=str(e))
</del><ins>+            log.debug(&quot;ScheduleOrganizerSendWork - exception ID: {id}, UID: '{uid}', {err}&quot;, id=self.workID, uid=self.icalendarUid, err=str(e))
</ins><span class="cx">             log.debug(traceback.format_exc())
</span><span class="cx">             raise
</span><span class="cx">         except:
</span><del>-            log.debug(&quot;ScheduleOrganizerWork - bare exception ID: {id}, UID: '{uid}'&quot;, id=self.workID, uid=self.icalendarUid)
</del><ins>+            log.debug(&quot;ScheduleOrganizerSendWork - bare exception ID: {id}, UID: '{uid}'&quot;, id=self.workID, uid=self.icalendarUid)
</ins><span class="cx">             log.debug(traceback.format_exc())
</span><span class="cx">             raise
</span><span class="cx"> 
</span><del>-        log.debug(&quot;ScheduleOrganizerWork - done for ID: {id}, UID: {uid}, organizer: {org}&quot;, id=self.workID, uid=self.icalendarUid, org=organizer)
</del><ins>+        log.debug(
+            &quot;ScheduleOrganizerSendWork - for ID: {id}, UID: {uid}, organizer: {org}, attendee: {att}&quot;,
+            id=self.workID,
+            uid=self.icalendarUid,
+            org=organizer,
+            att=self.attendee
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -298,18 +557,7 @@
</span><span class="cx">         log.debug(&quot;ScheduleReplyWork - enqueued for ID: {id}, UID: {uid}, attendee: {att}&quot;, id=proposal.workItem.workID, uid=resource.uid(), att=attendee)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    @classmethod
</del><span class="cx">     @inlineCallbacks
</span><del>-    def hasWork(cls, txn):
-        srw = schema.SCHEDULE_REPLY_WORK
-        rows = (yield Select(
-            (srw.WORK_ID,),
-            From=srw,
-        ).on(txn))
-        returnValue(len(rows) &gt; 0)
-
-
-    @inlineCallbacks
</del><span class="cx">     def doWork(self):
</span><span class="cx"> 
</span><span class="cx">         try:
</span><span class="lines">@@ -332,11 +580,12 @@
</span><span class="cx"> 
</span><span class="cx">             # Send scheduling message and process response
</span><span class="cx">             response = (yield self.sendToOrganizer(home, &quot;REPLY&quot;, itipmsg, attendee, organizer))
</span><del>-            changed = yield self.handleSchedulingResponse(response, calendar, False)
</del><ins>+            responses, all_delivered = self.extractSchedulingResponse((response,))
+            if not all_delivered:
+                changed = yield self.handleSchedulingResponse(responses, calendar, False)
+                if changed:
+                    yield resource._setComponentInternal(calendar, internal_state=ComponentUpdateState.ATTENDEE_ITIP_UPDATE)
</ins><span class="cx"> 
</span><del>-            if changed:
-                yield resource._setComponentInternal(calendar, internal_state=ComponentUpdateState.ATTENDEE_ITIP_UPDATE)
-
</del><span class="cx">             self._dequeued()
</span><span class="cx"> 
</span><span class="cx">         except Exception, e:
</span><span class="lines">@@ -360,10 +609,6 @@
</span><span class="cx">     of the original resource data.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    # Schedule work is grouped based on calendar object UID
-    group = property(lambda self: &quot;ScheduleWork:%s&quot; % (self.icalendarUid,))
-
-
</del><span class="cx">     @classmethod
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def replyCancel(cls, txn, home, calendar, attendee):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/sql.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/sql.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/sql.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -1365,13 +1365,13 @@
</span><span class="cx">         # Initialize these for all shares
</span><span class="cx">         for ename in self._shadowProperties:
</span><span class="cx">             if ename not in self.properties() and ename.toString() in props:
</span><del>-                self.properties()[ename] = WebDAVDocument.fromString(props[ename]).root_element
</del><ins>+                self.properties()[ename] = WebDAVDocument.fromString(props[ename.toString()]).root_element
</ins><span class="cx"> 
</span><span class="cx">         # Only initialize these for direct shares
</span><span class="cx">         if self.direct():
</span><span class="cx">             for ename in (PropertyName.fromElement(element.DisplayName),):
</span><span class="cx">                 if ename not in self.properties() and ename.toString() in props:
</span><del>-                    self.properties()[ename] = WebDAVDocument.fromString(props[ename]).root_element
</del><ins>+                    self.properties()[ename] = WebDAVDocument.fromString(props[ename.toString()]).root_element
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     # FIXME: this is DAV-ish.  Data store calendar objects don't have
</span><span class="lines">@@ -1781,7 +1781,7 @@
</span><span class="cx">         record = yield self._txn.directoryService().recordWithUID(shareeUID.decode(&quot;utf-8&quot;))
</span><span class="cx">         if (
</span><span class="cx">             record is None or
</span><del>-            record.recordType != RecordType.group or not (False and
</del><ins>+            record.type() != RecordType.group or not (False and
</ins><span class="cx">                 config.Sharing.Enabled and
</span><span class="cx">                 config.Sharing.Calendars.Enabled and
</span><span class="cx">                 config.Sharing.Calendars.Groups.Enabled
</span><span class="lines">@@ -1793,8 +1793,8 @@
</span><span class="cx">             )
</span><span class="cx"> 
</span><span class="cx">         # shareWith every member of group not already shared to
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate = yield self._txn.groupByUID(record.uid)
-        memberUIDs = yield self._txn.membersOfGroup(groupID)
</del><ins>+        groupID = (yield self._txn.groupByUID(record.uid))[0]
+        memberUIDs = yield self._txn.groupMemberUIDs(groupID)
</ins><span class="cx">         for memberUID in memberUIDs:
</span><span class="cx">             shareeHome = yield self._txn.calendarHomeWithUID(memberUID, create=True)
</span><span class="cx">             if (yield shareeHome.childWithID(self._resourceID)) is None:
</span><span class="lines">@@ -1814,8 +1814,8 @@
</span><span class="cx">             record = (
</span><span class="cx">                 yield self._txn.directoryService().recordWithUID(groupUID.decode(&quot;utf-8&quot;))
</span><span class="cx">             )
</span><del>-            groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate = yield self._txn.groupByUID(record.uid)
-            memberUIDs = yield self._txn.membersOfGroup(groupID)
</del><ins>+            groupID = (yield self._txn.groupByUID(record.uid))[0]
+            memberUIDs = yield self._txn.groupMemberUIDs(groupID)
</ins><span class="cx">             boundUIDs = set()
</span><span class="cx"> 
</span><span class="cx">             bind = schema.CALENDAR_BIND
</span><span class="lines">@@ -1851,7 +1851,10 @@
</span><span class="cx">         update schema.GROUP_SHAREE
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         changed = False
</span><del>-        groupID, _ignore_name, membershipHash, _ignore_modDate = yield self._txn.groupByUID(groupUID)
</del><ins>+        (
+            groupID, _ignore_name, membershipHash, _ignore_modDate,
+            _ignore_extant
+        ) = yield self._txn.groupByUID(groupUID)
</ins><span class="cx"> 
</span><span class="cx">         gs = schema.GROUP_SHAREE
</span><span class="cx">         rows = yield Select(
</span><span class="lines">@@ -1923,33 +1926,26 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         # see if after share is removed, user is still shared by a group sharee
</span><del>-        effectiveShareMode = None
-        oldShareMode = shareeView.shareMode()
-        if oldShareMode in (_BIND_MODE_DIRECT, _BIND_MODE_READ, _BIND_MODE_WRITE, _BIND_MODE_GROUP,):
</del><ins>+        if shareeView.shareMode() in (_BIND_MODE_DIRECT, _BIND_MODE_READ, _BIND_MODE_WRITE, _BIND_MODE_GROUP):
</ins><span class="cx"> 
</span><span class="cx">             gs = schema.GROUP_SHAREE
</span><span class="cx">             rows = yield Select(
</span><del>-                [gs.GROUP_ID, gs.GROUP_BIND_MODE],
</del><ins>+                [gs.GROUP_ID],
</ins><span class="cx">                 From=gs,
</span><span class="cx">                 Where=(gs.CALENDAR_HOME_ID == self.ownerHome()._resourceID).And(
</span><span class="cx">                     gs.CALENDAR_ID == self._resourceID)
</span><span class="cx">             ).on(self._txn)
</span><ins>+            groupIDs = [row[0] for row in rows]
+
</ins><span class="cx">             shareeHomeUID = shareeView.viewerHome().uid()
</span><del>-            for groupID, groupShareMode in rows:
-                memberUIDs = yield self._txn.membersOfGroup(groupID)
</del><ins>+            for groupID in groupIDs:
+                memberUIDs = yield self._txn.groupMemberUIDs(groupID) # must be cached
</ins><span class="cx">                 if shareeHomeUID in memberUIDs:
</span><del>-                    if effectiveShareMode is None:
-                        effectiveShareMode = groupShareMode
-                    elif groupShareMode &gt; effectiveShareMode:
-                        effectiveShareMode = groupShareMode
</del><ins>+                    yield self.updateShare(shareeView, mode=_BIND_MODE_GROUP)
+                    returnValue(None)
</ins><span class="cx"> 
</span><del>-        if effectiveShareMode is None:
-            # no group sharee for this user so let super do work
-            returnValue((yield super(Calendar, self).removeShare(shareeView)))
-        elif oldShareMode != _BIND_MODE_GROUP:
-            # change to group share
-            yield self.updateShare(shareeView, mode=_BIND_MODE_GROUP)
-            returnValue(None)
</del><ins>+        # no group sharee for this user so let super do work
+        returnValue((yield super(Calendar, self).removeShare(shareeView)))
</ins><span class="cx"> 
</span><span class="cx">         #TODO: effectiveShareMode may change between _BIND_MODE_READ &amp; _BIND_MODE_READ.
</span><span class="cx">         #        Is that OK?
</span><span class="lines">@@ -1975,8 +1971,8 @@
</span><span class="cx">             returnValue(None)
</span><span class="cx"> 
</span><span class="cx">         # get group membership
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate = yield self._txn.groupByUID(record.uid)
-        memberUIDs = yield self._txn.membersOfGroup(groupID)
</del><ins>+        groupID = (yield self._txn.groupByUID(record.uid))[0]
+        memberUIDs = yield self._txn.groupMemberUIDs(groupID)
</ins><span class="cx"> 
</span><span class="cx">         # update groupsharee so that removeShare works
</span><span class="cx">         gs = schema.GROUP_SHAREE
</span><span class="lines">@@ -2116,6 +2112,8 @@
</span><span class="cx">     _objectSchema = schema.CALENDAR_OBJECT
</span><span class="cx">     _componentClass = VComponent
</span><span class="cx"> 
</span><ins>+    _currentDataVersion = 1
+
</ins><span class="cx">     def __init__(self, calendar, name, uid, resourceID=None, options=None):
</span><span class="cx"> 
</span><span class="cx">         super(CalendarObject, self).__init__(calendar, name, uid, resourceID)
</span><span class="lines">@@ -2177,7 +2175,8 @@
</span><span class="cx">             obj.SCHEDULE_ETAGS,
</span><span class="cx">             obj.PRIVATE_COMMENTS,
</span><span class="cx">             obj.CREATED,
</span><del>-            obj.MODIFIED
</del><ins>+            obj.MODIFIED,
+            obj.DATAVERSION,
</ins><span class="cx">         ]
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -2198,6 +2197,7 @@
</span><span class="cx">             &quot;_private_comments&quot;,
</span><span class="cx">             &quot;_created&quot;,
</span><span class="cx">             &quot;_modified&quot;,
</span><ins>+            &quot;_dataversion&quot;,
</ins><span class="cx">          )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -2221,7 +2221,7 @@
</span><span class="cx"> 
</span><span class="cx">         # Possible timezone stripping
</span><span class="cx">         if config.EnableTimezonesByReference:
</span><del>-            component.stripKnownTimezones()
</del><ins>+            component.stripStandardTimezones()
</ins><span class="cx"> 
</span><span class="cx">         # Do validation on external requests
</span><span class="cx">         if internal_state == ComponentUpdateState.NORMAL:
</span><span class="lines">@@ -2250,7 +2250,7 @@
</span><span class="cx">             yield self.validAttendeeListSizeCheck(component, inserting)
</span><span class="cx"> 
</span><span class="cx">         # Check location/resource organizer requirement
</span><del>-        self.validLocationResourceOrganizer(component, inserting, internal_state)
</del><ins>+        yield self.validLocationResourceOrganizer(component, inserting, internal_state)
</ins><span class="cx"> 
</span><span class="cx">         # Check access
</span><span class="cx">         if config.EnablePrivateEvents:
</span><span class="lines">@@ -2277,19 +2277,16 @@
</span><span class="cx">         groupCUAToAttendeeMemberPropMap = {}
</span><span class="cx">         for groupCUA in groupCUAs:
</span><span class="cx"> 
</span><ins>+            groupCUAToAttendeeMemberPropMap[groupCUA] = ()
</ins><span class="cx">             groupRecord = yield self.directoryService().recordWithCalendarUserAddress(groupCUA)
</span><span class="cx">             if groupRecord:
</span><del>-                # get members from cached membership
-                groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate = yield self._txn.groupByUID(groupRecord.uid)
-                members = [(yield self.directoryService().recordWithUID(memberUID))
-                    for memberUID in
-                    (yield self._txn.membersOfGroup(groupID))
-                ]
-                groupCUAToAttendeeMemberPropMap[groupRecord.canonicalCalendarUserAddress()] = tuple(
-                    [member.attendeeProperty(params={&quot;MEMBER&quot;: groupCUA}) for member in sorted(members, key=lambda x: x.uid)]
-                )
-            else:
-                groupCUAToAttendeeMemberPropMap[groupCUA] = ()
</del><ins>+                # get members
+                groupID = (yield self._txn.groupByUID(groupRecord.uid))[0]
+                if groupID is not None:
+                    members = yield self._txn.groupMembers(groupID)
+                    groupCUAToAttendeeMemberPropMap[groupRecord.canonicalCalendarUserAddress()] = tuple(
+                        [member.attendeeProperty(params={&quot;MEMBER&quot;: groupCUA}) for member in sorted(members, key=lambda x: x.uid)]
+                    )
</ins><span class="cx"> 
</span><span class="cx">         # sync group attendee members if inserting or group changed
</span><span class="cx">         changed = False
</span><span class="lines">@@ -2328,7 +2325,10 @@
</span><span class="cx">                 groupUID = groupRecord.uid
</span><span class="cx">             else:
</span><span class="cx">                 groupUID = uidFromCalendarUserAddress(groupCUA)
</span><del>-            groupID, _ignore_name, membershipHash, _ignore_modDate = yield self._txn.groupByUID(groupUID)
</del><ins>+            (
+                groupID, _ignore_name, membershipHash, _ignore_modDate,
+                _ignore_extant
+            ) = yield self._txn.groupByUID(groupUID)
</ins><span class="cx"> 
</span><span class="cx">             if groupID in groupIDToMembershipHashMap:
</span><span class="cx">                 if groupIDToMembershipHashMap[groupID] != membershipHash:
</span><span class="lines">@@ -3309,7 +3309,8 @@
</span><span class="cx">                 co.SCHEDULE_TAG                    : self._schedule_tag,
</span><span class="cx">                 co.SCHEDULE_ETAGS                  : self._schedule_etags,
</span><span class="cx">                 co.PRIVATE_COMMENTS                : self._private_comments,
</span><del>-                co.MD5                             : self._md5
</del><ins>+                co.MD5                             : self._md5,
+                co.DATAVERSION                     : self._currentDataVersion,
</ins><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             # Only needed if indexing being changed
</span><span class="lines">@@ -3328,8 +3329,9 @@
</span><span class="cx">                 values[co.MODIFIED] = utcNowSQL
</span><span class="cx">                 self._modified = (
</span><span class="cx">                     yield Update(
</span><del>-                        values, Return=co.MODIFIED,
-                        Where=co.RESOURCE_ID == self._resourceID
</del><ins>+                        values,
+                        Where=co.RESOURCE_ID == self._resourceID,
+                        Return=co.MODIFIED,
</ins><span class="cx">                     ).on(txn)
</span><span class="cx">                 )[0][0]
</span><span class="cx"> 
</span><span class="lines">@@ -3483,14 +3485,6 @@
</span><span class="cx">             # Fix any bogus data we can
</span><span class="cx">             fixed, unfixed = component.validCalendarData(doFix=True, doRaise=False)
</span><span class="cx"> 
</span><del>-            # Normalize CUAs:
-            # FIXME: update the DB copy as well so we don't keep going through
-            # this normalization?
-            yield component.normalizeCalendarUserAddresses(
-                normalizationLookup,
-                self.directoryService().recordWithCalendarUserAddress
-            )
-
</del><span class="cx">             if unfixed:
</span><span class="cx">                 self.log.error(
</span><span class="cx">                     &quot;Calendar data id={0} had unfixable problems:\n  {1}&quot;.format(
</span><span class="lines">@@ -3505,6 +3499,10 @@
</span><span class="cx">                     )
</span><span class="cx">                 )
</span><span class="cx"> 
</span><ins>+            # Check for on-demand data upgrade
+            if self._dataversion &lt; self._currentDataVersion:
+                yield self.upgradeData(component)
+
</ins><span class="cx">             self._cachedComponent = component
</span><span class="cx">             self._cachedCommponentPerUser = {}
</span><span class="cx"> 
</span><span class="lines">@@ -3533,6 +3531,25 @@
</span><span class="cx">         returnValue(self._cachedCommponentPerUser[user_uuid])
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def upgradeData(self, component):
+        &quot;&quot;&quot;
+        Implement in sub-classes. If the data version of this item does not match
+        the current data version, call this method and implement a data upgrade,
+        writing back the new data and updating the data version.
+        &quot;&quot;&quot;
+
+        if self._dataversion &lt; 1:
+            # Normalize CUAs:
+            yield component.normalizeCalendarUserAddresses(
+                normalizationLookup,
+                self.directoryService().recordWithCalendarUserAddress
+            )
+
+        self._dataversion = self._currentDataVersion
+        yield self.updateDatabase(component)
+
+
</ins><span class="cx">     def moveValidation(self, destination, name):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Validate whether a move to the specified collection is allowed.
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcaldavdatastoretesttest_sqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/test_sql.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/test_sql.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/caldav/datastore/test/test_sql.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -39,6 +39,7 @@
</span><span class="cx"> from twistedcaldav.dateops import datetimeMktime
</span><span class="cx"> from twistedcaldav.ical import Component, normalize_iCalStr, diff_iCalStrs
</span><span class="cx"> from twistedcaldav.instance import InvalidOverriddenInstanceError
</span><ins>+from twistedcaldav.timezones import TimezoneCache
</ins><span class="cx"> 
</span><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><span class="cx"> from txdav.caldav.datastore.query.filter import Filter
</span><span class="lines">@@ -2017,7 +2018,195 @@
</span><span class="cx">         yield self.abort()
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def test_standardTimezone(self):
+        &quot;&quot;&quot;
+        Make sure a standard timezone is not stored and not returned in the calendar data when timezones
+        by reference is on.
+        &quot;&quot;&quot;
+        data = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTIMEZONE
+TZID:America/New_York
+X-LIC-LOCATION:America/New_York
+BEGIN:STANDARD
+DTSTART:18000101T000000
+RDATE:18000101T000000
+TZNAME:GMT+1
+TZOFFSETFROM:-0100
+TZOFFSETTO:-0100
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;TZID=America/New_York:20130806T000000
+DURATION:PT1H
+DTSTAMP:20051222T210507Z
+SUMMARY:1
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
</ins><span class="cx"> 
</span><ins>+        self.patch(config, &quot;EnableTimezonesByReference&quot;, True)
+        TimezoneCache.create()
+        self.addCleanup(TimezoneCache.clear)
+
+        yield self.homeUnderTest(name=&quot;user01&quot;, create=True)
+        calendar = yield self.calendarUnderTest(name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        yield calendar.createCalendarObjectWithName(&quot;data1.ics&quot;, Component.fromString(data))
+        yield self.commit()
+
+        obj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        txt = yield obj._text()
+        self.assertTrue(&quot;BEGIN:VTIMEZONE&quot; not in txt)
+        cal = yield obj.componentForUser(&quot;user01&quot;)
+        self.assertEqual(len(tuple(cal.subcomponents())), 1)
+        txt = cal.getTextWithTimezones(False)
+        self.assertTrue(&quot;BEGIN:VTIMEZONE&quot; not in txt)
+
+
+    @inlineCallbacks
+    def test_nonStandardTimezone(self):
+        &quot;&quot;&quot;
+        Make sure a non-standard timezone is stored and returned in the calendar data when timezones
+        by reference is on.
+        &quot;&quot;&quot;
+        data = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTIMEZONE
+TZID:GMTPlusOne
+X-LIC-LOCATION:GMTPlusOne
+BEGIN:STANDARD
+DTSTART:18000101T000000
+RDATE:18000101T000000
+TZNAME:GMT+1
+TZOFFSETFROM:-0100
+TZOFFSETTO:-0100
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;TZID=GMTPlusOne:20130806T000000
+DURATION:PT1H
+DTSTAMP:20051222T210507Z
+SUMMARY:1
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
+
+        self.patch(config, &quot;EnableTimezonesByReference&quot;, True)
+        TimezoneCache.create()
+        self.addCleanup(TimezoneCache.clear)
+
+        yield self.homeUnderTest(name=&quot;user01&quot;, create=True)
+        calendar = yield self.calendarUnderTest(name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        yield calendar.createCalendarObjectWithName(&quot;data1.ics&quot;, Component.fromString(data))
+        yield self.commit()
+
+        obj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        txt = yield obj._text()
+        self.assertTrue(&quot;TZID:GMTPlusOne&quot; in txt)
+        cal = yield obj.componentForUser(&quot;user01&quot;)
+        self.assertEqual(len(tuple(cal.subcomponents())), 2)
+        txt = cal.getTextWithTimezones(False)
+        self.assertTrue(&quot;BEGIN:VTIMEZONE&quot; in txt)
+
+
+    @inlineCallbacks
+    def test_dataVersion(self):
+        &quot;&quot;&quot;
+        Make sure L{CalendarObject}'s data version is set when object is created.
+        &quot;&quot;&quot;
+        olddata = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-dataversion
+DTSTART:20130806T000000Z
+DURATION:PT1H
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01@example.com
+ATTENDEE:mailto:user01@example.com
+SUMMARY:1
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
+
+        yield self.homeUnderTest(name=&quot;user01&quot;, create=True)
+        calendar = yield self.calendarUnderTest(name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        yield calendar.createCalendarObjectWithName(&quot;data1.ics&quot;, Component.fromString(olddata))
+        yield self.commit()
+
+        obj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        self.assertEqual(obj._dataversion, obj._currentDataVersion)
+        yield self.commit()
+
+
+    @inlineCallbacks
+    def test_dataUpgrade(self):
+        &quot;&quot;&quot;
+        Make sure L{CalendarObject.upgradeData} works correctly.
+        &quot;&quot;&quot;
+        olddata = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20130806T000000Z
+DURATION:PT1H
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01@example.com
+ATTENDEE:mailto:user01@example.com
+SUMMARY:1
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
+
+        yield self.homeUnderTest(name=&quot;user01&quot;, create=True)
+        calendar = yield self.calendarUnderTest(name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        yield calendar.createCalendarObjectWithName(&quot;data1.ics&quot;, Component.fromString(olddata))
+        yield self.commit()
+
+        # Make it look old
+        obj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        co = schema.CALENDAR_OBJECT
+        yield Update(
+            {
+                co.ICALENDAR_TEXT: olddata,
+                co.DATAVERSION: 0,
+            },
+            Where=co.RESOURCE_ID == obj._resourceID,
+        ).on(self.transactionUnderTest())
+        yield self.commit()
+
+        # Still looks old
+        obj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        txt = yield obj._text()
+        self.assertTrue(&quot;mailto:user01@example.com&quot; in txt)
+        self.assertEqual(obj._dataversion, 0)
+        yield self.commit()
+
+        # Now it is new
+        obj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        cal = yield obj.componentForUser(&quot;user01&quot;)
+        txt = cal.getTextWithTimezones(False)
+        self.assertTrue(&quot;mailto:user01@example.com&quot; not in txt)
+        self.assertTrue(&quot;urn:x-uid:user01&quot; in txt)
+        self.assertEqual(obj._dataversion, obj._currentDataVersion)
+        yield self.commit()
+
+        # Still new
+        obj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;user01&quot;)
+        txt = yield obj._text()
+        self.assertTrue(&quot;mailto:user01@example.com&quot; not in txt)
+        self.assertTrue(&quot;urn:x-uid:user01&quot; in txt)
+        self.assertEqual(obj._dataversion, obj._currentDataVersion)
+        yield self.commit()
+
+
+
</ins><span class="cx"> class SchedulingTests(CommonCommonTests, unittest.TestCase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     CalendarObject splitting tests
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcarddavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/carddav/datastore/sql.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/carddav/datastore/sql.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/carddav/datastore/sql.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -224,7 +224,7 @@
</span><span class="cx">         Unbinds any collections that have been shared to this home but not yet
</span><span class="cx">         accepted.  Associated invite entries are also removed.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        super(AddressBookHome, self).removeUnacceptedShares()
</del><ins>+        yield super(AddressBookHome, self).removeUnacceptedShares()
</ins><span class="cx"> 
</span><span class="cx">         # Remove group binds too
</span><span class="cx">         bind = AddressBookObject._bindSchema
</span><span class="lines">@@ -358,7 +358,7 @@
</span><span class="cx"> 
</span><span class="cx"> class AddressBookSharingMixIn(SharingMixIn):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    Sharing code shared between AddressBook and AddressBookObject
</del><ins>+        Sharing code shared between AddressBook and AddressBookObject
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     def sharedResourceType(self):
</span><span class="lines">@@ -805,7 +805,7 @@
</span><span class="cx">                 self.ownerHome()._addressbookPropertyStoreID,  # not ._resourceID as in CommonHomeChild._loadPropertyStore()
</span><span class="cx">                 notifyCallback=self.notifyPropertyChanged
</span><span class="cx">             )
</span><del>-        super(AddressBook, self)._loadPropertyStore(props)
</del><ins>+        yield super(AddressBook, self)._loadPropertyStore(props)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def initPropertyStore(self, props):
</span><span class="lines">@@ -839,13 +839,13 @@
</span><span class="cx">         # Initialize these for all shares
</span><span class="cx">         for ename in self._shadowProperties:
</span><span class="cx">             if ename not in self.properties() and ename.toString() in props:
</span><del>-                self.properties()[ename] = WebDAVDocument.fromString(props[ename]).root_element
</del><ins>+                self.properties()[ename] = WebDAVDocument.fromString(props[ename.toString()]).root_element
</ins><span class="cx"> 
</span><span class="cx">         # Only initialize these for direct shares
</span><span class="cx">         if self.direct():
</span><span class="cx">             for ename in (PropertyName.fromElement(element.DisplayName),):
</span><span class="cx">                 if ename not in self.properties() and ename.toString() in props:
</span><del>-                    self.properties()[ename] = WebDAVDocument.fromString(props[ename]).root_element
</del><ins>+                    self.properties()[ename] = WebDAVDocument.fromString(props[ename.toString()]).root_element
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def contentType(self):
</span><span class="lines">@@ -868,7 +868,7 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def removedObjectResource(self, child):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-            just like CommonHomeChild.removedObjectResource() but does not call self._deleteRevision()
</del><ins>+        Just like CommonHomeChild.removedObjectResource() but does not call self._deleteRevision()
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         self._objects.pop(child.name(), None)
</span><span class="cx">         self._objects.pop(child.uid(), None)
</span><span class="lines">@@ -1856,6 +1856,8 @@
</span><span class="cx"> 
</span><span class="cx">     _componentClass = VCard
</span><span class="cx"> 
</span><ins>+    _currentDataVersion = 0
+
</ins><span class="cx">     # used by CommonHomeChild._childrenAndMetadataForHomeID() only
</span><span class="cx">     # _homeChildSchema = schema.ADDRESSBOOK_OBJECT
</span><span class="cx">     # _homeChildMetaDataSchema = schema.ADDRESSBOOK_OBJECT
</span><span class="lines">@@ -2232,7 +2234,8 @@
</span><span class="cx">             obj.MD5,
</span><span class="cx">             Len(obj.TEXT),
</span><span class="cx">             obj.CREATED,
</span><del>-            obj.MODIFIED
</del><ins>+            obj.MODIFIED,
+            obj.DATAVERSION,
</ins><span class="cx">         ]
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -2248,6 +2251,7 @@
</span><span class="cx">             &quot;_size&quot;,
</span><span class="cx">             &quot;_created&quot;,
</span><span class="cx">             &quot;_modified&quot;,
</span><ins>+            &quot;_dataversion&quot;,
</ins><span class="cx">          )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -2451,17 +2455,21 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         abo = schema.ADDRESSBOOK_OBJECT
</span><span class="cx">         return Insert(
</span><del>-            {abo.RESOURCE_ID: schema.RESOURCE_ID_SEQ,
-             abo.ADDRESSBOOK_HOME_RESOURCE_ID: Parameter(&quot;addressbookResourceID&quot;),
-             abo.RESOURCE_NAME: Parameter(&quot;name&quot;),
-             abo.VCARD_TEXT: Parameter(&quot;text&quot;),
-             abo.VCARD_UID: Parameter(&quot;uid&quot;),
-             abo.KIND: Parameter(&quot;kind&quot;),
-             abo.MD5: Parameter(&quot;md5&quot;),
-             },
-            Return=(abo.RESOURCE_ID,
-                    abo.CREATED,
-                    abo.MODIFIED))
</del><ins>+            {
+                abo.RESOURCE_ID: schema.RESOURCE_ID_SEQ,
+                abo.ADDRESSBOOK_HOME_RESOURCE_ID: Parameter(&quot;addressbookResourceID&quot;),
+                abo.RESOURCE_NAME: Parameter(&quot;name&quot;),
+                abo.VCARD_TEXT: Parameter(&quot;text&quot;),
+                abo.VCARD_UID: Parameter(&quot;uid&quot;),
+                abo.KIND: Parameter(&quot;kind&quot;),
+                abo.MD5: Parameter(&quot;md5&quot;),
+                abo.DATAVERSION: Parameter(&quot;dataVersion&quot;),
+            },
+            Return=(
+                abo.RESOURCE_ID,
+                abo.CREATED,
+                abo.MODIFIED
+        ))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><span class="lines">@@ -2578,6 +2586,7 @@
</span><span class="cx">                     uid=self._uid,
</span><span class="cx">                     md5=self._md5,
</span><span class="cx">                     kind=self._kind,
</span><ins>+                    dataVersion=self._currentDataVersion,
</ins><span class="cx">                 )
</span><span class="cx">             )[0]
</span><span class="cx"> 
</span><span class="lines">@@ -2611,9 +2620,12 @@
</span><span class="cx"> 
</span><span class="cx">         else:
</span><span class="cx">             self._modified = (yield Update(
</span><del>-                {abo.VCARD_TEXT: self._objectText,
-                 abo.MD5: self._md5,
-                 abo.MODIFIED: utcNowSQL},
</del><ins>+                {
+                    abo.VCARD_TEXT: self._objectText,
+                    abo.MD5: self._md5,
+                    abo.DATAVERSION: self._dataversion,
+                    abo.MODIFIED: utcNowSQL,
+                },
</ins><span class="cx">                 Where=abo.RESOURCE_ID == self._resourceID,
</span><span class="cx">                 Return=abo.MODIFIED).on(self._txn))[0][0]
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcarddavdatastoretesttest_sqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/carddav/datastore/test/test_sql.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/carddav/datastore/test/test_sql.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/carddav/datastore/test/test_sql.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -877,3 +877,31 @@
</span><span class="cx">         obj = (yield self.addressbookObjectUnderTest())
</span><span class="cx">         addressbookObject = (yield home.objectResourceWithID(obj._resourceID))
</span><span class="cx">         self.assertNotEquals(addressbookObject, None)
</span><ins>+
+
+    @inlineCallbacks
+    def test_dataVersion(self):
+        &quot;&quot;&quot;
+        Make sure L{AddressBookObject}'s data version is set when object is created.
+        &quot;&quot;&quot;
+        olddata = &quot;&quot;&quot;BEGIN:VCARD
+VERSION:3.0
+N:Thompson;Default1;;;
+FN:Default1 Thompson
+EMAIL;type=INTERNET;type=WORK;type=pref:lthompson1@example.com
+TEL;type=WORK;type=pref:1-555-555-5555
+TEL;type=CELL:1-444-444-4444
+item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
+item1.X-ABADR:us
+UID:uid-dataversion-test
+END:VCARD
+&quot;&quot;&quot;
+
+        yield self.homeUnderTest()
+        adbk = yield self.addressbookUnderTest(name=&quot;addressbook&quot;)
+        yield adbk.createAddressBookObjectWithName(&quot;data1.ics&quot;, VCard.fromString(olddata))
+        yield self.commit()
+
+        obj = yield self.addressbookObjectUnderTest(name=&quot;data1.ics&quot;, addressbook_name=&quot;addressbook&quot;)
+        self.assertEqual(obj._dataversion, obj._currentDataVersion)
+        yield self.commit()
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -81,6 +81,7 @@
</span><span class="cx"> from txdav.common.inotifications import INotificationCollection, \
</span><span class="cx">     INotificationObject
</span><span class="cx"> from txdav.idav import ChangeCategory
</span><ins>+from twext.who.idirectory import RecordType
</ins><span class="cx"> from txdav.xml import element
</span><span class="cx"> 
</span><span class="cx"> from uuid import uuid4, UUID
</span><span class="lines">@@ -1033,8 +1034,8 @@
</span><span class="cx">             {
</span><span class="cx">                 gr.MEMBERSHIP_HASH: Parameter(&quot;membershipHash&quot;),
</span><span class="cx">                 gr.NAME: Parameter(&quot;name&quot;),
</span><del>-                gr.MODIFIED:
-                Parameter(&quot;timestamp&quot;)
</del><ins>+                gr.MODIFIED: Parameter(&quot;timestamp&quot;),
+                gr.EXTANT: Parameter(&quot;extant&quot;),
</ins><span class="cx">             },
</span><span class="cx">             Where=(gr.GROUP_UID == Parameter(&quot;groupUID&quot;))
</span><span class="cx">         )
</span><span class="lines">@@ -1044,7 +1045,7 @@
</span><span class="cx">     def _groupByUID(cls):
</span><span class="cx">         gr = schema.GROUPS
</span><span class="cx">         return Select(
</span><del>-            [gr.GROUP_ID, gr.NAME, gr.MEMBERSHIP_HASH, gr.MODIFIED],
</del><ins>+            [gr.GROUP_ID, gr.NAME, gr.MEMBERSHIP_HASH, gr.MODIFIED, gr.EXTANT],
</ins><span class="cx">             From=gr,
</span><span class="cx">             Where=(gr.GROUP_UID == Parameter(&quot;groupUID&quot;))
</span><span class="cx">         )
</span><span class="lines">@@ -1054,7 +1055,7 @@
</span><span class="cx">     def _groupByID(cls):
</span><span class="cx">         gr = schema.GROUPS
</span><span class="cx">         return Select(
</span><del>-            [gr.GROUP_UID, gr.NAME, gr.MEMBERSHIP_HASH],
</del><ins>+            [gr.GROUP_UID, gr.NAME, gr.MEMBERSHIP_HASH, gr.EXTANT],
</ins><span class="cx">             From=gr,
</span><span class="cx">             Where=(gr.GROUP_ID == Parameter(&quot;groupID&quot;))
</span><span class="cx">         )
</span><span class="lines">@@ -1069,25 +1070,33 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def addGroup(self, groupUID, name, membershipHash):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @type groupUID: C{unicode}
</span><span class="cx">         @type name: C{unicode}
</span><span class="cx">         @type membershipHash: C{str}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        return self._addGroupQuery.on(
</del><ins>+        groupID = yield self._addGroupQuery.on(
</ins><span class="cx">             self,
</span><span class="cx">             name=name.encode(&quot;utf-8&quot;),
</span><span class="cx">             groupUID=groupUID.encode(&quot;utf-8&quot;),
</span><span class="cx">             membershipHash=membershipHash
</span><span class="cx">         )
</span><span class="cx"> 
</span><ins>+        record = yield self.directoryService().recordWithUID(groupUID)
+        yield self._refreshGroup(
+            groupUID, record, groupID, name.encode(&quot;utf-8&quot;), membershipHash
+        )
+        returnValue(groupID)
</ins><span class="cx"> 
</span><del>-    def updateGroup(self, groupUID, name, membershipHash):
</del><ins>+
+    def updateGroup(self, groupUID, name, membershipHash, extant=True):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @type groupUID: C{unicode}
</span><span class="cx">         @type name: C{unicode}
</span><span class="cx">         @type membershipHash: C{str}
</span><ins>+        @type extant: C{boolean}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         timestamp = datetime.datetime.utcnow()
</span><span class="cx">         return self._updateGroupQuery.on(
</span><span class="lines">@@ -1095,7 +1104,8 @@
</span><span class="cx">             name=name.encode(&quot;utf-8&quot;),
</span><span class="cx">             groupUID=groupUID.encode(&quot;utf-8&quot;),
</span><span class="cx">             timestamp=timestamp,
</span><del>-            membershipHash=membershipHash
</del><ins>+            membershipHash=membershipHash,
+            extant=(1 if extant else 0)
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1107,7 +1117,8 @@
</span><span class="cx">         @type groupUID: C{unicode}
</span><span class="cx"> 
</span><span class="cx">         @return: Deferred firing with tuple of group ID C{str}, group name
</span><del>-            C{unicode}, membership hash C{str}, and modified timestamp
</del><ins>+            C{unicode}, membership hash C{str}, modified timestamp, and
+            extant C{boolean}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         results = (
</span><span class="cx">             yield self._groupByUID.on(
</span><span class="lines">@@ -1120,6 +1131,7 @@
</span><span class="cx">                 results[0][1].decode(&quot;utf-8&quot;),  # name
</span><span class="cx">                 results[0][2],  # membership hash
</span><span class="cx">                 results[0][3],  # modified timestamp
</span><ins>+                bool(results[0][4]),  # extant
</ins><span class="cx">             ))
</span><span class="cx">         elif create:
</span><span class="cx">             savepoint = SavepointAction(&quot;groupByUID&quot;)
</span><span class="lines">@@ -1139,6 +1151,7 @@
</span><span class="cx">                         results[0][1].decode(&quot;utf-8&quot;),  # name
</span><span class="cx">                         results[0][2],  # membership hash
</span><span class="cx">                         results[0][3],  # modified timestamp
</span><ins>+                        bool(results[0][4]),  # extant
</ins><span class="cx">                     ))
</span><span class="cx">                 else:
</span><span class="cx">                     raise
</span><span class="lines">@@ -1155,11 +1168,12 @@
</span><span class="cx">                         results[0][1].decode(&quot;utf-8&quot;),  # name
</span><span class="cx">                         results[0][2],  # membership hash
</span><span class="cx">                         results[0][3],  # modified timestamp
</span><ins>+                        bool(results[0][4]),  # extant
</ins><span class="cx">                     ))
</span><span class="cx">                 else:
</span><span class="cx">                     raise
</span><span class="cx">         else:
</span><del>-            returnValue((None, None, None, None))
</del><ins>+            returnValue((None, None, None, None, None))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -1169,7 +1183,7 @@
</span><span class="cx"> 
</span><span class="cx">         @type groupID: C{str}
</span><span class="cx">         @return: Deferred firing with a tuple of group UID C{unicode},
</span><del>-            group name C{unicode}, and membership hash C{str}
</del><ins>+            group name C{unicode}, membership hash C{str}, and extant C{boolean}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         try:
</span><span class="cx">             results = (yield self._groupByID.on(self, groupID=groupID))[0]
</span><span class="lines">@@ -1177,7 +1191,8 @@
</span><span class="cx">                 results = (
</span><span class="cx">                     results[0].decode(&quot;utf-8&quot;),
</span><span class="cx">                     results[1].decode(&quot;utf-8&quot;),
</span><del>-                    results[2]
</del><ins>+                    results[2],
+                    bool(results[3])
</ins><span class="cx">                 )
</span><span class="cx">             returnValue(results)
</span><span class="cx">         except IndexError:
</span><span class="lines">@@ -1263,7 +1278,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def membersOfGroup(self, groupID):
</del><ins>+    def groupMemberUIDs(self, groupID):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Returns the cached set of UIDs for members of the given groupID.
</span><span class="cx">         Sub-groups are not returned in the results but their members are,
</span><span class="lines">@@ -1283,6 +1298,131 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><ins>+    def refreshGroup(self, groupUID):
+        &quot;&quot;&quot;
+        Refreshes the group membership cache.
+
+        @param groupUID: the group UID
+        @type groupUID: C{unicode}
+
+        @return: Deferred firing with tuple of group ID C{str}, and
+            membershipChanged C{boolean}
+
+        &quot;&quot;&quot;
+        log.debug(&quot;Faulting in group: {g}&quot;, g=groupUID)
+        record = (yield self.directoryService().recordWithUID(groupUID))
+        if record is None:
+            # the group has disappeared from the directory
+            log.info(&quot;Group is missing: {g}&quot;, g=groupUID)
+        else:
+            log.debug(&quot;Got group record: {u}&quot;, u=record.uid)
+
+        (
+            groupID, cachedName, cachedMembershipHash, _ignore_modified,
+            _ignore_extant
+        ) = yield self.groupByUID(
+            groupUID,
+            create=(record is not None)
+        )
+
+        membershipChanged = False
+        if groupID:
+            membershipChanged = yield self._refreshGroup(
+                groupUID, record, groupID, cachedName, cachedMembershipHash
+            )
+
+        returnValue((groupID, membershipChanged))
+
+
+    @inlineCallbacks
+    def _refreshGroup(self, groupUID, record, groupID, cachedName, cachedMembershipHash):
+        &quot;&quot;&quot;
+        @param groupUID: the directory record
+        @type groupUID: C{unicode}
+        @param record: the directory record
+        @type record: C{iDirectoryRecord}
+        @param groupID: group resource id
+        @type groupID: C{str}
+        @param cachedName: group name in the database
+        @type cachedName: C{unicode}
+        @param cachedMembershipHash: membership hash in the database
+        @type cachedMembershipHash: C{str}
+
+        @return: Deferred firing with membershipChanged C{boolean}
+
+        &quot;&quot;&quot;
+        if record is not None:
+            members = yield record.expandedMembers()
+            name = record.displayName
+            extant = True
+        else:
+            members = frozenset()
+            name = cachedName
+            extant = False
+
+        membershipHashContent = hashlib.md5()
+        members = list(members)
+        members.sort(key=lambda x: x.uid)
+        for member in members:
+            membershipHashContent.update(str(member.uid))
+        membershipHash = membershipHashContent.hexdigest()
+
+        if cachedMembershipHash != membershipHash:
+            membershipChanged = True
+            log.debug(
+                &quot;Group '{group}' changed&quot;, group=name
+            )
+        else:
+            membershipChanged = False
+
+        if membershipChanged or record is not None:
+            # also updates group mod date
+            yield self.updateGroup(
+                groupUID, name, membershipHash, extant=extant
+            )
+
+        if membershipChanged:
+            newMemberUIDs = set()
+            for member in members:
+                newMemberUIDs.add(member.uid)
+            yield self.synchronizeMembers(groupID, newMemberUIDs)
+
+        returnValue(membershipChanged)
+
+
+    @inlineCallbacks
+    def synchronizeMembers(self, groupID, newMemberUIDs):
+        numRemoved = numAdded = 0
+        cachedMemberUIDs = (yield self.groupMemberUIDs(groupID))
+
+        for memberUID in cachedMemberUIDs:
+            if memberUID not in newMemberUIDs:
+                numRemoved += 1
+                yield self.removeMemberFromGroup(memberUID, groupID)
+
+        for memberUID in newMemberUIDs:
+            if memberUID not in cachedMemberUIDs:
+                numAdded += 1
+                yield self.addMemberToGroup(memberUID, groupID)
+
+        returnValue((numAdded, numRemoved))
+
+
+    @inlineCallbacks
+    def groupMembers(self, groupID):
+        &quot;&quot;&quot;
+        The members of the given group as recorded in the db
+        &quot;&quot;&quot;
+        members = set()
+        memberUIDs = (yield self.groupMemberUIDs(groupID))
+        for uid in memberUIDs:
+            record = (yield self.directoryService().recordWithUID(uid))
+            if record is not None:
+                members.add(record)
+        returnValue(members)
+
+
+    @inlineCallbacks
</ins><span class="cx">     def groupsFor(self, uid):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Returns the cached set of UIDs for the groups this given uid is
</span><span class="lines">@@ -3475,6 +3615,11 @@
</span><span class="cx">         We do the same SQL query for both depth &quot;1&quot; and &quot;infinity&quot;, but filter the results for
</span><span class="cx">         &quot;1&quot; to only account for a collection change.
</span><span class="cx"> 
</span><ins>+        Now that we are truncating the revision table, we need to handle the full sync (revision == 0)
+        case a little differently as the revision table will not contain data for resources that exist,
+        but were last modified before the revision cut-off. Instead for revision == 0 we need to list
+        all existing child resources.  
+  
</ins><span class="cx">         We need to handle shared collection a little differently from owned ones. When a shared collection
</span><span class="cx">         is bound into a home we record a revision for it using the sharee home id and sharee collection name.
</span><span class="cx">         That revision is the &quot;starting point&quot; for changes: so if sync occurs with a revision earlier than
</span><span class="lines">@@ -3494,61 +3639,96 @@
</span><span class="cx">         @type depth: C{str}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        changed = set()
-        deleted = set()
-        invalid = set()
</del><span class="cx">         if revision:
</span><span class="cx">             minValidRevision = yield self._txn.calendarserverValue(&quot;MIN-VALID-REVISION&quot;)
</span><span class="cx">             if revision &lt; int(minValidRevision):
</span><span class="cx">                 raise SyncTokenValidException
</span><ins>+        else:
+            results = yield self.resourceNamesSinceRevisionZero(depth)
+            returnValue(results)
</ins><span class="cx"> 
</span><del>-            results = [
-                (
-                    path if path else (collection if collection else &quot;&quot;),
-                    name if name else &quot;&quot;,
-                    wasdeleted
-                )
-                for path, collection, name, wasdeleted in
-                (yield self.doChangesQuery(revision))
-            ]
</del><ins>+        # Use revision table to find changes since the last revision - this will not include
+        # changes to child resources of shared collections - those we will get later
+        results = [
+            (
+                path if path else (collection if collection else &quot;&quot;),
+                name if name else &quot;&quot;,
+                wasdeleted
+            )
+            for path, collection, name, wasdeleted in
+            (yield self.doChangesQuery(revision))
+        ]
</ins><span class="cx"> 
</span><del>-            deleted_collections = set()
-            for path, name, wasdeleted in results:
-                if wasdeleted:
-                    if name:
-                        # Resource deleted - for depth &quot;1&quot; report collection as changed,
-                        # otherwise report resource as deleted
-                        if depth == &quot;1&quot;:
-                            changed.add(&quot;%s/&quot; % (path,))
-                        else:
-                            deleted.add(&quot;%s/%s&quot; % (path, name,))
</del><ins>+        changed = set()
+        deleted = set()
+        invalid = set()
+        deleted_collections = set()
+        for path, name, wasdeleted in results:
+            if wasdeleted:
+                if name:
+                    # Resource deleted - for depth &quot;1&quot; report collection as changed,
+                    # otherwise report resource as deleted
+                    if depth == &quot;1&quot;:
+                        changed.add(&quot;%s/&quot; % (path,))
</ins><span class="cx">                     else:
</span><del>-                        # Collection was deleted
-                        deleted.add(&quot;%s/&quot; % (path,))
-                        deleted_collections.add(path)
</del><ins>+                        deleted.add(&quot;%s/%s&quot; % (path, name,))
+                else:
+                    # Collection was deleted
+                    deleted.add(&quot;%s/&quot; % (path,))
+                    deleted_collections.add(path)
</ins><span class="cx"> 
</span><del>-                if path not in deleted_collections:
-                    # Always report collection as changed
-                    changed.add(&quot;%s/&quot; % (path,))
-                    if name:
-                        # Resource changed - for depth &quot;infinity&quot; report resource as changed
-                        if depth != &quot;1&quot;:
-                            changed.add(&quot;%s/%s&quot; % (path, name,))
</del><ins>+            if path not in deleted_collections:
+                # Always report collection as changed
+                changed.add(&quot;%s/&quot; % (path,))
</ins><span class="cx"> 
</span><del>-        # Now deal with shared collections (and owned if revision == 0)
</del><ins>+                # Resource changed - for depth &quot;infinity&quot; report resource as changed
+                if name and depth != &quot;1&quot;:
+                    changed.add(&quot;%s/%s&quot; % (path, name,))
+
+        # Now deal with existing shared collections
+        # TODO: think about whether this can be done in one query rather than looping over each share
</ins><span class="cx">         for share in (yield self.children()):
</span><del>-            if share.owned():
-                if not revision:
-                    path = share.name()
-                    # Always report collection as changed
-                    changed.add(&quot;%s/&quot; % (path,))
</del><ins>+            if not share.owned():
+                sharedChanged, sharedDeleted, sharedInvalid = yield share.sharedChildResourceNamesSinceRevision(revision, depth)
+                changed |= sharedChanged
+                changed -= sharedInvalid
+                deleted |= sharedDeleted
+                deleted -= sharedInvalid
+                invalid |= sharedInvalid
</ins><span class="cx"> 
</span><del>-                    # Resource changed - for depth &quot;infinity&quot; report resource as changed
-                    if depth != &quot;1&quot;:
-                        for name in (yield share.listObjectResources()):
-                            changed.add(&quot;%s/%s&quot; % (path, name,))
</del><ins>+        changed = sorted(changed)
+        deleted = sorted(deleted)
+        invalid = sorted(invalid)
+        returnValue((changed, deleted, invalid,))
+
+
+
+
+    @inlineCallbacks
+    def resourceNamesSinceRevisionZero(self, depth):
+        &quot;&quot;&quot;
+        Revision == 0 specialization of L{resourceNamesSinceRevision} .
+
+        @param depth: depth for determine what changed
+        @type depth: C{str}
+        &quot;&quot;&quot;
+
+        # Scan each child
+        changed = set()
+        deleted = set()
+        invalid = set()
+        for child in (yield self.children()):
+            if child.owned():
+                path = child.name()
+                # Always report collection as changed
+                changed.add(&quot;%s/&quot; % (path,))
+
+                # Resource changed - for depth &quot;infinity&quot; report resource as changed
+                if depth != &quot;1&quot;:
+                    for name in (yield child.listObjectResources()):
+                        changed.add(&quot;%s/%s&quot; % (path, name,))
</ins><span class="cx">             else:
</span><del>-                sharedChanged, sharedDeleted, sharedInvalid = yield share.sharedChildResourceNamesSinceRevision(revision, depth)
</del><ins>+                sharedChanged, sharedDeleted, sharedInvalid = yield child.sharedChildResourceNamesSinceRevisionZero(depth)
</ins><span class="cx">                 changed |= sharedChanged
</span><span class="cx">                 changed -= sharedInvalid
</span><span class="cx">                 deleted |= sharedDeleted
</span><span class="lines">@@ -6235,57 +6415,83 @@
</span><span class="cx">             else:
</span><span class="cx">                 invalid.add(self.name() + &quot;/&quot;)
</span><span class="cx">         else:
</span><ins>+            # If revision is prior to when the share was created, then treat as a full sync of the share
</ins><span class="cx">             if revision != 0 and revision &lt; self._bindRevision:
</span><span class="cx">                 if depth != &quot;1&quot;:
</span><ins>+                    # This should never happen unless the client the share existed, was removed and then
+                    # re-added and the client has a token from before the remove. In that case the token is no
+                    # longer valid - a full sync has to be done.
</ins><span class="cx">                     raise SyncTokenValidException
</span><span class="cx">                 else:
</span><del>-                    revision = 0
</del><ins>+                    results = yield self.sharedChildResourceNamesSinceRevisionZero(depth)
+                    returnValue(results)
</ins><span class="cx"> 
</span><del>-            if revision:
-                rev = self._revisionsSchema
-                results = [
-                    (
-                        self.name(),
-                        name if name else &quot;&quot;,
-                        wasdeleted
-                    )
-                    for name, wasdeleted in
-                    (yield Select(
-                        [rev.RESOURCE_NAME, rev.DELETED],
-                        From=rev,
-                        Where=(rev.REVISION &gt; revision).And(
-                        rev.RESOURCE_ID == self._resourceID)
-                    ).on(self._txn))
-                    if name
-                ]
</del><ins>+            rev = self._revisionsSchema
+            results = [
+                (
+                    self.name(),
+                    name if name else &quot;&quot;,
+                    wasdeleted
+                )
+                for name, wasdeleted in
+                (yield Select(
+                    [rev.RESOURCE_NAME, rev.DELETED],
+                    From=rev,
+                    Where=(rev.REVISION &gt; revision).And(
+                    rev.RESOURCE_ID == self._resourceID)
+                ).on(self._txn))
+                if name
+            ]
</ins><span class="cx"> 
</span><del>-                for path, name, wasdeleted in results:
-                    if wasdeleted:
-                        if depth == &quot;1&quot;:
-                            changed.add(&quot;%s/&quot; % (path,))
-                        else:
-                            deleted.add(&quot;%s/%s&quot; % (path, name,))
</del><ins>+            for path, name, wasdeleted in results:
+                if wasdeleted:
+                    if depth == &quot;1&quot;:
+                        changed.add(&quot;%s/&quot; % (path,))
+                    else:
+                        deleted.add(&quot;%s/%s&quot; % (path, name,))
</ins><span class="cx"> 
</span><del>-                    # Always report collection as changed
-                    changed.add(&quot;%s/&quot; % (path,))
-
-                    # Resource changed - for depth &quot;infinity&quot; report resource as changed
-                    if depth != &quot;1&quot;:
-                        changed.add(&quot;%s/%s&quot; % (path, name,))
-            else:
-                path = self.name()
</del><span class="cx">                 # Always report collection as changed
</span><span class="cx">                 changed.add(&quot;%s/&quot; % (path,))
</span><span class="cx"> 
</span><span class="cx">                 # Resource changed - for depth &quot;infinity&quot; report resource as changed
</span><del>-                if depth != &quot;1&quot;:
-                    for name in (yield self.listObjectResources()):
-                        changed.add(&quot;%s/%s&quot; % (path, name,))
</del><ins>+                if name and depth != &quot;1&quot;:
+                    changed.add(&quot;%s/%s&quot; % (path, name,))
</ins><span class="cx"> 
</span><span class="cx">         returnValue((changed, deleted, invalid,))
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><ins>+    def sharedChildResourceNamesSinceRevisionZero(self, depth):
+        &quot;&quot;&quot;
+        Revision == 0 specialization of L{sharedChildResourceNamesSinceRevision}. We report on all
+        existing resources -= this collection and children (if depth == infinite).
+
+        @param depth: depth for determine what changed
+        @type depth: C{str}
+        &quot;&quot;&quot;
+        changed = set()
+        deleted = set()
+        invalid = set()
+        path = self.name()
+        if self.external():
+            if depth == &quot;1&quot;:
+                changed.add(&quot;{}/&quot;.format(path))
+            else:
+                invalid.add(&quot;{}/&quot;.format(path))
+        else:
+            path = self.name()
+            # Always report collection as changed
+            changed.add(path + &quot;/&quot;)
+
+            # Resource changed - for depth &quot;infinity&quot; report resource as changed
+            if depth != &quot;1&quot;:
+                for name in (yield self.listObjectResources()):
+                    changed.add(&quot;%s/%s&quot; % (path, name,))
+
+        returnValue((changed, deleted, invalid,))
+
+
+    @inlineCallbacks
</ins><span class="cx">     def _loadPropertyStore(self, props=None):
</span><span class="cx">         if props is None:
</span><span class="cx">             # Use any authz uid in place of the viewer uid so delegates have their own
</span><span class="lines">@@ -6482,6 +6688,11 @@
</span><span class="cx">     _objectSchema = None
</span><span class="cx">     _componentClass = None
</span><span class="cx"> 
</span><ins>+    # Sub-classes must override and set their version number. This is used for
+    # on-demand data upgrades - i.e., any time old data is read it will be
+    # converted to the latest format and written back.
+    _currentDataVersion = 0
+
</ins><span class="cx">     BATCH_LOAD_SIZE = 50
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -6569,6 +6780,7 @@
</span><span class="cx">         self._size = None
</span><span class="cx">         self._created = None
</span><span class="cx">         self._modified = None
</span><ins>+        self._dataversion = None
</ins><span class="cx">         self._textData = None
</span><span class="cx">         self._cachedComponent = None
</span><span class="cx"> 
</span><span class="lines">@@ -6814,7 +7026,8 @@
</span><span class="cx">             obj.MD5,
</span><span class="cx">             Len(obj.TEXT),
</span><span class="cx">             obj.CREATED,
</span><del>-            obj.MODIFIED
</del><ins>+            obj.MODIFIED,
+            obj.DATAVERSION,
</ins><span class="cx">         ]
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -6828,6 +7041,7 @@
</span><span class="cx">             &quot;_size&quot;,
</span><span class="cx">             &quot;_created&quot;,
</span><span class="cx">             &quot;_modified&quot;,
</span><ins>+            &quot;_dataversion&quot;,
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemacurrentoracledialectextrassql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current-oracle-dialect-extras.sql (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current-oracle-dialect-extras.sql        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current-oracle-dialect-extras.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -1,11 +1,17 @@
</span><del>-create or replace function next_job return integer is
-declare
-  cursor c1 is select JOB_ID from JOB for update skip locked;
-  result integer;
-begin
-  open c1;
-  fetch c1 into result;
-  select JOB_ID from JOB where ID = result for update;
-  return result;
-end;
-/
</del><ins>+----
+-- Copyright (c) 2010-2014 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- 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 &quot;AS IS&quot; 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.
+----
+
+-- Extra schema to add to current-oracle-dialect.sql
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemacurrentoracledialectsql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current-oracle-dialect.sql (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current-oracle-dialect.sql        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current-oracle-dialect.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -116,7 +116,6 @@
</span><span class="cx"> insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
</span><span class="cx"> insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
</span><span class="cx"> insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('indirect', 4);
</span><del>-insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('group', 5);
</del><span class="cx"> create table CALENDAR_BIND_STATUS (
</span><span class="cx">     &quot;ID&quot; integer primary key,
</span><span class="cx">     &quot;DESCRIPTION&quot; nvarchar2(16) unique
</span><span class="lines">@@ -153,7 +152,8 @@
</span><span class="cx">     &quot;PRIVATE_COMMENTS&quot; integer default 0 not null,
</span><span class="cx">     &quot;MD5&quot; nchar(32),
</span><span class="cx">     &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
</span><del>-    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
</del><ins>+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;DATAVERSION&quot; integer default 0 not null, 
</ins><span class="cx">     unique (&quot;CALENDAR_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;)
</span><span class="cx"> );
</span><span class="cx"> 
</span><span class="lines">@@ -269,7 +269,8 @@
</span><span class="cx">     &quot;KIND&quot; integer not null,
</span><span class="cx">     &quot;MD5&quot; nchar(32),
</span><span class="cx">     &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
</span><del>-    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
</del><ins>+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;DATAVERSION&quot; integer default 0 not null, 
</ins><span class="cx">     unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;), 
</span><span class="cx">     unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;VCARD_UID&quot;)
</span><span class="cx"> );
</span><span class="lines">@@ -401,6 +402,13 @@
</span><span class="cx">     &quot;GROUP_UID&quot; nvarchar2(255)
</span><span class="cx"> );
</span><span class="cx"> 
</span><ins>+create table GROUP_ATTENDEE_RECONCILE_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;RESOURCE_ID&quot; integer,
+    &quot;GROUP_ID&quot; integer
+);
+
</ins><span class="cx"> create table GROUPS (
</span><span class="cx">     &quot;GROUP_ID&quot; integer primary key,
</span><span class="cx">     &quot;NAME&quot; nvarchar2(255),
</span><span class="lines">@@ -417,13 +425,6 @@
</span><span class="cx">     primary key (&quot;GROUP_ID&quot;, &quot;MEMBER_UID&quot;)
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create table GROUP_ATTENDEE_RECONCILE_WORK (
-    &quot;WORK_ID&quot; integer primary key not null,
-    &quot;JOB_ID&quot; integer not null references JOB,
-    &quot;RESOURCE_ID&quot; integer,
-    &quot;GROUP_ID&quot; integer
-);
-
</del><span class="cx"> create table GROUP_ATTENDEE (
</span><span class="cx">     &quot;GROUP_ID&quot; integer not null references GROUPS on delete cascade,
</span><span class="cx">     &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
</span><span class="lines">@@ -431,22 +432,6 @@
</span><span class="cx">     primary key (&quot;GROUP_ID&quot;, &quot;RESOURCE_ID&quot;)
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create table GROUP_SHAREE_RECONCILE_WORK (
-    &quot;WORK_ID&quot; integer primary key not null,
-    &quot;JOB_ID&quot; integer not null references JOB,
-    &quot;CALENDAR_ID&quot; integer not null references CALENDAR on delete cascade,
-    &quot;GROUP_ID&quot; integer not null references GROUPS on delete cascade
-);
-
-create table GROUP_SHAREE (
-    &quot;GROUP_ID&quot; integer not null references GROUPS on delete cascade,
-    &quot;CALENDAR_HOME_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
-    &quot;CALENDAR_ID&quot; integer not null references CALENDAR on delete cascade,
-    &quot;GROUP_BIND_MODE&quot; integer not null,
-    &quot;MEMBERSHIP_HASH&quot; nvarchar2(255), 
-    primary key (&quot;GROUP_ID&quot;, &quot;CALENDAR_HOME_ID&quot;, &quot;CALENDAR_ID&quot;)
-);
-
</del><span class="cx"> create table DELEGATES (
</span><span class="cx">     &quot;DELEGATOR&quot; nvarchar2(255),
</span><span class="cx">     &quot;DELEGATE&quot; nvarchar2(255),
</span><span class="lines">@@ -495,10 +480,15 @@
</span><span class="cx">     &quot;HOME_ID&quot; integer not null unique references CALENDAR_HOME on delete cascade
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create table SCHEDULE_REFRESH_WORK (
</del><ins>+create table SCHEDULE_WORK (
</ins><span class="cx">     &quot;WORK_ID&quot; integer primary key not null,
</span><span class="cx">     &quot;JOB_ID&quot; integer not null references JOB,
</span><span class="cx">     &quot;ICALENDAR_UID&quot; nvarchar2(255),
</span><ins>+    &quot;WORK_TYPE&quot; nvarchar2(255)
+);
+
+create table SCHEDULE_REFRESH_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
</ins><span class="cx">     &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
</span><span class="cx">     &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
</span><span class="cx">     &quot;ATTENDEE_COUNT&quot; integer
</span><span class="lines">@@ -511,18 +501,14 @@
</span><span class="cx"> );
</span><span class="cx"> 
</span><span class="cx"> create table SCHEDULE_AUTO_REPLY_WORK (
</span><del>-    &quot;WORK_ID&quot; integer primary key not null,
-    &quot;JOB_ID&quot; integer not null references JOB,
-    &quot;ICALENDAR_UID&quot; nvarchar2(255),
</del><ins>+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
</ins><span class="cx">     &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
</span><span class="cx">     &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
</span><span class="cx">     &quot;PARTSTAT&quot; nvarchar2(255)
</span><span class="cx"> );
</span><span class="cx"> 
</span><span class="cx"> create table SCHEDULE_ORGANIZER_WORK (
</span><del>-    &quot;WORK_ID&quot; integer primary key not null,
-    &quot;JOB_ID&quot; integer not null references JOB,
-    &quot;ICALENDAR_UID&quot; nvarchar2(255),
</del><ins>+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
</ins><span class="cx">     &quot;SCHEDULE_ACTION&quot; integer not null,
</span><span class="cx">     &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
</span><span class="cx">     &quot;RESOURCE_ID&quot; integer,
</span><span class="lines">@@ -541,19 +527,25 @@
</span><span class="cx"> insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('modify', 1);
</span><span class="cx"> insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('modify-cancelled', 2);
</span><span class="cx"> insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('remove', 3);
</span><ins>+create table SCHEDULE_ORGANIZER_SEND_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;SCHEDULE_ACTION&quot; integer not null,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer,
+    &quot;ATTENDEE&quot; nvarchar2(255),
+    &quot;ITIP_MSG&quot; nclob,
+    &quot;NO_REFRESH&quot; integer
+);
+
</ins><span class="cx"> create table SCHEDULE_REPLY_WORK (
</span><del>-    &quot;WORK_ID&quot; integer primary key not null,
-    &quot;JOB_ID&quot; integer not null references JOB,
-    &quot;ICALENDAR_UID&quot; nvarchar2(255),
</del><ins>+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
</ins><span class="cx">     &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
</span><span class="cx">     &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
</span><span class="cx">     &quot;CHANGED_RIDS&quot; nclob
</span><span class="cx"> );
</span><span class="cx"> 
</span><span class="cx"> create table SCHEDULE_REPLY_CANCEL_WORK (
</span><del>-    &quot;WORK_ID&quot; integer primary key not null,
-    &quot;JOB_ID&quot; integer not null references JOB,
-    &quot;ICALENDAR_UID&quot; nvarchar2(255),
</del><ins>+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
</ins><span class="cx">     &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
</span><span class="cx">     &quot;ICALENDAR_TEXT&quot; nclob
</span><span class="cx"> );
</span><span class="lines">@@ -586,7 +578,7 @@
</span><span class="cx">     &quot;VALUE&quot; nvarchar2(255)
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '44');
</del><ins>+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '45');
</ins><span class="cx"> insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '6');
</span><span class="cx"> insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
</span><span class="cx"> insert into CALENDARSERVER (NAME, VALUE) values ('NOTIFICATION-DATAVERSION', '1');
</span><span class="lines">@@ -744,6 +736,10 @@
</span><span class="cx">     JOB_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><ins>+create index GROUP_ATTENDEE_RECONC_da73d3c2 on GROUP_ATTENDEE_RECONCILE_WORK (
+    JOB_ID
+);
+
</ins><span class="cx"> create index GROUPS_GROUP_UID_b35cce23 on GROUPS (
</span><span class="cx">     GROUP_UID
</span><span class="cx"> );
</span><span class="lines">@@ -752,22 +748,10 @@
</span><span class="cx">     MEMBER_UID
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index GROUP_ATTENDEE_RECONC_da73d3c2 on GROUP_ATTENDEE_RECONCILE_WORK (
-    JOB_ID
-);
-
-create index GROUP_ATTENDEE_ID_d497ffdb on GROUP_ATTENDEE (
</del><ins>+create index GROUP_ATTENDEE_RESOUR_855124dc on GROUP_ATTENDEE (
</ins><span class="cx">     RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index GROUP_SHAREE_RECONCIL_9aad0858 on GROUP_SHAREE_RECONCILE_WORK (
-    JOB_ID
-);
-
-create index GROUP_SHAREE_CALENDAR_28a88850 on GROUP_SHAREE (
-    CALENDAR_ID
-);
-
</del><span class="cx"> create index DELEGATE_TO_DELEGATOR_5e149b11 on DELEGATES (
</span><span class="cx">     DELEGATE,
</span><span class="cx">     READ_WRITE,
</span><span class="lines">@@ -802,6 +786,14 @@
</span><span class="cx">     JOB_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><ins>+create index SCHEDULE_WORK_JOB_ID_65e810ee on SCHEDULE_WORK (
+    JOB_ID
+);
+
+create index SCHEDULE_WORK_ICALEND_089f33dc on SCHEDULE_WORK (
+    ICALENDAR_UID
+);
+
</ins><span class="cx"> create index SCHEDULE_REFRESH_WORK_26084c7b on SCHEDULE_REFRESH_WORK (
</span><span class="cx">     HOME_RESOURCE_ID
</span><span class="cx"> );
</span><span class="lines">@@ -810,10 +802,6 @@
</span><span class="cx">     RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index SCHEDULE_REFRESH_WORK_3ffa2718 on SCHEDULE_REFRESH_WORK (
-    JOB_ID
-);
-
</del><span class="cx"> create index SCHEDULE_REFRESH_ATTE_83053b91 on SCHEDULE_REFRESH_ATTENDEES (
</span><span class="cx">     RESOURCE_ID,
</span><span class="cx">     ATTENDEE
</span><span class="lines">@@ -827,10 +815,6 @@
</span><span class="cx">     RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index SCHEDULE_AUTO_REPLY_W_4d7bb5a8 on SCHEDULE_AUTO_REPLY_WORK (
-    JOB_ID
-);
-
</del><span class="cx"> create index SCHEDULE_ORGANIZER_WO_18ce4edd on SCHEDULE_ORGANIZER_WORK (
</span><span class="cx">     HOME_RESOURCE_ID
</span><span class="cx"> );
</span><span class="lines">@@ -839,10 +823,14 @@
</span><span class="cx">     RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index SCHEDULE_ORGANIZER_WO_1e9f246d on SCHEDULE_ORGANIZER_WORK (
-    JOB_ID
</del><ins>+create index SCHEDULE_ORGANIZER_SE_9ec9f827 on SCHEDULE_ORGANIZER_SEND_WORK (
+    HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx"> 
</span><ins>+create index SCHEDULE_ORGANIZER_SE_699fefc4 on SCHEDULE_ORGANIZER_SEND_WORK (
+    RESOURCE_ID
+);
+
</ins><span class="cx"> create index SCHEDULE_REPLY_WORK_H_745af8cf on SCHEDULE_REPLY_WORK (
</span><span class="cx">     HOME_RESOURCE_ID
</span><span class="cx"> );
</span><span class="lines">@@ -851,18 +839,10 @@
</span><span class="cx">     RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index SCHEDULE_REPLY_WORK_J_5913b4a4 on SCHEDULE_REPLY_WORK (
-    JOB_ID
-);
-
</del><span class="cx"> create index SCHEDULE_REPLY_CANCEL_dab513ef on SCHEDULE_REPLY_CANCEL_WORK (
</span><span class="cx">     HOME_RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index SCHEDULE_REPLY_CANCEL_94a0c766 on SCHEDULE_REPLY_CANCEL_WORK (
-    JOB_ID
-);
-
</del><span class="cx"> create index PRINCIPAL_PURGE_POLLI_6383e68a on PRINCIPAL_PURGE_POLLING_WORK (
</span><span class="cx">     JOB_ID
</span><span class="cx"> );
</span><span class="lines">@@ -883,18 +863,4 @@
</span><span class="cx">     HOME_RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>--- Skipped Function next_job
-
--- Extras
-
-create or replace function next_job return integer is
-declare
-  cursor c1 is select JOB_ID from JOB for update skip locked;
-  result integer;
-begin
-  open c1;
-  fetch c1 into result;
-  select JOB_ID from JOB where ID = result for update;
-  return result;
-end;
-/
</del><ins>+-- Extra schema to add to current-oracle-dialect.sql
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemacurrentsql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current.sql (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current.sql        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/current.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -61,18 +61,9 @@
</span><span class="cx">   NOT_BEFORE  timestamp not null,
</span><span class="cx">   ASSIGNED    timestamp default null,
</span><span class="cx">   OVERDUE     timestamp default null,
</span><del>-  FAILED          integer default 0
</del><ins>+  FAILED      integer default 0
</ins><span class="cx"> );
</span><span class="cx"> 
</span><del>-create or replace function next_job() returns integer as $$
-declare
-  result integer;
-begin
-  select JOB_ID into result from JOB where pg_try_advisory_xact_lock(JOB_ID) limit 1 for update;
-  return result;
-end
-$$ LANGUAGE plpgsql;
-
</del><span class="cx"> -------------------
</span><span class="cx"> -- Calendar Home --
</span><span class="cx"> -------------------
</span><span class="lines">@@ -264,6 +255,7 @@
</span><span class="cx">   MD5                  char(32)     not null,
</span><span class="cx">   CREATED              timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
</span><span class="cx">   MODIFIED             timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
</span><ins>+  DATAVERSION          integer      default 0 not null,
</ins><span class="cx"> 
</span><span class="cx">   unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
</span><span class="cx"> 
</span><span class="lines">@@ -362,7 +354,7 @@
</span><span class="cx">   TIME_RANGE_INSTANCE_ID      integer      not null references TIME_RANGE on delete cascade,
</span><span class="cx">   USER_ID                     varchar(255) not null,
</span><span class="cx">   TRANSPARENT                 boolean      not null,
</span><del>-  ADJUSTED_START_DATE         timestamp           default null,
</del><ins>+  ADJUSTED_START_DATE         timestamp    default null,
</ins><span class="cx">   ADJUSTED_END_DATE           timestamp    default null
</span><span class="cx"> );
</span><span class="cx"> 
</span><span class="lines">@@ -484,6 +476,7 @@
</span><span class="cx">   MD5                           char(32)        not null,
</span><span class="cx">   CREATED                       timestamp       default timezone('UTC', CURRENT_TIMESTAMP),
</span><span class="cx">   MODIFIED                      timestamp       default timezone('UTC', CURRENT_TIMESTAMP),
</span><ins>+  DATAVERSION                   integer         default 0 not null,
</ins><span class="cx"> 
</span><span class="cx">   unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
</span><span class="cx">   unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID)      -- implicit index
</span><span class="lines">@@ -808,7 +801,7 @@
</span><span class="cx">   primary key (GROUP_ID, RESOURCE_ID)
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index GROUP_ATTENDEE_ID on
</del><ins>+create index GROUP_ATTENDEE_ID on  -- FIXME: Rename to GROUP_ATTENDEE_RESOURCE_ID
</ins><span class="cx">   GROUP_ATTENDEE(RESOURCE_ID);
</span><span class="cx"> 
</span><span class="cx">   
</span><span class="lines">@@ -923,14 +916,28 @@
</span><span class="cx"> create index CLEANUP_ONE_INBOX_WORK_JOB_ID on
</span><span class="cx">   CLEANUP_ONE_INBOX_WORK(JOB_ID);
</span><span class="cx"> 
</span><ins>+-------------------
+-- Schedule Work --
+-------------------
+
+create table SCHEDULE_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  ICALENDAR_UID                 varchar(255) not null,
+  WORK_TYPE                     varchar(255) not null
+);
+
+create index SCHEDULE_WORK_JOB_ID on
+  SCHEDULE_WORK(JOB_ID);
+create index SCHEDULE_WORK_ICALENDAR_UID on
+  SCHEDULE_WORK(ICALENDAR_UID);
+
</ins><span class="cx"> ---------------------------
</span><span class="cx"> -- Schedule Refresh Work --
</span><span class="cx"> ---------------------------
</span><span class="cx"> 
</span><span class="cx"> create table SCHEDULE_REFRESH_WORK (
</span><del>-  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
-  JOB_ID                        integer      references JOB not null,
-  ICALENDAR_UID                 varchar(255) not null,
</del><ins>+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
</ins><span class="cx">   HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
</span><span class="cx">   RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
</span><span class="cx">   ATTENDEE_COUNT                integer
</span><span class="lines">@@ -940,8 +947,6 @@
</span><span class="cx">   SCHEDULE_REFRESH_WORK(HOME_RESOURCE_ID);
</span><span class="cx"> create index SCHEDULE_REFRESH_WORK_RESOURCE_ID on
</span><span class="cx">   SCHEDULE_REFRESH_WORK(RESOURCE_ID);
</span><del>-create index SCHEDULE_REFRESH_WORK_JOB_ID on
-  SCHEDULE_REFRESH_WORK(JOB_ID);
</del><span class="cx"> 
</span><span class="cx"> create table SCHEDULE_REFRESH_ATTENDEES (
</span><span class="cx">   RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
</span><span class="lines">@@ -958,9 +963,7 @@
</span><span class="cx"> ------------------------------
</span><span class="cx"> 
</span><span class="cx"> create table SCHEDULE_AUTO_REPLY_WORK (
</span><del>-  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
-  JOB_ID                        integer      references JOB not null,
-  ICALENDAR_UID                 varchar(255) not null,
</del><ins>+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
</ins><span class="cx">   HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
</span><span class="cx">   RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
</span><span class="cx">   PARTSTAT                      varchar(255) not null
</span><span class="lines">@@ -970,17 +973,13 @@
</span><span class="cx">   SCHEDULE_AUTO_REPLY_WORK(HOME_RESOURCE_ID);
</span><span class="cx"> create index SCHEDULE_AUTO_REPLY_WORK_RESOURCE_ID on
</span><span class="cx">   SCHEDULE_AUTO_REPLY_WORK(RESOURCE_ID);
</span><del>-create index SCHEDULE_AUTO_REPLY_WORK_JOB_ID on
-  SCHEDULE_AUTO_REPLY_WORK(JOB_ID);
</del><span class="cx"> 
</span><span class="cx"> -----------------------------
</span><span class="cx"> -- Schedule Organizer Work --
</span><span class="cx"> -----------------------------
</span><span class="cx"> 
</span><span class="cx"> create table SCHEDULE_ORGANIZER_WORK (
</span><del>-  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
-  JOB_ID                        integer      references JOB not null,
-  ICALENDAR_UID                 varchar(255) not null,
</del><ins>+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
</ins><span class="cx">   SCHEDULE_ACTION               integer      not null, -- Enum SCHEDULE_ACTION
</span><span class="cx">   HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
</span><span class="cx">   RESOURCE_ID                   integer,     -- this references a possibly non-existent CALENDR_OBJECT
</span><span class="lines">@@ -994,8 +993,6 @@
</span><span class="cx">   SCHEDULE_ORGANIZER_WORK(HOME_RESOURCE_ID);
</span><span class="cx"> create index SCHEDULE_ORGANIZER_WORK_RESOURCE_ID on
</span><span class="cx">   SCHEDULE_ORGANIZER_WORK(RESOURCE_ID);
</span><del>-create index SCHEDULE_ORGANIZER_WORK_JOB_ID on
-  SCHEDULE_ORGANIZER_WORK(JOB_ID);
</del><span class="cx"> 
</span><span class="cx"> -- Enumeration of schedule actions
</span><span class="cx"> 
</span><span class="lines">@@ -1009,14 +1006,31 @@
</span><span class="cx"> insert into SCHEDULE_ACTION values (2, 'modify-cancelled');
</span><span class="cx"> insert into SCHEDULE_ACTION values (3, 'remove');
</span><span class="cx"> 
</span><ins>+----------------------------------
+-- Schedule Organizer Send Work --
+----------------------------------
+
+create table SCHEDULE_ORGANIZER_SEND_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  SCHEDULE_ACTION               integer      not null, -- Enum SCHEDULE_ACTION
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer,     -- this references a possibly non-existent CALENDAR_OBJECT
+  ATTENDEE                      varchar(255) not null,
+  ITIP_MSG                      text,
+  NO_REFRESH                    boolean
+);
+
+create index SCHEDULE_ORGANIZER_SEND_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_SEND_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_ORGANIZER_SEND_WORK_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_SEND_WORK(RESOURCE_ID);
+
</ins><span class="cx"> -------------------------
</span><span class="cx"> -- Schedule Reply Work --
</span><span class="cx"> -------------------------
</span><span class="cx"> 
</span><span class="cx"> create table SCHEDULE_REPLY_WORK (
</span><del>-  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
-  JOB_ID                        integer      references JOB not null,
-  ICALENDAR_UID                 varchar(255) not null,
</del><ins>+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
</ins><span class="cx">   HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
</span><span class="cx">   RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
</span><span class="cx">   CHANGED_RIDS                  text
</span><span class="lines">@@ -1026,25 +1040,19 @@
</span><span class="cx">   SCHEDULE_REPLY_WORK(HOME_RESOURCE_ID);
</span><span class="cx"> create index SCHEDULE_REPLY_WORK_RESOURCE_ID on
</span><span class="cx">   SCHEDULE_REPLY_WORK(RESOURCE_ID);
</span><del>-create index SCHEDULE_REPLY_WORK_JOB_ID on
-  SCHEDULE_REPLY_WORK(JOB_ID);
</del><span class="cx"> 
</span><span class="cx"> --------------------------------
</span><span class="cx"> -- Schedule Reply Cancel Work --
</span><span class="cx"> --------------------------------
</span><span class="cx"> 
</span><span class="cx"> create table SCHEDULE_REPLY_CANCEL_WORK (
</span><del>-  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
-  JOB_ID                        integer      references JOB not null,
-  ICALENDAR_UID                 varchar(255) not null,
</del><ins>+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
</ins><span class="cx">   HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
</span><span class="cx">   ICALENDAR_TEXT                text         not null
</span><span class="cx"> );
</span><span class="cx"> 
</span><span class="cx"> create index SCHEDULE_REPLY_CANCEL_WORK_HOME_RESOURCE_ID on
</span><span class="cx">   SCHEDULE_REPLY_CANCEL_WORK(HOME_RESOURCE_ID);
</span><del>-create index SCHEDULE_REPLY_CANCEL_WORK_JOB_ID on
-  SCHEDULE_REPLY_CANCEL_WORK(JOB_ID);
</del><span class="cx"> 
</span><span class="cx"> ----------------------------------
</span><span class="cx"> -- Principal Purge Polling Work --
</span><span class="lines">@@ -1110,7 +1118,7 @@
</span><span class="cx">   VALUE                         varchar(255)
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-insert into CALENDARSERVER values ('VERSION', '44');
</del><ins>+insert into CALENDARSERVER values ('VERSION', '45');
</ins><span class="cx"> insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '6');
</span><span class="cx"> insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</span><span class="cx"> insert into CALENDARSERVER values ('NOTIFICATION-DATAVERSION', '1');
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaoldoracledialectv44sqlfromrev13731CalendarServertrunktxdavcommondatastoresql_schemaoldoracledialectv44sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/oracle-dialect/v44.sql (from rev 13731, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v44.sql) (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/oracle-dialect/v44.sql                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/oracle-dialect/v44.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,866 @@
</span><ins>+create sequence RESOURCE_ID_SEQ;
+create sequence JOB_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+    &quot;HOSTNAME&quot; nvarchar2(255),
+    &quot;PID&quot; integer not null,
+    &quot;PORT&quot; integer not null,
+    &quot;TIME&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null, 
+    primary key (&quot;HOSTNAME&quot;, &quot;PORT&quot;)
+);
+
+create table NAMED_LOCK (
+    &quot;LOCK_NAME&quot; nvarchar2(255) primary key
+);
+
+create table JOB (
+    &quot;JOB_ID&quot; integer primary key not null,
+    &quot;WORK_TYPE&quot; nvarchar2(255),
+    &quot;PRIORITY&quot; integer default 0,
+    &quot;WEIGHT&quot; integer default 0,
+    &quot;NOT_BEFORE&quot; timestamp not null,
+    &quot;ASSIGNED&quot; timestamp default null,
+    &quot;OVERDUE&quot; timestamp default null,
+    &quot;FAILED&quot; integer default 0
+);
+
+create table CALENDAR_HOME (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;OWNER_UID&quot; nvarchar2(255) unique,
+    &quot;STATUS&quot; integer default 0 not null,
+    &quot;DATAVERSION&quot; integer default 0 not null
+);
+
+create table HOME_STATUS (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into HOME_STATUS (DESCRIPTION, ID) values ('normal', 0);
+insert into HOME_STATUS (DESCRIPTION, ID) values ('external', 1);
+insert into HOME_STATUS (DESCRIPTION, ID) values ('purging', 2);
+create table CALENDAR (
+    &quot;RESOURCE_ID&quot; integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+    &quot;RESOURCE_ID&quot; integer primary key references CALENDAR_HOME on delete cascade,
+    &quot;QUOTA_USED_BYTES&quot; integer default 0 not null,
+    &quot;DEFAULT_EVENTS&quot; integer default null references CALENDAR on delete set null,
+    &quot;DEFAULT_TASKS&quot; integer default null references CALENDAR on delete set null,
+    &quot;DEFAULT_POLLS&quot; integer default null references CALENDAR on delete set null,
+    &quot;ALARM_VEVENT_TIMED&quot; nclob default null,
+    &quot;ALARM_VEVENT_ALLDAY&quot; nclob default null,
+    &quot;ALARM_VTODO_TIMED&quot; nclob default null,
+    &quot;ALARM_VTODO_ALLDAY&quot; nclob default null,
+    &quot;AVAILABILITY&quot; nclob default null,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+    &quot;RESOURCE_ID&quot; integer primary key references CALENDAR on delete cascade,
+    &quot;SUPPORTED_COMPONENTS&quot; nvarchar2(255) default null,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;OWNER_UID&quot; nvarchar2(255) unique,
+    &quot;STATUS&quot; integer default 0 not null,
+    &quot;DATAVERSION&quot; integer default 0 not null
+);
+
+create table NOTIFICATION (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;NOTIFICATION_HOME_RESOURCE_ID&quot; integer not null references NOTIFICATION_HOME,
+    &quot;NOTIFICATION_UID&quot; nvarchar2(255),
+    &quot;NOTIFICATION_TYPE&quot; nvarchar2(255),
+    &quot;NOTIFICATION_DATA&quot; nclob,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique (&quot;NOTIFICATION_UID&quot;, &quot;NOTIFICATION_HOME_RESOURCE_ID&quot;)
+);
+
+create table CALENDAR_BIND (
+    &quot;CALENDAR_HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer not null references CALENDAR on delete cascade,
+    &quot;EXTERNAL_ID&quot; integer default null,
+    &quot;CALENDAR_RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;BIND_MODE&quot; integer not null,
+    &quot;BIND_STATUS&quot; integer not null,
+    &quot;BIND_REVISION&quot; integer default 0 not null,
+    &quot;MESSAGE&quot; nclob,
+    &quot;TRANSP&quot; integer default 0 not null,
+    &quot;ALARM_VEVENT_TIMED&quot; nclob default null,
+    &quot;ALARM_VEVENT_ALLDAY&quot; nclob default null,
+    &quot;ALARM_VTODO_TIMED&quot; nclob default null,
+    &quot;ALARM_VTODO_ALLDAY&quot; nclob default null,
+    &quot;TIMEZONE&quot; nclob default null, 
+    primary key (&quot;CALENDAR_HOME_RESOURCE_ID&quot;, &quot;CALENDAR_RESOURCE_ID&quot;), 
+    unique (&quot;CALENDAR_HOME_RESOURCE_ID&quot;, &quot;CALENDAR_RESOURCE_NAME&quot;)
+);
+
+create table CALENDAR_BIND_MODE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('indirect', 4);
+create table CALENDAR_BIND_STATUS (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('deleted', 4);
+create table CALENDAR_TRANSP (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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 (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer not null references CALENDAR on delete cascade,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;ICALENDAR_TEXT&quot; nclob,
+    &quot;ICALENDAR_UID&quot; nvarchar2(255),
+    &quot;ICALENDAR_TYPE&quot; nvarchar2(255),
+    &quot;ATTACHMENTS_MODE&quot; integer default 0 not null,
+    &quot;DROPBOX_ID&quot; nvarchar2(255),
+    &quot;ORGANIZER&quot; nvarchar2(255),
+    &quot;RECURRANCE_MIN&quot; date,
+    &quot;RECURRANCE_MAX&quot; date,
+    &quot;ACCESS&quot; integer default 0 not null,
+    &quot;SCHEDULE_OBJECT&quot; integer default 0,
+    &quot;SCHEDULE_TAG&quot; nvarchar2(36) default null,
+    &quot;SCHEDULE_ETAGS&quot; nclob default null,
+    &quot;PRIVATE_COMMENTS&quot; integer default 0 not null,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique (&quot;CALENDAR_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;)
+);
+
+create table CALENDAR_OBJ_ATTACHMENTS_MODE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJ_ATTACHMENTS_MODE (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJ_ATTACHMENTS_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJ_ATTACHMENTS_MODE (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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 (
+    &quot;INSTANCE_ID&quot; integer primary key,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer not null references CALENDAR on delete cascade,
+    &quot;CALENDAR_OBJECT_RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;FLOATING&quot; integer not null,
+    &quot;START_DATE&quot; timestamp not null,
+    &quot;END_DATE&quot; timestamp not null,
+    &quot;FBTYPE&quot; integer not null,
+    &quot;TRANSPARENT&quot; integer not null
+);
+
+create table FREE_BUSY_TYPE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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 PERUSER (
+    &quot;TIME_RANGE_INSTANCE_ID&quot; integer not null references TIME_RANGE on delete cascade,
+    &quot;USER_ID&quot; nvarchar2(255),
+    &quot;TRANSPARENT&quot; integer not null,
+    &quot;ADJUSTED_START_DATE&quot; timestamp default null,
+    &quot;ADJUSTED_END_DATE&quot; timestamp default null
+);
+
+create table ATTACHMENT (
+    &quot;ATTACHMENT_ID&quot; integer primary key,
+    &quot;CALENDAR_HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME,
+    &quot;DROPBOX_ID&quot; nvarchar2(255),
+    &quot;CONTENT_TYPE&quot; nvarchar2(255),
+    &quot;SIZE&quot; integer not null,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;PATH&quot; nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+    &quot;ATTACHMENT_ID&quot; integer not null references ATTACHMENT on delete cascade,
+    &quot;MANAGED_ID&quot; nvarchar2(255),
+    &quot;CALENDAR_OBJECT_RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade, 
+    primary key (&quot;ATTACHMENT_ID&quot;, &quot;CALENDAR_OBJECT_RESOURCE_ID&quot;), 
+    unique (&quot;MANAGED_ID&quot;, &quot;CALENDAR_OBJECT_RESOURCE_ID&quot;)
+);
+
+create table RESOURCE_PROPERTY (
+    &quot;RESOURCE_ID&quot; integer not null,
+    &quot;NAME&quot; nvarchar2(255),
+    &quot;VALUE&quot; nclob,
+    &quot;VIEWER_UID&quot; nvarchar2(255), 
+    primary key (&quot;RESOURCE_ID&quot;, &quot;NAME&quot;, &quot;VIEWER_UID&quot;)
+);
+
+create table ADDRESSBOOK_HOME (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;ADDRESSBOOK_PROPERTY_STORE_ID&quot; integer not null,
+    &quot;OWNER_UID&quot; nvarchar2(255) unique,
+    &quot;STATUS&quot; integer default 0 not null,
+    &quot;DATAVERSION&quot; integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+    &quot;RESOURCE_ID&quot; integer primary key references ADDRESSBOOK_HOME on delete cascade,
+    &quot;QUOTA_USED_BYTES&quot; integer default 0 not null,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME,
+    &quot;OWNER_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;EXTERNAL_ID&quot; integer default null,
+    &quot;ADDRESSBOOK_RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;BIND_MODE&quot; integer not null,
+    &quot;BIND_STATUS&quot; integer not null,
+    &quot;BIND_REVISION&quot; integer default 0 not null,
+    &quot;MESSAGE&quot; nclob, 
+    primary key (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;OWNER_HOME_RESOURCE_ID&quot;), 
+    unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;ADDRESSBOOK_RESOURCE_NAME&quot;)
+);
+
+create table ADDRESSBOOK_OBJECT (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;VCARD_TEXT&quot; nclob,
+    &quot;VCARD_UID&quot; nvarchar2(255),
+    &quot;KIND&quot; integer not null,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;), 
+    unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;VCARD_UID&quot;)
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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 (
+    &quot;GROUP_ID&quot; integer not null,
+    &quot;ADDRESSBOOK_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;MEMBER_ID&quot; integer not null,
+    &quot;REVISION&quot; integer not null,
+    &quot;REMOVED&quot; integer default 0 not null,
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    primary key (&quot;GROUP_ID&quot;, &quot;MEMBER_ID&quot;, &quot;REVISION&quot;)
+);
+
+create table ABO_FOREIGN_MEMBERS (
+    &quot;GROUP_ID&quot; integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+    &quot;ADDRESSBOOK_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;MEMBER_ADDRESS&quot; nvarchar2(255), 
+    primary key (&quot;GROUP_ID&quot;, &quot;MEMBER_ADDRESS&quot;)
+);
+
+create table SHARED_GROUP_BIND (
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME,
+    &quot;GROUP_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+    &quot;EXTERNAL_ID&quot; integer default null,
+    &quot;GROUP_ADDRESSBOOK_NAME&quot; nvarchar2(255),
+    &quot;BIND_MODE&quot; integer not null,
+    &quot;BIND_STATUS&quot; integer not null,
+    &quot;BIND_REVISION&quot; integer default 0 not null,
+    &quot;MESSAGE&quot; nclob, 
+    primary key (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;GROUP_RESOURCE_ID&quot;), 
+    unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;GROUP_ADDRESSBOOK_NAME&quot;)
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+    &quot;CALENDAR_HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer references CALENDAR,
+    &quot;CALENDAR_NAME&quot; nvarchar2(255) default null,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;REVISION&quot; integer not null,
+    &quot;DELETED&quot; integer not null,
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME,
+    &quot;OWNER_HOME_RESOURCE_ID&quot; integer references ADDRESSBOOK_HOME,
+    &quot;ADDRESSBOOK_NAME&quot; nvarchar2(255) default null,
+    &quot;OBJECT_RESOURCE_ID&quot; integer default 0,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;REVISION&quot; integer not null,
+    &quot;DELETED&quot; integer not null,
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+    &quot;NOTIFICATION_HOME_RESOURCE_ID&quot; integer not null references NOTIFICATION_HOME on delete cascade,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;REVISION&quot; integer not null,
+    &quot;DELETED&quot; integer not null,
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique (&quot;NOTIFICATION_HOME_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;)
+);
+
+create table APN_SUBSCRIPTIONS (
+    &quot;TOKEN&quot; nvarchar2(255),
+    &quot;RESOURCE_KEY&quot; nvarchar2(255),
+    &quot;MODIFIED&quot; integer not null,
+    &quot;SUBSCRIBER_GUID&quot; nvarchar2(255),
+    &quot;USER_AGENT&quot; nvarchar2(255) default null,
+    &quot;IP_ADDR&quot; nvarchar2(255) default null, 
+    primary key (&quot;TOKEN&quot;, &quot;RESOURCE_KEY&quot;)
+);
+
+create table IMIP_TOKENS (
+    &quot;TOKEN&quot; nvarchar2(255),
+    &quot;ORGANIZER&quot; nvarchar2(255),
+    &quot;ATTENDEE&quot; nvarchar2(255),
+    &quot;ICALUID&quot; nvarchar2(255),
+    &quot;ACCESSED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    primary key (&quot;ORGANIZER&quot;, &quot;ATTENDEE&quot;, &quot;ICALUID&quot;)
+);
+
+create table IMIP_INVITATION_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;FROM_ADDR&quot; nvarchar2(255),
+    &quot;TO_ADDR&quot; nvarchar2(255),
+    &quot;ICALENDAR_TEXT&quot; nclob
+);
+
+create table IMIP_POLLING_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table IMIP_REPLY_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;ORGANIZER&quot; nvarchar2(255),
+    &quot;ATTENDEE&quot; nvarchar2(255),
+    &quot;ICALENDAR_TEXT&quot; nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;PUSH_ID&quot; nvarchar2(255),
+    &quot;PUSH_PRIORITY&quot; integer not null
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table GROUP_REFRESH_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;GROUP_UID&quot; nvarchar2(255)
+);
+
+create table GROUP_ATTENDEE_RECONCILE_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;RESOURCE_ID&quot; integer,
+    &quot;GROUP_ID&quot; integer
+);
+
+create table GROUPS (
+    &quot;GROUP_ID&quot; integer primary key,
+    &quot;NAME&quot; nvarchar2(255),
+    &quot;GROUP_UID&quot; nvarchar2(255),
+    &quot;MEMBERSHIP_HASH&quot; nvarchar2(255),
+    &quot;EXTANT&quot; integer default 1,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table GROUP_MEMBERSHIP (
+    &quot;GROUP_ID&quot; integer not null references GROUPS on delete cascade,
+    &quot;MEMBER_UID&quot; nvarchar2(255), 
+    primary key (&quot;GROUP_ID&quot;, &quot;MEMBER_UID&quot;)
+);
+
+create table GROUP_ATTENDEE (
+    &quot;GROUP_ID&quot; integer not null references GROUPS on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;MEMBERSHIP_HASH&quot; nvarchar2(255), 
+    primary key (&quot;GROUP_ID&quot;, &quot;RESOURCE_ID&quot;)
+);
+
+create table DELEGATES (
+    &quot;DELEGATOR&quot; nvarchar2(255),
+    &quot;DELEGATE&quot; nvarchar2(255),
+    &quot;READ_WRITE&quot; integer not null, 
+    primary key (&quot;DELEGATOR&quot;, &quot;READ_WRITE&quot;, &quot;DELEGATE&quot;)
+);
+
+create table DELEGATE_GROUPS (
+    &quot;DELEGATOR&quot; nvarchar2(255),
+    &quot;GROUP_ID&quot; integer not null references GROUPS on delete cascade,
+    &quot;READ_WRITE&quot; integer not null,
+    &quot;IS_EXTERNAL&quot; integer not null, 
+    primary key (&quot;DELEGATOR&quot;, &quot;READ_WRITE&quot;, &quot;GROUP_ID&quot;)
+);
+
+create table EXTERNAL_DELEGATE_GROUPS (
+    &quot;DELEGATOR&quot; nvarchar2(255) primary key,
+    &quot;GROUP_UID_READ&quot; nvarchar2(255),
+    &quot;GROUP_UID_WRITE&quot; nvarchar2(255)
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table FIND_MIN_VALID_REVISION_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table REVISION_CLEANUP_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table INBOX_CLEANUP_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table CLEANUP_ONE_INBOX_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;HOME_ID&quot; integer not null unique references CALENDAR_HOME on delete cascade
+);
+
+create table SCHEDULE_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;ICALENDAR_UID&quot; nvarchar2(255),
+    &quot;WORK_TYPE&quot; nvarchar2(255)
+);
+
+create table SCHEDULE_REFRESH_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;ATTENDEE_COUNT&quot; integer
+);
+
+create table SCHEDULE_REFRESH_ATTENDEES (
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;ATTENDEE&quot; nvarchar2(255), 
+    primary key (&quot;RESOURCE_ID&quot;, &quot;ATTENDEE&quot;)
+);
+
+create table SCHEDULE_AUTO_REPLY_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;PARTSTAT&quot; nvarchar2(255)
+);
+
+create table SCHEDULE_ORGANIZER_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;SCHEDULE_ACTION&quot; integer not null,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer,
+    &quot;ICALENDAR_TEXT_OLD&quot; nclob,
+    &quot;ICALENDAR_TEXT_NEW&quot; nclob,
+    &quot;ATTENDEE_COUNT&quot; integer,
+    &quot;SMART_MERGE&quot; integer
+);
+
+create table SCHEDULE_ACTION (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('create', 0);
+insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('modify', 1);
+insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('modify-cancelled', 2);
+insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('remove', 3);
+create table SCHEDULE_ORGANIZER_SEND_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;SCHEDULE_ACTION&quot; integer not null,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer,
+    &quot;ATTENDEE&quot; nvarchar2(255),
+    &quot;ITIP_MSG&quot; nclob,
+    &quot;NO_REFRESH&quot; integer
+);
+
+create table SCHEDULE_REPLY_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;CHANGED_RIDS&quot; nclob
+);
+
+create table SCHEDULE_REPLY_CANCEL_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;ICALENDAR_TEXT&quot; nclob
+);
+
+create table PRINCIPAL_PURGE_POLLING_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table PRINCIPAL_PURGE_CHECK_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;UID&quot; nvarchar2(255)
+);
+
+create table PRINCIPAL_PURGE_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;UID&quot; nvarchar2(255)
+);
+
+create table PRINCIPAL_PURGE_HOME_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade
+);
+
+create table CALENDARSERVER (
+    &quot;NAME&quot; nvarchar2(255) primary key,
+    &quot;VALUE&quot; nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '44');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '6');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+insert into CALENDARSERVER (NAME, VALUE) values ('NOTIFICATION-DATAVERSION', '1');
+insert into CALENDARSERVER (NAME, VALUE) values ('MIN-VALID-REVISION', '1');
+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 PERUSER_TIME_RANGE_IN_5468a226 on PERUSER (
+    TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+    CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_DROPBOX_ID_5073cf23 on ATTACHMENT (
+    DROPBOX_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 IMIP_INVITATION_WORK__586d064c on IMIP_INVITATION_WORK (
+    JOB_ID
+);
+
+create index IMIP_POLLING_WORK_JOB_d5535891 on IMIP_POLLING_WORK (
+    JOB_ID
+);
+
+create index IMIP_REPLY_WORK_JOB_I_bf4ae73e on IMIP_REPLY_WORK (
+    JOB_ID
+);
+
+create index PUSH_NOTIFICATION_WOR_8bbab117 on PUSH_NOTIFICATION_WORK (
+    JOB_ID
+);
+
+create index GROUP_CACHER_POLLING__6eb3151c on GROUP_CACHER_POLLING_WORK (
+    JOB_ID
+);
+
+create index GROUP_REFRESH_WORK_JO_717ede20 on GROUP_REFRESH_WORK (
+    JOB_ID
+);
+
+create index GROUP_ATTENDEE_RECONC_da73d3c2 on GROUP_ATTENDEE_RECONCILE_WORK (
+    JOB_ID
+);
+
+create index GROUPS_GROUP_UID_b35cce23 on GROUPS (
+    GROUP_UID
+);
+
+create index GROUP_MEMBERSHIP_MEMB_0ca508e8 on GROUP_MEMBERSHIP (
+    MEMBER_UID
+);
+
+create index GROUP_ATTENDEE_RESOUR_855124dc on GROUP_ATTENDEE (
+    RESOURCE_ID
+);
+
+create index DELEGATE_TO_DELEGATOR_5e149b11 on DELEGATES (
+    DELEGATE,
+    READ_WRITE,
+    DELEGATOR
+);
+
+create index DELEGATE_GROUPS_GROUP_25117446 on DELEGATE_GROUPS (
+    GROUP_ID
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+    RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_SPLIT_33603b72 on CALENDAR_OBJECT_SPLITTER_WORK (
+    JOB_ID
+);
+
+create index FIND_MIN_VALID_REVISI_78d17400 on FIND_MIN_VALID_REVISION_WORK (
+    JOB_ID
+);
+
+create index REVISION_CLEANUP_WORK_eb062686 on REVISION_CLEANUP_WORK (
+    JOB_ID
+);
+
+create index INBOX_CLEANUP_WORK_JO_799132bd on INBOX_CLEANUP_WORK (
+    JOB_ID
+);
+
+create index CLEANUP_ONE_INBOX_WOR_375dac36 on CLEANUP_ONE_INBOX_WORK (
+    JOB_ID
+);
+
+create index SCHEDULE_WORK_JOB_ID_65e810ee on SCHEDULE_WORK (
+    JOB_ID
+);
+
+create index SCHEDULE_WORK_ICALEND_089f33dc on SCHEDULE_WORK (
+    ICALENDAR_UID
+);
+
+create index SCHEDULE_REFRESH_WORK_26084c7b on SCHEDULE_REFRESH_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index SCHEDULE_REFRESH_WORK_989efe54 on SCHEDULE_REFRESH_WORK (
+    RESOURCE_ID
+);
+
+create index SCHEDULE_REFRESH_ATTE_83053b91 on SCHEDULE_REFRESH_ATTENDEES (
+    RESOURCE_ID,
+    ATTENDEE
+);
+
+create index SCHEDULE_AUTO_REPLY_W_0256478d on SCHEDULE_AUTO_REPLY_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index SCHEDULE_AUTO_REPLY_W_0755e754 on SCHEDULE_AUTO_REPLY_WORK (
+    RESOURCE_ID
+);
+
+create index SCHEDULE_ORGANIZER_WO_18ce4edd on SCHEDULE_ORGANIZER_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index SCHEDULE_ORGANIZER_WO_14702035 on SCHEDULE_ORGANIZER_WORK (
+    RESOURCE_ID
+);
+
+create index SCHEDULE_ORGANIZER_SE_9ec9f827 on SCHEDULE_ORGANIZER_SEND_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index SCHEDULE_ORGANIZER_SE_699fefc4 on SCHEDULE_ORGANIZER_SEND_WORK (
+    RESOURCE_ID
+);
+
+create index SCHEDULE_REPLY_WORK_H_745af8cf on SCHEDULE_REPLY_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index SCHEDULE_REPLY_WORK_R_11bd3fbb on SCHEDULE_REPLY_WORK (
+    RESOURCE_ID
+);
+
+create index SCHEDULE_REPLY_CANCEL_dab513ef on SCHEDULE_REPLY_CANCEL_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index PRINCIPAL_PURGE_POLLI_6383e68a on PRINCIPAL_PURGE_POLLING_WORK (
+    JOB_ID
+);
+
+create index PRINCIPAL_PURGE_CHECK_b0c024c1 on PRINCIPAL_PURGE_CHECK_WORK (
+    JOB_ID
+);
+
+create index PRINCIPAL_PURGE_WORK__7a8141a3 on PRINCIPAL_PURGE_WORK (
+    JOB_ID
+);
+
+create index PRINCIPAL_PURGE_HOME__f35eea7a on PRINCIPAL_PURGE_HOME_WORK (
+    JOB_ID
+);
+
+create index PRINCIPAL_PURGE_HOME__967e4480 on PRINCIPAL_PURGE_HOME_WORK (
+    HOME_RESOURCE_ID
+);
+
+
+-- Extras
+
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaoldoracledialectv45sql"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/oracle-dialect/v45.sql (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/oracle-dialect/v45.sql                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/oracle-dialect/v45.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,866 @@
</span><ins>+create sequence RESOURCE_ID_SEQ;
+create sequence JOB_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+    &quot;HOSTNAME&quot; nvarchar2(255),
+    &quot;PID&quot; integer not null,
+    &quot;PORT&quot; integer not null,
+    &quot;TIME&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null, 
+    primary key (&quot;HOSTNAME&quot;, &quot;PORT&quot;)
+);
+
+create table NAMED_LOCK (
+    &quot;LOCK_NAME&quot; nvarchar2(255) primary key
+);
+
+create table JOB (
+    &quot;JOB_ID&quot; integer primary key not null,
+    &quot;WORK_TYPE&quot; nvarchar2(255),
+    &quot;PRIORITY&quot; integer default 0,
+    &quot;WEIGHT&quot; integer default 0,
+    &quot;NOT_BEFORE&quot; timestamp not null,
+    &quot;ASSIGNED&quot; timestamp default null,
+    &quot;OVERDUE&quot; timestamp default null,
+    &quot;FAILED&quot; integer default 0
+);
+
+create table CALENDAR_HOME (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;OWNER_UID&quot; nvarchar2(255) unique,
+    &quot;STATUS&quot; integer default 0 not null,
+    &quot;DATAVERSION&quot; integer default 0 not null
+);
+
+create table HOME_STATUS (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into HOME_STATUS (DESCRIPTION, ID) values ('normal', 0);
+insert into HOME_STATUS (DESCRIPTION, ID) values ('external', 1);
+insert into HOME_STATUS (DESCRIPTION, ID) values ('purging', 2);
+create table CALENDAR (
+    &quot;RESOURCE_ID&quot; integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+    &quot;RESOURCE_ID&quot; integer primary key references CALENDAR_HOME on delete cascade,
+    &quot;QUOTA_USED_BYTES&quot; integer default 0 not null,
+    &quot;DEFAULT_EVENTS&quot; integer default null references CALENDAR on delete set null,
+    &quot;DEFAULT_TASKS&quot; integer default null references CALENDAR on delete set null,
+    &quot;DEFAULT_POLLS&quot; integer default null references CALENDAR on delete set null,
+    &quot;ALARM_VEVENT_TIMED&quot; nclob default null,
+    &quot;ALARM_VEVENT_ALLDAY&quot; nclob default null,
+    &quot;ALARM_VTODO_TIMED&quot; nclob default null,
+    &quot;ALARM_VTODO_ALLDAY&quot; nclob default null,
+    &quot;AVAILABILITY&quot; nclob default null,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+    &quot;RESOURCE_ID&quot; integer primary key references CALENDAR on delete cascade,
+    &quot;SUPPORTED_COMPONENTS&quot; nvarchar2(255) default null,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;OWNER_UID&quot; nvarchar2(255) unique,
+    &quot;STATUS&quot; integer default 0 not null,
+    &quot;DATAVERSION&quot; integer default 0 not null
+);
+
+create table NOTIFICATION (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;NOTIFICATION_HOME_RESOURCE_ID&quot; integer not null references NOTIFICATION_HOME,
+    &quot;NOTIFICATION_UID&quot; nvarchar2(255),
+    &quot;NOTIFICATION_TYPE&quot; nvarchar2(255),
+    &quot;NOTIFICATION_DATA&quot; nclob,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique (&quot;NOTIFICATION_UID&quot;, &quot;NOTIFICATION_HOME_RESOURCE_ID&quot;)
+);
+
+create table CALENDAR_BIND (
+    &quot;CALENDAR_HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer not null references CALENDAR on delete cascade,
+    &quot;EXTERNAL_ID&quot; integer default null,
+    &quot;CALENDAR_RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;BIND_MODE&quot; integer not null,
+    &quot;BIND_STATUS&quot; integer not null,
+    &quot;BIND_REVISION&quot; integer default 0 not null,
+    &quot;MESSAGE&quot; nclob,
+    &quot;TRANSP&quot; integer default 0 not null,
+    &quot;ALARM_VEVENT_TIMED&quot; nclob default null,
+    &quot;ALARM_VEVENT_ALLDAY&quot; nclob default null,
+    &quot;ALARM_VTODO_TIMED&quot; nclob default null,
+    &quot;ALARM_VTODO_ALLDAY&quot; nclob default null,
+    &quot;TIMEZONE&quot; nclob default null, 
+    primary key (&quot;CALENDAR_HOME_RESOURCE_ID&quot;, &quot;CALENDAR_RESOURCE_ID&quot;), 
+    unique (&quot;CALENDAR_HOME_RESOURCE_ID&quot;, &quot;CALENDAR_RESOURCE_NAME&quot;)
+);
+
+create table CALENDAR_BIND_MODE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('indirect', 4);
+create table CALENDAR_BIND_STATUS (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('deleted', 4);
+create table CALENDAR_TRANSP (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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 (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer not null references CALENDAR on delete cascade,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;ICALENDAR_TEXT&quot; nclob,
+    &quot;ICALENDAR_UID&quot; nvarchar2(255),
+    &quot;ICALENDAR_TYPE&quot; nvarchar2(255),
+    &quot;ATTACHMENTS_MODE&quot; integer default 0 not null,
+    &quot;DROPBOX_ID&quot; nvarchar2(255),
+    &quot;ORGANIZER&quot; nvarchar2(255),
+    &quot;RECURRANCE_MIN&quot; date,
+    &quot;RECURRANCE_MAX&quot; date,
+    &quot;ACCESS&quot; integer default 0 not null,
+    &quot;SCHEDULE_OBJECT&quot; integer default 0,
+    &quot;SCHEDULE_TAG&quot; nvarchar2(36) default null,
+    &quot;SCHEDULE_ETAGS&quot; nclob default null,
+    &quot;PRIVATE_COMMENTS&quot; integer default 0 not null,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;DATAVERSION&quot; integer default 0 not null, 
+    unique (&quot;CALENDAR_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;)
+);
+
+create table CALENDAR_OBJ_ATTACHMENTS_MODE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJ_ATTACHMENTS_MODE (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJ_ATTACHMENTS_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJ_ATTACHMENTS_MODE (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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 (
+    &quot;INSTANCE_ID&quot; integer primary key,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer not null references CALENDAR on delete cascade,
+    &quot;CALENDAR_OBJECT_RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;FLOATING&quot; integer not null,
+    &quot;START_DATE&quot; timestamp not null,
+    &quot;END_DATE&quot; timestamp not null,
+    &quot;FBTYPE&quot; integer not null,
+    &quot;TRANSPARENT&quot; integer not null
+);
+
+create table FREE_BUSY_TYPE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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 PERUSER (
+    &quot;TIME_RANGE_INSTANCE_ID&quot; integer not null references TIME_RANGE on delete cascade,
+    &quot;USER_ID&quot; nvarchar2(255),
+    &quot;TRANSPARENT&quot; integer not null,
+    &quot;ADJUSTED_START_DATE&quot; timestamp default null,
+    &quot;ADJUSTED_END_DATE&quot; timestamp default null
+);
+
+create table ATTACHMENT (
+    &quot;ATTACHMENT_ID&quot; integer primary key,
+    &quot;CALENDAR_HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME,
+    &quot;DROPBOX_ID&quot; nvarchar2(255),
+    &quot;CONTENT_TYPE&quot; nvarchar2(255),
+    &quot;SIZE&quot; integer not null,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;PATH&quot; nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+    &quot;ATTACHMENT_ID&quot; integer not null references ATTACHMENT on delete cascade,
+    &quot;MANAGED_ID&quot; nvarchar2(255),
+    &quot;CALENDAR_OBJECT_RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade, 
+    primary key (&quot;ATTACHMENT_ID&quot;, &quot;CALENDAR_OBJECT_RESOURCE_ID&quot;), 
+    unique (&quot;MANAGED_ID&quot;, &quot;CALENDAR_OBJECT_RESOURCE_ID&quot;)
+);
+
+create table RESOURCE_PROPERTY (
+    &quot;RESOURCE_ID&quot; integer not null,
+    &quot;NAME&quot; nvarchar2(255),
+    &quot;VALUE&quot; nclob,
+    &quot;VIEWER_UID&quot; nvarchar2(255), 
+    primary key (&quot;RESOURCE_ID&quot;, &quot;NAME&quot;, &quot;VIEWER_UID&quot;)
+);
+
+create table ADDRESSBOOK_HOME (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;ADDRESSBOOK_PROPERTY_STORE_ID&quot; integer not null,
+    &quot;OWNER_UID&quot; nvarchar2(255) unique,
+    &quot;STATUS&quot; integer default 0 not null,
+    &quot;DATAVERSION&quot; integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+    &quot;RESOURCE_ID&quot; integer primary key references ADDRESSBOOK_HOME on delete cascade,
+    &quot;QUOTA_USED_BYTES&quot; integer default 0 not null,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME,
+    &quot;OWNER_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;EXTERNAL_ID&quot; integer default null,
+    &quot;ADDRESSBOOK_RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;BIND_MODE&quot; integer not null,
+    &quot;BIND_STATUS&quot; integer not null,
+    &quot;BIND_REVISION&quot; integer default 0 not null,
+    &quot;MESSAGE&quot; nclob, 
+    primary key (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;OWNER_HOME_RESOURCE_ID&quot;), 
+    unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;ADDRESSBOOK_RESOURCE_NAME&quot;)
+);
+
+create table ADDRESSBOOK_OBJECT (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;VCARD_TEXT&quot; nclob,
+    &quot;VCARD_UID&quot; nvarchar2(255),
+    &quot;KIND&quot; integer not null,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;DATAVERSION&quot; integer default 0 not null, 
+    unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;), 
+    unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;VCARD_UID&quot;)
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; 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 (
+    &quot;GROUP_ID&quot; integer not null,
+    &quot;ADDRESSBOOK_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;MEMBER_ID&quot; integer not null,
+    &quot;REVISION&quot; integer not null,
+    &quot;REMOVED&quot; integer default 0 not null,
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    primary key (&quot;GROUP_ID&quot;, &quot;MEMBER_ID&quot;, &quot;REVISION&quot;)
+);
+
+create table ABO_FOREIGN_MEMBERS (
+    &quot;GROUP_ID&quot; integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+    &quot;ADDRESSBOOK_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;MEMBER_ADDRESS&quot; nvarchar2(255), 
+    primary key (&quot;GROUP_ID&quot;, &quot;MEMBER_ADDRESS&quot;)
+);
+
+create table SHARED_GROUP_BIND (
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME,
+    &quot;GROUP_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+    &quot;EXTERNAL_ID&quot; integer default null,
+    &quot;GROUP_ADDRESSBOOK_NAME&quot; nvarchar2(255),
+    &quot;BIND_MODE&quot; integer not null,
+    &quot;BIND_STATUS&quot; integer not null,
+    &quot;BIND_REVISION&quot; integer default 0 not null,
+    &quot;MESSAGE&quot; nclob, 
+    primary key (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;GROUP_RESOURCE_ID&quot;), 
+    unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;GROUP_ADDRESSBOOK_NAME&quot;)
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+    &quot;CALENDAR_HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer references CALENDAR,
+    &quot;CALENDAR_NAME&quot; nvarchar2(255) default null,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;REVISION&quot; integer not null,
+    &quot;DELETED&quot; integer not null,
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME,
+    &quot;OWNER_HOME_RESOURCE_ID&quot; integer references ADDRESSBOOK_HOME,
+    &quot;ADDRESSBOOK_NAME&quot; nvarchar2(255) default null,
+    &quot;OBJECT_RESOURCE_ID&quot; integer default 0,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;REVISION&quot; integer not null,
+    &quot;DELETED&quot; integer not null,
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+    &quot;NOTIFICATION_HOME_RESOURCE_ID&quot; integer not null references NOTIFICATION_HOME on delete cascade,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;REVISION&quot; integer not null,
+    &quot;DELETED&quot; integer not null,
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique (&quot;NOTIFICATION_HOME_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;)
+);
+
+create table APN_SUBSCRIPTIONS (
+    &quot;TOKEN&quot; nvarchar2(255),
+    &quot;RESOURCE_KEY&quot; nvarchar2(255),
+    &quot;MODIFIED&quot; integer not null,
+    &quot;SUBSCRIBER_GUID&quot; nvarchar2(255),
+    &quot;USER_AGENT&quot; nvarchar2(255) default null,
+    &quot;IP_ADDR&quot; nvarchar2(255) default null, 
+    primary key (&quot;TOKEN&quot;, &quot;RESOURCE_KEY&quot;)
+);
+
+create table IMIP_TOKENS (
+    &quot;TOKEN&quot; nvarchar2(255),
+    &quot;ORGANIZER&quot; nvarchar2(255),
+    &quot;ATTENDEE&quot; nvarchar2(255),
+    &quot;ICALUID&quot; nvarchar2(255),
+    &quot;ACCESSED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    primary key (&quot;ORGANIZER&quot;, &quot;ATTENDEE&quot;, &quot;ICALUID&quot;)
+);
+
+create table IMIP_INVITATION_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;FROM_ADDR&quot; nvarchar2(255),
+    &quot;TO_ADDR&quot; nvarchar2(255),
+    &quot;ICALENDAR_TEXT&quot; nclob
+);
+
+create table IMIP_POLLING_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table IMIP_REPLY_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;ORGANIZER&quot; nvarchar2(255),
+    &quot;ATTENDEE&quot; nvarchar2(255),
+    &quot;ICALENDAR_TEXT&quot; nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;PUSH_ID&quot; nvarchar2(255),
+    &quot;PUSH_PRIORITY&quot; integer not null
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table GROUP_REFRESH_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;GROUP_UID&quot; nvarchar2(255)
+);
+
+create table GROUP_ATTENDEE_RECONCILE_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;RESOURCE_ID&quot; integer,
+    &quot;GROUP_ID&quot; integer
+);
+
+create table GROUPS (
+    &quot;GROUP_ID&quot; integer primary key,
+    &quot;NAME&quot; nvarchar2(255),
+    &quot;GROUP_UID&quot; nvarchar2(255),
+    &quot;MEMBERSHIP_HASH&quot; nvarchar2(255),
+    &quot;EXTANT&quot; integer default 1,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table GROUP_MEMBERSHIP (
+    &quot;GROUP_ID&quot; integer not null references GROUPS on delete cascade,
+    &quot;MEMBER_UID&quot; nvarchar2(255), 
+    primary key (&quot;GROUP_ID&quot;, &quot;MEMBER_UID&quot;)
+);
+
+create table GROUP_ATTENDEE (
+    &quot;GROUP_ID&quot; integer not null references GROUPS on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;MEMBERSHIP_HASH&quot; nvarchar2(255), 
+    primary key (&quot;GROUP_ID&quot;, &quot;RESOURCE_ID&quot;)
+);
+
+create table DELEGATES (
+    &quot;DELEGATOR&quot; nvarchar2(255),
+    &quot;DELEGATE&quot; nvarchar2(255),
+    &quot;READ_WRITE&quot; integer not null, 
+    primary key (&quot;DELEGATOR&quot;, &quot;READ_WRITE&quot;, &quot;DELEGATE&quot;)
+);
+
+create table DELEGATE_GROUPS (
+    &quot;DELEGATOR&quot; nvarchar2(255),
+    &quot;GROUP_ID&quot; integer not null references GROUPS on delete cascade,
+    &quot;READ_WRITE&quot; integer not null,
+    &quot;IS_EXTERNAL&quot; integer not null, 
+    primary key (&quot;DELEGATOR&quot;, &quot;READ_WRITE&quot;, &quot;GROUP_ID&quot;)
+);
+
+create table EXTERNAL_DELEGATE_GROUPS (
+    &quot;DELEGATOR&quot; nvarchar2(255) primary key,
+    &quot;GROUP_UID_READ&quot; nvarchar2(255),
+    &quot;GROUP_UID_WRITE&quot; nvarchar2(255)
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table FIND_MIN_VALID_REVISION_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table REVISION_CLEANUP_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table INBOX_CLEANUP_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table CLEANUP_ONE_INBOX_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;HOME_ID&quot; integer not null unique references CALENDAR_HOME on delete cascade
+);
+
+create table SCHEDULE_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;ICALENDAR_UID&quot; nvarchar2(255),
+    &quot;WORK_TYPE&quot; nvarchar2(255)
+);
+
+create table SCHEDULE_REFRESH_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;ATTENDEE_COUNT&quot; integer
+);
+
+create table SCHEDULE_REFRESH_ATTENDEES (
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;ATTENDEE&quot; nvarchar2(255), 
+    primary key (&quot;RESOURCE_ID&quot;, &quot;ATTENDEE&quot;)
+);
+
+create table SCHEDULE_AUTO_REPLY_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;PARTSTAT&quot; nvarchar2(255)
+);
+
+create table SCHEDULE_ORGANIZER_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;SCHEDULE_ACTION&quot; integer not null,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer,
+    &quot;ICALENDAR_TEXT_OLD&quot; nclob,
+    &quot;ICALENDAR_TEXT_NEW&quot; nclob,
+    &quot;ATTENDEE_COUNT&quot; integer,
+    &quot;SMART_MERGE&quot; integer
+);
+
+create table SCHEDULE_ACTION (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('create', 0);
+insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('modify', 1);
+insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('modify-cancelled', 2);
+insert into SCHEDULE_ACTION (DESCRIPTION, ID) values ('remove', 3);
+create table SCHEDULE_ORGANIZER_SEND_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;SCHEDULE_ACTION&quot; integer not null,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer,
+    &quot;ATTENDEE&quot; nvarchar2(255),
+    &quot;ITIP_MSG&quot; nclob,
+    &quot;NO_REFRESH&quot; integer
+);
+
+create table SCHEDULE_REPLY_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;CHANGED_RIDS&quot; nclob
+);
+
+create table SCHEDULE_REPLY_CANCEL_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;ICALENDAR_TEXT&quot; nclob
+);
+
+create table PRINCIPAL_PURGE_POLLING_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB
+);
+
+create table PRINCIPAL_PURGE_CHECK_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;UID&quot; nvarchar2(255)
+);
+
+create table PRINCIPAL_PURGE_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;UID&quot; nvarchar2(255)
+);
+
+create table PRINCIPAL_PURGE_HOME_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade
+);
+
+create table CALENDARSERVER (
+    &quot;NAME&quot; nvarchar2(255) primary key,
+    &quot;VALUE&quot; nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '45');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '6');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+insert into CALENDARSERVER (NAME, VALUE) values ('NOTIFICATION-DATAVERSION', '1');
+insert into CALENDARSERVER (NAME, VALUE) values ('MIN-VALID-REVISION', '1');
+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 PERUSER_TIME_RANGE_IN_5468a226 on PERUSER (
+    TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+    CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_DROPBOX_ID_5073cf23 on ATTACHMENT (
+    DROPBOX_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 IMIP_INVITATION_WORK__586d064c on IMIP_INVITATION_WORK (
+    JOB_ID
+);
+
+create index IMIP_POLLING_WORK_JOB_d5535891 on IMIP_POLLING_WORK (
+    JOB_ID
+);
+
+create index IMIP_REPLY_WORK_JOB_I_bf4ae73e on IMIP_REPLY_WORK (
+    JOB_ID
+);
+
+create index PUSH_NOTIFICATION_WOR_8bbab117 on PUSH_NOTIFICATION_WORK (
+    JOB_ID
+);
+
+create index GROUP_CACHER_POLLING__6eb3151c on GROUP_CACHER_POLLING_WORK (
+    JOB_ID
+);
+
+create index GROUP_REFRESH_WORK_JO_717ede20 on GROUP_REFRESH_WORK (
+    JOB_ID
+);
+
+create index GROUP_ATTENDEE_RECONC_da73d3c2 on GROUP_ATTENDEE_RECONCILE_WORK (
+    JOB_ID
+);
+
+create index GROUPS_GROUP_UID_b35cce23 on GROUPS (
+    GROUP_UID
+);
+
+create index GROUP_MEMBERSHIP_MEMB_0ca508e8 on GROUP_MEMBERSHIP (
+    MEMBER_UID
+);
+
+create index GROUP_ATTENDEE_RESOUR_855124dc on GROUP_ATTENDEE (
+    RESOURCE_ID
+);
+
+create index DELEGATE_TO_DELEGATOR_5e149b11 on DELEGATES (
+    DELEGATE,
+    READ_WRITE,
+    DELEGATOR
+);
+
+create index DELEGATE_GROUPS_GROUP_25117446 on DELEGATE_GROUPS (
+    GROUP_ID
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+    RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_SPLIT_33603b72 on CALENDAR_OBJECT_SPLITTER_WORK (
+    JOB_ID
+);
+
+create index FIND_MIN_VALID_REVISI_78d17400 on FIND_MIN_VALID_REVISION_WORK (
+    JOB_ID
+);
+
+create index REVISION_CLEANUP_WORK_eb062686 on REVISION_CLEANUP_WORK (
+    JOB_ID
+);
+
+create index INBOX_CLEANUP_WORK_JO_799132bd on INBOX_CLEANUP_WORK (
+    JOB_ID
+);
+
+create index CLEANUP_ONE_INBOX_WOR_375dac36 on CLEANUP_ONE_INBOX_WORK (
+    JOB_ID
+);
+
+create index SCHEDULE_WORK_JOB_ID_65e810ee on SCHEDULE_WORK (
+    JOB_ID
+);
+
+create index SCHEDULE_WORK_ICALEND_089f33dc on SCHEDULE_WORK (
+    ICALENDAR_UID
+);
+
+create index SCHEDULE_REFRESH_WORK_26084c7b on SCHEDULE_REFRESH_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index SCHEDULE_REFRESH_WORK_989efe54 on SCHEDULE_REFRESH_WORK (
+    RESOURCE_ID
+);
+
+create index SCHEDULE_REFRESH_ATTE_83053b91 on SCHEDULE_REFRESH_ATTENDEES (
+    RESOURCE_ID,
+    ATTENDEE
+);
+
+create index SCHEDULE_AUTO_REPLY_W_0256478d on SCHEDULE_AUTO_REPLY_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index SCHEDULE_AUTO_REPLY_W_0755e754 on SCHEDULE_AUTO_REPLY_WORK (
+    RESOURCE_ID
+);
+
+create index SCHEDULE_ORGANIZER_WO_18ce4edd on SCHEDULE_ORGANIZER_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index SCHEDULE_ORGANIZER_WO_14702035 on SCHEDULE_ORGANIZER_WORK (
+    RESOURCE_ID
+);
+
+create index SCHEDULE_ORGANIZER_SE_9ec9f827 on SCHEDULE_ORGANIZER_SEND_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index SCHEDULE_ORGANIZER_SE_699fefc4 on SCHEDULE_ORGANIZER_SEND_WORK (
+    RESOURCE_ID
+);
+
+create index SCHEDULE_REPLY_WORK_H_745af8cf on SCHEDULE_REPLY_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index SCHEDULE_REPLY_WORK_R_11bd3fbb on SCHEDULE_REPLY_WORK (
+    RESOURCE_ID
+);
+
+create index SCHEDULE_REPLY_CANCEL_dab513ef on SCHEDULE_REPLY_CANCEL_WORK (
+    HOME_RESOURCE_ID
+);
+
+create index PRINCIPAL_PURGE_POLLI_6383e68a on PRINCIPAL_PURGE_POLLING_WORK (
+    JOB_ID
+);
+
+create index PRINCIPAL_PURGE_CHECK_b0c024c1 on PRINCIPAL_PURGE_CHECK_WORK (
+    JOB_ID
+);
+
+create index PRINCIPAL_PURGE_WORK__7a8141a3 on PRINCIPAL_PURGE_WORK (
+    JOB_ID
+);
+
+create index PRINCIPAL_PURGE_HOME__f35eea7a on PRINCIPAL_PURGE_HOME_WORK (
+    JOB_ID
+);
+
+create index PRINCIPAL_PURGE_HOME__967e4480 on PRINCIPAL_PURGE_HOME_WORK (
+    HOME_RESOURCE_ID
+);
+
+-- Extra schema to add to current-oracle-dialect.sql
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaoldpostgresdialectv44sqlfromrev13731CalendarServertrunktxdavcommondatastoresql_schemaoldpostgresdialectv44sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/postgres-dialect/v44.sql (from rev 13731, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v44.sql) (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/postgres-dialect/v44.sql                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/postgres-dialect/v44.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,1096 @@
</span><ins>+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2014 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- 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 &quot;AS IS&quot; 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
+);
+
+
+--------------------
+-- Jobs           --
+--------------------
+
+create sequence JOB_SEQ;
+
+create table JOB (
+  JOB_ID      integer primary key default nextval('JOB_SEQ') not null, --implicit index
+  WORK_TYPE   varchar(255) not null,
+  PRIORITY    integer default 0,
+  WEIGHT      integer default 0,
+  NOT_BEFORE  timestamp not null,
+  ASSIGNED    timestamp default null,
+  OVERDUE     timestamp default null,
+  FAILED      integer default 0
+);
+
+-------------------
+-- 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
+  STATUS           integer      default 0 not null,                             -- enum HOME_STATUS
+  DATAVERSION      integer      default 0 not null
+);
+
+-- Enumeration of statuses
+
+create table HOME_STATUS (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into HOME_STATUS values (0, 'normal' );
+insert into HOME_STATUS values (1, 'external');
+insert into HOME_STATUS values (2, 'purging');
+
+
+--------------
+-- 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
+  STATUS      integer      default 0 not null,                             -- enum HOME_STATUS
+  DATAVERSION integer      default 0 not null
+);
+
+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,
+  NOTIFICATION_TYPE             varchar(255) not null,
+  NOTIFICATION_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,
+  EXTERNAL_ID               integer      default null,
+  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');
+insert into CALENDAR_BIND_MODE values (4, 'indirect');
+
+-- 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');
+insert into CALENDAR_BIND_STATUS values (4, 'deleted');
+
+
+-- 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_OBJ_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_OBJ_ATTACHMENTS_MODE (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJ_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJ_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJ_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'  );
+
+
+-------------------
+-- Per-user data --
+-------------------
+
+create table PERUSER (
+  TIME_RANGE_INSTANCE_ID      integer      not null references TIME_RANGE on delete cascade,
+  USER_ID                     varchar(255) not null,
+  TRANSPARENT                 boolean      not null,
+  ADJUSTED_START_DATE         timestamp    default null,
+  ADJUSTED_END_DATE           timestamp    default null
+);
+
+create index PERUSER_TIME_RANGE_INSTANCE_ID on
+  PERUSER(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);
+
+create index ATTACHMENT_DROPBOX_ID on
+  ATTACHMENT(DROPBOX_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
+  STATUS                        integer         default 0 not null,                             -- enum HOME_STATUS
+  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,
+  EXTERNAL_ID                           integer         default null,
+  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');
+
+
+----------------------------------
+-- Revisions, forward reference --
+----------------------------------
+
+create sequence REVISION_SEQ;
+
+---------------------------------
+-- 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
+  REVISION        integer     default nextval('REVISION_SEQ') not null,
+  REMOVED         boolean     default false not null,
+  MODIFIED        timestamp   default timezone('UTC', CURRENT_TIMESTAMP),
+
+    primary key (GROUP_ID, MEMBER_ID, REVISION) -- 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,
+  EXTERNAL_ID                       integer      default null,
+  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,
+  MODIFIED                  timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+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,
+  OBJECT_RESOURCE_ID            integer      default 0,
+  RESOURCE_NAME                 varchar(255),
+  REVISION                      integer      default nextval('REVISION_SEQ') not null,
+  DELETED                       boolean      not null,
+  MODIFIED                      timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+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,
+  MODIFIED                      timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+
+  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
+  JOB_ID                        integer      references JOB not null,
+  FROM_ADDR                     varchar(255) not null,
+  TO_ADDR                       varchar(255) not null,
+  ICALENDAR_TEXT                text         not null
+);
+
+create index IMIP_INVITATION_WORK_JOB_ID on
+  IMIP_INVITATION_WORK(JOB_ID);
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index IMIP_POLLING_WORK_JOB_ID on
+  IMIP_POLLING_WORK(JOB_ID);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  ORGANIZER                     varchar(255) not null,
+  ATTENDEE                      varchar(255) not null,
+  ICALENDAR_TEXT                text         not null
+);
+
+create index IMIP_REPLY_WORK_JOB_ID on
+  IMIP_REPLY_WORK(JOB_ID);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  PUSH_ID                       varchar(255) not null,
+  PUSH_PRIORITY                 integer      not null -- 1:low 5:medium 10:high
+);
+
+create index PUSH_NOTIFICATION_WORK_JOB_ID on
+  PUSH_NOTIFICATION_WORK(JOB_ID);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index GROUP_CACHER_POLLING_WORK_JOB_ID on
+  GROUP_CACHER_POLLING_WORK(JOB_ID);
+
+create table GROUP_REFRESH_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  GROUP_UID                     varchar(255) not null
+);
+
+create index GROUP_REFRESH_WORK_JOB_ID on
+  GROUP_REFRESH_WORK(JOB_ID);
+
+create table GROUP_ATTENDEE_RECONCILE_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  RESOURCE_ID                   integer,
+  GROUP_ID                      integer
+);
+
+create index GROUP_ATTENDEE_RECONCILE_WORK_JOB_ID on
+  GROUP_ATTENDEE_RECONCILE_WORK(JOB_ID);
+
+
+create table GROUPS (
+  GROUP_ID                      integer      primary key default nextval('RESOURCE_ID_SEQ'),    -- implicit index
+  NAME                          varchar(255) not null,
+  GROUP_UID                     varchar(255) not null,
+  MEMBERSHIP_HASH               varchar(255) not null,
+  EXTANT                        integer default 1,
+  CREATED                       timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                      timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+create index GROUPS_GROUP_UID on
+  GROUPS(GROUP_UID);
+
+create table GROUP_MEMBERSHIP (
+  GROUP_ID                     integer not null references GROUPS on delete cascade,
+  MEMBER_UID                   varchar(255) not null,
+  
+  primary key (GROUP_ID, MEMBER_UID)
+);
+
+create index GROUP_MEMBERSHIP_MEMBER on
+  GROUP_MEMBERSHIP(MEMBER_UID);
+
+create table GROUP_ATTENDEE (
+  GROUP_ID                      integer not null references GROUPS on delete cascade,
+  RESOURCE_ID                   integer not null references CALENDAR_OBJECT on delete cascade,
+  MEMBERSHIP_HASH               varchar(255) not null,
+  
+  primary key (GROUP_ID, RESOURCE_ID)
+);
+create index GROUP_ATTENDEE_RESOURCE_ID on
+  GROUP_ATTENDEE(RESOURCE_ID);
+
+---------------
+-- Delegates --
+---------------
+
+create table DELEGATES (
+  DELEGATOR                     varchar(255) not null,
+  DELEGATE                      varchar(255) not null,
+  READ_WRITE                    integer      not null, -- 1 = ReadWrite, 0 = ReadOnly
+
+  primary key (DELEGATOR, READ_WRITE, DELEGATE)
+);
+create index DELEGATE_TO_DELEGATOR on
+  DELEGATES(DELEGATE, READ_WRITE, DELEGATOR);
+
+create table DELEGATE_GROUPS (
+  DELEGATOR                     varchar(255) not null,
+  GROUP_ID                      integer      not null references GROUPS on delete cascade,
+  READ_WRITE                    integer      not null, -- 1 = ReadWrite, 0 = ReadOnly
+  IS_EXTERNAL                   integer      not null, -- 1 = ReadWrite, 0 = ReadOnly
+
+  primary key (DELEGATOR, READ_WRITE, GROUP_ID)
+);
+create index DELEGATE_GROUPS_GROUP_ID on
+  DELEGATE_GROUPS(GROUP_ID);
+
+create table EXTERNAL_DELEGATE_GROUPS (
+  DELEGATOR                     varchar(255) primary key not null,
+  GROUP_UID_READ                varchar(255),
+  GROUP_UID_WRITE               varchar(255)
+);
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  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);
+create index CALENDAR_OBJECT_SPLITTER_WORK_JOB_ID on
+  CALENDAR_OBJECT_SPLITTER_WORK(JOB_ID);
+
+---------------------------
+-- Revision Cleanup Work --
+---------------------------
+
+create table FIND_MIN_VALID_REVISION_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index FIND_MIN_VALID_REVISION_WORK_JOB_ID on
+  FIND_MIN_VALID_REVISION_WORK(JOB_ID);
+
+create table REVISION_CLEANUP_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index REVISION_CLEANUP_WORK_JOB_ID on
+  REVISION_CLEANUP_WORK(JOB_ID);
+
+------------------------
+-- Inbox Cleanup Work --
+------------------------
+
+create table INBOX_CLEANUP_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index INBOX_CLEANUP_WORK_JOB_ID on
+   INBOX_CLEANUP_WORK(JOB_ID);
+
+create table CLEANUP_ONE_INBOX_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  HOME_ID                       integer      not null unique references CALENDAR_HOME on delete cascade
+);
+
+create index CLEANUP_ONE_INBOX_WORK_JOB_ID on
+  CLEANUP_ONE_INBOX_WORK(JOB_ID);
+
+-------------------
+-- Schedule Work --
+-------------------
+
+create table SCHEDULE_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  ICALENDAR_UID                 varchar(255) not null,
+  WORK_TYPE                     varchar(255) not null
+);
+
+create index SCHEDULE_WORK_JOB_ID on
+  SCHEDULE_WORK(JOB_ID);
+create index SCHEDULE_WORK_ICALENDAR_UID on
+  SCHEDULE_WORK(ICALENDAR_UID);
+
+---------------------------
+-- Schedule Refresh Work --
+---------------------------
+
+create table SCHEDULE_REFRESH_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  ATTENDEE_COUNT                integer
+);
+
+create index SCHEDULE_REFRESH_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_REFRESH_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_REFRESH_WORK_RESOURCE_ID on
+  SCHEDULE_REFRESH_WORK(RESOURCE_ID);
+
+create table SCHEDULE_REFRESH_ATTENDEES (
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  ATTENDEE                      varchar(255) not null,
+  
+  primary key (RESOURCE_ID, ATTENDEE)
+);
+
+create index SCHEDULE_REFRESH_ATTENDEES_RESOURCE_ID_ATTENDEE on
+  SCHEDULE_REFRESH_ATTENDEES(RESOURCE_ID, ATTENDEE);
+
+------------------------------
+-- Schedule Auto Reply Work --
+------------------------------
+
+create table SCHEDULE_AUTO_REPLY_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  PARTSTAT                      varchar(255) not null
+);
+
+create index SCHEDULE_AUTO_REPLY_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_AUTO_REPLY_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_AUTO_REPLY_WORK_RESOURCE_ID on
+  SCHEDULE_AUTO_REPLY_WORK(RESOURCE_ID);
+
+-----------------------------
+-- Schedule Organizer Work --
+-----------------------------
+
+create table SCHEDULE_ORGANIZER_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  SCHEDULE_ACTION               integer      not null, -- Enum SCHEDULE_ACTION
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer,     -- this references a possibly non-existent CALENDR_OBJECT
+  ICALENDAR_TEXT_OLD            text,
+  ICALENDAR_TEXT_NEW            text,
+  ATTENDEE_COUNT                integer,
+  SMART_MERGE                   boolean
+);
+
+create index SCHEDULE_ORGANIZER_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_ORGANIZER_WORK_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_WORK(RESOURCE_ID);
+
+-- Enumeration of schedule actions
+
+create table SCHEDULE_ACTION (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into SCHEDULE_ACTION values (0, 'create');
+insert into SCHEDULE_ACTION values (1, 'modify');
+insert into SCHEDULE_ACTION values (2, 'modify-cancelled');
+insert into SCHEDULE_ACTION values (3, 'remove');
+
+----------------------------------
+-- Schedule Organizer Send Work --
+----------------------------------
+
+create table SCHEDULE_ORGANIZER_SEND_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  SCHEDULE_ACTION               integer      not null, -- Enum SCHEDULE_ACTION
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer,     -- this references a possibly non-existent CALENDAR_OBJECT
+  ATTENDEE                      varchar(255) not null,
+  ITIP_MSG                      text,
+  NO_REFRESH                    boolean
+);
+
+create index SCHEDULE_ORGANIZER_SEND_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_SEND_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_ORGANIZER_SEND_WORK_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_SEND_WORK(RESOURCE_ID);
+
+-------------------------
+-- Schedule Reply Work --
+-------------------------
+
+create table SCHEDULE_REPLY_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  CHANGED_RIDS                  text
+);
+
+create index SCHEDULE_REPLY_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_REPLY_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_REPLY_WORK_RESOURCE_ID on
+  SCHEDULE_REPLY_WORK(RESOURCE_ID);
+
+--------------------------------
+-- Schedule Reply Cancel Work --
+--------------------------------
+
+create table SCHEDULE_REPLY_CANCEL_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  ICALENDAR_TEXT                text         not null
+);
+
+create index SCHEDULE_REPLY_CANCEL_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_REPLY_CANCEL_WORK(HOME_RESOURCE_ID);
+
+----------------------------------
+-- Principal Purge Polling Work --
+----------------------------------
+
+create table PRINCIPAL_PURGE_POLLING_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index PRINCIPAL_PURGE_POLLING_WORK_JOB_ID on
+  PRINCIPAL_PURGE_POLLING_WORK(JOB_ID);
+
+--------------------------------
+-- Principal Purge Check Work --
+--------------------------------
+
+create table PRINCIPAL_PURGE_CHECK_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  UID                           varchar(255) not null
+);
+
+create index PRINCIPAL_PURGE_CHECK_WORK_JOB_ID on
+  PRINCIPAL_PURGE_CHECK_WORK(JOB_ID);
+
+--------------------------
+-- Principal Purge Work --
+--------------------------
+
+create table PRINCIPAL_PURGE_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  UID                           varchar(255) not null
+);
+
+create index PRINCIPAL_PURGE_WORK_JOB_ID on
+  PRINCIPAL_PURGE_WORK(JOB_ID);
+
+
+--------------------------------
+-- Principal Home Remove Work --
+--------------------------------
+
+create table PRINCIPAL_PURGE_HOME_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade
+);
+
+create index PRINCIPAL_PURGE_HOME_WORK_JOB_ID on
+  PRINCIPAL_PURGE_HOME_WORK(JOB_ID);
+create index PRINCIPAL_PURGE_HOME_HOME_RESOURCE_ID on
+  PRINCIPAL_PURGE_HOME_WORK(HOME_RESOURCE_ID);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+  NAME                          varchar(255) primary key, -- implicit index
+  VALUE                         varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '44');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '6');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
+insert into CALENDARSERVER values ('NOTIFICATION-DATAVERSION', '1');
+insert into CALENDARSERVER values ('MIN-VALID-REVISION', '1');
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaoldpostgresdialectv45sql"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/postgres-dialect/v45.sql (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/postgres-dialect/v45.sql                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/old/postgres-dialect/v45.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,1098 @@
</span><ins>+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2014 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- 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 &quot;AS IS&quot; 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
+);
+
+
+--------------------
+-- Jobs           --
+--------------------
+
+create sequence JOB_SEQ;
+
+create table JOB (
+  JOB_ID      integer primary key default nextval('JOB_SEQ') not null, --implicit index
+  WORK_TYPE   varchar(255) not null,
+  PRIORITY    integer default 0,
+  WEIGHT      integer default 0,
+  NOT_BEFORE  timestamp not null,
+  ASSIGNED    timestamp default null,
+  OVERDUE     timestamp default null,
+  FAILED      integer default 0
+);
+
+-------------------
+-- 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
+  STATUS           integer      default 0 not null,                             -- enum HOME_STATUS
+  DATAVERSION      integer      default 0 not null
+);
+
+-- Enumeration of statuses
+
+create table HOME_STATUS (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into HOME_STATUS values (0, 'normal' );
+insert into HOME_STATUS values (1, 'external');
+insert into HOME_STATUS values (2, 'purging');
+
+
+--------------
+-- 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
+  STATUS      integer      default 0 not null,                             -- enum HOME_STATUS
+  DATAVERSION integer      default 0 not null
+);
+
+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,
+  NOTIFICATION_TYPE             varchar(255) not null,
+  NOTIFICATION_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,
+  EXTERNAL_ID               integer      default null,
+  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');
+insert into CALENDAR_BIND_MODE values (4, 'indirect');
+
+-- 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');
+insert into CALENDAR_BIND_STATUS values (4, 'deleted');
+
+
+-- 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_OBJ_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),
+  DATAVERSION          integer      default 0 not null,
+
+  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_OBJ_ATTACHMENTS_MODE (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJ_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJ_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJ_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'  );
+
+
+-------------------
+-- Per-user data --
+-------------------
+
+create table PERUSER (
+  TIME_RANGE_INSTANCE_ID      integer      not null references TIME_RANGE on delete cascade,
+  USER_ID                     varchar(255) not null,
+  TRANSPARENT                 boolean      not null,
+  ADJUSTED_START_DATE         timestamp    default null,
+  ADJUSTED_END_DATE           timestamp    default null
+);
+
+create index PERUSER_TIME_RANGE_INSTANCE_ID on
+  PERUSER(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);
+
+create index ATTACHMENT_DROPBOX_ID on
+  ATTACHMENT(DROPBOX_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
+  STATUS                        integer         default 0 not null,                             -- enum HOME_STATUS
+  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,
+  EXTERNAL_ID                           integer         default null,
+  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),
+  DATAVERSION                   integer         default 0 not null,
+
+  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');
+
+
+----------------------------------
+-- Revisions, forward reference --
+----------------------------------
+
+create sequence REVISION_SEQ;
+
+---------------------------------
+-- 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
+  REVISION        integer     default nextval('REVISION_SEQ') not null,
+  REMOVED         boolean     default false not null,
+  MODIFIED        timestamp   default timezone('UTC', CURRENT_TIMESTAMP),
+
+    primary key (GROUP_ID, MEMBER_ID, REVISION) -- 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,
+  EXTERNAL_ID                       integer      default null,
+  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,
+  MODIFIED                  timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+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,
+  OBJECT_RESOURCE_ID            integer      default 0,
+  RESOURCE_NAME                 varchar(255),
+  REVISION                      integer      default nextval('REVISION_SEQ') not null,
+  DELETED                       boolean      not null,
+  MODIFIED                      timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+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,
+  MODIFIED                      timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+
+  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
+  JOB_ID                        integer      references JOB not null,
+  FROM_ADDR                     varchar(255) not null,
+  TO_ADDR                       varchar(255) not null,
+  ICALENDAR_TEXT                text         not null
+);
+
+create index IMIP_INVITATION_WORK_JOB_ID on
+  IMIP_INVITATION_WORK(JOB_ID);
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index IMIP_POLLING_WORK_JOB_ID on
+  IMIP_POLLING_WORK(JOB_ID);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  ORGANIZER                     varchar(255) not null,
+  ATTENDEE                      varchar(255) not null,
+  ICALENDAR_TEXT                text         not null
+);
+
+create index IMIP_REPLY_WORK_JOB_ID on
+  IMIP_REPLY_WORK(JOB_ID);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  PUSH_ID                       varchar(255) not null,
+  PUSH_PRIORITY                 integer      not null -- 1:low 5:medium 10:high
+);
+
+create index PUSH_NOTIFICATION_WORK_JOB_ID on
+  PUSH_NOTIFICATION_WORK(JOB_ID);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index GROUP_CACHER_POLLING_WORK_JOB_ID on
+  GROUP_CACHER_POLLING_WORK(JOB_ID);
+
+create table GROUP_REFRESH_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  GROUP_UID                     varchar(255) not null
+);
+
+create index GROUP_REFRESH_WORK_JOB_ID on
+  GROUP_REFRESH_WORK(JOB_ID);
+
+create table GROUP_ATTENDEE_RECONCILE_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  RESOURCE_ID                   integer,
+  GROUP_ID                      integer
+);
+
+create index GROUP_ATTENDEE_RECONCILE_WORK_JOB_ID on
+  GROUP_ATTENDEE_RECONCILE_WORK(JOB_ID);
+
+
+create table GROUPS (
+  GROUP_ID                      integer      primary key default nextval('RESOURCE_ID_SEQ'),    -- implicit index
+  NAME                          varchar(255) not null,
+  GROUP_UID                     varchar(255) not null,
+  MEMBERSHIP_HASH               varchar(255) not null,
+  EXTANT                        integer default 1,
+  CREATED                       timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                      timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+create index GROUPS_GROUP_UID on
+  GROUPS(GROUP_UID);
+
+create table GROUP_MEMBERSHIP (
+  GROUP_ID                     integer not null references GROUPS on delete cascade,
+  MEMBER_UID                   varchar(255) not null,
+  
+  primary key (GROUP_ID, MEMBER_UID)
+);
+
+create index GROUP_MEMBERSHIP_MEMBER on
+  GROUP_MEMBERSHIP(MEMBER_UID);
+
+create table GROUP_ATTENDEE (
+  GROUP_ID                      integer not null references GROUPS on delete cascade,
+  RESOURCE_ID                   integer not null references CALENDAR_OBJECT on delete cascade,
+  MEMBERSHIP_HASH               varchar(255) not null,
+  
+  primary key (GROUP_ID, RESOURCE_ID)
+);
+create index GROUP_ATTENDEE_RESOURCE_ID on
+  GROUP_ATTENDEE(RESOURCE_ID);
+
+---------------
+-- Delegates --
+---------------
+
+create table DELEGATES (
+  DELEGATOR                     varchar(255) not null,
+  DELEGATE                      varchar(255) not null,
+  READ_WRITE                    integer      not null, -- 1 = ReadWrite, 0 = ReadOnly
+
+  primary key (DELEGATOR, READ_WRITE, DELEGATE)
+);
+create index DELEGATE_TO_DELEGATOR on
+  DELEGATES(DELEGATE, READ_WRITE, DELEGATOR);
+
+create table DELEGATE_GROUPS (
+  DELEGATOR                     varchar(255) not null,
+  GROUP_ID                      integer      not null references GROUPS on delete cascade,
+  READ_WRITE                    integer      not null, -- 1 = ReadWrite, 0 = ReadOnly
+  IS_EXTERNAL                   integer      not null, -- 1 = ReadWrite, 0 = ReadOnly
+
+  primary key (DELEGATOR, READ_WRITE, GROUP_ID)
+);
+create index DELEGATE_GROUPS_GROUP_ID on
+  DELEGATE_GROUPS(GROUP_ID);
+
+create table EXTERNAL_DELEGATE_GROUPS (
+  DELEGATOR                     varchar(255) primary key not null,
+  GROUP_UID_READ                varchar(255),
+  GROUP_UID_WRITE               varchar(255)
+);
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  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);
+create index CALENDAR_OBJECT_SPLITTER_WORK_JOB_ID on
+  CALENDAR_OBJECT_SPLITTER_WORK(JOB_ID);
+
+---------------------------
+-- Revision Cleanup Work --
+---------------------------
+
+create table FIND_MIN_VALID_REVISION_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index FIND_MIN_VALID_REVISION_WORK_JOB_ID on
+  FIND_MIN_VALID_REVISION_WORK(JOB_ID);
+
+create table REVISION_CLEANUP_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index REVISION_CLEANUP_WORK_JOB_ID on
+  REVISION_CLEANUP_WORK(JOB_ID);
+
+------------------------
+-- Inbox Cleanup Work --
+------------------------
+
+create table INBOX_CLEANUP_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index INBOX_CLEANUP_WORK_JOB_ID on
+   INBOX_CLEANUP_WORK(JOB_ID);
+
+create table CLEANUP_ONE_INBOX_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  HOME_ID                       integer      not null unique references CALENDAR_HOME on delete cascade
+);
+
+create index CLEANUP_ONE_INBOX_WORK_JOB_ID on
+  CLEANUP_ONE_INBOX_WORK(JOB_ID);
+
+-------------------
+-- Schedule Work --
+-------------------
+
+create table SCHEDULE_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  ICALENDAR_UID                 varchar(255) not null,
+  WORK_TYPE                     varchar(255) not null
+);
+
+create index SCHEDULE_WORK_JOB_ID on
+  SCHEDULE_WORK(JOB_ID);
+create index SCHEDULE_WORK_ICALENDAR_UID on
+  SCHEDULE_WORK(ICALENDAR_UID);
+
+---------------------------
+-- Schedule Refresh Work --
+---------------------------
+
+create table SCHEDULE_REFRESH_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  ATTENDEE_COUNT                integer
+);
+
+create index SCHEDULE_REFRESH_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_REFRESH_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_REFRESH_WORK_RESOURCE_ID on
+  SCHEDULE_REFRESH_WORK(RESOURCE_ID);
+
+create table SCHEDULE_REFRESH_ATTENDEES (
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  ATTENDEE                      varchar(255) not null,
+  
+  primary key (RESOURCE_ID, ATTENDEE)
+);
+
+create index SCHEDULE_REFRESH_ATTENDEES_RESOURCE_ID_ATTENDEE on
+  SCHEDULE_REFRESH_ATTENDEES(RESOURCE_ID, ATTENDEE);
+
+------------------------------
+-- Schedule Auto Reply Work --
+------------------------------
+
+create table SCHEDULE_AUTO_REPLY_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  PARTSTAT                      varchar(255) not null
+);
+
+create index SCHEDULE_AUTO_REPLY_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_AUTO_REPLY_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_AUTO_REPLY_WORK_RESOURCE_ID on
+  SCHEDULE_AUTO_REPLY_WORK(RESOURCE_ID);
+
+-----------------------------
+-- Schedule Organizer Work --
+-----------------------------
+
+create table SCHEDULE_ORGANIZER_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  SCHEDULE_ACTION               integer      not null, -- Enum SCHEDULE_ACTION
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer,     -- this references a possibly non-existent CALENDR_OBJECT
+  ICALENDAR_TEXT_OLD            text,
+  ICALENDAR_TEXT_NEW            text,
+  ATTENDEE_COUNT                integer,
+  SMART_MERGE                   boolean
+);
+
+create index SCHEDULE_ORGANIZER_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_ORGANIZER_WORK_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_WORK(RESOURCE_ID);
+
+-- Enumeration of schedule actions
+
+create table SCHEDULE_ACTION (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into SCHEDULE_ACTION values (0, 'create');
+insert into SCHEDULE_ACTION values (1, 'modify');
+insert into SCHEDULE_ACTION values (2, 'modify-cancelled');
+insert into SCHEDULE_ACTION values (3, 'remove');
+
+----------------------------------
+-- Schedule Organizer Send Work --
+----------------------------------
+
+create table SCHEDULE_ORGANIZER_SEND_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  SCHEDULE_ACTION               integer      not null, -- Enum SCHEDULE_ACTION
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer,     -- this references a possibly non-existent CALENDAR_OBJECT
+  ATTENDEE                      varchar(255) not null,
+  ITIP_MSG                      text,
+  NO_REFRESH                    boolean
+);
+
+create index SCHEDULE_ORGANIZER_SEND_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_SEND_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_ORGANIZER_SEND_WORK_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_SEND_WORK(RESOURCE_ID);
+
+-------------------------
+-- Schedule Reply Work --
+-------------------------
+
+create table SCHEDULE_REPLY_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  CHANGED_RIDS                  text
+);
+
+create index SCHEDULE_REPLY_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_REPLY_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_REPLY_WORK_RESOURCE_ID on
+  SCHEDULE_REPLY_WORK(RESOURCE_ID);
+
+--------------------------------
+-- Schedule Reply Cancel Work --
+--------------------------------
+
+create table SCHEDULE_REPLY_CANCEL_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  ICALENDAR_TEXT                text         not null
+);
+
+create index SCHEDULE_REPLY_CANCEL_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_REPLY_CANCEL_WORK(HOME_RESOURCE_ID);
+
+----------------------------------
+-- Principal Purge Polling Work --
+----------------------------------
+
+create table PRINCIPAL_PURGE_POLLING_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null
+);
+
+create index PRINCIPAL_PURGE_POLLING_WORK_JOB_ID on
+  PRINCIPAL_PURGE_POLLING_WORK(JOB_ID);
+
+--------------------------------
+-- Principal Purge Check Work --
+--------------------------------
+
+create table PRINCIPAL_PURGE_CHECK_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  UID                           varchar(255) not null
+);
+
+create index PRINCIPAL_PURGE_CHECK_WORK_JOB_ID on
+  PRINCIPAL_PURGE_CHECK_WORK(JOB_ID);
+
+--------------------------
+-- Principal Purge Work --
+--------------------------
+
+create table PRINCIPAL_PURGE_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  UID                           varchar(255) not null
+);
+
+create index PRINCIPAL_PURGE_WORK_JOB_ID on
+  PRINCIPAL_PURGE_WORK(JOB_ID);
+
+
+--------------------------------
+-- Principal Home Remove Work --
+--------------------------------
+
+create table PRINCIPAL_PURGE_HOME_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade
+);
+
+create index PRINCIPAL_PURGE_HOME_WORK_JOB_ID on
+  PRINCIPAL_PURGE_HOME_WORK(JOB_ID);
+create index PRINCIPAL_PURGE_HOME_HOME_RESOURCE_ID on
+  PRINCIPAL_PURGE_HOME_WORK(HOME_RESOURCE_ID);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+  NAME                          varchar(255) primary key, -- implicit index
+  VALUE                         varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '45');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '6');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
+insert into CALENDARSERVER values ('NOTIFICATION-DATAVERSION', '1');
+insert into CALENDARSERVER values ('MIN-VALID-REVISION', '1');
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_43_to_44sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_43_to_44.sql (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_43_to_44.sql        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_43_to_44.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -18,33 +18,153 @@
</span><span class="cx"> -- Upgrade database schema from VERSION 43 to 44 --
</span><span class="cx"> ---------------------------------------------------
</span><span class="cx"> 
</span><del>-insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('group', 5);
</del><ins>+-----------------
+-- Job Changes --
+-----------------
</ins><span class="cx"> 
</span><del>-create table GROUP_SHAREE_RECONCILE_WORK (
-  WORK_ID                       integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
-  JOB_ID                        integer not null references JOB,
-  CALENDAR_ID                   integer        not null references CALENDAR on delete cascade,
-  GROUP_ID                      integer not null references GROUPS on delete cascade
</del><ins>+drop function next_job;
+
+-- The scheduling work schema has changed a lot - to avoid a complex migration process this
+-- script just drops all the existing tables and adds back the new set
+
+-------------------
+-- Schedule Work --
+-------------------
+
+create table SCHEDULE_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;JOB_ID&quot; integer not null references JOB,
+    &quot;ICALENDAR_UID&quot; nvarchar2(255),
+    &quot;WORK_TYPE&quot; nvarchar2(255)
</ins><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index GROUP_SHAREE_RECONCILE_WORK_JOB_ID on GROUP_SHAREE_RECONCILE_WORK(
-        JOB_ID
</del><ins>+create index SCHEDULE_WORK_JOB_ID_65e810ee on SCHEDULE_WORK (
+    JOB_ID
</ins><span class="cx"> );
</span><ins>+create index SCHEDULE_WORK_ICALEND_089f33dc on SCHEDULE_WORK (
+    ICALENDAR_UID
+);
</ins><span class="cx"> 
</span><ins>+---------------------------
+-- Schedule Refresh Work --
+---------------------------
</ins><span class="cx"> 
</span><del>-create table GROUP_SHAREE (
-  GROUP_ID                      integer not null references GROUPS on delete cascade,
-  CALENDAR_HOME_ID                                 integer not null references CALENDAR_HOME on delete cascade,
-  CALENDAR_ID                                      integer not null references CALENDAR on delete cascade,
-  GROUP_BIND_MODE               integer not null, -- enum CALENDAR_BIND_MODE
-  MEMBERSHIP_HASH               varchar(255) not null,
-  
-  primary key (GROUP_ID, CALENDAR_HOME_ID, CALENDAR_ID) -- implicit index
</del><ins>+drop table SCHEDULE_REFRESH_WORK;
+
+create table SCHEDULE_REFRESH_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;ATTENDEE_COUNT&quot; integer
</ins><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index GROUP_SHAREE_CALENDAR_ID on GROUP_SHAREE(
-        CALENDAR_ID
</del><ins>+create index SCHEDULE_REFRESH_WORK_26084c7b on SCHEDULE_REFRESH_WORK (
+    HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><ins>+create index SCHEDULE_REFRESH_WORK_989efe54 on SCHEDULE_REFRESH_WORK (
+    RESOURCE_ID
+);
</ins><span class="cx"> 
</span><ins>+------------------------------
+-- Schedule Auto Reply Work --
+------------------------------
+
+drop table SCHEDULE_AUTO_REPLY_WORK;
+
+create table SCHEDULE_AUTO_REPLY_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;PARTSTAT&quot; nvarchar2(255)
+);
+
+create index SCHEDULE_AUTO_REPLY_W_0256478d on SCHEDULE_AUTO_REPLY_WORK (
+    HOME_RESOURCE_ID
+);
+create index SCHEDULE_AUTO_REPLY_W_0755e754 on SCHEDULE_AUTO_REPLY_WORK (
+    RESOURCE_ID
+);
+
+-----------------------------
+-- Schedule Organizer Work --
+-----------------------------
+
+drop table SCHEDULE_ORGANIZER_WORK;
+
+create table SCHEDULE_ORGANIZER_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;SCHEDULE_ACTION&quot; integer not null,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer,
+    &quot;ICALENDAR_TEXT_OLD&quot; nclob,
+    &quot;ICALENDAR_TEXT_NEW&quot; nclob,
+    &quot;ATTENDEE_COUNT&quot; integer,
+    &quot;SMART_MERGE&quot; integer
+);
+
+create index SCHEDULE_ORGANIZER_WO_18ce4edd on SCHEDULE_ORGANIZER_WORK (
+    HOME_RESOURCE_ID
+);
+create index SCHEDULE_ORGANIZER_WO_14702035 on SCHEDULE_ORGANIZER_WORK (
+    RESOURCE_ID
+);
+
+----------------------------------
+-- Schedule Organizer Send Work --
+----------------------------------
+
+create table SCHEDULE_ORGANIZER_SEND_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;SCHEDULE_ACTION&quot; integer not null,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer,
+    &quot;ATTENDEE&quot; nvarchar2(255),
+    &quot;ITIP_MSG&quot; nclob,
+    &quot;NO_REFRESH&quot; integer
+);
+
+create index SCHEDULE_ORGANIZER_SE_9ec9f827 on SCHEDULE_ORGANIZER_SEND_WORK (
+    HOME_RESOURCE_ID
+);
+create index SCHEDULE_ORGANIZER_SE_699fefc4 on SCHEDULE_ORGANIZER_SEND_WORK (
+    RESOURCE_ID
+);
+
+-------------------------
+-- Schedule Reply Work --
+-------------------------
+
+drop table SCHEDULE_REPLY_WORK;
+
+create table SCHEDULE_REPLY_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;CHANGED_RIDS&quot; nclob
+);
+
+create index SCHEDULE_REPLY_WORK_H_745af8cf on SCHEDULE_REPLY_WORK (
+    HOME_RESOURCE_ID
+);
+create index SCHEDULE_REPLY_WORK_R_11bd3fbb on SCHEDULE_REPLY_WORK (
+    RESOURCE_ID
+);
+
+--------------------------------
+-- Schedule Reply Cancel Work --
+--------------------------------
+
+drop table SCHEDULE_REPLY_CANCEL_WORK;
+
+create table SCHEDULE_REPLY_CANCEL_WORK (
+    &quot;WORK_ID&quot; integer primary key references SCHEDULE_WORK on delete cascade,
+    &quot;HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME on delete cascade,
+    &quot;ICALENDAR_TEXT&quot; nclob
+);
+
+create index SCHEDULE_REPLY_CANCEL_dab513ef on SCHEDULE_REPLY_CANCEL_WORK (
+    HOME_RESOURCE_ID
+);
+
</ins><span class="cx"> -- update the version
</span><span class="cx"> update CALENDARSERVER set VALUE = '44' where NAME = 'VERSION';
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_44_to_45sqlfromrev13731CalendarServertrunktxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_44_to_45sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_44_to_45.sql (from rev 13731, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_44_to_45.sql) (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_44_to_45.sql                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_44_to_45.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+----
+-- Copyright (c) 2012-2014 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- 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 &quot;AS IS&quot; 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 44 to 45 --
+---------------------------------------------------
+
+-------------------
+-- Data Versions --
+-------------------
+
+alter table CALENDAR_OBJECT
+  add (&quot;DATAVERSION&quot; integer default 0 not null);
+
+alter table ADDRESSBOOK_OBJECT
+  add (&quot;DATAVERSION&quot; integer default 0 not null);
+
+-- update the version
+update CALENDARSERVER set VALUE = '45' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_45_to_46sqlfromrev13684CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_43_to_44sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_45_to_46.sql (from rev 13684, CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_43_to_44.sql) (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_45_to_46.sql                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_45_to_46.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,50 @@
</span><ins>+----
+-- Copyright (c) 2012-2014 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- 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 &quot;AS IS&quot; 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 45 to 46 --
+---------------------------------------------------
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('group', 5);
+
+create table GROUP_SHAREE_RECONCILE_WORK (
+  WORK_ID                       integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer not null references JOB,
+  CALENDAR_ID                   integer        not null references CALENDAR on delete cascade,
+  GROUP_ID                      integer not null references GROUPS on delete cascade
+);
+
+create index GROUP_SHAREE_RECONCILE_WORK_JOB_ID on GROUP_SHAREE_RECONCILE_WORK(
+        JOB_ID
+);
+
+
+create table GROUP_SHAREE (
+  GROUP_ID                      integer not null references GROUPS on delete cascade,
+  CALENDAR_HOME_ID                                 integer not null references CALENDAR_HOME on delete cascade,
+  CALENDAR_ID                                      integer not null references CALENDAR on delete cascade,
+  GROUP_BIND_MODE               integer not null, -- enum CALENDAR_BIND_MODE
+  MEMBERSHIP_HASH               varchar(255) not null,
+  
+  primary key (GROUP_ID, CALENDAR_HOME_ID, CALENDAR_ID) -- implicit index
+);
+
+create index GROUP_SHAREE_CALENDAR_ID on GROUP_SHAREE(
+        CALENDAR_ID
+);
+
+-- update the version
+update CALENDARSERVER set VALUE = '44' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_43_to_44sql"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_43_to_44.sql (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_43_to_44.sql        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_43_to_44.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -1,48 +0,0 @@
</span><del>-----
--- Copyright (c) 2012-2014 Apple Inc. All rights reserved.
---
--- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
--- 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 &quot;AS IS&quot; 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 43 to 44 --
----------------------------------------------------
-
-insert into CALENDAR_BIND_MODE values (5, 'group');
-
-create table GROUP_SHAREE_RECONCILE_WORK (
-  WORK_ID                       integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
-  JOB_ID                        integer not null references JOB,
-  CALENDAR_ID                   integer        not null references CALENDAR on delete cascade,
-  GROUP_ID                      integer not null references GROUPS on delete cascade
-);
-
-create index GROUP_SHAREE_RECONCILE_WORK_JOB_ID on
-  GROUP_SHAREE_RECONCILE_WORK(JOB_ID);
-
-
-create table GROUP_SHAREE (
-  GROUP_ID                      integer not null references GROUPS on delete cascade,
-  CALENDAR_HOME_ID                                 integer not null references CALENDAR_HOME on delete cascade,
-  CALENDAR_ID                                      integer not null references CALENDAR on delete cascade,
-  GROUP_BIND_MODE               integer not null, -- enum CALENDAR_BIND_MODE
-  MEMBERSHIP_HASH               varchar(255) not null,
-  
-  primary key (GROUP_ID, CALENDAR_HOME_ID, CALENDAR_ID) -- implicit index
-);
-
-create index GROUP_SHAREE_CALENDAR_ID on
-  GROUP_SHAREE(CALENDAR_ID);
-
--- update the version
-update CALENDARSERVER set VALUE = '44' where NAME = 'VERSION';
</del></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_43_to_44sqlfromrev13731CalendarServertrunktxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_43_to_44sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_43_to_44.sql (from rev 13731, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_43_to_44.sql) (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_43_to_44.sql                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_43_to_44.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,157 @@
</span><ins>+----
+-- Copyright (c) 2012-2014 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- 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 &quot;AS IS&quot; 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 43 to 44 --
+---------------------------------------------------
+
+-----------------
+-- Job Changes --
+-----------------
+
+drop function next_job();
+
+-- The scheduling work schema has changed a lot - to avoid a complex migration process this
+-- script just drops all the existing tables and adds back the new set
+
+-------------------
+-- Schedule Work --
+-------------------
+
+create table SCHEDULE_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer      references JOB not null,
+  ICALENDAR_UID                 varchar(255) not null,
+  WORK_TYPE                     varchar(255) not null
+);
+
+create index SCHEDULE_WORK_JOB_ID on
+  SCHEDULE_WORK(JOB_ID);
+create index SCHEDULE_WORK_ICALENDAR_UID on
+  SCHEDULE_WORK(ICALENDAR_UID);
+
+---------------------------
+-- Schedule Refresh Work --
+---------------------------
+
+drop table SCHEDULE_REFRESH_WORK;
+
+create table SCHEDULE_REFRESH_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  ATTENDEE_COUNT                integer
+);
+
+create index SCHEDULE_REFRESH_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_REFRESH_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_REFRESH_WORK_RESOURCE_ID on
+  SCHEDULE_REFRESH_WORK(RESOURCE_ID);
+
+------------------------------
+-- Schedule Auto Reply Work --
+------------------------------
+
+drop table SCHEDULE_AUTO_REPLY_WORK;
+
+create table SCHEDULE_AUTO_REPLY_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  PARTSTAT                      varchar(255) not null
+);
+
+create index SCHEDULE_AUTO_REPLY_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_AUTO_REPLY_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_AUTO_REPLY_WORK_RESOURCE_ID on
+  SCHEDULE_AUTO_REPLY_WORK(RESOURCE_ID);
+
+-----------------------------
+-- Schedule Organizer Work --
+-----------------------------
+
+drop table SCHEDULE_ORGANIZER_WORK;
+
+create table SCHEDULE_ORGANIZER_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  SCHEDULE_ACTION               integer      not null, -- Enum SCHEDULE_ACTION
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer,     -- this references a possibly non-existent CALENDR_OBJECT
+  ICALENDAR_TEXT_OLD            text,
+  ICALENDAR_TEXT_NEW            text,
+  ATTENDEE_COUNT                integer,
+  SMART_MERGE                   boolean
+);
+
+create index SCHEDULE_ORGANIZER_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_ORGANIZER_WORK_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_WORK(RESOURCE_ID);
+
+----------------------------------
+-- Schedule Organizer Send Work --
+----------------------------------
+
+create table SCHEDULE_ORGANIZER_SEND_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  SCHEDULE_ACTION               integer      not null, -- Enum SCHEDULE_ACTION
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer,     -- this references a possibly non-existent CALENDAR_OBJECT
+  ATTENDEE                      varchar(255) not null,
+  ITIP_MSG                      text,
+  NO_REFRESH                    boolean
+);
+
+create index SCHEDULE_ORGANIZER_SEND_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_SEND_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_ORGANIZER_SEND_WORK_RESOURCE_ID on
+  SCHEDULE_ORGANIZER_SEND_WORK(RESOURCE_ID);
+
+-------------------------
+-- Schedule Reply Work --
+-------------------------
+
+drop table SCHEDULE_REPLY_WORK;
+
+create table SCHEDULE_REPLY_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade,
+  CHANGED_RIDS                  text
+);
+
+create index SCHEDULE_REPLY_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_REPLY_WORK(HOME_RESOURCE_ID);
+create index SCHEDULE_REPLY_WORK_RESOURCE_ID on
+  SCHEDULE_REPLY_WORK(RESOURCE_ID);
+
+--------------------------------
+-- Schedule Reply Cancel Work --
+--------------------------------
+
+drop table SCHEDULE_REPLY_CANCEL_WORK;
+
+create table SCHEDULE_REPLY_CANCEL_WORK (
+  WORK_ID                       integer      primary key references SCHEDULE_WORK on delete cascade, -- implicit index
+  HOME_RESOURCE_ID              integer      not null references CALENDAR_HOME on delete cascade,
+  ICALENDAR_TEXT                text         not null
+);
+
+create index SCHEDULE_REPLY_CANCEL_WORK_HOME_RESOURCE_ID on
+  SCHEDULE_REPLY_CANCEL_WORK(HOME_RESOURCE_ID);
+
+-- update the version
+update CALENDARSERVER set VALUE = '44' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_44_to_45sqlfromrev13731CalendarServertrunktxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_44_to_45sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_44_to_45.sql (from rev 13731, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_44_to_45.sql) (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_44_to_45.sql                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_44_to_45.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+----
+-- Copyright (c) 2012-2014 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- 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 &quot;AS IS&quot; 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 44 to 45 --
+---------------------------------------------------
+
+-------------------
+-- Data Versions --
+-------------------
+
+alter table CALENDAR_OBJECT
+  add column DATAVERSION integer default 0 not null;
+
+alter table ADDRESSBOOK_OBJECT
+  add column DATAVERSION integer default 0 not null;
+
+-- update the version
+update CALENDARSERVER set VALUE = '45' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_45_to_46sqlfromrev13684CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_43_to_44sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_45_to_46.sql (from rev 13684, CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_43_to_44.sql) (0 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_45_to_46.sql                                (rev 0)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_45_to_46.sql        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -0,0 +1,48 @@
</span><ins>+----
+-- Copyright (c) 2012-2014 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- 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 &quot;AS IS&quot; 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 45 to 46 --
+---------------------------------------------------
+
+insert into CALENDAR_BIND_MODE values (5, 'group');
+
+create table GROUP_SHAREE_RECONCILE_WORK (
+  WORK_ID                       integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  JOB_ID                        integer not null references JOB,
+  CALENDAR_ID                   integer        not null references CALENDAR on delete cascade,
+  GROUP_ID                      integer not null references GROUPS on delete cascade
+);
+
+create index GROUP_SHAREE_RECONCILE_WORK_JOB_ID on
+  GROUP_SHAREE_RECONCILE_WORK(JOB_ID);
+
+
+create table GROUP_SHAREE (
+  GROUP_ID                      integer not null references GROUPS on delete cascade,
+  CALENDAR_HOME_ID                                 integer not null references CALENDAR_HOME on delete cascade,
+  CALENDAR_ID                                      integer not null references CALENDAR on delete cascade,
+  GROUP_BIND_MODE               integer not null, -- enum CALENDAR_BIND_MODE
+  MEMBERSHIP_HASH               varchar(255) not null,
+  
+  primary key (GROUP_ID, CALENDAR_HOME_ID, CALENDAR_ID) -- implicit index
+);
+
+create index GROUP_SHAREE_CALENDAR_ID on
+  GROUP_SHAREE(CALENDAR_ID);
+
+-- update the version
+update CALENDARSERVER set VALUE = '46' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavcommondatastoresql_tablespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_tables.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_tables.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/common/datastore/sql_tables.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -31,6 +31,7 @@
</span><span class="cx"> from sqlparse import parse
</span><span class="cx"> from re import compile
</span><span class="cx"> import hashlib
</span><ins>+import itertools
</ins><span class="cx"> 
</span><span class="cx"> def _schemaFiles(version=None):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -70,8 +71,7 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     if extras.exists():
</span><del>-        out.write(&quot;\n-- Extras\n\n&quot;)
-        out.write(extras.getContent())
</del><ins>+        out.write(&quot;\n&quot;.join(itertools.dropwhile(lambda x: not x.startswith(&quot;-- Extra schema to add&quot;), extras.getContent().splitlines())) + &quot;\n&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavdpsclientpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/dps/client.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/dps/client.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/dps/client.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -15,6 +15,7 @@
</span><span class="cx"> ##
</span><span class="cx"> 
</span><span class="cx"> import cPickle as pickle
</span><ins>+import time
</ins><span class="cx"> import uuid
</span><span class="cx"> 
</span><span class="cx"> from twext.python.log import Logger
</span><span class="lines">@@ -25,7 +26,7 @@
</span><span class="cx"> import twext.who.idirectory
</span><span class="cx"> from twext.who.util import ConstantsContainer
</span><span class="cx"> from twisted.internet import reactor
</span><del>-from twisted.internet.defer import inlineCallbacks, returnValue
</del><ins>+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
</ins><span class="cx"> from twisted.internet.protocol import ClientCreator
</span><span class="cx"> from twisted.protocols import amp
</span><span class="cx"> from twisted.python.constants import Names, NamedConstant
</span><span class="lines">@@ -41,6 +42,7 @@
</span><span class="cx">     VerifyPlaintextPasswordCommand, VerifyHTTPDigestCommand,
</span><span class="cx">     WikiAccessForUIDCommand, ContinuationCommand
</span><span class="cx"> )
</span><ins>+from txdav.who.delegates import RecordType as DelegatesRecordType
</ins><span class="cx"> from txdav.who.directory import (
</span><span class="cx">     CalendarDirectoryRecordMixin, CalendarDirectoryServiceMixin
</span><span class="cx"> )
</span><span class="lines">@@ -59,13 +61,9 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> ## MOVE2WHO TODOs:
</span><del>-## SACLs
</del><span class="cx"> ## LDAP
</span><del>-## Tests from old twistedcaldav/directory
-## Cmd line tools
</del><span class="cx"> ## Store based directory service (records in the store, i.e.
</span><span class="cx"> ##    locations/resources)
</span><del>-## Separate store for DPS (augments and delegates separate from calendar data)
</del><span class="cx"> ## Store autoAcceptGroups in the group db?
</span><span class="cx"> 
</span><span class="cx"> @implementer(IDirectoryService, IStoreDirectoryService)
</span><span class="lines">@@ -189,6 +187,20 @@
</span><span class="cx">         returnValue(results)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _logResultTiming(self, command, startTime, results):
+        duration = time.time() - startTime
+        numResults = 0
+        if &quot;fields&quot; in results:
+            numResults = 1
+        if &quot;fieldsList&quot; in results:
+            numResults = len(results[&quot;fieldsList&quot;])
+        log.debug(
+            &quot;DPS call {command} duration={duration:.2f}s, results={numResults}&quot;,
+            command=command, duration=duration, numResults=numResults
+        )
+
+
+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _call(self, command, postProcess, **kwds):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -204,9 +216,11 @@
</span><span class="cx">             L{Deferred} which fires with the post-processed results
</span><span class="cx">         @type postProcess: callable
</span><span class="cx">         &quot;&quot;&quot;
</span><ins>+        startTime = time.time()
</ins><span class="cx">         results = yield self._sendCommand(command, **kwds)
</span><span class="cx">         if results.get(&quot;continuation&quot;, None) is None:
</span><span class="cx">             # We have all the results
</span><ins>+            self._logResultTiming(command, startTime, results)
</ins><span class="cx">             returnValue(postProcess(results))
</span><span class="cx"> 
</span><span class="cx">         # There are more results to fetch, so loop until the continuation
</span><span class="lines">@@ -225,6 +239,7 @@
</span><span class="cx">         for result in multi:
</span><span class="cx">             results[&quot;fieldsList&quot;].extend(result[&quot;fieldsList&quot;])
</span><span class="cx"> 
</span><ins>+        self._logResultTiming(command, startTime, results)
</ins><span class="cx">         returnValue(postProcess(results))
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -367,11 +382,20 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def members(self):
</span><del>-        return self.service._call(
-            MembersCommand,
-            self.service._processMultipleRecords,
-            uid=self.uid.encode(&quot;utf-8&quot;)
-        )
</del><ins>+        if self.recordType in (
+            RecordType.group,
+            DelegatesRecordType.readDelegateGroup,
+            DelegatesRecordType.writeDelegateGroup,
+            DelegatesRecordType.readDelegatorGroup,
+            DelegatesRecordType.writeDelegatorGroup,
+        ):
+            return self.service._call(
+                MembersCommand,
+                self.service._processMultipleRecords,
+                uid=self.uid.encode(&quot;utf-8&quot;)
+            )
+        else:
+            return succeed([])
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def groups(self):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavdpsserverpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/dps/server.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/dps/server.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/dps/server.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -100,7 +100,7 @@
</span><span class="cx">         @return: an iterable of records, or None if the token does not exist
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if token in self._continuations:
</span><del>-            timestamp, records = self._continuations[token]
</del><ins>+            _ignore_timestamp, records = self._continuations[token]
</ins><span class="cx">             del self._continuations[token]
</span><span class="cx">         else:
</span><span class="cx">             records = None
</span><span class="lines">@@ -119,7 +119,7 @@
</span><span class="cx">         log.debug(&quot;Continuation: {c}&quot;, c=continuation)
</span><span class="cx">         records = self._retrieveContinuation(continuation)
</span><span class="cx">         response = self._recordsToResponse(records)
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         return response
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -194,7 +194,7 @@
</span><span class="cx">         response = {
</span><span class="cx">             &quot;fields&quot;: pickle.dumps(fields),
</span><span class="cx">         }
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -212,7 +212,7 @@
</span><span class="cx">         response = {
</span><span class="cx">             &quot;fields&quot;: pickle.dumps(fields),
</span><span class="cx">         }
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -226,7 +226,7 @@
</span><span class="cx">         response = {
</span><span class="cx">             &quot;fields&quot;: pickle.dumps(fields),
</span><span class="cx">         }
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -239,7 +239,7 @@
</span><span class="cx">             self._directory.recordType.lookupByName(recordType))
</span><span class="cx">         )
</span><span class="cx">         response = self._recordsToResponse(records)
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -250,7 +250,7 @@
</span><span class="cx">         log.debug(&quot;RecordsWithEmailAddress: {e}&quot;, e=emailAddress)
</span><span class="cx">         records = (yield self._directory.recordsWithEmailAddress(emailAddress))
</span><span class="cx">         response = self._recordsToResponse(records)
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -265,7 +265,7 @@
</span><span class="cx">             tokens, context=context
</span><span class="cx">         )
</span><span class="cx">         response = self._recordsToResponse(records)
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -306,7 +306,7 @@
</span><span class="cx">             newFields, operand=operand, recordType=recordType
</span><span class="cx">         )
</span><span class="cx">         response = self._recordsToResponse(records)
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -327,7 +327,7 @@
</span><span class="cx">                 records.append(member)
</span><span class="cx"> 
</span><span class="cx">         response = self._recordsToResponse(records)
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -357,7 +357,7 @@
</span><span class="cx">         response = {
</span><span class="cx">             &quot;success&quot;: success,
</span><span class="cx">         }
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -376,7 +376,7 @@
</span><span class="cx">         for group in (yield record.groups()):
</span><span class="cx">             records.append(group)
</span><span class="cx">         response = self._recordsToResponse(records)
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -392,7 +392,7 @@
</span><span class="cx">         response = {
</span><span class="cx">             &quot;authenticated&quot;: authenticated,
</span><span class="cx">         }
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -426,7 +426,7 @@
</span><span class="cx">         response = {
</span><span class="cx">             &quot;authenticated&quot;: authenticated,
</span><span class="cx">         }
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -444,7 +444,7 @@
</span><span class="cx">         response = {
</span><span class="cx">             &quot;access&quot;: access.name.encode(&quot;utf-8&quot;),
</span><span class="cx">         }
</span><del>-        log.debug(&quot;Responding with: {response}&quot;, response=response)
</del><ins>+        # log.debug(&quot;Responding with: {response}&quot;, response=response)
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavwhoaugmentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/augment.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/augment.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/augment.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -320,9 +320,10 @@
</span><span class="cx">         return (baseFields, augmentFields)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def removeRecords(self, uids):
</span><del>-        self._augmentDB.removeAugmentRecords(uids)
-        return self._directory.removeRecords(uids)
</del><ins>+        yield self._augmentDB.removeAugmentRecords(uids)
+        yield self._directory.removeRecords(uids)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def _assignToField(self, fields, name, value):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavwhodelegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/delegates.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/delegates.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/delegates.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -236,7 +236,10 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     if delegate.recordType == BaseRecordType.group:
</span><span class="cx">         # find the groupID
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modified = yield txn.groupByUID(
</del><ins>+        (
+            groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+            _ignore_extant
+        ) = yield txn.groupByUID(
</ins><span class="cx">             delegate.uid
</span><span class="cx">         )
</span><span class="cx">         yield txn.addDelegateGroup(delegator.uid, groupID, readWrite)
</span><span class="lines">@@ -260,7 +263,10 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     if delegate.recordType == BaseRecordType.group:
</span><span class="cx">         # find the groupID
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modified = yield txn.groupByUID(
</del><ins>+        (
+            groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+            _ignore_extant
+        ) = yield txn.groupByUID(
</ins><span class="cx">             delegate.uid
</span><span class="cx">         )
</span><span class="cx">         yield txn.removeDelegateGroup(delegator.uid, groupID, readWrite)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavwhodirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/directory.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/directory.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/directory.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -175,8 +175,6 @@
</span><span class="cx">         else:
</span><span class="cx">             expression = CompoundExpression(outer, Operand.AND)
</span><span class="cx"> 
</span><del>-        results = []
-
</del><span class="cx">         if context is not None:
</span><span class="cx">             recordTypes = self.recordTypesForSearchContext(context)
</span><span class="cx">         else:
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavwhogroupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/groups.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/groups.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/groups.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -157,12 +157,14 @@
</span><span class="cx">             if (component.masterComponent() is None or not component.isRecurring()):
</span><span class="cx"> 
</span><span class="cx">                 # skip non-recurring old events, no instances
</span><del>-                if (yield calendarObject.removeOldEventGroupLink(
-                    component,
-                    instances=None,
-                    inserting=False,
-                    txn=self.transaction
-                )):
</del><ins>+                if (
+                    yield calendarObject.removeOldEventGroupLink(
+                        component,
+                        instances=None,
+                        inserting=False,
+                        txn=self.transaction
+                    )
+                ):
</ins><span class="cx">                     returnValue(None)
</span><span class="cx">             else:
</span><span class="cx">                 # skip recurring old events
</span><span class="lines">@@ -180,12 +182,14 @@
</span><span class="cx">                     lowerLimit=truncateLowerLimit,
</span><span class="cx">                     ignoreInvalidInstances=True
</span><span class="cx">                 )
</span><del>-                if (yield calendarObject.removeOldEventGroupLink(
-                    component,
-                    instances=instances,
-                    inserting=False,
-                    txn=self.transaction
-                )):
</del><ins>+                if (
+                    yield calendarObject.removeOldEventGroupLink(
+                        component,
+                        instances=instances,
+                        inserting=False,
+                        txn=self.transaction
+                    )
+                ):
</ins><span class="cx">                     returnValue(None)
</span><span class="cx"> 
</span><span class="cx">                 # split spanning events and only update present-future split result
</span><span class="lines">@@ -345,11 +349,17 @@
</span><span class="cx">             ) in changed:
</span><span class="cx">                 readDelegateGroupID = writeDelegateGroupID = None
</span><span class="cx">                 if readDelegateUID:
</span><del>-                    readDelegateGroupID, _ignore_name, _ignore_hash, _ignore_modified = (
</del><ins>+                    (
+                        readDelegateGroupID, _ignore_name, _ignore_hash,
+                        _ignore_modified, _ignore_extant
+                    ) = (
</ins><span class="cx">                         yield txn.groupByUID(readDelegateUID)
</span><span class="cx">                     )
</span><span class="cx">                 if writeDelegateUID:
</span><del>-                    writeDelegateGroupID, _ignore_name, _ignore_hash, _ignore_modified = (
</del><ins>+                    (
+                        writeDelegateGroupID, _ignore_name, _ignore_hash,
+                        _ignore_modified, _ignore_extant
+                    ) = (
</ins><span class="cx">                         yield txn.groupByUID(writeDelegateUID)
</span><span class="cx">                     )
</span><span class="cx">                 yield txn.assignExternalDelegates(
</span><span class="lines">@@ -371,90 +381,25 @@
</span><span class="cx">             and updates the GROUP_MEMBERSHIP table
</span><span class="cx">             WorkProposal is returned for tests
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        self.log.debug(&quot;Faulting in group: {g}&quot;, g=groupUID)
-        record = (yield self.directory.recordWithUID(groupUID))
-        if record is None:
-            # the group has disappeared from the directory
-            self.log.info(&quot;Group is missing: {g}&quot;, g=groupUID)
</del><ins>+        groupID, membershipChanged = yield txn.refreshGroup(groupUID)
+
+        if membershipChanged:
+            wps = yield self.scheduleGroupAttendeeReconciliations(txn, groupID)
</ins><span class="cx">         else:
</span><del>-            self.log.debug(&quot;Got group record: {u}&quot;, u=record.uid)
</del><ins>+            wps = ()
</ins><span class="cx"> 
</span><del>-        groupID, cachedName, cachedMembershipHash, _ignore_modified = (
-            yield txn.groupByUID(
-                groupUID,
-                create=(record is not None)
-            )
-        )
-        wps = tuple()
-        if groupID:
-            if record is not None:
-                members = yield record.expandedMembers()
-                name = record.fullNames[0]
-            else:
-                members = frozenset()
-                name = cachedName
-
-            membershipHashContent = hashlib.md5()
-            members = list(members)
-            members.sort(key=lambda x: x.uid)
-            for member in members:
-                membershipHashContent.update(str(member.uid))
-            membershipHash = membershipHashContent.hexdigest()
-
-            if cachedMembershipHash != membershipHash:
-                membershipChanged = True
-                self.log.debug(
-                    &quot;Group '{group}' changed&quot;, group=name
-                )
-            else:
-                membershipChanged = False
-
-            if membershipChanged or record is not None:
-                # also updates group mod date
-                yield txn.updateGroup(groupUID, name, membershipHash)
-
-            if membershipChanged:
-                newMemberUIDs = set()
-                for member in members:
-                    newMemberUIDs.add(member.uid)
-                yield self.synchronizeMembers(txn, groupID, newMemberUIDs)
-
-                wps = yield self.scheduleGroupAttendeeReconciliations(txn, groupID)
-                wps = wps + (yield self.scheduleGroupShareeReconciliations(txn, groupID))
-
</del><span class="cx">         returnValue(wps)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    @inlineCallbacks
</del><span class="cx">     def synchronizeMembers(self, txn, groupID, newMemberUIDs):
</span><del>-        numRemoved = numAdded = 0
-        cachedMemberUIDs = (yield txn.membersOfGroup(groupID))
</del><ins>+        return txn.synchronizeMembers(groupID, newMemberUIDs)
</ins><span class="cx"> 
</span><del>-        for memberUID in cachedMemberUIDs:
-            if memberUID not in newMemberUIDs:
-                numRemoved += 1
-                yield txn.removeMemberFromGroup(memberUID, groupID)
</del><span class="cx"> 
</span><del>-        for memberUID in newMemberUIDs:
-            if memberUID not in cachedMemberUIDs:
-                numAdded += 1
-                yield txn.addMemberToGroup(memberUID, groupID)
-
-        returnValue((numAdded, numRemoved))
-
-
-    @inlineCallbacks
</del><span class="cx">     def cachedMembers(self, txn, groupID):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         The members of the given group as recorded in the db
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        members = set()
-        memberUIDs = (yield txn.membersOfGroup(groupID))
-        for uid in memberUIDs:
-            record = (yield self.directory.recordWithUID(uid))
-            if record is not None:
-                members.add(record)
-        returnValue(members)
</del><ins>+        return txn.groupMembers(groupID)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def cachedGroupsFor(self, txn, uid):
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavwhoopendirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/opendirectory.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/opendirectory.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/opendirectory.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> 
</span><span class="cx"> from twext.who.opendirectory import DirectoryService
</span><span class="cx"> 
</span><ins>+DirectoryService    # Something has to use the import
</ins><span class="cx"> 
</span><span class="cx"> # Hoorj OMG haxx
</span><span class="cx"> from twext.who.opendirectory._constants import ODRecordType as _ODRecordType
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavwhotesttest_delegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_delegates.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_delegates.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_delegates.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -207,7 +207,10 @@
</span><span class="cx">                 yield self.directory.recordWithShortName(RecordType.user, name)
</span><span class="cx">             )
</span><span class="cx">             newSet.add(record.uid)
</span><del>-        groupID, name, _ignore_membershipHash, _ignore_modified = (yield txn.groupByUID(group1.uid))
</del><ins>+        (
+            groupID, name, _ignore_membershipHash, _ignore_modified,
+            _ignore_extant
+        ) = (yield txn.groupByUID(group1.uid))
</ins><span class="cx">         _ignore_numAdded, _ignore_numRemoved = (
</span><span class="cx">             yield self.groupCacher.synchronizeMembers(txn, groupID, newSet)
</span><span class="cx">         )
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavwhotesttest_group_attendeespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_group_attendees.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_group_attendees.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_group_attendees.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -841,8 +841,7 @@
</span><span class="cx"> 
</span><span class="cx">         wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), &quot;20000000-0000-0000-0000-000000000001&quot;)
</span><span class="cx">         yield self.commit()
</span><del>-        self.assertEqual(len(wps), 1)
-        yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
</del><ins>+        self.assertEqual(len(wps), 0)
</ins><span class="cx"> 
</span><span class="cx">         cobj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;10000000-0000-0000-0000-000000000002&quot;)
</span><span class="cx">         vcalendar = yield cobj.component()
</span><span class="lines">@@ -868,7 +867,10 @@
</span><span class="cx">         #finally, simulate an event that has become old
</span><span class="cx">         self.patch(CalendarDirectoryRecordMixin, &quot;expandedMembers&quot;, unpatchedExpandedMembers)
</span><span class="cx"> 
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate = yield self.transactionUnderTest().groupByUID(&quot;20000000-0000-0000-0000-000000000001&quot;)
</del><ins>+        (
+            groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate,
+            _ignore_extant
+        ) = yield self.transactionUnderTest().groupByUID(&quot;20000000-0000-0000-0000-000000000001&quot;)
</ins><span class="cx">         ga = schema.GROUP_ATTENDEE
</span><span class="cx">         yield Insert({
</span><span class="cx">                 ga.RESOURCE_ID: cobj._resourceID,
</span><span class="lines">@@ -1001,8 +1003,7 @@
</span><span class="cx"> 
</span><span class="cx">         wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), &quot;20000000-0000-0000-0000-000000000001&quot;)
</span><span class="cx">         yield self.commit()
</span><del>-        self.assertEqual(len(wps), 1)
-        yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
</del><ins>+        self.assertEqual(len(wps), 0)
</ins><span class="cx"> 
</span><span class="cx">         cobj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;10000000-0000-0000-0000-000000000002&quot;)
</span><span class="cx">         vcalendar = yield cobj.component()
</span><span class="lines">@@ -1029,7 +1030,10 @@
</span><span class="cx">         #finally, simulate an event that has become old
</span><span class="cx">         self.patch(CalendarDirectoryRecordMixin, &quot;expandedMembers&quot;, unpatchedExpandedMembers)
</span><span class="cx"> 
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate = yield self.transactionUnderTest().groupByUID(&quot;20000000-0000-0000-0000-000000000001&quot;)
</del><ins>+        (
+            groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate,
+            _ignore_extant
+        ) = yield self.transactionUnderTest().groupByUID(&quot;20000000-0000-0000-0000-000000000001&quot;)
</ins><span class="cx">         ga = schema.GROUP_ATTENDEE
</span><span class="cx">         yield Insert({
</span><span class="cx">                 ga.RESOURCE_ID: cobj._resourceID,
</span><span class="lines">@@ -1478,8 +1482,7 @@
</span><span class="cx"> 
</span><span class="cx">         wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), &quot;20000000-0000-0000-0000-000000000002&quot;)
</span><span class="cx">         yield self.commit()
</span><del>-        self.assertEqual(len(wps), 1)
-        yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
</del><ins>+        self.assertEqual(len(wps), 0)
</ins><span class="cx"> 
</span><span class="cx">         cobj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;10000000-0000-0000-0000-000000000001&quot;)
</span><span class="cx">         vcalendar = yield cobj.component()
</span><span class="lines">@@ -1637,12 +1640,10 @@
</span><span class="cx"> 
</span><span class="cx">         wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), &quot;20000000-0000-0000-0000-000000000002&quot;)
</span><span class="cx">         yield self.commit()
</span><del>-        self.assertEqual(len(wps), 1)
-        yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
</del><ins>+        self.assertEqual(len(wps), 0)
</ins><span class="cx">         wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), &quot;20000000-0000-0000-0000-000000000003&quot;)
</span><span class="cx">         yield self.commit()
</span><del>-        self.assertEqual(len(wps), 1)
-        yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
</del><ins>+        self.assertEqual(len(wps), 0)
</ins><span class="cx"> 
</span><span class="cx">         cobj = yield self.calendarObjectUnderTest(name=&quot;data1.ics&quot;, calendar_name=&quot;calendar&quot;, home=&quot;10000000-0000-0000-0000-000000000001&quot;)
</span><span class="cx">         vcalendar = yield cobj.component()
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavwhotesttest_groupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_groups.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_groups.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/test/test_groups.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -18,11 +18,12 @@
</span><span class="cx"> Group membership caching implementation tests
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-from txdav.who.groups import GroupCacher, diffAssignments
</del><span class="cx"> from twext.who.idirectory import RecordType
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase
</span><span class="cx"> from txdav.common.icommondatastore import NotFoundError
</span><ins>+from txdav.who.groups import GroupCacher, diffAssignments
+from txdav.who.test.support import TestRecord, CalendarInMemoryDirectoryService
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -44,8 +45,8 @@
</span><span class="cx">         txn = store.newTransaction()
</span><span class="cx"> 
</span><span class="cx">         record = yield self.directory.recordWithUID(u&quot;__top_group_1__&quot;)
</span><del>-        _ignore_groupID, _ignore_name, _ignore_membershipHash, _ignore_modified = (yield txn.groupByUID(record.uid))
-        _ignore_groupID, _ignore_name, _ignore_membershipHash, _ignore_modified = (yield txn.groupByUID(record.uid))
</del><ins>+        yield txn.groupByUID(record.uid)
+        yield txn.groupByUID(record.uid)
</ins><span class="cx"> 
</span><span class="cx">         yield txn.commit()
</span><span class="cx"> 
</span><span class="lines">@@ -63,16 +64,21 @@
</span><span class="cx">         record = yield self.directory.recordWithUID(u&quot;__top_group_1__&quot;)
</span><span class="cx">         yield self.groupCacher.refreshGroup(txn, record.uid)
</span><span class="cx"> 
</span><del>-        groupID, _ignore_name, membershipHash, _ignore_modified = (yield txn.groupByUID(record.uid))
</del><ins>+        (
+            groupID, _ignore_name, membershipHash, _ignore_modified,
+            extant
+        ) = (yield txn.groupByUID(record.uid))
</ins><span class="cx"> 
</span><ins>+        self.assertEquals(extant, True)
</ins><span class="cx">         self.assertEquals(membershipHash, &quot;553eb54e3bbb26582198ee04541dbee4&quot;)
</span><span class="cx"> 
</span><del>-        groupUID, name, membershipHash = (yield txn.groupByID(groupID))
</del><ins>+        groupUID, name, membershipHash, extant = (yield txn.groupByID(groupID))
</ins><span class="cx">         self.assertEquals(groupUID, record.uid)
</span><span class="cx">         self.assertEquals(name, u&quot;Top Group 1&quot;)
</span><span class="cx">         self.assertEquals(membershipHash, &quot;553eb54e3bbb26582198ee04541dbee4&quot;)
</span><ins>+        self.assertEquals(extant, True)
</ins><span class="cx"> 
</span><del>-        members = (yield txn.membersOfGroup(groupID))
</del><ins>+        members = (yield txn.groupMemberUIDs(groupID))
</ins><span class="cx">         self.assertEquals(
</span><span class="cx">             set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__']),
</span><span class="cx">             members
</span><span class="lines">@@ -107,7 +113,10 @@
</span><span class="cx">         # Refresh the group so it's assigned a group_id
</span><span class="cx">         uid = u&quot;__top_group_1__&quot;
</span><span class="cx">         yield self.groupCacher.refreshGroup(txn, uid)
</span><del>-        groupID, name, _ignore_membershipHash, _ignore_modified = (yield txn.groupByUID(uid))
</del><ins>+        (
+            groupID, name, _ignore_membershipHash, _ignore_modified,
+            _ignore_extant
+        ) = yield txn.groupByUID(uid)
</ins><span class="cx"> 
</span><span class="cx">         # Remove two members, and add one member
</span><span class="cx">         newSet = set()
</span><span class="lines">@@ -156,9 +165,12 @@
</span><span class="cx">         uid = u&quot;__top_group_1__&quot;
</span><span class="cx">         hash = &quot;553eb54e3bbb26582198ee04541dbee4&quot;
</span><span class="cx">         yield self.groupCacher.refreshGroup(txn, uid)
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modified = yield txn.groupByUID(uid)
-        results = (yield txn.groupByID(groupID))
-        self.assertEquals((uid, u&quot;Top Group 1&quot;, hash), results)
</del><ins>+        (
+            groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+            extant
+        ) = yield txn.groupByUID(uid)
+        results = yield txn.groupByID(groupID)
+        self.assertEquals((uid, u&quot;Top Group 1&quot;, hash, True), results)
</ins><span class="cx"> 
</span><span class="cx">         yield txn.commit()
</span><span class="cx"> 
</span><span class="lines">@@ -414,3 +426,125 @@
</span><span class="cx">                 {&quot;D&quot;: (&quot;7&quot;, &quot;8&quot;), &quot;C&quot;: (&quot;4&quot;, &quot;5&quot;), &quot;A&quot;: (&quot;1&quot;, &quot;2&quot;)},
</span><span class="cx">             )
</span><span class="cx">         )
</span><ins>+
+
+class DynamicGroupTest(StoreTestCase):
+
+
+    @inlineCallbacks
+    def setUp(self):
+        yield super(DynamicGroupTest, self).setUp()
+
+        self.directory = CalendarInMemoryDirectoryService(None)
+        self.store.setDirectoryService(self.directory)
+        self.groupCacher = GroupCacher(self.directory)
+
+        self.numUsers = 100
+
+        # Add users
+        records = []
+        fieldName = self.directory.fieldName
+        for i in xrange(self.numUsers):
+            records.append(
+                TestRecord(
+                    self.directory,
+                    {
+                        fieldName.uid: u&quot;foo{ctr:05d}&quot;.format(ctr=i),
+                        fieldName.shortNames: (u&quot;foo{ctr:05d}&quot;.format(ctr=i),),
+                        fieldName.fullNames: (u&quot;foo{ctr:05d}&quot;.format(ctr=i),),
+                        fieldName.recordType: RecordType.user,
+                    }
+                )
+            )
+
+        # Add a group
+        records.append(
+            TestRecord(
+                self.directory,
+                {
+                    fieldName.uid: u&quot;testgroup&quot;,
+                    fieldName.recordType: RecordType.group,
+                }
+            )
+        )
+
+        yield self.directory.updateRecords(records, create=True)
+
+        group = yield self.directory.recordWithUID(u&quot;testgroup&quot;)
+        members = yield self.directory.recordsWithRecordType(RecordType.user)
+        yield group.setMembers(members)
+
+
+    @inlineCallbacks
+    def test_extant(self):
+        &quot;&quot;&quot;
+        Verify that once a group is removed from the directory, the next call
+        to refreshGroup() will set the &quot;extent&quot; to False.  Add the group back
+        to the directory and &quot;extent&quot; becomes True.
+        &quot;&quot;&quot;
+        store = self.storeUnderTest()
+
+        txn = store.newTransaction()
+        yield self.groupCacher.refreshGroup(txn, u&quot;testgroup&quot;)
+        (
+            groupID, _ignore_name, membershipHash, _ignore_modified,
+            extant
+        ) = (yield txn.groupByUID(u&quot;testgroup&quot;))
+        yield txn.commit()
+
+        self.assertTrue(extant)
+
+        # Remove the group
+        yield self.directory.removeRecords([u&quot;testgroup&quot;])
+
+        txn = store.newTransaction()
+        yield self.groupCacher.refreshGroup(txn, u&quot;testgroup&quot;)
+        (
+            groupID, _ignore_name, membershipHash, _ignore_modified,
+            extant
+        ) = (yield txn.groupByUID(u&quot;testgroup&quot;))
+        yield txn.commit()
+
+        # Extant = False
+        self.assertFalse(extant)
+
+        # The list of members stored in the DB for this group is now empty
+        txn = store.newTransaction()
+        members = yield txn.groupMemberUIDs(groupID)
+        yield txn.commit()
+        self.assertEquals(members, set())
+
+        # Add the group back into the directory
+        fieldName = self.directory.fieldName
+        yield self.directory.updateRecords(
+            (
+                TestRecord(
+                    self.directory,
+                    {
+                        fieldName.uid: u&quot;testgroup&quot;,
+                        fieldName.recordType: RecordType.group,
+                    }
+                ),
+            ),
+            create=True
+        )
+        group = yield self.directory.recordWithUID(u&quot;testgroup&quot;)
+        members = yield self.directory.recordsWithRecordType(RecordType.user)
+        yield group.setMembers(members)
+
+        txn = store.newTransaction()
+        yield self.groupCacher.refreshGroup(txn, u&quot;testgroup&quot;)
+        (
+            groupID, _ignore_name, membershipHash, _ignore_modified,
+            extant
+        ) = (yield txn.groupByUID(u&quot;testgroup&quot;))
+        yield txn.commit()
+
+        # Extant = True
+        self.assertTrue(extant)
+
+        # The list of members stored in the DB for this group has 100 users
+        txn = store.newTransaction()
+        members = yield txn.groupMemberUIDs(groupID)
+        yield txn.commit()
+        self.assertEquals(len(members), 100)
</ins></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavwhoutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/util.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/util.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/util.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -168,12 +168,30 @@
</span><span class="cx">                         ),
</span><span class="cx">                     ),
</span><span class="cx"> 
</span><ins>+                    CalRecordType.location: RecordTypeSchema(
+                        relativeDN=u&quot;ou=places&quot;,
+
+                        attributes=(),
+                    ),
+
+                    CalRecordType.resource: RecordTypeSchema(
+                        relativeDN=u&quot;ou=resources&quot;,
+
+                        attributes=(),
+                    ),
+
+                    CalRecordType.address: RecordTypeSchema(
+                        relativeDN=u&quot;ou=buildings&quot;,
+
+                        attributes=(),
+                    ),
+
</ins><span class="cx">                 })
</span><span class="cx">             )
</span><span class="cx"> 
</span><span class="cx">         elif &quot;inmemory&quot; in directoryType:
</span><del>-            from txdav.who.test.support import InMemoryDirectoryService
-            directory = InMemoryDirectoryService()
</del><ins>+            from txdav.who.test.support import CalendarInMemoryDirectoryService
+            directory = CalendarInMemoryDirectoryService()
</ins><span class="cx"> 
</span><span class="cx">         else:
</span><span class="cx">             log.error(&quot;Invalid DirectoryType: {dt}&quot;, dt=directoryType)
</span></span></pre></div>
<a id="CalendarServerbranchesusersgayagroupsharee2txdavwhowikipy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/gaya/groupsharee2/txdav/who/wiki.py (13731 => 13732)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/gaya/groupsharee2/txdav/who/wiki.py        2014-07-07 23:41:26 UTC (rev 13731)
+++ CalendarServer/branches/users/gaya/groupsharee2/txdav/who/wiki.py        2014-07-08 07:05:29 UTC (rev 13732)
</span><span class="lines">@@ -194,6 +194,7 @@
</span><span class="cx">                 &quot;in wiki {log_source}: {error}&quot;,
</span><span class="cx">                 record=record, error=e
</span><span class="cx">             )
</span><ins>+            returnValue(WikiAccessLevel.none)
</ins><span class="cx"> 
</span><span class="cx">         except WebError as e:
</span><span class="cx">             status = int(e.status)
</span><span class="lines">@@ -216,6 +217,7 @@
</span><span class="cx">                 &quot;Unable to look up wiki access: {error}&quot;,
</span><span class="cx">                 record=record, error=e
</span><span class="cx">             )
</span><ins>+            returnValue(WikiAccessLevel.none)
</ins><span class="cx"> 
</span><span class="cx">         try:
</span><span class="cx">             returnValue({
</span></span></pre>
</div>
</div>

</body>
</html>