[Testbot] Plone 5.0 - Python 2.7 - Build # 2458 - Still failing! - 7 failure(s)

jenkins at plone.org jenkins at plone.org
Wed May 7 12:48:22 UTC 2014


-------------------------------------------------------------------------------
Plone 5.0 - Python 2.7 - Build # 2458 - Still Failing!
-------------------------------------------------------------------------------

http://jenkins.plone.org/job/plone-5.0-python-2.7/2458/


-------------------------------------------------------------------------------
CHANGES
-------------------------------------------------------------------------------

Repository: plone.app.querystring
Branch: refs/heads/master
Date: 2014-02-18T18:05:51+02:00
Author: ichim-david (ichim-david) <ichim.david at gmail.com>
Commit: https://github.com/plone/plone.app.querystring/commit/f9f6ea8a6b376f42b1c49550f8ba08f6b2211b54

WIP: adding an show_inactive operation which should allow you to select which user roles can see the inactive objects of the collection query

Files changed:
M plone/app/querystring/profiles/default/registry.xml
M plone/app/querystring/queryparser.py

diff --git a/plone/app/querystring/profiles/default/registry.xml b/plone/app/querystring/profiles/default/registry.xml
index 07450f9..53c7d2b 100644
--- a/plone/app/querystring/profiles/default/registry.xml
+++ b/plone/app/querystring/profiles/default/registry.xml
@@ -155,6 +155,14 @@
     </records>
 
     <records interface="plone.app.querystring.interfaces.IQueryOperation"
+             prefix="plone.app.querystring.operation.string.showInactive">
+        <value key="title" i18n:translate="">Show Inactive</value>
+        <value key="description" i18n:translate="">The user roles which are allowed to see inactive content</value>
+        <value key="operation">plone.app.querystring.queryparser._showInactive</value>
+        <value key="widget">MultipleSelectionWidget</value>
+    </records>
+
+    <records interface="plone.app.querystring.interfaces.IQueryOperation"
              prefix="plone.app.querystring.operation.string.is">
         <value key="title" i18n:translate="">Is</value>
         <value key="description" i18n:translate="">Tip: you can use * to autocomplete.</value>
@@ -409,6 +417,19 @@
     </records>
 
     <records interface="plone.app.querystring.interfaces.IQueryField"
+             prefix="plone.app.querystring.field.show_inactive">
+        <value key="title" i18n:translate="">Show Inactive</value>
+        <value key="description" i18n:translate="">Select which roles have the permission to view inactive objects</value>
+        <value key="enabled">True</value>
+        <value key="sortable">False</value>
+        <value key="operations">
+            <element>plone.app.querystring.operation.string.showInactive</element>
+        </value>
+        <value key="vocabulary">plone.app.vocabularies.Roles</value>
+        <value key="group" i18n:translate="">Metadata</value>
+    </records>
+
+    <records interface="plone.app.querystring.interfaces.IQueryField"
              prefix="plone.app.querystring.field.review_state">
         <value key="title" i18n:translate="">Review state</value>
         <value key="description" i18n:translate="">An item's workflow state (e.g.published)</value>
diff --git a/plone/app/querystring/queryparser.py b/plone/app/querystring/queryparser.py
index c6023cd..a61b22b 100644
--- a/plone/app/querystring/queryparser.py
+++ b/plone/app/querystring/queryparser.py
@@ -17,6 +17,7 @@
 
 
 def parseFormquery(context, formquery, sort_on=None, sort_order=None):
+
     if not formquery:
         return {}
     reg = getUtility(IRegistry)
@@ -117,6 +118,15 @@ def _currentUser(context, row):
               },
           }
 
+def _showInactive(context, row):
+    """ Current user roles lookup in order to determine whether user should
+        be allowed to view inactive content
+    """
+    mt = getToolByName(context, 'portal_membership')
+    user = mt.getAuthenticatedMember()
+    value = True
+    return {row.index: value}
+
 
 def _lessThanRelativeDate(context, row):
     """ "Between now and N days from now." """


Repository: plone.app.querystring
Branch: refs/heads/master
Date: 2014-02-24T12:59:38+02:00
Author: ichim-david (ichim-david) <ichim.david at gmail.com>
Commit: https://github.com/plone/plone.app.querystring/commit/e2dcf9c411238be6cabb4f1d37ce77c0d119a9ef

Added vocabularies.py with customized Roles vocabularies since the default one from plone.app.vocabularies do not include the Anonymous role and made use of it within the showInactive logic

Files changed:
A plone/app/querystring/vocabularies.py
M plone/app/querystring/configure.zcml
M plone/app/querystring/profiles/default/registry.xml
M plone/app/querystring/querybuilder.py
M plone/app/querystring/queryparser.py

diff --git a/plone/app/querystring/configure.zcml b/plone/app/querystring/configure.zcml
index 300c366..d617831 100644
--- a/plone/app/querystring/configure.zcml
+++ b/plone/app/querystring/configure.zcml
@@ -64,4 +64,10 @@
     for="*"
     />
 
+  <utility
+    component=".vocabularies.RolesVocabularyFactory"
+    name="plone.app.querystring.Roles"
+    />
+
+
 </configure>
diff --git a/plone/app/querystring/profiles/default/registry.xml b/plone/app/querystring/profiles/default/registry.xml
index 53c7d2b..e9509d3 100644
--- a/plone/app/querystring/profiles/default/registry.xml
+++ b/plone/app/querystring/profiles/default/registry.xml
@@ -425,7 +425,7 @@
         <value key="operations">
             <element>plone.app.querystring.operation.string.showInactive</element>
         </value>
-        <value key="vocabulary">plone.app.vocabularies.Roles</value>
+        <value key="vocabulary">plone.app.querystring.Roles</value>
         <value key="group" i18n:translate="">Metadata</value>
     </records>
 
