Source code for collective.transmute.utils.querystring

"""
Querystring utilities for ``collective.transmute``.

This module provides helper functions for cleaning up, deduplicating, and
post-processing querystring definitions used in Plone collections and
listing blocks. Functions support normalization and transformation of
querystring items and values.
"""

from .portal_types import fix_portal_type
from collective.transmute import _types as t

import re


_PATH_UID_PATTERN = re.compile(r"UID##(?P<UID>.*)##")


[docs] def parse_path_value(value: str) -> str: """ Parse a path value to ensure it is a valid URL or UID reference. Parameters ---------- value : str The path value to parse. Returns ------- str The parsed path value, possibly converted to UID format. Example ------- .. code-block:: pycon >>> parse_path_value('12345678901234567890123456789012') 'UID##12345678901234567890123456789012##' """ parts = value.split(":") path = parts[0] if "/" not in path and len(path) == 32: value = value.replace(path, f"UID##{path}##") return value
[docs] def _process_date_between(raw_value: list[str]) -> tuple[str, list[str] | str]: """ Process a date between operation for querystring items. Parameters ---------- raw_value : list[str] List containing two date strings. Returns ------- tuple[str, list[str] | str] The operation and processed value(s). """ oper = "plone.app.querystring.operation.date.between" if len(raw_value) != 2: raise ValueError("Date between operation requires two values.") from_, to_ = raw_value if from_ is None and to_ is None: oper = "" value = [] elif from_ is None: oper = "plone.app.querystring.operation.date.lessThan" value = to_.split("T")[0] elif to_ is None: oper = "plone.app.querystring.operation.date.largerThan" value = from_.split("T")[0] else: value = [from_, to_] return oper, value
[docs] def deduplicate_value(value: list | None) -> list | None: """ Deduplicate values in a list, preserving None. Parameters ---------- value : list or None The list to deduplicate. Returns ------- list or None The deduplicated list, or None if input is None. """ return list(set(value)) if value is not None else None
[docs] def cleanup_querystring_item(item: dict) -> tuple[dict, bool]: """ Clean up a single item in a querystring definition. Parameters ---------- item : dict The querystring item to clean up. Returns ------- tuple[dict, bool] The cleaned item and a post-processing status flag. """ prefix = "plone.app.querystring.operation" post_processing = False index = item["i"] oper = item["o"] value = item["v"] match index: case "portal_type": value = [fix_portal_type(v) for v in value] value = [v for v in value if v.strip()] case "section": value = None match oper: case ( "plone.app.querystring.operation.selection.is" | "plone.app.querystring.operation.selection.any" ): oper = "plone.app.querystring.operation.selection.any" value = deduplicate_value(value) case "plone.app.querystring.operation.date.between": oper, value = _process_date_between(value) case "plone.app.querystring.operation.string.path": value = parse_path_value(str(value)) post_processing = value.startswith("UID##") case "plone.app.querystring.operation.date.lessThanRelativeDate": if isinstance(value, int) and value < 0: oper = f"{prefix}.date.largerThanRelativeDate" value = abs(value) if oper and value: item["v"] = value item["o"] = oper else: item = {} return item, post_processing
[docs] def cleanup_querystring(query: list[dict]) -> tuple[list[dict], bool]: """ Clean up the querystring of a collection-like object or listing block. Parameters ---------- query : list[dict] The querystring to clean up. Returns ------- tuple[list[dict], bool] The cleaned querystring and a post-processing status flag. """ post_processing = False query = query if query else [] new_query = [] for item in query: item, status = cleanup_querystring_item(item) if not item: continue post_processing = post_processing or status new_query.append(item) return new_query, post_processing
[docs] def post_process_querystring(query: list[dict], state: t.PipelineState) -> list[dict]: """ Post-process a querystring, replacing UID references with actual paths. Parameters ---------- query : list[dict] The querystring to post-process. state : PipelineState The pipeline state object containing UID-path mapping. Returns ------- list[dict] The post-processed querystring. """ query = query if query else [] new_query = [] for item in query: oper = item["o"] value = item["v"] match oper: case "plone.app.querystring.operation.string.path": value = str(value) if match := re.match(_PATH_UID_PATTERN, value): uid = match.group("UID") if path := state.uid_path.get(uid): value = re.sub(_PATH_UID_PATTERN, path, value) else: value = re.sub(_PATH_UID_PATTERN, uid, value) if value: item["v"] = value item["o"] = oper new_query.append(item) return new_query