Skip to content

Commit

Permalink
Fix a circular dependency to plone.app.vocabularies
Browse files Browse the repository at this point in the history
  • Loading branch information
jensens authored and gforcada committed May 15, 2023
1 parent e874380 commit d7fdfe9
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 0 deletions.
5 changes: 5 additions & 0 deletions news/fix-circular-dep-pavocabularies.bugfix
@@ -0,0 +1,5 @@
Fix a circular transitive dependency to `plone.app.querystring`.
New direct dependency explicit on `plone.app.vocabularies`.
Move `plone.app.querystring.catalog.CatalogVocabularyFactory` to `.vocabularies`, move the ZCML to register the factory, move the the test.
Move `plone.app.querystring.utils.parse_query` with new name `parseAndModifyFormquery` to `.queryparser`.
[@jensens]
4 changes: 4 additions & 0 deletions plone/app/querystring/configure.zcml
Expand Up @@ -64,4 +64,8 @@
name="1000"
component=".querymodifiers.modify_query_to_enforce_navigation_root"
/>
<utility
factory=".vocabularies.CatalogVocabularyFactory"
name="plone.app.vocabularies.Catalog"
/>
</configure>
19 changes: 19 additions & 0 deletions plone/app/querystring/queryparser.py
@@ -1,3 +1,4 @@
from .interfaces import IParsedQueryIndexModifier
from Acquisition import aq_parent
from collections import namedtuple
from DateTime import DateTime
Expand All @@ -8,6 +9,7 @@
from plone.registry.interfaces import IRegistry
from plone.uuid.interfaces import IUUID
from Products.CMFCore.utils import getToolByName
from zope.component import getUtilitiesFor
from zope.component import getUtility
from zope.dottedname.resolve import resolve

Expand Down Expand Up @@ -67,6 +69,23 @@ def parseFormquery(context, formquery, sort_on=None, sort_order=None):
return query


def parseAndModifyFormquery(context, query, sort_on=None, sort_order=None):
parsedquery = parseFormquery(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

return parsedquery


# Query operators


Expand Down
19 changes: 19 additions & 0 deletions plone/app/querystring/tests/testVocabularies.py
@@ -0,0 +1,19 @@
from plone.app.vocabularies.tests.test_vocabularies import vocabSetUp
from plone.app.vocabularies.tests.test_vocabularies import vocabTearDown

import doctest
import unittest


def test_suite():
optionflags = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE
return unittest.TestSuite(
(
doctest.DocTestSuite(
"plone.app.vocabularies.catalog",
setUp=vocabSetUp,
tearDown=vocabTearDown,
optionflags=optionflags,
),
)
)
63 changes: 63 additions & 0 deletions plone/app/querystring/vocabularies.py
@@ -0,0 +1,63 @@
from .queryparser import parseAndModifyFormquery
from plone.app.vocabularies.catalog import CatalogVocabulary
from plone.base.navigationroot import get_navigation_root_object
from zope.component.hooks import getSite
from zope.interface import implementer
from zope.schema.interfaces import IVocabularyFactory


# this vocabulary is in this package by intend.
# since plone.app.querystring depends on plone.app.vocabularies
# we can not put it over there without creating a circular dependency.


@implementer(IVocabularyFactory)
class CatalogVocabularyFactory:
"""
Test application of Navigation Root:
>>> from plone.app.vocabularies.tests.base import create_context
>>> from plone.app.vocabularies.tests.base import DummyUrlTool
>>> from plone.app.vocabularies.tests.base import DummyCatalog
>>> class DummyPathCatalog(DummyCatalog):
... def __call__(self, **query):
... if 'path' in query and 'query' in query['path']:
... return [v for v in self.values() if
... v.getPath().startswith(query['path']['query'])]
... return self.values()
>>> catalog = DummyPathCatalog(['/abcd', '/defg', '/dummy/sub-site',
... '/dummy/sub-site/ghij'])
>>> context = create_context()
>>> context.portal_catalog = catalog
>>> context.portal_url = DummyUrlTool(context)
>>> factory = CatalogVocabularyFactory()
>>> sorted(t.token for t in factory(context))
['/abcd', '/defg', '/dummy/sub-site', '/dummy/sub-site/ghij']
>>> from plone.app.vocabularies.tests.base import DummyNavRoot
>>> nav_root = DummyNavRoot('sub-site', parent=context)
>>> [t.token for t in factory(nav_root)]
['/dummy/sub-site', '/dummy/sub-site/ghij']
"""

def __call__(self, context, query=None):
parsed = {}
if query:
parsed = parseAndModifyFormquery(context, query["criteria"])
if "sort_on" in query:
parsed["sort_on"] = query["sort_on"]
if "sort_order" in query:
parsed["sort_order"] = str(query["sort_order"])

if "path" not in parsed:
site = getSite()
nav_root = get_navigation_root_object(context, site)
site_path = site.getPhysicalPath()
if nav_root and nav_root.getPhysicalPath() != site_path:
parsed["path"] = {
"query": "/".join(nav_root.getPhysicalPath()),
"depth": -1,
}
return CatalogVocabulary.fromItems(parsed, context)
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -42,6 +42,7 @@
"setuptools",
"plone.app.contentlisting",
"plone.app.registry>=1.1",
"plone.app.vocabularies",
"plone.base",
"plone.batching",
"plone.i18n",
Expand Down

0 comments on commit d7fdfe9

Please sign in to comment.