diff --git a/plone/app/querystring/querybuilder.py b/plone/app/querystring/querybuilder.py
index e2e3ed1..0396e66 100644
--- a/plone/app/querystring/querybuilder.py
+++ b/plone/app/querystring/querybuilder.py
@@ -112,8 +112,7 @@ def _makequery(self, query=None, batch=False, b_start=0, b_size=30,
 
         if limit and parsedquery.get('b_size', limit) >= limit:
             parsedquery['b_size'] = limit
-
-        results = catalog(parsedquery)
+        results = catalog(**parsedquery)
 
         if results and limit and results.actual_result_count > limit:
             results.actual_result_count = limit
diff --git a/plone/app/querystring/queryparser.py b/plone/app/querystring/queryparser.py
index a61b22b..cab3b86 100644
--- a/plone/app/querystring/queryparser.py
+++ b/plone/app/querystring/queryparser.py
@@ -124,7 +124,14 @@ def _showInactive(context, row):
     """
     mt = getToolByName(context, 'portal_membership')
     user = mt.getAuthenticatedMember()
-    value = True
+    value = False
+    user_roles = user.getRoles()
+    row_values = row.values
+    if row_values:
+        for role in user_roles:
+            if role in row_values:
+                value = True
+                break
     return {row.index: value}
 
 
diff --git a/plone/app/querystring/vocabularies.py b/plone/app/querystring/vocabularies.py
new file mode 100644
index 0000000..5df1095
--- /dev/null
+++ b/plone/app/querystring/vocabularies.py
@@ -0,0 +1,21 @@
+from zope.component import queryUtility
+from zope.interface import implements
+from zope.schema.interfaces import IVocabularyFactory
+from zope.schema.vocabulary import SimpleVocabulary
+
+
+class RolesVocabulary(object):
+    """Vocabulary factory for roles found in the portal plus the Anonymous Role
+    """
+    implements(IVocabularyFactory)
+
+    def __call__(self, context):
+        voc = queryUtility(IVocabularyFactory, 'plone.app.vocabularies.Roles')
+        if not voc:
+            voc = SimpleVocabulary([])
+        voc = voc(context)
+        anon = "Anonymous"
+        voc._terms.append(voc.createTerm(anon, anon, anon))
+        return SimpleVocabulary(voc._terms)
+
+RolesVocabularyFactory = RolesVocabulary()


Repository: plone.app.querystring
Branch: refs/heads/master
Date: 2014-02-24T15:06:56+02:00
Author: ichim-david (ichim-david) <ichim.david at gmail.com>
Commit: https://github.com/plone/plone.app.querystring/commit/88dc4ec198584f540252169532d88908bf2b1536

Added test for the showInactive operation

Files changed:
M plone/app/querystring/tests/testQueryParser.py

diff --git a/plone/app/querystring/tests/testQueryParser.py b/plone/app/querystring/tests/testQueryParser.py
index e7b823f..b5da4db 100644
--- a/plone/app/querystring/tests/testQueryParser.py
+++ b/plone/app/querystring/tests/testQueryParser.py
@@ -84,14 +84,18 @@ def getPhysicalPath(self):
 
 class MockUser(object):
 
-    def __init__(self, username=None):
+    def __init__(self, username=None, roles=None):
         self.username = 'Anonymous User'
         if username:
             self.username = username
+        self.roles = roles or "Anonymous"
 
     def getUserName(self):
         return self.username
 
+    def getRoles(self):
+        return self.roles
+
 
 class MockPortal_membership(object):
 
@@ -227,6 +231,31 @@ def test__currentUser(self):
         expected = {'Creator': {'query': 'admin'}}
         self.assertEqual(parsed, expected)
 
+    def test__showInactive(self):
+        # Anonymous user
+        u = MockUser()
+        pm = MockPortal_membership(user=u)
+        context = MockSite(portal_membership=pm)
+        data = Row(index='show_inactive',
+                   operator='_showInactive',
+                   values=["Manager"])
+        parsed = queryparser._showInactive(context, data)
+        # False is expected since Anonymous doesn't have Manager role
+        expected = {'show_inactive': False}
+        self.assertEqual(parsed, expected)
+
+        # Logged in user 'admin'
+        u = MockUser(username='admin', roles=("Manager",))
+        pm = MockPortal_membership(user=u)
+        context = MockSite(portal_membership=pm)
+        data = Row(index='show_inactive',
+                   operator='_showInactive',
+                   values=["Manager"])
+        parsed = queryparser._showInactive(context, data)
+        # True is expected since Admin should have Manager role
+        expected = {'show_inactive': True}
+        self.assertEqual(parsed, expected)
+
     def test__lessThanRelativeDate(self):
         days = 2
         now = DateTime()


Repository: plone.app.querystring
Branch: refs/heads/master
Date: 2014-02-24T15:16:23+02:00
Author: ichim-david (ichim-david) <ichim.david at gmail.com>
Commit: https://github.com/plone/plone.app.querystring/commit/174f18ab980dd3b79740add4990fa908c32e5173

Added Changes.rst entry for the showInactive operator

Files changed:
M CHANGES.rst

diff --git a/CHANGES.rst b/CHANGES.rst
index 00b7c09..c3fbba6 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -4,7 +4,9 @@ Changelog
 1.1.2 (unreleased)
 ------------------
 
-- Nothing changed yet.
+- Added show inactive operation which uses the roles vocabulary in order
+  to assign permission to show or hide the inactive objects of the given query
+  [ichim-david]
 
 
 1.1.1 (2014-01-27)


Repository: plone.app.querystring
Branch: refs/heads/master
Date: 2014-04-14T12:12:05+03:00
Author: ichim-david (ichim-david) <ichim.david at gmail.com>
Commit: https://github.com/plone/plone.app.querystring/commit/67a4c79e23303289aa16a61a95be13ed7a006f04

Updated branch with latest changed from master

Files changed:
A plone/app/querystring/indexmodifiers/__init__.py
A plone/app/querystring/indexmodifiers/configure.zcml
A plone/app/querystring/indexmodifiers/query_index_modifiers.py
A plone/app/querystring/tests/index_testmodifier.py
A plone/app/querystring/tests/registry_minimal_correct.xml
A plone/app/querystring/tests/registry_test_missing_operator.xml
A plone/app/querystring/tests/registry_test_vocabulary.xml
A plone/app/querystring/tests/testQueryBuilderModifiers.py
A plone/app/querystring/upgrades.py
M CHANGES.rst
M plone/app/querystring/__init__.py
M plone/app/querystring/configure.zcml
M plone/app/querystring/interfaces.py
M plone/app/querystring/profiles/default/metadata.xml
M plone/app/querystring/profiles/default/registry.xml
M plone/app/querystring/querybuilder.py
M plone/app/querystring/queryparser.py
M plone/app/querystring/registryreader.py
M plone/app/querystring/tests/registry_testdata.py
M plone/app/querystring/tests/testQueryBuilder.py
M plone/app/querystring/tests/testQueryParser.py
M plone/app/querystring/tests/testRegistryIntegration.py
M plone/app/querystring/tests/testRegistryReader.py
M setup.py

diff --git a/CHANGES.rst b/CHANGES.rst
index c3fbba6..3ff2cb9 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,7 +1,7 @@
 Changelog
 =========
 
-1.1.2 (unreleased)
+1.2.1 (unreleased)
 ------------------
 
 - Added show inactive operation which uses the roles vocabulary in order
@@ -9,10 +9,30 @@ Changelog
   [ichim-david]
 
 
+1.2.0 (2014-04-05)
+------------------
+
+- bugfix for #22: Names not matching for operations getObjPositionInParent
+  plus test
+  [jensens]
+
+- Implement multipath queries:
+  - Parsing a path returns always a list.
+  - Special handling for paths in parseFormquery.
+  [maethu]
+
+- Fixes https://dev.plone.org/ticket/13251
+  [mathias.leimgruber]
+
+- querybuilder results can now be manipulated using
+  ``IParsedQueryIndexModifier`` named utilities.
+  [keul]
+
+
 1.1.1 (2014-01-27)
 ------------------
 
-- fixed broken handling of limit and batch size. 
+- fixed broken handling of limit and batch size.
   [bosim]
 
 - pep8 fixes
@@ -28,7 +48,6 @@ Changelog
 - Use plone.batching.
   [khink]
 
-
 1.0.8 (2013-03-14)
 ------------------
 
diff --git a/plone/app/querystring/__init__.py b/plone/app/querystring/__init__.py
index 4287ca8..792d600 100644
--- a/plone/app/querystring/__init__.py
+++ b/plone/app/querystring/__init__.py
@@ -1 +1 @@
-#
\ No newline at end of file
+#
diff --git a/plone/app/querystring/configure.zcml b/plone/app/querystring/configure.zcml
index d617831..02df5fe 100644
--- a/plone/app/querystring/configure.zcml
+++ b/plone/app/querystring/configure.zcml
@@ -6,6 +6,7 @@
 
   <include package="plone.app.contentlisting" />
   <include package="plone.app.registry" />
+  <include package=".indexmodifiers" />
 
   <genericsetup:registerProfile
     name="default"
@@ -15,6 +16,15 @@
     provides="Products.GenericSetup.interfaces.EXTENSION"
     />
 
+  <genericsetup:upgradeStep
+      source="1"
+      destination="2"
+      title="Fix Typo in Registry"
+      description="Migrate registry to fix a typo."
+      profile="plone.app.querystring:default"
+      handler=".upgrades.upgrade_1_to_2_typo_in_registry"
+      />
+
   <adapter
     for="plone.registry.interfaces.IRegistry"
     factory=".registryreader.QuerystringRegistryReader"
diff --git a/plone/app/querystring/indexmodifiers/__init__.py b/plone/app/querystring/indexmodifiers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/plone/app/querystring/indexmodifiers/configure.zcml b/plone/app/querystring/indexmodifiers/configure.zcml
new file mode 100644
index 0000000..7c8e139
--- /dev/null
+++ b/plone/app/querystring/indexmodifiers/configure.zcml
@@ -0,0 +1,11 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="plone">
+
+   <utility
+      factory=".query_index_modifiers.Subject"
+      provides="..interfaces.IParsedQueryIndexModifier"
+      name="Subject"
+      />
+
+</configure>
diff --git a/plone/app/querystring/indexmodifiers/query_index_modifiers.py b/plone/app/querystring/indexmodifiers/query_index_modifiers.py
new file mode 100644
index 0000000..6bb3981
--- /dev/null
+++ b/plone/app/querystring/indexmodifiers/query_index_modifiers.py
@@ -0,0 +1,40 @@
+# -*- coding: utf8 -*-
+from plone.app.querystring.interfaces import IParsedQueryIndexModifier
+from zope.interface import implements
+
+
+class Subject(object):
+    """
+    The Subject field in Plone currently uses a utf-8 encoded string.
+    When a catalog query tries to compare a unicode string from the
+    parsedquery with existing utf-8 encoded string indexes unindexing
+    will fail with a UnicodeDecodeError. To prevent this from happening
+    we always encode the Subject query.
+
+    XXX: As soon as Plone uses unicode for all indexes, this code can
+    be removed.
+    """
+
+    implements(IParsedQueryIndexModifier)
+
+    def __call__(self, value):
+        query = value['query']
+        # query can be a unicode string or a list of unicode strings.
+        if isinstance(query, unicode):
+            query = query.encode("utf-8")
+        elif isinstance(query, list):
+            # We do not want to change the collections' own query string,
+            # therefore we create a new copy of the list.
+            copy_of_query = list(query)
+            # Iterate over all query items and encode them if they are
+            # unicode strings
+            i = 0
+            for item in copy_of_query:
+                if isinstance(item, unicode):
+                    copy_of_query[i] = item.encode("utf-8")
+                i += 1
+            query = copy_of_query
+        else:
+            pass
+        value['query'] = query
+        return ('Subject', value)
diff --git a/plone/app/querystring/interfaces.py b/plone/app/querystring/interfaces.py
index 9f51539..aacf056 100644
--- a/plone/app/querystring/interfaces.py
+++ b/plone/app/querystring/interfaces.py
@@ -1,12 +1,18 @@
 from zope.interface import Interface
-from zope.schema import TextLine, Text, Bool, List, DottedName
+from zope.schema import Bool
+from zope.schema import DottedName
+from zope.schema import List
+from zope.schema import Text
+from zope.schema import TextLine
 
 
 class IQuerystringRegistryReader(Interface):
-    """Adapts a registry object to parse the querystring data"""
+    """Adapts a registry object to parse the querystring data
+    """
 
     def __call__():
-        """Return query string in dict-format."""
+        """Return query string in dict-format.
+        """
 
 
 class IQueryOperation(Interface):
@@ -25,3 +31,15 @@ class IQueryField(Interface):
                       value_type=DottedName(title=u"Operation ID"))
     vocabulary = TextLine(title=u"Vocabulary")
     group = TextLine(title=u"Group")
+
+
+class IParsedQueryIndexModifier(Interface):
+    """Transform a parsed query index in something different
+    """
+
+    def __call__(value):
+        """
+        Return a tuple with a new index name and a new value.
+        if the index name returned is different from the native one, caller
+        must replace treated index with the new ones.
+        """
diff --git a/plone/app/querystring/profiles/default/metadata.xml b/plone/app/querystring/profiles/default/metadata.xml
index 06dcbf7..103e8eb 100644
--- a/plone/app/querystring/profiles/default/metadata.xml
+++ b/plone/app/querystring/profiles/default/metadata.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <metadata>
-  <version>1</version>
+  <version>2</version>
   <dependencies>
       <dependency>profile-plone.app.registry:default</dependency>
   </dependencies>
diff --git a/plone/app/querystring/profiles/default/registry.xml b/plone/app/querystring/profiles/default/registry.xml
index e9509d3..fa49fed 100644
--- a/plone/app/querystring/profiles/default/registry.xml
+++ b/plone/app/querystring/profiles/default/registry.xml
@@ -322,7 +322,7 @@
         <value key="operations">
             <element>plone.app.querystring.operation.int.is</element>
             <element>plone.app.querystring.operation.int.lessThan</element>
-            <element>plone.app.querystring.operation.int.greaterThan</element>
+            <element>plone.app.querystring.operation.int.largerThan</element>
         </value>
        <value key="group" i18n:translate="">Metadata</value>
     </records>
diff --git a/plone/app/querystring/querybuilder.py b/plone/app/querystring/querybuilder.py
index 0396e66..aac1e4a 100644
--- a/plone/app/querystring/querybuilder.py
+++ b/plone/app/querystring/querybuilder.py
@@ -1,21 +1,23 @@
 import json
+import logging
 
-from zope.component import getMultiAdapter, getUtility
+from zope.component import getMultiAdapter, getUtility, getUtilitiesFor
 from zope.i18n import translate
 
 from zope.i18nmessageid import MessageFactory
 from zope.publisher.browser import BrowserView
 
-
 from plone.app.contentlisting.interfaces import IContentListing
 from plone.registry.interfaces import IRegistry
 from plone.app.querystring import queryparser
+from plone.app.querystring.interfaces import IParsedQueryIndexModifier
 
 from Products.CMFCore.utils import getToolByName
 from plone.batching import Batch
 
 from .interfaces import IQuerystringRegistryReader
 
+logger = logging.getLogger('plone.app.querystring')
 _ = MessageFactory('plone')
 
 
@@ -67,13 +69,36 @@ def _makequery(self, query=None, batch=False, b_start=0, b_size=30,
         """Parse the (form)query and return using multi-adapter"""
         parsedquery = queryparser.parseFormquery(
             self.context, query, sort_on, sort_order)
+
+        index_modifiers = getUtilitiesFor(IParsedQueryIndexModifier)
+        for name, modifier in index_modifiers:
+            if name in parsedquery:
+                new_name, query = modifier(parsedquery[name])
+                parsedquery[name] = query
+                # if a new index name has been returned, we need to replace
+                # the native ones
+                if name != new_name:
+                    del parsedquery[name]
+                    parsedquery[new_name] = query
+
+       # Check for valid indexes
+        catalog = getToolByName(self.context, 'portal_catalog')
+        valid_indexes = [index for index in parsedquery
+                         if index in catalog.indexes()]
+
+        # We'll ignore any invalid index, but will return an empty set if none
+        # of the indexes are valid.
+        if not valid_indexes:
+            logger.warning(
+                "Using empty query because there are no valid indexes used.")
+            parsedquery = {}
+
         if not parsedquery:
             if brains:
                 return []
             else:
                 return IContentListing([])
 
-        catalog = getToolByName(self.context, 'portal_catalog')
         if batch:
             parsedquery['b_start'] = b_start
             parsedquery['b_size'] = b_size
@@ -83,35 +108,6 @@ def _makequery(self, query=None, batch=False, b_start=0, b_size=30,
         if 'path' not in parsedquery:
             parsedquery['path'] = {'query': ''}
 
-        # The Subject field in Plone currently uses a utf-8 encoded string.
-        # When a catalog query tries to compare a unicode string from the
-        # parsedquery with existing utf-8 encoded string indexes unindexing
-        # will fail with a UnicodeDecodeError. To prevent this from happening
-        # we always encode the Subject query.
-        # XXX: As soon as Plone uses unicode for all indexes, this code can
-        # be removed.
-        if 'Subject' in parsedquery:
-            query = parsedquery['Subject']['query']
-            # query can be a unicode string or a list of unicode strings.
-            if isinstance(query, unicode):
-                parsedquery['Subject']['query'] = query.encode("utf-8")
-            elif isinstance(query, list):
-                # We do not want to change the collections' own query string,
-                # therefore we create a new copy of the list.
-                copy_of_query = list(query)
-                # Iterate over all query items and encode them if they are
-                # unicode strings
-                i = 0
-                for item in copy_of_query:
-                    if isinstance(item, unicode):
-                        copy_of_query[i] = item.encode("utf-8")
-                    i += 1
-                parsedquery['Subject']['query'] = copy_of_query
-            else:
-                pass
-
-        if limit and parsedquery.get('b_size', limit) >= limit:
-            parsedquery['b_size'] = limit
         results = catalog(**parsedquery)
 
         if results and limit and results.actual_result_count > limit:
diff --git a/plone/app/querystring/queryparser.py b/plone/app/querystring/queryparser.py
index cab3b86..d3fccf7 100644
--- a/plone/app/querystring/queryparser.py
+++ b/plone/app/querystring/queryparser.py
@@ -1,18 +1,14 @@
-from collections import namedtuple
-import logging
-
 from Acquisition import aq_parent
 from DateTime import DateTime
-from plone.app.layout.navigation.interfaces import INavigationRoot
-from plone.registry.interfaces import IRegistry
 from Products.CMFCore.utils import getToolByName
 from Products.CMFPlone.browser.navtree import getNavigationRoot
 from Products.CMFPlone.utils import base_hasattr
+from collections import namedtuple
+from plone.app.layout.navigation.interfaces import INavigationRoot
+from plone.registry.interfaces import IRegistry
 from zope.component import getUtility
 from zope.dottedname.resolve import resolve
 
-logger = logging.getLogger('plone.app.querystring')
-
 Row = namedtuple('Row', ['index', 'operator', 'values'])
 
 
@@ -39,23 +35,17 @@ def parseFormquery(context, formquery, sort_on=None, sort_order=None):
         kwargs = {}
         parser = resolve(row.operator)
         kwargs = parser(context, row)
-        query.update(kwargs)
+
+        # Special path handling - since multipath queries are possible
+        if 'path' in query and 'path' in kwargs:
+            query['path']['query'].extend(kwargs['path']['query'])
+        else:
+            query.update(kwargs)
 
     if not query:
         # If the query is empty fall back onto the equality query
         query = _equal(context, row)
 
-    # Check for valid indexes
-    catalog = getToolByName(context, 'portal_catalog')
-    valid_indexes = [index for index in query if index in catalog.indexes()]
-
-    # We'll ignore any invalid index, but will return an empty set if none of
-    # the indexes are valid.
-    if not valid_indexes:
-        logger.warning(
-            "Using empty query because there are no valid indexes used.")
-        return {}
-
     # Add sorting (sort_on and sort_order) to the query
     if sort_on:
         query['sort_on'] = sort_on
@@ -83,29 +73,32 @@ def _isFalse(context, row):
 
 
 def _between(context, row):
-    tmp = {row.index: {
-              'query': sorted(row.values),
-              'range': 'minmax',
-              },
-          }
+    tmp = {row.index:
+           {
+               'query': sorted(row.values),
+               'range': 'minmax',
+           },
+           }
     return tmp
 
 
 def _largerThan(context, row):
-    tmp = {row.index: {
-              'query': row.values,
-              'range': 'min',
-              },
-          }
+    tmp = {row.index:
+           {
+               'query': row.values,
+               'range': 'min',
+           },
+           }
     return tmp
 
 
 def _lessThan(context, row):
-    tmp = {row.index: {
-              'query': row.values,
-              'range': 'max',
-              },
-          }
+    tmp = {row.index:
+           {
+               'query': row.values,
+               'range': 'max',
+           },
+           }
     return tmp
 
 
@@ -113,10 +106,7 @@ def _currentUser(context, row):
     """Current user lookup"""
     mt = getToolByName(context, 'portal_membership')
     user = mt.getAuthenticatedMember()
-    return {row.index: {
-              'query': user.getUserName(),
-              },
-          }
+    return {row.index: {'query': user.getUserName()}}
 
 def _showInactive(context, row):
     """ Current user roles lookup in order to determine whether user should
@@ -230,14 +220,16 @@ def _path(context, row):
     if not values.startswith(nav_root):
         values = nav_root + values
 
-    query = {'query': values}
+    query = {}
     if depth is not None:
         query['depth'] = depth
         # when a depth value is specified, a trailing slash matters on the
         # query
-        query['query'] = query['query'].rstrip('/')
-    tmp = {row.index: query}
-    return tmp
+        values = values.rstrip('/')
+
+    query['query'] = [values]
+
+    return {row.index: query}
 
 
 def _relativePath(context, row):
diff --git a/plone/app/querystring/registryreader.py b/plone/app/querystring/registryreader.py
index b3bc65d..8060a2f 100644
--- a/plone/app/querystring/registryreader.py
+++ b/plone/app/querystring/registryreader.py
@@ -1,19 +1,16 @@
+from .interfaces import IQuerystringRegistryReader
 from operator import attrgetter
-import logging
-
-from plone.registry.interfaces import IRegistry
 from zope.component import queryUtility
-from zope.component import adapts
-from zope.interface import implements
 from zope.globalrequest import getRequest
 from zope.i18n import translate
 from zope.i18nmessageid import Message
+from zope.interface import implements
 from zope.schema.interfaces import IVocabularyFactory
-
-from .interfaces import IQuerystringRegistryReader
+import logging
 
 logger = logging.getLogger("plone.app.querystring")
 
+
 class DottedDict(dict):
     """A dictionary where you can access nested dicts with dotted names"""
 
diff --git a/plone/app/querystring/tests/index_testmodifier.py b/plone/app/querystring/tests/index_testmodifier.py
new file mode 100644
index 0000000..daa87fe
--- /dev/null
+++ b/plone/app/querystring/tests/index_testmodifier.py
@@ -0,0 +1,35 @@
+# -*- coding: utf8 -*-
+
+from zope.interface import implements
+from plone.app.querystring.interfaces import IParsedQueryIndexModifier
+
+
+class SimpleFooIndexModifier(object):
+    """Test simple index modifier that do nothing"""
+
+    implements(IParsedQueryIndexModifier)
+
+    def __call__(self, value):
+        raise Exception("SimpleFooIndexModifier has been called!")
+
+
+class TitleFooIndexModifier(object):
+    """Test index modifier that check always Foo"""
+
+    implements(IParsedQueryIndexModifier)
+
+    def __call__(self, value):
+        return ('Title', 'Foo')
+
+
+class AbstractToDescriptionIndexModifier(object):
+    """
+    Test index modifier that translate "Abstract" to Description index
+    but where value do not count letter "e"
+    """
+
+    implements(IParsedQueryIndexModifier)
+
+    def __call__(self, value):
+        value['query'] = value['query'].replace('e', '')
+        return ('Description', value)
diff --git a/plone/app/querystring/tests/registry_minimal_correct.xml b/plone/app/querystring/tests/registry_minimal_correct.xml
new file mode 100644
index 0000000..5d61538
--- /dev/null
+++ b/plone/app/querystring/tests/registry_minimal_correct.xml
@@ -0,0 +1,50 @@
+<registry>
+    <records interface="plone.app.querystring.interfaces.IQueryOperation"
+             prefix="plone.app.querystring.operation.string.is">
+        <value key="title">equals</value>
+        <value key="description">Tip: you can use * to autocomplete.</value>
+        <value key="operation">plone.app.querystring.queryparser:_equal</value>
+        <value key="widget"></value>
+    </records>
+
+    <records interface="plone.app.querystring.interfaces.IQueryOperation"
+             prefix="plone.app.querystring.operation.date.lessThan">
+        <value key="title">before</value>
+        <value key="description">Please use YYYY/MM/DD.</value>
+        <value key="operation">plone.app.querystring.queryparser:_lessThan</value>
+        <value key="widget"></value>
+    </records>
+
+    <records interface="plone.app.querystring.interfaces.IQueryOperation"
+             prefix="plone.app.querystring.operation.date.largerThan">
+        <value key="title">after</value>
+        <value key="description">Please use YYYY/MM/DD.</value>
+        <value key="operation">plone.app.querystring.queryparser:_largerThan</value>
+        <value key="widget"></value>
+    </records>
+
+    <records interface="plone.app.querystring.interfaces.IQueryField"
+             prefix="plone.app.querystring.field.getId">
+       <value key="title">Short Name</value>
+       <value key="description">The short name of an item (used in the url)</value>
+       <value key="enabled">True</value>
+       <value key="sortable">True</value>
+       <value key="operations">
+            <element>plone.app.querystring.operation.string.is</element>
+       </value>
+       <value key="group">Metadata</value>
+    </records>
+
+    <records interface="plone.app.querystring.interfaces.IQueryField"
+             prefix="plone.app.querystring.field.created">
+       <value key="title">Creation Date</value>
+       <value key="description">The time and date an item was created</value>
+       <value key="enabled">True</value>
+       <value key="sortable">False</value>
+       <value key="operations">
+           <element>plone.app.querystring.operation.date.lessThan</element>
+           <element>plone.app.querystring.operation.date.largerThan</element>
+       </value>
+       <value key="group">Dates</value>
+    </records>
+</registry>
\ No newline at end of file
diff --git a/plone/app/querystring/tests/registry_test_missing_operator.xml b/plone/app/querystring/tests/registry_test_missing_operator.xml
new file mode 100644
index 0000000..81de888
--- /dev/null
+++ b/plone/app/querystring/tests/registry_test_missing_operator.xml
@@ -0,0 +1,22 @@
+<registry>
+    <records interface="plone.app.querystring.interfaces.IQueryOperation"
+             prefix="plone.app.querystring.operation.date.lessThan">
+        <value key="title">before</value>
+        <value key="description">Please use YYYY/MM/DD.</value>
+        <value key="operation">plone.app.querystring.queryparser:_lessThan</value>
+        <value key="widget"></value>
+    </records>
+
+    <records interface="plone.app.querystring.interfaces.IQueryField"
+             prefix="plone.app.querystring.field.created">
+       <value key="title">Creation Date</value>
+       <value key="description">The time and date an item was created</value>
+       <value key="enabled">True</value>
+       <value key="sortable">False</value>
+       <value key="operations">
+           <element>plone.app.querystring.operation.date.lessThan</element>
+           <element>plone.app.querystring.operation.date.largerThan</element>
+       </value>
+       <value key="group">Dates</value>
+    </records>
+</registry>
\ No newline at end of file
diff --git a/plone/app/querystring/tests/registry_test_vocabulary.xml b/plone/app/querystring/tests/registry_test_vocabulary.xml
new file mode 100644
index 0000000..6b0affa
--- /dev/null
+++ b/plone/app/querystring/tests/registry_test_vocabulary.xml
@@ -0,0 +1,22 @@
+<registry>
+    <records interface="plone.app.querystring.interfaces.IQueryOperation"
+             prefix="plone.app.querystring.operation.string.is">
+        <value key="title">equals</value>
+        <value key="description">Tip: you can use * to autocomplete.</value>
+        <value key="operation">plone.app.querystring.queryparser:_equal</value>
+        <value key="widget"></value>
+    </records>
+
+    <records interface="plone.app.querystring.interfaces.IQueryField"
+             prefix="plone.app.querystring.field.reviewState">
+        <value key="title">Review state</value>
+        <value key="description">An item's workflow state (e.g.published)</value>
+        <value key="enabled">True</value>
+        <value key="sortable">True</value>
+        <value key="operations">
+            <element>plone.app.querystring.operation.string.is</element>
+        </value>
+        <value key="vocabulary">plone.app.querystring.tests.testvocabulary</value>
+       <value key="group">Metadata</value>
+    </records>
+</registry>
\ No newline at end of file
diff --git a/plone/app/querystring/tests/registry_testdata.py b/plone/app/querystring/tests/registry_testdata.py
index dc62933..619033f 100644
--- a/plone/app/querystring/tests/registry_testdata.py
+++ b/plone/app/querystring/tests/registry_testdata.py
@@ -1,104 +1,74 @@
-parsed_correct = {'plone': {'app': {'querystring': {'field': {'getId': {'operations': ['plone.app.querystring.operation.string.is'], 'group': u'Metadata', 'description': u'The short name of an item (used in the url)', 'vocabulary': None, 'title': u'Short Name', 'enabled': True, 'sortable': True}, 'created': {'operations': ['plone.app.querystring.operation.date.lessThan', 'plone.app.querystring.operation.date.largerThan'], 'group': u'Dates', 'description': u'The time and date an item was created', 'vocabulary': None, 'title': u'Creation Date', 'enabled': True, 'sortable': False}}, 'operation': {'date': {'largerThan': {'widget': None, 'operation': u'plone.app.querystring.queryparser:_largerThan', 'description': u'Please use YYYY/MM/DD.', 'title': u'after'}, 'lessThan': {'widget': None, 'operation': u'plone.app.querystring.queryparser:_lessThan', 'description': u'Please use YYYY/MM/DD.', 'title': u'before'}}, 'string': {'is': {'widget': None, 'operation': u'plone.app.querystring.queryparser:_equal', 'description': u'Tip: you can use * to autocomplete.', 'title': u'equals'}}}}}}}
+# -*- coding: utf-8 -*-
+import os
 
-minimal_correct_xml = """
-<registry>
-    <records interface="plone.app.querystring.interfaces.IQueryOperation"
-             prefix="plone.app.querystring.operation.string.is">
-        <value key="title">equals</value>
-        <value key="description">Tip: you can use * to autocomplete.</value>
-        <value key="operation">plone.app.querystring.queryparser:_equal</value>
-        <value key="widget"></value>
-    </records>
+parsed_correct = {
+    'plone': {
+        'app': {
+            'querystring': {
+                'field': {
+                    'getId': {
+                        'operations': [
+                            'plone.app.querystring.operation.string.is'],
+                        'group': u'Metadata',
+                        'description': u'The short name of an item '
+                                       u'(used in the url)',
+                        'vocabulary': None,
+                        'title': u'Short Name',
+                        'enabled': True,
+                        'sortable': True
+                    },
+                    'created': {
+                        'operations': [
+                            'plone.app.querystring.operation.date.lessThan',
+                            'plone.app.querystring.operation.date.largerThan'
+                        ],
+                        'group': u'Dates',
+                        'description': u'The time and date an item was '
+                                       u'created',
+                        'vocabulary': None,
+                        'title': u'Creation Date',
+                        'enabled': True,
+                        'sortable': False
+                    }
+                },
+                'operation': {
+                    'date': {
+                        'largerThan': {
+                            'widget': None,
+                            'operation': u'plone.app.querystring.queryparser'
+                                         u':_largerThan',
+                            'description': u'Please use YYYY/MM/DD.',
+                            'title': u'after'
+                        },
+                        'lessThan': {
+                            'widget': None,
+                            'operation': u'plone.app.querystring.queryparser:'
+                                         u'_lessThan',
+                            'description': u'Please use YYYY/MM/DD.',
+                            'title': u'before'
+                        }
+                    },
+                    'string': {
+                        'is': {
+                            'widget': None,
+                            'operation': u'plone.app.querystring.queryparser:'
+                                         u'_equal',
+                            'description': u'Tip: you can use * to '
+                                           u'autocomplete.',
+                            'title': u'equals'
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
 
-    <records interface="plone.app.querystring.interfaces.IQueryOperation"
-             prefix="plone.app.querystring.operation.date.lessThan">
-        <value key="title">before</value>
-        <value key="description">Please use YYYY/MM/DD.</value>
-        <value key="operation">plone.app.querystring.queryparser:_lessThan</value>
-        <value key="widget"></value>
-    </records>
 
-    <records interface="plone.app.querystring.interfaces.IQueryOperation"
-             prefix="plone.app.querystring.operation.date.largerThan">
-        <value key="title">after</value>
-        <value key="description">Please use YYYY/MM/DD.</value>
-        <value key="operation">plone.app.querystring.queryparser:_largerThan</value>
-        <value key="widget"></value>
-    </records>
+def reg_load_xml(filename):
+    with open(os.path.join(os.path.dirname(__file__), filename)) as rx:
+        return rx.read()
 
-    <records interface="plone.app.querystring.interfaces.IQueryField"
-             prefix="plone.app.querystring.field.getId">
-       <value key="title">Short Name</value>
-       <value key="description">The short name of an item (used in the url)</value>
-       <value key="enabled">True</value>
-       <value key="sortable">True</value>
-       <value key="operations">
-            <element>plone.app.querystring.operation.string.is</element>
-       </value>
-       <value key="group">Metadata</value>
-    </records>
-
-    <records interface="plone.app.querystring.interfaces.IQueryField"
-             prefix="plone.app.querystring.field.created">
-       <value key="title">Creation Date</value>
-       <value key="description">The time and date an item was created</value>
-       <value key="enabled">True</value>
-       <value key="sortable">False</value>
-       <value key="operations">
-           <element>plone.app.querystring.operation.date.lessThan</element>
-           <element>plone.app.querystring.operation.date.largerThan</element>
-       </value>
-       <value key="group">Dates</value>
-    </records>
-</registry>
-"""
-
-test_missing_operator_xml = """
-<registry>
-    <records interface="plone.app.querystring.interfaces.IQueryOperation"
-             prefix="plone.app.querystring.operation.date.lessThan">
-        <value key="title">before</value>
-        <value key="description">Please use YYYY/MM/DD.</value>
-        <value key="operation">plone.app.querystring.queryparser:_lessThan</value>
-        <value key="widget"></value>
-    </records>
-
-    <records interface="plone.app.querystring.interfaces.IQueryField"
-             prefix="plone.app.querystring.field.created">
-       <value key="title">Creation Date</value>
-       <value key="description">The time and date an item was created</value>
-       <value key="enabled">True</value>
-       <value key="sortable">False</value>
-       <value key="operations">
-           <element>plone.app.querystring.operation.date.lessThan</element>
-           <element>plone.app.querystring.operation.date.largerThan</element>
-       </value>
-       <value key="group">Dates</value>
-    </records>
-</registry>
-"""
-
-test_vocabulary_xml = """
-<registry>
-    <records interface="plone.app.querystring.interfaces.IQueryOperation"
-             prefix="plone.app.querystring.operation.string.is">
-        <value key="title">equals</value>
-        <value key="description">Tip: you can use * to autocomplete.</value>
-        <value key="operation">plone.app.querystring.queryparser:_equal</value>
-        <value key="widget"></value>
-    </records>
-
-    <records interface="plone.app.querystring.interfaces.IQueryField"
-             prefix="plone.app.querystring.field.reviewState">
-        <value key="title">Review state</value>
-        <value key="description">An item's workflow state (e.g.published)</value>
-        <value key="enabled">True</value>
-        <value key="sortable">True</value>
-        <value key="operations">
-            <element>plone.app.querystring.operation.string.is</element>
-        </value>
-        <value key="vocabulary">plone.app.querystring.tests.testvocabulary</value>
-       <value key="group">Metadata</value>
-    </records>
-</registry>
-"""
+minimal_correct_xml = reg_load_xml('registry_minimal_correct.xml')
+test_missing_operator_xml = reg_load_xml('registry_test_missing_operator.xml')
+test_vocabulary_xml = reg_load_xml('registry_test_vocabulary.xml')
diff --git a/plone/app/querystring/tests/testQueryBuilder.py b/plone/app/querystring/tests/testQueryBuilder.py
index dace663..08236bf 100644
--- a/plone/app/querystring/tests/testQueryBuilder.py
+++ b/plone/app/querystring/tests/testQueryBuilder.py
@@ -17,8 +17,10 @@ def afterSetUp(self):
         self.testpage = testpage
         self.portal.portal_workflow.doActionFor(testpage, 'publish')
         self.request = TestRequest()
-        self.querybuilder = getMultiAdapter((self.portal, self.request),
-                                             name='querybuilderresults')
+        self.querybuilder = getMultiAdapter(
+            (self.portal, self.request),
+            name='querybuilderresults'
+        )
         self.query = [{
             'i': 'Title',
             'o': 'plone.app.querystring.operation.string.is',
diff --git a/plone/app/querystring/tests/testQueryBuilderModifiers.py b/plone/app/querystring/tests/testQueryBuilderModifiers.py
new file mode 100644
index 0000000..82bf0bb
--- /dev/null
+++ b/plone/app/querystring/tests/testQueryBuilderModifiers.py
@@ -0,0 +1,98 @@
+# -*- coding: utf8 -*-
+
+from .base import QuerystringTestCase
+import index_testmodifier
+
+from zope.component import getMultiAdapter
+from zope.publisher.browser import TestRequest
+from plone.app.querystring.interfaces import IParsedQueryIndexModifier
+from zope.component import getGlobalSiteManager
+
+
+class TestQuerybuilderExtended(QuerystringTestCase):
+    """Testing the IParsedQueryIndexModifier registration feature"""
+
+    def afterSetUp(self):
+        self.loginAsPortalOwner()
+        self.portal.invokeFactory("Document",
+                                  "collectionstestpage1",
+                                  title="Collectionstestpage")
+        testpage1 = self.portal['collectionstestpage1']
+        self.portal.portal_workflow.doActionFor(testpage1, 'publish')
+        self.portal.invokeFactory("Document",
+                                  "collectionstestpage2",
+                                  title="Foo",
+                                  description="Collectionstestpage")
+        testpage2 = self.portal['collectionstestpage2']
+        self.portal.portal_workflow.doActionFor(testpage2, 'publish')
+        self.portal.invokeFactory("Document",
+                                  "collectionstestpage3",
+                                  title="Bar",
+                                  description="Collctionststpag")
+        testpage3 = self.portal['collectionstestpage3']
+        self.portal.portal_workflow.doActionFor(testpage3, 'publish')
+        self.request = TestRequest()
+        self.querybuilder = getMultiAdapter(
+            (self.portal, self.request),
+            name='querybuilderresults'
+        )
+
+    def testModifierNotCalled(self):
+        gsm = getGlobalSiteManager()
+        gsm.registerUtility(
+            index_testmodifier.SimpleFooIndexModifier(),
+            name=u'Foo'
+        )
+        query = [{
+            'i': 'Title',
+            'o': 'plone.app.querystring.operation.string.is',
+            'v': 'Collectionstestpage',
+        }]
+
+        try:
+            results = self.querybuilder(query=query)
+        except:
+            self.fail("Unexpected: index modifier has been called")
+        self.assertEqual(len(results), 1)
+        self.assertEqual(results[0].Title(), "Collectionstestpage")
+        gsm.unregisterUtility(provided=IParsedQueryIndexModifier, name=u'Foo')
+
+    def testModifierChangeQuery(self):
+        gsm = getGlobalSiteManager()
+        gsm.registerUtility(
+            index_testmodifier.TitleFooIndexModifier(),
+            name=u'Title'
+        )
+        query = [{
+            'i': 'Title',
+            'o': 'plone.app.querystring.operation.string.is',
+            'v': 'Collectionstestpage',
+        }]
+
+        results = self.querybuilder(query=query)
+        self.assertEqual(len(results), 1)
+        self.assertEqual(results[0].Title(), "Foo")
+        gsm.unregisterUtility(
+            provided=IParsedQueryIndexModifier,
+            name=u'Title'
+        )
+
+    def testModifierChangeQueryAndIndex(self):
+        gsm = getGlobalSiteManager()
+        gsm.registerUtility(
+            index_testmodifier.AbstractToDescriptionIndexModifier(),
+            name=u'Abstract'
+        )
+        query = [{
+            'i': 'Abstract',
+            'o': 'plone.app.querystring.operation.string.is',
+            'v': 'Collectionstestpage',
+        }]
+
+        results = self.querybuilder(query=query)
+        self.assertEqual(len(results), 1)
+        self.assertEqual(results[0].Title(), "Bar")
+        gsm.unregisterUtility(
+            provided=IParsedQueryIndexModifier,
+            name=u'Abstract'
+        )
diff --git a/plone/app/querystring/tests/testQueryParser.py b/plone/app/querystring/tests/testQueryParser.py
index b5da4db..ec50ad3 100644
--- a/plone/app/querystring/tests/testQueryParser.py
+++ b/plone/app/querystring/tests/testQueryParser.py
@@ -144,7 +144,8 @@ def test_path_explicit(self):
             'v': '/foo',
         }
         parsed = queryparser.parseFormquery(MockSite(), [data, ])
-        self.assertEqual(parsed, {'path': {'query': '/%s/foo' % MOCK_SITE_ID}})
+        self.assertEqual(
+            parsed, {'path': {'query': ['/%s/foo' % MOCK_SITE_ID]}})
 
     def test_path_computed(self):
         data = {
@@ -154,7 +155,8 @@ def test_path_computed(self):
         }
 
         parsed = queryparser.parseFormquery(MockSite(), [data, ])
-        self.assertEqual(parsed, {'path': {'query': '/%s/foo' % MOCK_SITE_ID}})
+        self.assertEqual(
+            parsed, {'path': {'query': ['/%s/foo' % MOCK_SITE_ID]}})
 
     def test_path_with_depth_computed(self):
         data = {
@@ -166,44 +168,88 @@ def test_path_with_depth_computed(self):
         parsed = queryparser.parseFormquery(MockSite(), [data, ])
         self.assertEqual(parsed, {
             'path': {
-                'query': '/%s/foo' % MOCK_SITE_ID,
+                'query': ['/%s/foo' % MOCK_SITE_ID],
                 'depth': 2
             }
         })
 
+    def test_multi_path(self):
+        data_1 = {
+            'i': 'path',
+            'o': 'plone.app.querystring.operation.string.path',
+            'v': '/foo',
+        }
+        data_2 = {
+            'i': 'path',
+            'o': 'plone.app.querystring.operation.string.path',
+            'v': '/bar',
+        }
+
+        parsed = queryparser.parseFormquery(MockSite(), [data_1, data_2])
+        self.assertEqual(
+            parsed, {'path': {'query': [
+                '/%s/foo' % MOCK_SITE_ID,
+                '/%s/bar' % MOCK_SITE_ID]}})
+
+    def test_multi_path_with_depth_computet(self):
+        data_1 = {
+            'i': 'path',
+            'o': 'plone.app.querystring.operation.string.path',
+            'v': '/foo::2',
+        }
+        data_2 = {
+            'i': 'path',
+            'o': 'plone.app.querystring.operation.string.path',
+            'v': '/bar::5',
+        }
+
+        parsed = queryparser.parseFormquery(MockSite(), [data_1, data_2])
+        self.assertEqual(
+            parsed, {'path': {'query': [
+                '/%s/foo' % MOCK_SITE_ID,
+                '/%s/bar' % MOCK_SITE_ID], 'depth': 2}})
+
 
 class TestQueryGenerators(TestQueryParserBase):
 
     def test__between(self):
-        data = Row(index='modified',
-                  operator='_between',
-                  values=['2009/08/12', '2009/08/14'])
+        data = Row(
+            index='modified',
+            operator='_between',
+            values=['2009/08/12', '2009/08/14']
+        )
         parsed = queryparser._between(MockSite(), data)
         expected = {'modified': {'query': ['2009/08/12', '2009/08/14'],
                     'range': 'minmax'}}
         self.assertEqual(parsed, expected)
 
     def test__between_reversed_dates(self):
-        data = Row(index='modified',
-                  operator='_between',
-                  values=['2009/08/14', '2009/08/12'])
+        data = Row(
+            index='modified',
+            operator='_between',
+            values=['2009/08/14', '2009/08/12']
+        )
         parsed = queryparser._between(MockSite(), data)
         expected = {'modified': {'query': ['2009/08/12', '2009/08/14'],
                     'range': 'minmax'}}
         self.assertEqual(parsed, expected)
 
     def test__largerThan(self):
-        data = Row(index='modified',
-                  operator='_largerThan',
-                  values='2010/03/18')
+        data = Row(
+            index='modified',
+            operator='_largerThan',
+            values='2010/03/18'
+        )
         parsed = queryparser._largerThan(MockSite(), data)
         expected = {'modified': {'query': '2010/03/18', 'range': 'min'}}
         self.assertEqual(parsed, expected)
 
     def test__lessThan(self):
-        data = Row(index='modified',
-                  operator='_lessThan',
-                  values='2010/03/18')
+        data = Row(
+            index='modified',
+            operator='_lessThan',
+            values='2010/03/18'
+        )
         parsed = queryparser._lessThan(MockSite(), data)
         expected = {'modified': {'query': '2010/03/18', 'range': 'max'}}
         self.assertEqual(parsed, expected)
@@ -213,9 +259,11 @@ def test__currentUser(self):
         u = MockUser()
         pm = MockPortal_membership(user=u)
         context = MockSite(portal_membership=pm)
-        data = Row(index='Creator',
-                  operator='_currentUser',
-                  values=None)
+        data = Row(
+            index='Creator',
+            operator='_currentUser',
+            values=None
+        )
         parsed = queryparser._currentUser(context, data)
         expected = {'Creator': {'query': 'Anonymous User'}}
         self.assertEqual(parsed, expected)
@@ -224,9 +272,11 @@ def test__currentUser(self):
         u = MockUser(username='admin')
         pm = MockPortal_membership(user=u)
         context = MockSite(portal_membership=pm)
-        data = Row(index='Creator',
-                  operator='_currentUser',
-                  values=None)
+        data = Row(
+            index='Creator',
+            operator='_currentUser',
+            values=None
+        )
         parsed = queryparser._currentUser(context, data)
         expected = {'Creator': {'query': 'admin'}}
         self.assertEqual(parsed, expected)
@@ -262,9 +312,11 @@ def test__lessThanRelativeDate(self):
         mydate = now + days
         expected_dates = [now.earliestTime(), mydate.latestTime()]
         expected = {'modified': {'query': expected_dates, 'range': 'minmax'}}
-        data = Row(index='modified',
-                  operator='_lessThanRelativeDate',
-                  values=days)
+        data = Row(
+            index='modified',
+            operator='_lessThanRelativeDate',
+            values=days
+        )
         parsed = queryparser._lessThanRelativeDate(MockSite(), data)
         self.assertEqual(parsed, expected)
 
@@ -274,9 +326,11 @@ def test__moreThanRelativeDate(self):
         mydate = now - days
         expected_dates = [mydate.earliestTime(), now.latestTime()]
         expected = {'modified': {'query': expected_dates, 'range': 'minmax'}}
-        data = Row(index='modified',
-                  operator='_moreThanRelativeDate',
-                  values=days)
+        data = Row(
+            index='modified',
+            operator='_moreThanRelativeDate',
+            values=days
+        )
         parsed = queryparser._moreThanRelativeDate(MockSite(), data)
         self.assertEqual(parsed, expected)
 
@@ -284,27 +338,33 @@ def test__today(self):
         now = DateTime()
         expected_dates = [now.earliestTime(), now.latestTime()]
         expected = {'modified': {'query': expected_dates, 'range': 'minmax'}}
-        data = Row(index='modified',
-                  operator='_today',
-                  values=expected_dates)
+        data = Row(
+            index='modified',
+            operator='_today',
+            values=expected_dates
+        )
         parsed = queryparser._today(MockSite(), data)
         self.assertEqual(parsed, expected)
 
     def test__path(self):
         # normal path
-        data = Row(index='path',
-                  operator='_path',
-                  values='/news/')
+        data = Row(
+            index='path',
+            operator='_path',
+            values='/news/'
+        )
         parsed = queryparser._path(MockSite(), data)
-        expected = {'path': {'query': '/%s/news/' % MOCK_SITE_ID}}
+        expected = {'path': {'query': ['/%s/news/' % MOCK_SITE_ID]}}
         self.assertEqual(parsed, expected)
 
         # by uid
-        data = Row(index='path',
-                  operator='_path',
-                  values='00000000000000001')
+        data = Row(
+            index='path',
+            operator='_path',
+            values='00000000000000001'
+        )
         parsed = queryparser._path(MockSite(), data)
-        expected = {'path': {'query': '/%s/foo' % MOCK_SITE_ID}}
+        expected = {'path': {'query': ['/%s/foo' % MOCK_SITE_ID]}}
         self.assertEqual(parsed, expected)
 
     def test__relativePath(self):
@@ -327,27 +387,31 @@ def test__relativePath(self):
                                             path="/%s/bar/egg" % MOCK_SITE_ID)
 
         # show my siblings
-        data = Row(index='path',
-                  operator='_relativePath',
-                  values='..')
+        data = Row(
+            index='path',
+            operator='_relativePath',
+            values='..'
+        )
         parsed = queryparser._relativePath(context, data)
-        expected = {'path': {'query': '/%s/bar' % MOCK_SITE_ID}}
+        expected = {'path': {'query': ['/%s/bar' % MOCK_SITE_ID]}}
         self.assertEqual(parsed, expected)
 
         # walk upwards
-        data = Row(index='path',
-                  operator='_relativePath',
-                  values='../../')
+        data = Row(
+            index='path',
+            operator='_relativePath',
+            values='../../'
+        )
         parsed = queryparser._relativePath(context, data)
-        expected = {'path': {'query': '/%s' % MOCK_SITE_ID}}
+        expected = {'path': {'query': ['/%s' % MOCK_SITE_ID]}}
         self.assertEqual(parsed, expected)
 
         # if you walk beyond INavigatinRoot it should stop and return
         data = Row(index='path',
-                  operator='_relativePath',
-                  values='../../../')
+                   operator='_relativePath',
+                   values='../../../')
         parsed = queryparser._relativePath(context, data)
-        expected = {'path': {'query': '/%s' % MOCK_SITE_ID}}
+        expected = {'path': {'query': ['/%s' % MOCK_SITE_ID]}}
         self.assertEqual(parsed, expected)
 
         # reach a subfolder on Plone root
@@ -355,7 +419,7 @@ def test__relativePath(self):
                    operator='_relativePath',
                    values='../../ham')
         parsed = queryparser._relativePath(context, data)
