Source code for collective.multiworkflow.querystring.query_index_modifiers
"""Redirect ``review_state`` collection queries onto the chain index."""
from ..indexers import format_state
from ..indexers import STATE_SEPARATOR
from ..indexers import WORKFLOW_STATES
from plone import api
from plone.app.querystring.interfaces import IParsedQueryIndexModifier
from Products.CMFPlone.WorkflowTool import WorkflowTool
from typing import Any
from zope.interface import implementer
#: The keys of a parsed query whose contents are state values.
VALUE_KEYS = ("query", "not")
[docs]
def qualify(state: str) -> str:
"""Name the workflow a bare state id belongs to.
Values in the index read ``<workflow-id>|<state-id>``, but a criterion
written against ``review_state`` — a stored collection predating this
add-on, or a query built in code — carries a bare ``published``. Left
alone, such a value matches nothing at all, which reads as "no content in
that state" rather than as a query that could never have worked.
So a bare id is read as belonging to the site's default workflow, which is
the same workflow the index always lists first.
:param state: a state id, qualified or not.
:returns: the value as the index holds it; unchanged when it was already
qualified, or when the site declares no default chain to attribute it
to.
"""
if STATE_SEPARATOR in state:
return state
# The tool is typed as the interface rather than the implementation.
wftool: WorkflowTool = api.portal.get_tool("portal_workflow") # type: ignore[assignment]
chain = wftool.getDefaultChain()
if not chain:
return state
return format_state(chain[0], state)
[docs]
@implementer(IParsedQueryIndexModifier)
class ReviewStateModifier:
"""Rewrite a parsed ``review_state`` query to target ``workflow_states``."""
def __call__(self, value: dict[str, Any]) -> tuple[str, dict[str, Any]]:
"""Rename the index, qualifying bare state ids on the way through.
Every key but the value-carrying ones is passed on untouched, and that
matters more than it looks: ``plone.app.querystring`` parses the *all
of* operation to ``{"query": [...], "operator": "and"}`` and *excludes*
to ``{"not": [...]}``. Rebuilding the dict from ``query`` alone would
drop the operator — silently turning an AND into a KeywordIndex's
default OR — and would turn an exclusion into an empty query.
:param value: the parsed query for the ``review_state`` index.
:returns: the index to query instead, and the query to use.
"""
modified = dict(value)
for key in VALUE_KEYS:
if key in modified:
modified[key] = self._qualified(modified[key])
return (WORKFLOW_STATES, modified)
def _qualified(self, value: Any) -> Any:
"""Qualify one parsed value, whatever shape it arrived in.
A lone string becomes a one-item list: the index is a KeywordIndex, so
callers reading the query back get a consistent shape either way.
:param value: a state id, a sequence of them, or something else
entirely — a date range, say, which is passed through untouched.
:returns: the qualified equivalent.
"""
if isinstance(value, str):
return [qualify(value)]
if isinstance(value, list | tuple):
return [qualify(item) if isinstance(item, str) else item for item in value]
return value