<!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>[13398] CalendarServer/trunk</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/13398">13398</a></dd>
<dt>Author</dt> <dd>gaya@apple.com</dd>
<dt>Date</dt> <dd>2014-05-01 19:28:56 -0700 (Thu, 01 May 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Clean up GroupAttendeeReconciliationWork, its config, and tests.  Remove incorrect old-event-with-group-attendee detection.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunktwistedcaldavstdconfigpy">CalendarServer/trunk/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastoresqlpy">CalendarServer/trunk/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServertrunktxdavwhodirectorypy">CalendarServer/trunk/txdav/who/directory.py</a></li>
<li><a href="#CalendarServertrunktxdavwhogroupspy">CalendarServer/trunk/txdav/who/groups.py</a></li>
<li><a href="#CalendarServertrunktxdavwhotesttest_group_attendeespy">CalendarServer/trunk/txdav/who/test/test_group_attendees.py</a></li>
<li><a href="#CalendarServertrunktxdavwhovcardpy">CalendarServer/trunk/txdav/who/vcard.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunktwistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py (13397 => 13398)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/stdconfig.py        2014-05-02 02:18:30 UTC (rev 13397)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py        2014-05-02 02:28:56 UTC (rev 13398)
</span><span class="lines">@@ -750,7 +750,6 @@
</span><span class="cx">             &quot;AllowResourceAsOrganizer&quot;            : False, # Allow resources to be Organizers
</span><span class="cx">             &quot;AllowLocationWithoutOrganizer&quot;       : True, # Allow locations to have events without an Organizer
</span><span class="cx">             &quot;AllowResourceWithoutOrganizer&quot;       : True, # Allow resources to have events without an Organizer
</span><del>-            &quot;AllowGroupAsAttendee&quot;                : False, # Allow groups to be Attendees
</del><span class="cx">             &quot;TrackUnscheduledLocationData&quot;        : True, # Track who the last modifier of an unscheduled location event is
</span><span class="cx">             &quot;TrackUnscheduledResourceData&quot;        : True, # Track who the last modifier of an unscheduled resource event is
</span><span class="cx">             &quot;LimitFreeBusyAttendees&quot;              : 30, # Maximum number of attendees to request freebusy for
</span><span class="lines">@@ -1025,6 +1024,11 @@
</span><span class="cx">         &quot;UseExternalProxies&quot; : False,
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    &quot;GroupAttendees&quot; : {
+        &quot;Enabled&quot;: False,
+        &quot;ReconciliationDelaySeconds&quot; : 5,
+    },
+
</ins><span class="cx">     &quot;Manhole&quot;: {
</span><span class="cx">         &quot;Enabled&quot;: False,
</span><span class="cx">         &quot;StartingPortNumber&quot;: 5000,
</span></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py (13397 => 13398)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/sql.py        2014-05-02 02:18:30 UTC (rev 13397)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py        2014-05-02 02:28:56 UTC (rev 13398)
</span><span class="lines">@@ -1935,7 +1935,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         reconcile group attendees
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        if not config.Scheduling.Options.AllowGroupAsAttendee:
</del><ins>+        if not config.GroupAttendees.Enabled:
</ins><span class="cx">             returnValue(False)
</span><span class="cx"> 
</span><span class="cx">         attendeeProps = component.getAllAttendeeProperties()
</span><span class="lines">@@ -1962,7 +1962,7 @@
</span><span class="cx">             changed = component.reconcileGroupAttendees(groupCUAToAttendeeMemberPropMap)
</span><span class="cx"> 
</span><span class="cx">         # save for post processing when self._resourceID is non-zero
</span><del>-        if inserting:
</del><ins>+        if inserting and groupCUAToAttendeeMemberPropMap:
</ins><span class="cx">             self._groupCUAToAttendeeMemberPropMap = groupCUAToAttendeeMemberPropMap
</span><span class="cx"> 
</span><span class="cx">         returnValue(changed)
</span><span class="lines">@@ -1974,31 +1974,8 @@
</span><span class="cx">         update schema.GROUP_ATTENDEE
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if groupCUAToAttendeeMemberPropMap is None:
</span><del>-            # post processing.
-
-            # see if this event ends in the past
-            tr = schema.TIME_RANGE
-            rows = yield Select(
-                [Count(tr.CALENDAR_OBJECT_RESOURCE_ID)],
-                From=tr,
-                Where=(
-                    tr.CALENDAR_OBJECT_RESOURCE_ID == self._resourceID).And(
-                    tr.END_DATE &gt; datetime.datetime.utcnow()
-                ),
-            ).on(self._txn)
-
-            if rows[0][0] == 0:
-                # delete group attendee rows and exit
-                ga = schema.GROUP_ATTENDEE
-                rows = yield Delete(
-                    From=ga,
-                    Where=ga.RESOURCE_ID == self._resourceID,
-                    Return=[ga.GROUP_ID]
-                ).on(self._txn)
-                returnValue(bool(rows))
-
</del><span class="cx">             if hasattr(self, &quot;_groupCUAToAttendeeMemberPropMap&quot;):
</span><del>-                    groupCUAToAttendeeMemberPropMap = self._groupCUAToAttendeeMemberPropMap
</del><ins>+                groupCUAToAttendeeMemberPropMap = self._groupCUAToAttendeeMemberPropMap
</ins><span class="cx">             else:
</span><span class="cx">                 returnValue(False)
</span><span class="cx"> 
</span><span class="lines">@@ -2059,6 +2036,20 @@
</span><span class="cx">         returnValue(changed)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def deleteGROUP_ATTENDEE(self, groupCUAToAttendeeMemberPropMap=None):
+        &quot;&quot;&quot;
+        delete groupp attendee rows for this resource
+        &quot;&quot;&quot;
+        ga = schema.GROUP_ATTENDEE
+        rows = yield Delete(
+            From=ga,
+            Where=ga.RESOURCE_ID == self._resourceID,
+            Return=[ga.GROUP_ID]
+        ).on(self._txn)
+        returnValue(bool(rows))
+
+
</ins><span class="cx">     def validCalendarDataCheck(self, component, inserting):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Check that the calendar data is valid iCalendar.
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhodirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/directory.py (13397 => 13398)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/directory.py        2014-05-02 02:18:30 UTC (rev 13397)
+++ CalendarServer/trunk/txdav/who/directory.py        2014-05-02 02:28:56 UTC (rev 13398)
</span><span class="lines">@@ -97,7 +97,7 @@
</span><span class="cx"> 
</span><span class="cx">         if record:
</span><span class="cx">             if record.hasCalendars or (
</span><del>-                config.Scheduling.Options.AllowGroupAsAttendee and
</del><ins>+                config.GroupAttendees.Enabled and
</ins><span class="cx">                 record.recordType == BaseRecordType.group
</span><span class="cx">             ):
</span><span class="cx">                 returnValue(record)
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhogroupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/groups.py (13397 => 13398)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/groups.py        2014-05-02 02:18:30 UTC (rev 13397)
+++ CalendarServer/trunk/txdav/who/groups.py        2014-05-02 02:28:56 UTC (rev 13398)
</span><span class="lines">@@ -22,13 +22,14 @@
</span><span class="cx"> from twext.enterprise.dal.record import fromTable
</span><span class="cx"> from twext.enterprise.dal.syntax import Delete, Select
</span><span class="cx"> from twext.enterprise.jobqueue import WorkItem, PeerConnectionPool
</span><ins>+from twext.python.log import Logger
</ins><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><ins>+from twistedcaldav.config import config
</ins><span class="cx"> from txdav.caldav.datastore.sql import CalendarStoreFeatures
</span><span class="cx"> from txdav.common.datastore.sql_tables import schema
</span><span class="cx"> import datetime
</span><span class="cx"> import hashlib
</span><span class="cx"> 
</span><del>-from twext.python.log import Logger
</del><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -163,7 +164,27 @@
</span><span class="cx">     group = property(
</span><span class="cx">         lambda self: &quot;{0}, {1}&quot;.format(self.groupID, self.resourceID)
</span><span class="cx">     )
</span><ins>+    
+    @classmethod
+    @inlineCallbacks
+    def _schedule(cls, txn, eventID, groupID, seconds):
+        notBefore = datetime.datetime.utcnow() + datetime.timedelta(seconds=seconds)
+        log.debug(
+            &quot;scheduling group reconciliation for &quot;
+            &quot;({resourceID}, {groupID},): {when}&quot;,
+            resourceID=eventID,
+            groupID=groupID,
+            when=notBefore
+        )
+        wp = yield txn.enqueue(
+            cls,
+            resourceID=eventID,
+            groupID=groupID,
+            notBefore=notBefore,
+        )
+        returnValue(wp)
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def doWork(self):
</span><span class="cx"> 
</span><span class="lines">@@ -413,27 +434,13 @@
</span><span class="cx"> 
</span><span class="cx">         wps = []
</span><span class="cx">         for [eventID] in rows:
</span><del>-
-            notBefore = (
-                datetime.datetime.utcnow() +
-                datetime.timedelta(seconds=10)
</del><ins>+            wp = yield GroupAttendeeReconciliationWork._schedule(
+                txn, 
+                eventID=eventID, 
+                groupID=groupID, 
+                seconds=float(config.GroupAttendees.ReconciliationDelaySeconds)
</ins><span class="cx">             )
</span><del>-            log.debug(
-                &quot;scheduling group reconciliation for &quot;
-                &quot;({resourceID}, {groupID},): {when}&quot;,
-                resourceID=eventID,
-                groupID=groupID,
-                when=notBefore
-            )
-
-            wp = yield txn.enqueue(
-                GroupAttendeeReconciliationWork,
-                resourceID=eventID,
-                groupID=groupID,
-                notBefore=notBefore
-            )
</del><span class="cx">             wps.append(wp)
</span><del>-
</del><span class="cx">         returnValue(tuple(wps))
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhotesttest_group_attendeespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/test/test_group_attendees.py (13397 => 13398)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/test/test_group_attendees.py        2014-05-02 02:18:30 UTC (rev 13397)
+++ CalendarServer/trunk/txdav/who/test/test_group_attendees.py        2014-05-02 02:28:56 UTC (rev 13398)
</span><span class="lines">@@ -38,7 +38,8 @@
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def setUp(self):
</span><del>-        self.patch(config.Scheduling.Options, &quot;AllowGroupAsAttendee&quot;, &quot;True&quot;)
</del><ins>+        self.patch(config.GroupAttendees, &quot;Enabled&quot;, &quot;True&quot;)
+        self.patch(config.GroupAttendees, &quot;ReconciliationDelaySeconds&quot;, &quot;0&quot;)
</ins><span class="cx"> 
</span><span class="cx">         yield super(GroupAttendeeReconciliation, self).setUp()
</span><span class="cx">         self.xmlService = xmlService(self.mktemp(), xmlData=None)
</span><span class="lines">@@ -794,7 +795,9 @@
</span><span class="cx">         wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), &quot;20000000-0000-0000-0000-000000000001&quot;)
</span><span class="cx">         self.assertEqual(len(wps), 0)
</span><span class="cx"> 
</span><ins>+    test_groupChangeOldEvent.todo = &quot;Doesn't work yet&quot;
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_groupRemovalFromDirectory(self):
</span><span class="cx">         &quot;&quot;&quot;
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhovcardpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/vcard.py (13397 => 13398)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/vcard.py        2014-05-02 02:18:30 UTC (rev 13397)
+++ CalendarServer/trunk/txdav/who/vcard.py        2014-05-02 02:28:56 UTC (rev 13398)
</span><span class="lines">@@ -57,8 +57,8 @@
</span><span class="cx">     #&quot;PHOTO&quot;: {&quot;ENCODING&quot;: (&quot;B&quot;,), &quot;TYPE&quot;: (&quot;JPEG&quot;,), },
</span><span class="cx">     &quot;ADR&quot;: {&quot;TYPE&quot;: (&quot;WORK&quot;, &quot;PREF&quot;, &quot;POSTAL&quot;, &quot;PARCEL&quot;,),
</span><span class="cx">             &quot;LABEL&quot;: None, &quot;GEO&quot;: None, },
</span><del>-    #&quot;LABEL&quot;: {&quot;TYPE&quot;: (&quot;POSTAL&quot;, &quot;PARCEL&quot;,)},
-    #&quot;TEL&quot;: {&quot;TYPE&quot;: None, },  # None means param can contain can be anything
</del><ins>+    &quot;LABEL&quot;: {&quot;TYPE&quot;: (&quot;POSTAL&quot;, &quot;PARCEL&quot;,)},
+    #&quot;TEL&quot;: {&quot;TYPE&quot;: None, },  # None means param value can be anything
</ins><span class="cx">     &quot;EMAIL&quot;: {&quot;TYPE&quot;: None, },
</span><span class="cx">     #&quot;KEY&quot;: {&quot;ENCODING&quot;: (&quot;B&quot;,), &quot;TYPE&quot;: (&quot;PGPPUBILICKEY&quot;, &quot;USERCERTIFICATE&quot;, &quot;USERPKCS12DATA&quot;, &quot;USERSMIMECERTIFICATE&quot;,)},
</span><span class="cx">     #&quot;URL&quot;: {&quot;TYPE&quot;: (&quot;WEBLOG&quot;, &quot;HOMEPAGE&quot;,)},
</span><span class="lines">@@ -227,8 +227,10 @@
</span><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-    # UNIMPLEMENTED
-    #     3.2.2 LABEL
</del><ins>+    #    3.2.2 LABEL
+    #label = record.fields.get(CalFieldName.streetAddress)
+    if label:
+        vcard.addProperty(Property(&quot;LABEL&quot;, label.encode(&quot;utf-8&quot;), params={&quot;TYPE&quot;: [&quot;POSTAL&quot;, &quot;PARCEL&quot;, ]}))
</ins><span class="cx"> 
</span><span class="cx">     #===================================================================
</span><span class="cx">     # 3.3 TELECOMMUNICATIONS ADDRESSING TYPES http://tools.ietf.org/html/rfc2426#section-3.3
</span><span class="lines">@@ -241,7 +243,7 @@
</span><span class="cx">     preferredWorkParams = {&quot;TYPE&quot;: (&quot;WORK&quot;, &quot;PREF&quot;, &quot;INTERNET&quot;,), }
</span><span class="cx">     workParams = {&quot;TYPE&quot;: (&quot;WORK&quot;, &quot;INTERNET&quot;,), }
</span><span class="cx">     params = preferredWorkParams
</span><del>-    for emailAddress in record.fields.get(FieldName.emailAddresses, []):
</del><ins>+    for emailAddress in record.fields.get(FieldName.emailAddresses, ()):
</ins><span class="cx">         addUniqueProperty(Property(&quot;EMAIL&quot;, emailAddress.encode(&quot;utf-8&quot;), params=params), ignoredParameters={&quot;TYPE&quot;: (&quot;PREF&quot;,)})
</span><span class="cx">         params = workParams
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>