<!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>[12167] CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav</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/12167">12167</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2013-12-20 08:02:05 -0800 (Fri, 20 Dec 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Checkpoint: cross-pod searching.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequerybuilderpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/builder.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequeryfilterpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/filter.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequerytesttest_filterpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/test/test_filter.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastoresqlpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastorequeryfilterpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/filter.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastorequerytesttest_filterpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/test/test_filter.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastoresqlpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastorepoddingconduitpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/conduit.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastorepoddingtesttest_conduitpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/test/test_conduit.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastoresql_externalpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/sql_external.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequerybuilderpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/builder.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/builder.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/builder.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -94,7 +94,7 @@
</span><span class="cx">     logical = expression.andExpression if compfilter.filter_test == &quot;allof&quot; else expression.orExpression
</span><span class="cx"> 
</span><span class="cx">     expressions = []
</span><del>-    if isinstance(compfilter.filter_name, str):
</del><ins>+    if isinstance(compfilter.filter_name, str) or isinstance(compfilter.filter_name, unicode):
</ins><span class="cx">         expressions.append(expression.isExpression(fields[&quot;TYPE&quot;], compfilter.filter_name, True))
</span><span class="cx">     else:
</span><span class="cx">         expressions.append(expression.inExpression(fields[&quot;TYPE&quot;], compfilter.filter_name, True))
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequeryfilterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/filter.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/filter.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/filter.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -39,10 +39,44 @@
</span><span class="cx">     Determines which matching components are returned.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = None
+    deserialize_names = {}
+
+    @classmethod
+    def serialize_register(cls, register):
+        cls.deserialize_names[register.serialized_name] = register
+
+
</ins><span class="cx">     def __init__(self, xml_element):
</span><del>-        self.xmlelement = xml_element
</del><ins>+        pass
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @classmethod
+    def deserialize(cls, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        obj = cls.deserialize_names[data[&quot;type&quot;]](None)
+        obj._deserialize(data)
+        return obj
+
+
+    def _deserialize(self, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        pass
+
+
+    def serialize(self):
+        &quot;&quot;&quot;
+        Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+        &quot;&quot;&quot;
+        return {
+            &quot;type&quot;: self.serialized_name,
+        }
+
+
</ins><span class="cx">     def match(self, item, access=None):
</span><span class="cx">         raise NotImplementedError
</span><span class="cx"> 
</span><span class="lines">@@ -57,9 +91,13 @@
</span><span class="cx">     Determines which matching components are returned.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = &quot;Filter&quot;
+
</ins><span class="cx">     def __init__(self, xml_element):
</span><span class="cx"> 
</span><span class="cx">         super(Filter, self).__init__(xml_element)
</span><ins>+        if xml_element is None:
+            return
</ins><span class="cx"> 
</span><span class="cx">         # One comp-filter element must be present
</span><span class="cx">         if len(xml_element.children) != 1 or xml_element.children[0].qname() != (caldav_namespace, &quot;comp-filter&quot;):
</span><span class="lines">@@ -68,6 +106,24 @@
</span><span class="cx">         self.child = ComponentFilter(xml_element.children[0])
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _deserialize(self, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        self.child = FilterBase.deserialize(data[&quot;child&quot;])
+
+
+    def serialize(self):
+        &quot;&quot;&quot;
+        Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+        &quot;&quot;&quot;
+        result = super(Filter, self).serialize()
+        result.update({
+            &quot;child&quot;: self.child.serialize(),
+        })
+        return result
+
+
</ins><span class="cx">     def match(self, component, access=None):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Returns True if the given calendar component matches this filter, False
</span><span class="lines">@@ -148,8 +204,10 @@
</span><span class="cx"> 
</span><span class="cx">         return self.child.getmintimerange(None, False)
</span><span class="cx"> 
</span><ins>+FilterBase.serialize_register(Filter)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class FilterChildBase(FilterBase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     CalDAV filter element.
</span><span class="lines">@@ -158,6 +216,8 @@
</span><span class="cx">     def __init__(self, xml_element):
</span><span class="cx"> 
</span><span class="cx">         super(FilterChildBase, self).__init__(xml_element)
</span><ins>+        if xml_element is None:
+            return
</ins><span class="cx"> 
</span><span class="cx">         qualifier = None
</span><span class="cx">         filters = []
</span><span class="lines">@@ -205,6 +265,32 @@
</span><span class="cx">         self.filter_test = filter_test
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _deserialize(self, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        self.qualifier = FilterBase.deserialize(data[&quot;qualifier&quot;]) if data[&quot;qualifier&quot;] else None
+        self.filters = [FilterBase.deserialize(filter) for filter in data[&quot;filters&quot;]]
+        self.filter_name = data[&quot;filter_name&quot;]
+        self.defined = data[&quot;defined&quot;]
+        self.filter_test = data[&quot;filter_test&quot;]
+
+
+    def serialize(self):
+        &quot;&quot;&quot;
+        Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+        &quot;&quot;&quot;
+        result = super(FilterChildBase, self).serialize()
+        result.update({
+            &quot;qualifier&quot;: self.qualifier.serialize() if self.qualifier else None,
+            &quot;filters&quot;: [filter.serialize() for filter in self.filters],
+            &quot;filter_name&quot;: self.filter_name,
+            &quot;defined&quot;: self.defined,
+            &quot;filter_test&quot;: self.filter_test,
+        })
+        return result
+
+
</ins><span class="cx">     def match(self, item, access=None):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Returns True if the given calendar item (either a component, property or parameter value)
</span><span class="lines">@@ -235,6 +321,8 @@
</span><span class="cx">     Limits a search to only the chosen component types.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = &quot;ComponentFilter&quot;
+
</ins><span class="cx">     def match(self, item, access):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Returns True if the given calendar item (which is a component)
</span><span class="lines">@@ -414,13 +502,17 @@
</span><span class="cx"> 
</span><span class="cx">         return currentMinimum, currentIsEndTime
</span><span class="cx"> 
</span><ins>+FilterBase.serialize_register(ComponentFilter)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class PropertyFilter (FilterChildBase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Limits a search to specific properties.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = &quot;PropertyFilter&quot;
+
</ins><span class="cx">     def _match(self, component, access):
</span><span class="cx">         # When access restriction is in force, we need to only allow matches against the properties
</span><span class="cx">         # allowed by the access restriction level.
</span><span class="lines">@@ -516,13 +608,17 @@
</span><span class="cx"> 
</span><span class="cx">         return currentMinimum, currentIsEndTime
</span><span class="cx"> 
</span><ins>+FilterBase.serialize_register(PropertyFilter)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class ParameterFilter (FilterChildBase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Limits a search to specific parameters.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = &quot;ParameterFilter&quot;
+
</ins><span class="cx">     def _match(self, property, access):
</span><span class="cx"> 
</span><span class="cx">         # At least one parameter must match (or is-not-defined is set)
</span><span class="lines">@@ -534,13 +630,17 @@
</span><span class="cx"> 
</span><span class="cx">         return result
</span><span class="cx"> 
</span><ins>+FilterBase.serialize_register(ParameterFilter)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class IsNotDefined (FilterBase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Specifies that the named iCalendar item does not exist.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = &quot;IsNotDefined&quot;
+
</ins><span class="cx">     def match(self, component, access=None):
</span><span class="cx">         # Oddly, this needs always to return True so that it appears there is
</span><span class="cx">         # a match - but we then &quot;negate&quot; the result if is-not-defined is set.
</span><span class="lines">@@ -548,6 +648,7 @@
</span><span class="cx">         # is-not-defined option.
</span><span class="cx">         return True
</span><span class="cx"> 
</span><ins>+FilterBase.serialize_register(IsNotDefined)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> class TextMatch (FilterBase):
</span><span class="lines">@@ -555,9 +656,13 @@
</span><span class="cx">     Specifies a substring match on a property or parameter value.
</span><span class="cx">     (CalDAV-access-09, section 9.6.4)
</span><span class="cx">     &quot;&quot;&quot;
</span><ins>+    serialized_name = &quot;TextMatch&quot;
+
</ins><span class="cx">     def __init__(self, xml_element):
</span><span class="cx"> 
</span><span class="cx">         super(TextMatch, self).__init__(xml_element)
</span><ins>+        if xml_element is None:
+            return
</ins><span class="cx"> 
</span><span class="cx">         self.text = str(xml_element)
</span><span class="cx">         if &quot;caseless&quot; in xml_element.attributes:
</span><span class="lines">@@ -591,6 +696,30 @@
</span><span class="cx">             self.match_type = &quot;contains&quot;
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _deserialize(self, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        self.text = data[&quot;text&quot;]
+        self.caseless = data[&quot;caseless&quot;]
+        self.negate = data[&quot;negate&quot;]
+        self.match_type = data[&quot;match_type&quot;]
+
+
+    def serialize(self):
+        &quot;&quot;&quot;
+        Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+        &quot;&quot;&quot;
+        result = super(TextMatch, self).serialize()
+        result.update({
+            &quot;text&quot;: self.text,
+            &quot;caseless&quot;: self.caseless,
+            &quot;negate&quot;: self.negate,
+            &quot;match_type&quot;: self.match_type,
+        })
+        return result
+
+
</ins><span class="cx">     def match(self, item, access):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Match the text for the item.
</span><span class="lines">@@ -637,16 +766,22 @@
</span><span class="cx"> 
</span><span class="cx">         return self.negate
</span><span class="cx"> 
</span><ins>+FilterBase.serialize_register(TextMatch)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class TimeRange (FilterBase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Specifies a time for testing components against.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = &quot;TimeRange&quot;
+
</ins><span class="cx">     def __init__(self, xml_element):
</span><span class="cx"> 
</span><span class="cx">         super(TimeRange, self).__init__(xml_element)
</span><ins>+        if xml_element is None:
+            return
</ins><span class="cx"> 
</span><span class="cx">         # One of start or end must be present
</span><span class="cx">         if &quot;start&quot; not in xml_element.attributes and &quot;end&quot; not in xml_element.attributes:
</span><span class="lines">@@ -657,6 +792,28 @@
</span><span class="cx">         self.tzinfo = None
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _deserialize(self, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        self.start = DateTime.parseText(data[&quot;start&quot;]) if data[&quot;start&quot;] else None
+        self.end = DateTime.parseText(data[&quot;end&quot;]) if data[&quot;end&quot;] else None
+        self.tzinfo = Timezone(tzid=data[&quot;tzinfo&quot;]) if data[&quot;tzinfo&quot;] else None
+
+
+    def serialize(self):
+        &quot;&quot;&quot;
+        Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+        &quot;&quot;&quot;
+        result = super(TimeRange, self).serialize()
+        result.update({
+            &quot;start&quot;: self.start.getText() if self.start else None,
+            &quot;end&quot;: self.end.getText() if self.end else None,
+            &quot;tzinfo&quot;: self.tzinfo.getTimezoneID() if self.tzinfo else None,
+        })
+        return result
+
+
</ins><span class="cx">     def settzinfo(self, tzinfo):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Set the default timezone to use with this query.
</span><span class="lines">@@ -752,3 +909,5 @@
</span><span class="cx">                     return True
</span><span class="cx"> 
</span><span class="cx">         return False
</span><ins>+
+FilterBase.serialize_register(TimeRange)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequerytesttest_filterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/test/test_filter.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/test/test_filter.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/test/test_filter.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -24,12 +24,14 @@
</span><span class="cx"> 
</span><span class="cx"> from txdav.caldav.datastore.index_file import sqlcalendarquery
</span><span class="cx"> from txdav.caldav.datastore.query.builder import buildExpression
</span><del>-from txdav.caldav.datastore.query.filter import Filter
</del><ins>+from txdav.caldav.datastore.query.filter import Filter, FilterBase, TimeRange, \
+    PropertyFilter, TextMatch
</ins><span class="cx"> from txdav.caldav.datastore.query.generator import CalDAVSQLQueryGenerator
</span><span class="cx"> from txdav.common.datastore.sql_tables import schema
</span><span class="cx"> 
</span><span class="cx"> from dateutil.tz import tzutc
</span><span class="cx"> import datetime
</span><ins>+from twistedcaldav.ical import Component
</ins><span class="cx"> 
</span><span class="cx"> class TestQueryFilter(TestCase):
</span><span class="cx"> 
</span><span class="lines">@@ -218,3 +220,216 @@
</span><span class="cx">         self.assertTrue(sql.find(&quot;TIMESPAN&quot;) == -1)
</span><span class="cx">         self.assertTrue(sql.find(&quot;TRANSPARENCY&quot;) == -1)
</span><span class="cx">         self.assertTrue(&quot;VEVENT&quot; in args)
</span><ins>+
+
+
+class TestQueryFilterSerialize(TestCase):
+
+    def setUp(self):
+        super(TestQueryFilterSerialize, self).setUp()
+        TimezoneCache.create()
+
+
+    def test_query(self):
+        &quot;&quot;&quot;
+        Basic query test - no time range
+        &quot;&quot;&quot;
+
+        filter = caldavxml.Filter(
+            caldavxml.ComponentFilter(
+                *[caldavxml.ComponentFilter(
+                    **{&quot;name&quot;:(&quot;VEVENT&quot;, &quot;VFREEBUSY&quot;, &quot;VAVAILABILITY&quot;)}
+                )],
+                **{&quot;name&quot;: &quot;VCALENDAR&quot;}
+            )
+        )
+        filter = Filter(filter)
+        filter.child.settzinfo(Timezone(tzid=&quot;America/New_York&quot;))
+        j = filter.serialize()
+        self.assertEqual(j[&quot;type&quot;], &quot;Filter&quot;)
+
+        f = FilterBase.deserialize(j)
+        self.assertTrue(isinstance(f, Filter))
+
+
+    def test_timerange_query(self):
+        &quot;&quot;&quot;
+        Basic query test with time range
+        &quot;&quot;&quot;
+
+        filter = caldavxml.Filter(
+            caldavxml.ComponentFilter(
+                *[caldavxml.ComponentFilter(
+                    *[caldavxml.TimeRange(**{&quot;start&quot;:&quot;20060605T160000Z&quot;, &quot;end&quot;:&quot;20060605T170000Z&quot;})],
+                    **{&quot;name&quot;:(&quot;VEVENT&quot;, &quot;VFREEBUSY&quot;, &quot;VAVAILABILITY&quot;)}
+                )],
+                **{&quot;name&quot;: &quot;VCALENDAR&quot;}
+            )
+        )
+        filter = Filter(filter)
+        filter.child.settzinfo(Timezone(tzid=&quot;America/New_York&quot;))
+        j = filter.serialize()
+        self.assertEqual(j[&quot;type&quot;], &quot;Filter&quot;)
+
+        f = FilterBase.deserialize(j)
+        self.assertTrue(isinstance(f, Filter))
+        self.assertTrue(isinstance(f.child.filters[0].qualifier, TimeRange))
+        self.assertTrue(isinstance(f.child.filters[0].qualifier.tzinfo, Timezone))
+        self.assertEqual(f.child.filters[0].qualifier.tzinfo.getTimezoneID(), &quot;America/New_York&quot;)
+
+
+    def test_query_not_extended(self):
+        &quot;&quot;&quot;
+        Basic query test with time range
+        &quot;&quot;&quot;
+
+        filter = caldavxml.Filter(
+            caldavxml.ComponentFilter(
+                *[
+                    caldavxml.ComponentFilter(
+                        **{&quot;name&quot;:(&quot;VEVENT&quot;)}
+                    ),
+                    caldavxml.ComponentFilter(
+                        **{&quot;name&quot;:(&quot;VTODO&quot;)}
+                    ),
+                ],
+                **{&quot;name&quot;: &quot;VCALENDAR&quot;}
+            )
+        )
+        filter = Filter(filter)
+        filter.child.settzinfo(Timezone(tzid=&quot;America/New_York&quot;))
+        j = filter.serialize()
+        self.assertEqual(j[&quot;type&quot;], &quot;Filter&quot;)
+
+        f = FilterBase.deserialize(j)
+        self.assertTrue(isinstance(f, Filter))
+        self.assertEqual(len(f.child.filters), 2)
+
+
+    def test_query_extended(self):
+        &quot;&quot;&quot;
+        Basic query test with time range
+        &quot;&quot;&quot;
+
+        filter = caldavxml.Filter(
+            caldavxml.ComponentFilter(
+                *[
+                    caldavxml.ComponentFilter(
+                        *[caldavxml.TimeRange(**{&quot;start&quot;:&quot;20060605T160000Z&quot;, })],
+                        **{&quot;name&quot;:(&quot;VEVENT&quot;)}
+                    ),
+                    caldavxml.ComponentFilter(
+                        **{&quot;name&quot;:(&quot;VTODO&quot;)}
+                    ),
+                ],
+                **{&quot;name&quot;: &quot;VCALENDAR&quot;, &quot;test&quot;: &quot;anyof&quot;}
+            )
+        )
+        filter = Filter(filter)
+        filter.child.settzinfo(Timezone(tzid=&quot;America/New_York&quot;))
+        j = filter.serialize()
+        self.assertEqual(j[&quot;type&quot;], &quot;Filter&quot;)
+
+        f = FilterBase.deserialize(j)
+        self.assertTrue(isinstance(f, Filter))
+        self.assertEqual(len(f.child.filters), 2)
+        self.assertTrue(isinstance(f.child.filters[0].qualifier, TimeRange))
+
+
+    def test_query_text(self):
+        &quot;&quot;&quot;
+        Basic query test with time range
+        &quot;&quot;&quot;
+
+        filter = caldavxml.Filter(
+            caldavxml.ComponentFilter(
+                *[
+                    caldavxml.ComponentFilter(
+                        caldavxml.PropertyFilter(
+                            caldavxml.TextMatch.fromString(&quot;1234&quot;, False),
+                            name=&quot;UID&quot;,
+                        ),
+                        **{&quot;name&quot;:(&quot;VEVENT&quot;)}
+                    ),
+                ],
+                **{&quot;name&quot;: &quot;VCALENDAR&quot;, &quot;test&quot;: &quot;anyof&quot;}
+            )
+        )
+        filter = Filter(filter)
+        filter.child.settzinfo(Timezone(tzid=&quot;America/New_York&quot;))
+        j = filter.serialize()
+        self.assertEqual(j[&quot;type&quot;], &quot;Filter&quot;)
+
+        f = FilterBase.deserialize(j)
+        self.assertTrue(isinstance(f, Filter))
+        self.assertTrue(isinstance(f.child.filters[0].filters[0], PropertyFilter))
+        self.assertTrue(isinstance(f.child.filters[0].filters[0].qualifier, TextMatch))
+        self.assertEqual(f.child.filters[0].filters[0].qualifier.text, &quot;1234&quot;)
+
+
+
+class TestQueryFilterMatch(TestCase):
+
+    def setUp(self):
+        super(TestQueryFilterMatch, self).setUp()
+        TimezoneCache.create()
+
+
+    def test_vlarm_undefined(self):
+
+        filter = caldavxml.Filter(
+            caldavxml.ComponentFilter(
+                *[caldavxml.ComponentFilter(
+                    *[caldavxml.ComponentFilter(
+                        caldavxml.IsNotDefined(),
+                        **{&quot;name&quot;:&quot;VALARM&quot;}
+                    )],
+                    **{&quot;name&quot;:&quot;VEVENT&quot;}
+                )],
+                **{&quot;name&quot;: &quot;VCALENDAR&quot;}
+            )
+        )
+        filter = Filter(filter)
+        filter.child.settzinfo(Timezone(tzid=&quot;America/New_York&quot;))
+
+        self.assertFalse(filter.match(
+            Component.fromString(&quot;&quot;&quot;BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+LAST-MODIFIED:20040110T032845Z
+TZID:US/Eastern
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20051222T210412Z
+CREATED:20060102T150000Z
+DTSTART;TZID=US/Eastern:20130102T100000
+DURATION:PT1H
+RRULE:FREQ=DAILY;COUNT=5
+SUMMARY:event 5
+UID:945113826375CBB89184DC36@ninevah.local
+CATEGORIES:cool,hot
+CATEGORIES:warm
+BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
+        )))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/sql.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/sql.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/sql.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -1349,6 +1349,13 @@
</span><span class="cx">             C{name} is the resource name, C{uid} is the resource UID.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+        # We might be passed an L{Filter} or a serialization of one
+        if isinstance(filter, dict):
+            try:
+                filter = Filter.deserialize(filter)
+            except Exception:
+                file = None
+
</ins><span class="cx">         # Make sure we have a proper Filter element and get the partial SQL statement to use.
</span><span class="cx">         sql_stmt = self._sqlquery(filter, useruid, fbtype)
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastorequeryfilterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/filter.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/filter.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/filter.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -34,10 +34,44 @@
</span><span class="cx">     Determines which matching components are returned.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = None
+    deserialize_names = {}
+
+    @classmethod
+    def serialize_register(cls, register):
+        cls.deserialize_names[register.serialized_name] = register
+
+
</ins><span class="cx">     def __init__(self, xml_element):
</span><del>-        self.xmlelement = xml_element
</del><ins>+        pass
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @classmethod
+    def deserialize(cls, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        obj = cls.deserialize_names[data[&quot;type&quot;]](None)
+        obj._deserialize(data)
+        return obj
+
+
+    def _deserialize(self, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        pass
+
+
+    def serialize(self):
+        &quot;&quot;&quot;
+        Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+        &quot;&quot;&quot;
+        return {
+            &quot;type&quot;: self.serialized_name,
+        }
+
+
</ins><span class="cx">     def match(self, item, access=None):
</span><span class="cx">         raise NotImplementedError
</span><span class="cx"> 
</span><span class="lines">@@ -52,9 +86,13 @@
</span><span class="cx">     Determines which matching components are returned.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = &quot;Filter&quot;
+
</ins><span class="cx">     def __init__(self, xml_element):
</span><span class="cx"> 
</span><span class="cx">         super(Filter, self).__init__(xml_element)
</span><ins>+        if xml_element is None:
+            return
</ins><span class="cx"> 
</span><span class="cx">         filter_test = xml_element.attributes.get(&quot;test&quot;, &quot;anyof&quot;)
</span><span class="cx">         if filter_test not in (&quot;anyof&quot;, &quot;allof&quot;):
</span><span class="lines">@@ -65,6 +103,26 @@
</span><span class="cx">         self.children = [PropertyFilter(child) for child in xml_element.children]
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _deserialize(self, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        self.filter_test = data[&quot;filter_test&quot;]
+        self.child = [FilterBase.deserialize(child) for child in data[&quot;children&quot;]]
+
+
+    def serialize(self):
+        &quot;&quot;&quot;
+        Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+        &quot;&quot;&quot;
+        result = super(Filter, self).serialize()
+        result.update({
+            &quot;filter_test&quot;: self.filter_test,
+            &quot;children&quot;: [child.serialize() for child in self.children],
+        })
+        return result
+
+
</ins><span class="cx">     def match(self, vcard):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Returns True if the given address property matches this filter, False
</span><span class="lines">@@ -96,8 +154,10 @@
</span><span class="cx">         else:
</span><span class="cx">             return True
</span><span class="cx"> 
</span><ins>+FilterBase.serialize_register(Filter)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class FilterChildBase(FilterBase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     CardDAV filter element.
</span><span class="lines">@@ -106,6 +166,8 @@
</span><span class="cx">     def __init__(self, xml_element):
</span><span class="cx"> 
</span><span class="cx">         super(FilterChildBase, self).__init__(xml_element)
</span><ins>+        if xml_element is None:
+            return
</ins><span class="cx"> 
</span><span class="cx">         qualifier = None
</span><span class="cx">         filters = []
</span><span class="lines">@@ -147,6 +209,32 @@
</span><span class="cx">         self.defined = not self.qualifier or not isinstance(qualifier, IsNotDefined)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _deserialize(self, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        self.propfilter_test = data[&quot;propfilter_test&quot;]
+        self.qualifier = FilterBase.deserialize(data[&quot;qualifier&quot;]) if data[&quot;qualifier&quot;] else None
+        self.filters = [FilterBase.deserialize(filter) for filter in data[&quot;filters&quot;]]
+        self.filter_name = data[&quot;filter_name&quot;]
+        self.defined = data[&quot;defined&quot;]
+
+
+    def serialize(self):
+        &quot;&quot;&quot;
+        Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+        &quot;&quot;&quot;
+        result = super(FilterChildBase, self).serialize()
+        result.update({
+            &quot;propfilter_test&quot;: self.propfilter_test,
+            &quot;qualifier&quot;: self.qualifier.serialize() if self.qualifier else None,
+            &quot;filters&quot;: [filter.serialize() for filter in self.filters],
+            &quot;filter_name&quot;: self.filter_name,
+            &quot;defined&quot;: self.defined,
+        })
+        return result
+
+
</ins><span class="cx">     def match(self, item):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Returns True if the given address book item (either a property or parameter value)
</span><span class="lines">@@ -177,6 +265,8 @@
</span><span class="cx">     Limits a search to specific properties.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = &quot;PropertyFilter&quot;
+
</ins><span class="cx">     def _match(self, vcard):
</span><span class="cx">         # At least one property must match (or is-not-defined is set)
</span><span class="cx">         for property in vcard.properties():
</span><span class="lines">@@ -198,13 +288,17 @@
</span><span class="cx">         # No tests
</span><span class="cx">         return True
</span><span class="cx"> 
</span><ins>+FilterBase.serialize_register(PropertyFilter)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class ParameterFilter (FilterChildBase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Limits a search to specific parameters.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = &quot;ParameterFilter&quot;
+
</ins><span class="cx">     def _match(self, property):
</span><span class="cx"> 
</span><span class="cx">         # At least one parameter must match (or is-not-defined is set)
</span><span class="lines">@@ -216,13 +310,17 @@
</span><span class="cx"> 
</span><span class="cx">         return result
</span><span class="cx"> 
</span><ins>+FilterBase.serialize_register(ParameterFilter)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class IsNotDefined (FilterBase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Specifies that the named iCalendar item does not exist.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    serialized_name = &quot;IsNotDefined&quot;
+
</ins><span class="cx">     def match(self, component, access=None):
</span><span class="cx">         # Oddly, this needs always to return True so that it appears there is
</span><span class="cx">         # a match - but we then &quot;negate&quot; the result if is-not-defined is set.
</span><span class="lines">@@ -230,15 +328,21 @@
</span><span class="cx">         # is-not-defined option.
</span><span class="cx">         return True
</span><span class="cx"> 
</span><ins>+FilterBase.serialize_register(IsNotDefined)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class TextMatch (FilterBase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Specifies a substring match on a property or parameter value.
</span><span class="cx">     &quot;&quot;&quot;
</span><ins>+    serialized_name = &quot;TextMatch&quot;
+
</ins><span class="cx">     def __init__(self, xml_element):
</span><span class="cx"> 
</span><span class="cx">         super(TextMatch, self).__init__(xml_element)
</span><ins>+        if xml_element is None:
+            return
</ins><span class="cx"> 
</span><span class="cx">         self.text = str(xml_element)
</span><span class="cx"> 
</span><span class="lines">@@ -268,6 +372,30 @@
</span><span class="cx">             self.match_type = &quot;contains&quot;
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def _deserialize(self, data):
+        &quot;&quot;&quot;
+        Convert a JSON compatible serialization of this object into the actual object.
+        &quot;&quot;&quot;
+        self.text = data[&quot;text&quot;]
+        self.collation = data[&quot;collation&quot;]
+        self.negate = data[&quot;negate&quot;]
+        self.match_type = data[&quot;match_type&quot;]
+
+
+    def serialize(self):
+        &quot;&quot;&quot;
+        Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+        &quot;&quot;&quot;
+        result = super(TextMatch, self).serialize()
+        result.update({
+            &quot;text&quot;: self.text,
+            &quot;collation&quot;: self.collation,
+            &quot;negate&quot;: self.negate,
+            &quot;match_type&quot;: self.match_type,
+        })
+        return result
+
+
</ins><span class="cx">     def _match(self, item):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Match the text for the item.
</span><span class="lines">@@ -312,3 +440,5 @@
</span><span class="cx">                     return not self.negate
</span><span class="cx"> 
</span><span class="cx">         return self.negate
</span><ins>+
+FilterBase.serialize_register(TextMatch)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastorequerytesttest_filterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/test/test_filter.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/test/test_filter.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/test/test_filter.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -20,7 +20,7 @@
</span><span class="cx"> 
</span><span class="cx"> from twistedcaldav import carddavxml
</span><span class="cx"> 
</span><del>-from txdav.carddav.datastore.query.filter import Filter
</del><ins>+from txdav.carddav.datastore.query.filter import Filter, FilterBase
</ins><span class="cx"> from txdav.common.datastore.sql_tables import schema
</span><span class="cx"> from txdav.carddav.datastore.query.builder import buildExpression
</span><span class="cx"> from txdav.common.datastore.query.generator import SQLQueryGenerator
</span><span class="lines">@@ -72,3 +72,25 @@
</span><span class="cx"> 
</span><span class="cx">         self.assertEqual(sql, &quot; from RESOURCE where RESOURCE.UID GLOB :1&quot;)
</span><span class="cx">         self.assertEqual(args, [&quot;*Example*&quot;])
</span><ins>+
+
+
+class TestQueryFilterSerialize(TestCase):
+
+    def test_query(self):
+        &quot;&quot;&quot;
+        Basic query test - no time range
+        &quot;&quot;&quot;
+
+        filter = carddavxml.Filter(
+            *[carddavxml.PropertyFilter(
+                carddavxml.TextMatch.fromString(&quot;Example&quot;),
+                **{&quot;name&quot;:&quot;UID&quot;}
+            )]
+        )
+        filter = Filter(filter)
+        j = filter.serialize()
+        self.assertEqual(j[&quot;type&quot;], &quot;Filter&quot;)
+
+        f = FilterBase.deserialize(j)
+        self.assertTrue(isinstance(f, Filter))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/sql.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/sql.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/sql.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -806,6 +806,13 @@
</span><span class="cx">             C{name} is the resource name, C{uid} is the resource UID.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+        # We might be passed an L{Filter} or a serialization of one
+        if isinstance(filter, dict):
+            try:
+                filter = Filter.deserialize(filter)
+            except Exception:
+                filter = None
+
</ins><span class="cx">         # Make sure we have a proper Filter element and get the partial SQL statement to use.
</span><span class="cx">         sql_stmt = self._sqlquery(filter)
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastorepoddingconduitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/conduit.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/conduit.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/conduit.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -713,6 +713,7 @@
</span><span class="cx"> PoddingConduit._make_simple_homechild_action(&quot;resourcenamessincerevision&quot;, &quot;resourceNamesSinceRevision&quot;, transform_send=PoddingConduit._to_tuple)
</span><span class="cx"> PoddingConduit._make_simple_homechild_action(&quot;resourceuidforname&quot;, &quot;resourceUIDForName&quot;)
</span><span class="cx"> PoddingConduit._make_simple_homechild_action(&quot;resourcenameforuid&quot;, &quot;resourceNameForUID&quot;)
</span><ins>+PoddingConduit._make_simple_homechild_action(&quot;search&quot;, &quot;search&quot;)
</ins><span class="cx"> 
</span><span class="cx"> # Calls on L{CommonObjectResource} objects
</span><span class="cx"> PoddingConduit._make_simple_object_action(&quot;loadallobjects&quot;, &quot;loadAllObjects&quot;, transform_recv=PoddingConduit._to_externalize)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastorepoddingtesttest_conduitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/test/test_conduit.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/test/test_conduit.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/test/test_conduit.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -14,28 +14,35 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx"> 
</span><ins>+from pycalendar.datetime import DateTime
+from pycalendar.period import Period
+
</ins><span class="cx"> from twext.python.clsprop import classproperty
</span><del>-import twext.web2.dav.test.util
</del><ins>+
</ins><span class="cx"> from twisted.internet.defer import inlineCallbacks, succeed, returnValue
</span><ins>+
+from twistedcaldav import caldavxml
+from twistedcaldav.caldavxml import TimeRange
+from twistedcaldav.ical import Component, normalize_iCalStr
+
+from txdav.caldav.datastore.query.filter import Filter
+from txdav.caldav.datastore.scheduling.freebusy import generateFreeBusyInfo
</ins><span class="cx"> from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers, Server
</span><span class="cx"> from txdav.caldav.datastore.test.util import buildCalendarStore, \
</span><span class="cx">     TestCalendarStoreDirectoryRecord
</span><del>-from txdav.common.datastore.podding.resource import ConduitResource
-from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
</del><span class="cx"> from txdav.common.datastore.podding.conduit import PoddingConduit, \
</span><span class="cx">     FailedCrossPodRequestError
</span><del>-from txdav.common.idirectoryservice import DirectoryRecordNotFoundError
</del><ins>+from txdav.common.datastore.podding.resource import ConduitResource
</ins><span class="cx"> from txdav.common.datastore.podding.test.util import MultiStoreConduitTest, \
</span><span class="cx">     FakeConduitRequest
</span><span class="cx"> from txdav.common.datastore.sql_tables import _BIND_STATUS_ACCEPTED
</span><del>-from pycalendar.datetime import DateTime
-from twistedcaldav.ical import Component, normalize_iCalStr
</del><ins>+from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
</ins><span class="cx"> from txdav.common.icommondatastore import ObjectResourceNameAlreadyExistsError, \
</span><span class="cx">     ObjectResourceNameNotAllowedError
</span><del>-from txdav.caldav.datastore.scheduling.freebusy import generateFreeBusyInfo
-from twistedcaldav.caldavxml import TimeRange
-from pycalendar.period import Period
</del><ins>+from txdav.common.idirectoryservice import DirectoryRecordNotFoundError
</ins><span class="cx"> 
</span><ins>+import twext.web2.dav.test.util
+
</ins><span class="cx"> class TestConduit (CommonCommonTests, twext.web2.dav.test.util.TestCase):
</span><span class="cx"> 
</span><span class="cx">     class FakeConduit(object):
</span><span class="lines">@@ -533,6 +540,39 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><ins>+    def test_search(self):
+        &quot;&quot;&quot;
+        Test that action=resourcenameforuid works.
+        &quot;&quot;&quot;
+
+        yield self.createShare(&quot;user01&quot;, &quot;puser01&quot;)
+
+        calendar1 = yield self.calendarUnderTest(home=&quot;user01&quot;, name=&quot;calendar&quot;)
+        yield  calendar1.createCalendarObjectWithName(&quot;1.ics&quot;, Component.fromString(self.caldata1))
+        yield self.commit()
+
+        filter = caldavxml.Filter(
+            caldavxml.ComponentFilter(
+                *[caldavxml.ComponentFilter(
+                    **{&quot;name&quot;:(&quot;VEVENT&quot;, &quot;VFREEBUSY&quot;, &quot;VAVAILABILITY&quot;)}
+                )],
+                **{&quot;name&quot;: &quot;VCALENDAR&quot;}
+            )
+        )
+        filter = Filter(filter)
+
+        calendar1 = yield self.calendarUnderTest(home=&quot;user01&quot;, name=&quot;calendar&quot;)
+        names = [item[0] for item in (yield calendar1.search(filter))]
+        self.assertEqual(names, [&quot;1.ics&quot;, ])
+        yield self.commit()
+
+        shared = yield self.calendarUnderTest(txn=self.newOtherTransaction(), home=&quot;puser01&quot;, name=&quot;shared-calendar&quot;)
+        names = [item[0] for item in (yield shared.search(filter))]
+        self.assertEqual(names, [&quot;1.ics&quot;, ])
+        yield self.otherCommit()
+
+
+    @inlineCallbacks
</ins><span class="cx">     def test_loadallobjects(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Test that action=loadallobjects works.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastoresql_externalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/sql_external.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/sql_external.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/sql_external.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -341,7 +341,18 @@
</span><span class="cx">         returnValue(names)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def search(self, filter, **kwargs):
+        try:
+            results = yield self._txn.store().conduit.send_search(self, filter.serialize(), **kwargs)
+        except NonExistentExternalShare:
+            yield self.fixNonExistentExternalShare()
+            raise ExternalShareFailed(&quot;External share does not exist&quot;)
</ins><span class="cx"> 
</span><ins>+        returnValue(results)
+
+
+
</ins><span class="cx"> class CommonObjectResourceExternal(CommonObjectResource):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     A CommonObjectResource for a resource not hosted on this system, but on another pod. This will forward
</span></span></pre>
</div>
</div>

</body>
</html>