Source code for collective.transmute.steps.blocks

"""
Pipeline steps for handling Volto blocks in ``collective.transmute``.

This module provides functions and async generator steps for processing, normalizing,
and generating Volto blocks for Plone items in the transformation pipeline. These steps
handle block layouts for collections, folders, and other types, and support block
variation and customization.
"""

from collective.html2blocks.converter import volto_blocks
from collective.transmute import _types as t
from collective.transmute.settings import get_settings
from functools import cache


[docs] @cache def _possible_variations() -> dict[str, str]: """ Return a dictionary of possible variations for block layouts. Returns ------- dict[str, str] A dictionary mapping variation names to their corresponding layout types. """ settings = get_settings() variations = settings.steps.get("blocks", {}).get("variations", {}) return variations
[docs] def _blocks_collection( item: t.PloneItem, blocks: list[t.VoltoBlock] ) -> list[t.VoltoBlock]: """ Add a listing block to a collection or topic item. Parameters ---------- item : PloneItem The item to process. blocks : list[VoltoBlock] The list of blocks to append to. Returns ------- list[VoltoBlock] The updated list of blocks. """ # TODO: Process query to remove old types variations = _possible_variations() query = item.get("query") if variation := item.get("layout"): variation = variations.get(variation) if query: querystring: dict[str, list | int | str | None] = { "query": query, } if "sort_on" in item: querystring["sort_on"] = item["sort_on"] if "sort_order" in item: querystring["sort_order"] = item["sort_order"] querystring["sort_order_boolean"] = True elif "sort_reversed" in item: querystring["sort_order"] = ( "descending" if item.get("sort_reversed") else "ascending" ) querystring["sort_order_boolean"] = bool(item.get("sort_reversed")) if limit := item.get("limit"): querystring["limit"] = limit if b_size := item.get("item_count", 20): querystring["b_size"] = b_size block = { "@type": "listing", "headline": "", "headlineTag": "h2", "querystring": querystring, "styles": {}, "variation": variation, } blocks.append(block) return blocks
[docs] def _blocks_folder(item: t.PloneItem, blocks: list[t.VoltoBlock]) -> list[t.VoltoBlock]: """ Add a listing block to a folder item, using possible variations. Parameters ---------- item : PloneItem The item to process. blocks : list[VoltoBlock] The list of blocks to append to. Returns ------- list[VoltoBlock] The updated list of blocks. """ if variation := item.get("layout"): variations = _possible_variations() variation = variations.get(variation) if not variation: variation = "listing" block = { "@type": "listing", "headline": "", "headlineTag": "h2", "styles": {}, "variation": variation, } blocks.append(block) return blocks
BLOCKS_ORIG_TYPE = { "Collection": _blocks_collection, "Topic": _blocks_collection, "Folder": _blocks_folder, }
[docs] def _get_default_blocks( type_info: dict, has_image: bool, has_description: bool ) -> list[t.VoltoBlock]: """ Get the default blocks for an item type, filtering by image and description presence. Parameters ---------- type_info : dict Type information from settings. has_image : bool Whether the item has an image. has_description : bool Whether the item has a description. Returns ------- list[VoltoBlock] The list of default blocks for the item. """ default_blocks = type_info.get("override_blocks", type_info.get("blocks")) blocks = list(default_blocks) if default_blocks else [] if default_blocks: blocks = [] for block in default_blocks: block_type = block["@type"] if (block_type == "leadimage" and not has_image) or ( block_type == "description" and not has_description ): continue blocks.append(block) return blocks
[docs] async def process_blocks( item: t.PloneItem, state: t.PipelineState, settings: t.TransmuteSettings, ) -> t.PloneItemGenerator: """ Process and generate Volto blocks for an item, updating its block layout. Parameters ---------- item : PloneItem The item to process. state : PipelineState The pipeline state object. settings : TransmuteSettings The transmute settings object. Yields ------ PloneItem The updated item with Volto blocks and blocks_layout. Example ------- .. code-block:: pycon >>> async for result in process_blocks(item, state, settings): ... print(result['blocks']) """ type_ = item["@type"] has_image = bool(item.get("image")) has_description = has_description = bool( item.get("description") is not None and item.get("description", "").strip() ) type_info = settings.types.get(type_, {}) blocks = _get_default_blocks(type_info, has_image, has_description) additional_blocks: list[t.VoltoBlock] = [] # Blocks defined somewhere else item_blocks: list[t.VoltoBlock] = item.pop("_blocks_", []) if blocks or item_blocks: blocks.extend(item_blocks) orig_type = item.get("_orig_type", type_) if processor := BLOCKS_ORIG_TYPE.get(orig_type): additional_blocks = processor(item, additional_blocks) text = item.get("text", {}) src = text.get("data", "") if text else "" blocks_info = volto_blocks( source=src, default_blocks=blocks, additional_blocks=additional_blocks ) item["blocks"], item["blocks_layout"] = ( blocks_info.get("blocks", {}), blocks_info.get("blocks_layout", {}), ) yield item