-        expected = {'path': {'query': '/%s/ham' % MOCK_SITE_ID}}
+        expected = {'path': {'query': ['/%s/ham' % MOCK_SITE_ID]}}
         self.assertEqual(parsed, expected)
 
         # reach a subfolder on parent of collection
@@ -363,7 +427,7 @@ def test__relativePath(self):
                    operator='_relativePath',
                    values='../egg')
         parsed = queryparser._relativePath(context, data)
-        expected = {'path': {'query': '/%s/bar/egg' % MOCK_SITE_ID}}
+        expected = {'path': {'query': ['/%s/bar/egg' % MOCK_SITE_ID]}}
         self.assertEqual(parsed, expected)
 
     def test_getPathByUID(self):
diff --git a/plone/app/querystring/tests/testRegistryIntegration.py b/plone/app/querystring/tests/testRegistryIntegration.py
index 86c6672..9cd9247 100644
--- a/plone/app/querystring/tests/testRegistryIntegration.py
+++ b/plone/app/querystring/tests/testRegistryIntegration.py
@@ -52,3 +52,17 @@ def test_getId(self):
         self.assertEqual(registry[prefix + ".enabled"], True)
         self.assertEqual(registry[prefix + ".sortable"], True)
         self.assertEqual(registry[prefix + ".group"], "Metadata")
