Banner ====== The Banner feature provides a flexible system for displaying contextual banners across the site. Banners can be configured to appear on specific paths or excluded from certain sections, enabling targeted messaging for different areas of the website. Overview -------- Banners are Dexterity content types that support path-based visibility rules. They can be: - Displayed globally across the entire site - Restricted to specific sections using include paths - Hidden from certain areas using exclude paths - Managed through workflow states (publish/retract) - Cached efficiently using cache tags Content Type ------------ **Banner** A content item that can contain any content (text, images, links) and defines where it should be displayed. **Behaviors:** - ``plone.dublincore`` - Dublin Core metadata - ``plone.namefromtitle`` - Automatic ID from title - ``plone.versioning`` - Version history - ``wcs.backend.banner.interfaces.IBannerBehavior`` - Path-based visibility **Configuration:** - ``global_allow``: False (Banners are typically only addable in a designated folder) Schema Fields ^^^^^^^^^^^^^ The Banner behavior (``IBannerBehavior``) provides: **include_paths** List of paths where the banner should be displayed. Each path is relative to the site root. Leave empty to display on all pages. Example: ``['/news', '/events']`` **exclude_paths** List of paths where the banner should NOT be displayed. Exclude paths take precedence over include paths. Example: ``['/private', '/admin']`` Configuration ------------- Banner settings are managed through the Plone registry: **wcs.backend.banner_portal_type** Portal type used for banners. Default: ``Banner`` **wcs.backend.banner_folder_id** ID of the folder containing banners within the navigation root. Default: ``banners`` Registry Configuration ^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: xml Banner Portal Type Banner Banner Folder ID banners REST API -------- GET @banner ^^^^^^^^^^^ Retrieves all banners that should be displayed for the current context. .. http:example:: curl python-requests GET /Plone/my-page/@banner HTTP/1.1 Host: localhost:8080 Accept: application/json **Response Structure:** .. code-block:: json { "@id": "http://localhost:8080/Plone/my-page/@banner", "items": [ { "@id": "http://localhost:8080/Plone/banners/global-banner", "@type": "Banner", "title": "Important Announcement", "description": "Site-wide notification", "UID": "abc123...", "include_paths": [], "exclude_paths": [] }, { "@id": "http://localhost:8080/Plone/banners/news-banner", "@type": "Banner", "title": "News Section Banner", "include_paths": ["/news"], "exclude_paths": [] } ] } Path Matching Logic ------------------- The ``is_valid(context)`` method determines if a banner should be displayed: 1. **Exclude paths are checked first** - If the context path starts with any exclude path, the banner is hidden 2. **Include paths are checked** - If include paths are defined, the context must match at least one 3. **Empty include paths** - If no include paths are defined, the banner displays everywhere (except excluded paths) Path Normalization ^^^^^^^^^^^^^^^^^^ Paths can be specified with or without the portal path prefix: - ``/news`` is equivalent to ``/Plone/news`` - Both formats are automatically normalized **Examples:** +------------------------+-------------------------+------------------------+-------------------+ | Include Paths | Exclude Paths | Context Path | Banner Displayed? | +========================+=========================+========================+===================+ | ``[]`` | ``[]`` | Any | Yes | +------------------------+-------------------------+------------------------+-------------------+ | ``['/news']`` | ``[]`` | ``/Plone/news/article``| Yes | +------------------------+-------------------------+------------------------+-------------------+ | ``['/news']`` | ``[]`` | ``/Plone/events`` | No | +------------------------+-------------------------+------------------------+-------------------+ | ``[]`` | ``['/private']`` | ``/Plone/private/doc`` | No | +------------------------+-------------------------+------------------------+-------------------+ | ``['/news']`` | ``['/news/private']`` | ``/Plone/news/article``| Yes | +------------------------+-------------------------+------------------------+-------------------+ | ``['/news']`` | ``['/news/private']`` | ``/Plone/news/private``| No | +------------------------+-------------------------+------------------------+-------------------+ Caching ------- The Banner feature integrates with the 7inOne caching system using cache tags. Cache Tag Headers ^^^^^^^^^^^^^^^^^ When the ``@banner`` endpoint is called by anonymous users, the response includes cache tags: - ``banner`` - Generic tag for all banner-related content - Individual banner UIDs - For precise cache invalidation Cache Invalidation ^^^^^^^^^^^^^^^^^^ The ``BannerUIDContentCollector`` handles cache invalidation: - **On publish or schedule**: Invalidates the generic ``banner`` tag (affects all cached ``@banner`` responses) - **On retract**: Invalidates only the specific banner's UID This ensures: - New or updated published banners appear immediately across the site - Retracted banners only invalidate affected cached responses Integration Guide ----------------- Fetching Banners via REST API ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **JavaScript Example:** .. code-block:: javascript async function fetchBanners(contextUrl) { const response = await fetch(`${contextUrl}/@banner`, { headers: { 'Accept': 'application/json' } }); const data = await response.json(); return data.items; } // Usage const banners = await fetchBanners('http://localhost:8080/Plone/news/article'); banners.forEach(banner => { displayBanner(banner.title, banner.description); }); **Python Example:** .. code-block:: python import requests response = requests.get( 'http://localhost:8080/Plone/news/@banner', headers={'Accept': 'application/json'} ) banners = response.json()['items'] File Locations -------------- - **Content type**: ``wcs/backend/banner/content.py`` - **Interfaces/Behavior**: ``wcs/backend/banner/interfaces.py`` - **REST API endpoint**: ``wcs/backend/banner/restapi.py`` - **ZCML configuration**: ``wcs/backend/banner/configure.zcml`` - **Type definition**: ``wcs/backend/profiles/default/types/Banner.xml`` - **Registry configuration**: ``wcs/backend/profiles/default/registry/banner.xml`` - **Cache collector**: ``wcs/backend/caching/banner_collector.py`` - **Tests**: ``wcs/backend/tests/test_banner.py``