+
+    def test_getobjpositioninparent_largerthan(self):
+        """Bug reported as Issue #22
+
+        Names not matching for operations getObjPositionInParent
+        see also https://github.com/plone/plone.app.querystring/issues/22
+        """
+        key = 'plone.app.querystring.field.getObjPositionInParent.operations'
+        operation = 'plone.app.querystring.operation.int.largerThan'
+        registry = self.portal.portal_registry
+
+        # check if operation is used for getObjPositionInParent
+        operations = registry.get(key)
+        self.assertTrue(operation in operations)
diff --git a/plone/app/querystring/tests/testRegistryReader.py b/plone/app/querystring/tests/testRegistryReader.py
index d15ff69..6d621fb 100644
--- a/plone/app/querystring/tests/testRegistryReader.py
+++ b/plone/app/querystring/tests/testRegistryReader.py
@@ -98,10 +98,14 @@ def test_map_operations_missing(self):
         result = reader.mapOperations(result)
         operators = result.get(
             'plone.app.querystring.field.created.operators').keys()
-        self.assertTrue('plone.app.querystring.operation.date.lessThan'
-            in operators)
-        self.assertFalse('plone.app.querystring.operation.date.largerThan'
-            in operators)
+        self.assertTrue(
+            'plone.app.querystring.operation.date.lessThan'
+            in operators
+        )
+        self.assertFalse(
+            'plone.app.querystring.operation.date.largerThan'
+            in operators
+        )
 
     def test_sortable_indexes(self):
         """tests if sortable indexes from the registry will be available in
diff --git a/plone/app/querystring/upgrades.py b/plone/app/querystring/upgrades.py
new file mode 100644
index 0000000..3d5d5e4
--- /dev/null
+++ b/plone/app/querystring/upgrades.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+from Products.CMFCore.utils import getUtility
+from plone.registry.interfaces import IRegistry
+
+
+def upgrade_1_to_2_typo_in_registry(context):
+    registry = getUtility(IRegistry)
+    name = 'plone.app.querystring.field.getObjPositionInParent.operations'
+    wrong_value = 'plone.app.querystring.operation.int.greaterThan'
+    right_value = 'plone.app.querystring.operation.int.largerThan'
+    values = registry[name]
+    if wrong_value in values:
+        del values[values.index(wrong_value)]
+    if right_value not in values:
+        values.append(right_value)
+    registry[name] = values
diff --git a/setup.py b/setup.py
index 1f738ef..8f3d6e3 100644
--- a/setup.py
+++ b/setup.py
@@ -1,54 +1,58 @@
 from setuptools import setup, find_packages
 
-version = '1.1.2.dev0'
+version = '1.2.1.dev0'
 
-setup(name='plone.app.querystring',
-      version=version,
-      description="",
-      long_description=open("README.rst").read() + "\n" +
-                       open("CHANGES.rst").read(),
-      classifiers=[
+long_description = open("README.rst").read() + "\n"
+long_description += open("CHANGES.rst").read()
+
+setup(
+    name='plone.app.querystring',
+    version=version,
+    description="",
+    long_description=long_description,
+    classifiers=[
         "Framework :: Plone",
         "License :: OSI Approved :: GNU General Public License (GPL)",
         "Programming Language :: Python",
-        ],
-      keywords='',
-      author='Plone Foundation',
-      author_email='plone-developers at lists.sourceforge.net',
-      url='http://pypi.python.org/pypi/plone.app.querystring',
-      license='GPL version 2',
-      packages=find_packages(exclude=['ez_setup']),
-      namespace_packages=['plone', 'plone.app'],
-      include_package_data=True,
-      zip_safe=False,
-      install_requires=[
-          'setuptools',
-          'DateTime',
-          'Products.CMFCore',
-          'Products.CMFPlone',
-          'plone.app.contentlisting',
-          'plone.app.layout',
-          'plone.app.registry>=1.1dev',
-          'plone.app.vocabularies',
-          'plone.batching',
-          'plone.registry',
-          'zope.component',
-          'zope.dottedname',
-          'zope.globalrequest',
-          'zope.i18n',
-          'zope.i18nmessageid',
-          'zope.interface',
-          'zope.publisher',
-          'zope.schema',
-      ],
-      extras_require={
-          'test': [
-              'collective.testcaselayer',
-              'Products.PloneTestCase',
-          ]
-      },
-      entry_points="""
-      [z3c.autoinclude.plugin]
-      target = plone
-      """,
-      )
+    ],
+    keywords='',
+    author='Plone Foundation',
+    author_email='plone-developers at lists.sourceforge.net',
+    url='http://pypi.python.org/pypi/plone.app.querystring',
+    license='GPL version 2',
+    packages=find_packages(exclude=['ez_setup']),
+    namespace_packages=['plone', 'plone.app'],
+    include_package_data=True,
+    zip_safe=False,
+    install_requires=[
+        'DateTime',
+        'Products.CMFCore',
+        'Products.CMFPlone',
+        'plone.app.contentlisting',
+        'plone.app.layout',
+        'plone.app.registry>=1.1dev',
+        'plone.app.upgrade',
+        'plone.app.vocabularies',
+        'plone.batching',
+        'plone.registry',
+        'setuptools',
+        'zope.component',
+        'zope.dottedname',
+        'zope.globalrequest',
+        'zope.i18n',
+        'zope.i18nmessageid',
+        'zope.interface',
+        'zope.publisher',
+        'zope.schema',
+    ],
+    extras_require={
+        'test': [
+            'collective.testcaselayer',
+            'Products.PloneTestCase',
+        ]
+    },
+    entry_points="""
+    [z3c.autoinclude.plugin]
+    target = plone
+    """,
+)


Repository: plone.app.querystring
Branch: refs/heads/master
Date: 2014-04-14T16:38:41+03:00
Author: ichim-david (ichim-david) <ichim.david at gmail.com>
Commit: https://github.com/plone/plone.app.querystring/commit/72eb0c94a1d369d5d863dbb84acbe452b7c7c9ab

Removed the roles vocabulary from plone.app.querystring opting to use the vocabulary from plone.app.vocabularies as suggested in the comments for this pull request

Files changed:
M plone/app/querystring/configure.zcml
M plone/app/querystring/profiles/default/registry.xml
D plone/app/querystring/vocabularies.py

diff --git a/plone/app/querystring/configure.zcml b/plone/app/querystring/configure.zcml
index 02df5fe..6c43b4d 100644
--- a/plone/app/querystring/configure.zcml
+++ b/plone/app/querystring/configure.zcml
@@ -74,10 +74,4 @@
     for="*"
     />
 
-  <utility
-    component=".vocabularies.RolesVocabularyFactory"
-    name="plone.app.querystring.Roles"
-    />
-
-
 </configure>
diff --git a/plone/app/querystring/profiles/default/registry.xml b/plone/app/querystring/profiles/default/registry.xml
index fa49fed..6259dfe 100644
--- a/plone/app/querystring/profiles/default/registry.xml
+++ b/plone/app/querystring/profiles/default/registry.xml
@@ -425,7 +425,7 @@
         <value key="operations">
             <element>plone.app.querystring.operation.string.showInactive</element>
         </value>
-        <value key="vocabulary">plone.app.querystring.Roles</value>
+        <value key="vocabulary">plone.app.vocabularies.AllRoles</value>
         <value key="group" i18n:translate="">Metadata</value>
     </records>
 
diff --git a/plone/app/querystring/vocabularies.py b/plone/app/querystring/vocabularies.py
deleted file mode 100644
index 5df1095..0000000
--- a/plone/app/querystring/vocabularies.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from zope.component import queryUtility
-from zope.interface import implements
-from zope.schema.interfaces import IVocabularyFactory
-from zope.schema.vocabulary import SimpleVocabulary
-
-
-class RolesVocabulary(object):
-    """Vocabulary factory for roles found in the portal plus the Anonymous Role
-    """
-    implements(IVocabularyFactory)
-
-    def __call__(self, context):
-        voc = queryUtility(IVocabularyFactory, 'plone.app.vocabularies.Roles')
-        if not voc:
-            voc = SimpleVocabulary([])
-        voc = voc(context)
-        anon = "Anonymous"
-        voc._terms.append(voc.createTerm(anon, anon, anon))
-        return SimpleVocabulary(voc._terms)
-
-RolesVocabularyFactory = RolesVocabulary()


Repository: plone.app.querystring
Branch: refs/heads/master
Date: 2014-04-15T17:53:06+03:00
Author: ichim-david (ichim-david) <ichim.david at gmail.com>
Commit: https://github.com/plone/plone.app.querystring/commit/32716fddafaebc1eaacefb33d5eea8f4cbf843b7

Added upgrade profile which adds the new operations and fields with the use of registry.xml

Files changed:
A plone/app/querystring/profiles.zcml
A plone/app/querystring/profiles/upgrades/to_3/registry.xml
A plone/app/querystring/upgrades.zcml
M plone/app/querystring/configure.zcml
M plone/app/querystring/profiles/default/metadata.xml

diff --git a/plone/app/querystring/configure.zcml b/plone/app/querystring/configure.zcml
index 6c43b4d..9b04597 100644
--- a/plone/app/querystring/configure.zcml
+++ b/plone/app/querystring/configure.zcml
@@ -4,17 +4,13 @@
     xmlns:browser="http://namespaces.zope.org/browser"
     i18n_domain="plone">
 
+    <include file="profiles.zcml" />
+    <include file="upgrades.zcml" />
+
   <include package="plone.app.contentlisting" />
   <include package="plone.app.registry" />
   <include package=".indexmodifiers" />
 
-  <genericsetup:registerProfile
-    name="default"
-    title="Querystring parser/builder"
-    directory="profiles/default"
-    description="Querystring parser and builder, building block and transformer for the new style collections"
-    provides="Products.GenericSetup.interfaces.EXTENSION"
-    />
 
   <genericsetup:upgradeStep
       source="1"
diff --git a/plone/app/querystring/profiles.zcml b/plone/app/querystring/profiles.zcml
new file mode 100644
index 0000000..6138de8
--- /dev/null
+++ b/plone/app/querystring/profiles.zcml
@@ -0,0 +1,22 @@
+<configure
+        xmlns="http://namespaces.zope.org/zope"
+        xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
+        i18n_domain="plone">
+
+    <genericsetup:registerProfile
+            name="default"
+            title="Querystring parser/builder"
+            directory="profiles/default"
+            description="Querystring parser and builder, building block and transformer for the new style collections"
+            provides="Products.GenericSetup.interfaces.EXTENSION"
+            />
+
+    <genericsetup:registerProfile
+            name="upgrade_to_3"
+            title="Querystring Upgrade profile to v3"
+            description=""
+            directory="profiles/upgrades/to_3"
+            provides="Products.GenericSetup.interfaces.EXTENSION"
+            />
+
+ </configure>
diff --git a/plone/app/querystring/profiles/default/metadata.xml b/plone/app/querystring/profiles/default/metadata.xml
index 103e8eb..66882ff 100644
--- a/plone/app/querystring/profiles/default/metadata.xml
+++ b/plone/app/querystring/profiles/default/metadata.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <metadata>
-  <version>2</version>
+  <version>3</version>
   <dependencies>
       <dependency>profile-plone.app.registry:default</dependency>
   </dependencies>
diff --git a/plone/app/querystring/profiles/upgrades/to_3/registry.xml b/plone/app/querystring/profiles/upgrades/to_3/registry.xml
new file mode 100644
index 0000000..52aab56
--- /dev/null
+++ b/plone/app/querystring/profiles/upgrades/to_3/registry.xml
@@ -0,0 +1,27 @@
+<registry xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+          i18n:domain="plone">
+
+<!-- Operation Definitions -->
+    <records interface="plone.app.querystring.interfaces.IQueryOperation"
+             prefix="plone.app.querystring.operation.string.showInactive">
+        <value key="title" i18n:translate="">Show Inactive</value>
+        <value key="description" i18n:translate="">The user roles which are allowed to see inactive content</value>
+        <value key="operation">plone.app.querystring.queryparser._showInactive</value>
+        <value key="widget">MultipleSelectionWidget</value>
+    </records>
+
+<!-- Field Definitions -->
+    <records interface="plone.app.querystring.interfaces.IQueryField"
+             prefix="plone.app.querystring.field.show_inactive">
+        <value key="title" i18n:translate="">Show Inactive</value>
+        <value key="description" i18n:translate="">Select which roles have the permission to view inactive objects</value>
+        <value key="enabled">True</value>
+        <value key="sortable">False</value>
+        <value key="operations">
+            <element>plone.app.querystring.operation.string.showInactive</element>
+        </value>
+        <value key="vocabulary">plone.app.vocabularies.AllRoles</value>
+        <value key="group" i18n:translate="">Metadata</value>
+    </records>
+
+</registry>
diff --git a/plone/app/querystring/upgrades.zcml b/plone/app/querystring/upgrades.zcml
new file mode 100644
index 0000000..666537d
--- /dev/null
+++ b/plone/app/querystring/upgrades.zcml
@@ -0,0 +1,26 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:genericsetup="http://namespaces.zope.org/genericsetup">
+
+  <genericsetup:upgradeStep
+      source="1"
+      destination="2"
+      title="Fix Typo in Registry"
+      description="Migrate registry to fix a typo."
+      profile="plone.app.querystring:default"
+      handler=".upgrades.upgrade_1_to_2_typo_in_registry"
+      />
+
+    <genericsetup:upgradeSteps
+            source="2"
+            destination="3"
+            profile="plone.app.querystring:default">
+
+        <genericsetup:upgradeDepends
+                title="Add show inactive operation and field"
+                import_profile="plone.app.querystring:upgrade_to_3"
+                />
+
+    </genericsetup:upgradeSteps>
+
+</configure>


Repository: plone.app.querystring
Branch: refs/heads/master
Date: 2014-05-07T14:44:48+03:00
Author: ichim-david (ichim-david) <ichim.david at gmail.com>
Commit: https://github.com/plone/plone.app.querystring/commit/92884af98187d8da5ed0cb9bb4d6637e30e9c5b7

Use the canonical Roles vocabulary instead of a newly created AllRoles

Files changed:
M plone/app/querystring/profiles/upgrades/to_3/registry.xml

diff --git a/plone/app/querystring/profiles/upgrades/to_3/registry.xml b/plone/app/querystring/profiles/upgrades/to_3/registry.xml
index 52aab56..23bebf7 100644
--- a/plone/app/querystring/profiles/upgrades/to_3/registry.xml
+++ b/plone/app/querystring/profiles/upgrades/to_3/registry.xml
@@ -20,7 +20,7 @@
         <value key="operations">
             <element>plone.app.querystring.operation.string.showInactive</element>
         </value>
-        <value key="vocabulary">plone.app.vocabularies.AllRoles</value>
+        <value key="vocabulary">plone.app.vocabularies.Roles</value>
         <value key="group" i18n:translate="">Metadata</value>
     </records>
 


Repository: plone.app.querystring
Branch: refs/heads/master
Date: 2014-05-07T14:55:55+03:00
Author: ichim-david (ichim-david) <ichim.david at gmail.com>
Commit: https://github.com/plone/plone.app.querystring/commit/2f6f9c264fc7f9125ff19045bb14656170c21b9e

Merge pull request #20 from eea/show_inactive

Show inactive parameter implemeted as an operator

Files changed:
A plone/app/querystring/profiles.zcml
A plone/app/querystring/profiles/upgrades/to_3/registry.xml
A plone/app/querystring/upgrades.zcml
M CHANGES.rst
M plone/app/querystring/configure.zcml
M plone/app/querystring/profiles/default/metadata.xml
M plone/app/querystring/profiles/default/registry.xml
M plone/app/querystring/querybuilder.py
M plone/app/querystring/queryparser.py
M plone/app/querystring/tests/testQueryParser.py

diff --git a/CHANGES.rst b/CHANGES.rst
index 8c9332d..3ff2cb9 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -4,7 +4,9 @@ Changelog
 1.2.1 (unreleased)
 ------------------
 
-- Nothing changed yet.
+- Added show inactive operation which uses the roles vocabulary in order
+  to assign permission to show or hide the inactive objects of the given query
+  [ichim-david]
 
 
 1.2.0 (2014-04-05)
diff --git a/plone/app/querystring/configure.zcml b/plone/app/querystring/configure.zcml
index 6c43b4d..9b04597 100644
--- a/plone/app/querystring/configure.zcml
+++ b/plone/app/querystring/configure.zcml
@@ -4,17 +4,13 @@
     xmlns:browser="http://namespaces.zope.org/browser"
     i18n_domain="plone">
 
+    <include file="profiles.zcml" />
+    <include file="upgrades.zcml" />
+
   <include package="plone.app.contentlisting" />
   <include package="plone.app.registry" />
   <include package=".indexmodifiers" />
 
-  <genericsetup:registerProfile
-    name="default"
-    title="Querystring parser/builder"
-    directory="profiles/default"
-    description="Querystring parser and builder, building block and transformer for the new style collections"
-    provides="Products.GenericSetup.interfaces.EXTENSION"
-    />
 
   <genericsetup:upgradeStep
       source="1"
diff --git a/plone/app/querystring/profiles.zcml b/plone/app/querystring/profiles.zcml
new file mode 100644
index 0000000..6138de8
--- /dev/null
+++ b/plone/app/querystring/profiles.zcml
@@ -0,0 +1,22 @@
+<configure
+        xmlns="http://namespaces.zope.org/zope"
+        xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
+        i18n_domain="plone">
+
+    <genericsetup:registerProfile
+            name="default"
+            title="Querystring parser/builder"
+            directory="profiles/default"
+            description="Querystring parser and builder, building block and transformer for the new style collections"
+            provides="Products.GenericSetup.interfaces.EXTENSION"
+            />
+
+    <genericsetup:registerProfile
+            name="upgrade_to_3"
+            title="Querystring Upgrade profile to v3"
+            description=""
+            directory="profiles/upgrades/to_3"
+            provides="Products.GenericSetup.interfaces.EXTENSION"
+            />
+
+ </configure>
diff --git a/plone/app/querystring/profiles/default/metadata.xml b/plone/app/querystring/profiles/default/metadata.xml
index 103e8eb..66882ff 100644
--- a/plone/app/querystring/profiles/default/metadata.xml
+++ b/plone/app/querystring/profiles/default/metadata.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <metadata>
-  <version>2</version>
+  <version>3</version>
   <dependencies>
       <dependency>profile-plone.app.registry:default</dependency>
   </dependencies>
diff --git a/plone/app/querystring/profiles/default/registry.xml b/plone/app/querystring/profiles/default/registry.xml
index 9d3f610..6259dfe 100644
--- a/plone/app/querystring/profiles/default/registry.xml
+++ b/plone/app/querystring/profiles/default/registry.xml
@@ -155,6 +155,14 @@
     </records>
 
     <records interface="plone.app.querystring.interfaces.IQueryOperation"
+             prefix="plone.app.querystring.operation.string.showInactive">
+        <value key="title" i18n:translate="">Show Inactive</value>
+        <value key="description" i18n:translate="">The user roles which are allowed to see inactive content</value>
+        <value key="operation">plone.app.querystring.queryparser._showInactive</value>
+        <value key="widget">MultipleSelectionWidget</value>
+    </records>
+
+    <records interface="plone.app.querystring.interfaces.IQueryOperation"
              prefix="plone.app.querystring.operation.string.is">
         <value key="title" i18n:translate="">Is</value>
         <value key="description" i18n:translate="">Tip: you can use * to autocomplete.</value>
@@ -409,6 +417,19 @@
     </records>
 
     <records interface="plone.app.querystring.interfaces.IQueryField"
+             prefix="plone.app.querystring.field.show_inactive">
+        <value key="title" i18n:translate="">Show Inactive</value>
+        <value key="description" i18n:translate="">Select which roles have the permission to view inactive objects</value>
+        <value key="enabled">True</value>
+        <value key="sortable">False</value>
+        <value key="operations">
+            <element>plone.app.querystring.operation.string.showInactive</element>
+        </value>
+        <value key="vocabulary">plone.app.vocabularies.AllRoles</value>
+        <value key="group" i18n:translate="">Metadata</value>
+    </records>
+
+    <records interface="plone.app.querystring.interfaces.IQueryField"
              prefix="plone.app.querystring.field.review_state">
         <value key="title" i18n:translate="">Review state</value>
         <value key="description" i18n:translate="">An item's workflow state (e.g.published)</value>
diff --git a/plone/app/querystring/profiles/upgrades/to_3/registry.xml b/plone/app/querystring/profiles/upgrades/to_3/registry.xml
new file mode 100644
index 0000000..23bebf7
--- /dev/null
+++ b/plone/app/querystring/profiles/upgrades/to_3/registry.xml
@@ -0,0 +1,27 @@
+<registry xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+          i18n:domain="plone">
+
+<!-- Operation Definitions -->
+    <records interface="plone.app.querystring.interfaces.IQueryOperation"
+             prefix="plone.app.querystring.operation.string.showInactive">
+        <value key="title" i18n:translate="">Show Inactive</value>
+        <value key="description" i18n:translate="">The user roles which are allowed to see inactive content</value>
+        <value key="operation">plone.app.querystring.queryparser._showInactive</value>
+        <value key="widget">MultipleSelectionWidget</value>
+    </records>
+
+<!-- Field Definitions -->
+    <records interface="plone.app.querystring.interfaces.IQueryField"
+             prefix="plone.app.querystring.field.show_inactive">
+        <value key="title" i18n:translate="">Show Inactive</value>
+        <value key="description" i18n:translate="">Select which roles have the permission to view inactive objects</value>
+        <value key="enabled">True</value>
+        <value key="sortable">False</value>
+        <value key="operations">
+            <element>plone.app.querystring.operation.string.showInactive</element>
+        </value>
+        <value key="vocabulary">plone.app.vocabularies.Roles</value>
+        <value key="group" i18n:translate="">Metadata</value>
+    </records>
+
+</registry>
diff --git a/plone/app/querystring/querybuilder.py b/plone/app/querystring/querybuilder.py
index b2b5ff5..aac1e4a 100644
--- a/plone/app/querystring/querybuilder.py
+++ b/plone/app/querystring/querybuilder.py
@@ -108,7 +108,7 @@ def _makequery(self, query=None, batch=False, b_start=0, b_size=30,
         if 'path' not in parsedquery:
             parsedquery['path'] = {'query': ''}
 
-        results = catalog(parsedquery)
+        results = catalog(**parsedquery)
 
         if results and limit and results.actual_result_count > limit:
             results.actual_result_count = limit
diff --git a/plone/app/querystring/queryparser.py b/plone/app/querystring/queryparser.py
index 36955d0..d3fccf7 100644
--- a/plone/app/querystring/queryparser.py
+++ b/plone/app/querystring/queryparser.py
@@ -13,6 +13,7 @@
 
 
 def parseFormquery(context, formquery, sort_on=None, sort_order=None):
+
     if not formquery:
         return {}
     reg = getUtility(IRegistry)
@@ -107,6 +108,22 @@ def _currentUser(context, row):
     user = mt.getAuthenticatedMember()
     return {row.index: {'query': user.getUserName()}}
 
+def _showInactive(context, row):
+    """ Current user roles lookup in order to determine whether user should
+        be allowed to view inactive content
+    """
+    mt = getToolByName(context, 'portal_membership')
+    user = mt.getAuthenticatedMember()
+    value = False
+    user_roles = user.getRoles()
+    row_values = row.values
+    if row_values:
+        for role in user_roles:
+            if role in row_values:
+                value = True
+                break
+    return {row.index: value}
+
 
 def _lessThanRelativeDate(context, row):
     """ "Between now and N days from now." """
diff --git a/plone/app/querystring/tests/testQueryParser.py b/plone/app/querystring/tests/testQueryParser.py
index 61291c4..4fecc6b 100644
--- a/plone/app/querystring/tests/testQueryParser.py
+++ b/plone/app/querystring/tests/testQueryParser.py
@@ -88,14 +88,18 @@ def getPhysicalPath(self):
 
 class MockUser(object):
 
-    def __init__(self, username=None):
+    def __init__(self, username=None, roles=None):
         self.username = 'Anonymous User'
         if username:
             self.username = username
+        self.roles = roles or "Anonymous"
 
     def getUserName(self):
         return self.username
 
+    def getRoles(self):
+        return self.roles
+
 
 class MockPortal_membership(object):
 
@@ -283,6 +287,31 @@ def test__currentUser(self):
         expected = {'Creator': {'query': 'admin'}}
         self.assertEqual(parsed, expected)
 
+    def test__showInactive(self):
+        # Anonymous user
+        u = MockUser()
+        pm = MockPortal_membership(user=u)
+        context = MockSite(portal_membership=pm)
+        data = Row(index='show_inactive',
+                   operator='_showInactive',
+                   values=["Manager"])
+        parsed = queryparser._showInactive(context, data)
+        # False is expected since Anonymous doesn't have Manager role
+        expected = {'show_inactive': False}
+        self.assertEqual(parsed, expected)
+
+        # Logged in user 'admin'
+        u = MockUser(username='admin', roles=("Manager",))
+        pm = MockPortal_membership(user=u)
+        context = MockSite(portal_membership=pm)
+        data = Row(index='show_inactive',
+                   operator='_showInactive',
+                   values=["Manager"])
+        parsed = queryparser._showInactive(context, data)
+        # True is expected since Admin should have Manager role
+        expected = {'show_inactive': True}
+        self.assertEqual(parsed, expected)
+
     def test__lessThanRelativeDate(self):
         days = 2
         now = DateTime()
diff --git a/plone/app/querystring/upgrades.zcml b/plone/app/querystring/upgrades.zcml
new file mode 100644
index 0000000..666537d
--- /dev/null
+++ b/plone/app/querystring/upgrades.zcml
@@ -0,0 +1,26 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:genericsetup="http://namespaces.zope.org/genericsetup">
+
+  <genericsetup:upgradeStep
+      source="1"
+      destination="2"
+      title="Fix Typo in Registry"
+      description="Migrate registry to fix a typo."
+      profile="plone.app.querystring:default"
+      handler=".upgrades.upgrade_1_to_2_typo_in_registry"
+      />
+
+    <genericsetup:upgradeSteps
+            source="2"
+            destination="3"
+            profile="plone.app.querystring:default">
+
+        <genericsetup:upgradeDepends
+                title="Add show inactive operation and field"
+                import_profile="plone.app.querystring:upgrade_to_3"
+                />
+
+    </genericsetup:upgradeSteps>
+
+</configure>




-------------------------------------------------------------------------------
-------------- next part --------------
A non-text attachment was scrubbed...
Name: CHANGES.log
Type: application/octet-stream
Size: 87455 bytes
Desc: not available
URL: <http://lists.plone.org/pipermail/plone-testbot/attachments/20140507/778c7e36/attachment-0002.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: build.log
Type: application/octet-stream
Size: 100370 bytes
Desc: not available
URL: <http://lists.plone.org/pipermail/plone-testbot/attachments/20140507/778c7e36/attachment-0003.obj>


More information about the Testbot mailing list