Subsite

A Subsite is a self-contained area within a Plone site that acts as its own navigation root. Content placed inside a subsite gets its own navigation, theming, footer and search scope, while still living inside the same Plone site. This makes it possible to run several visually and structurally distinct sites from a single backend.

Overview

A Subsite is a container content type that is marked as a navigation root. Being a navigation root has two consequences a frontend cares about:

  • Navigation (@navigation, @contextnavigation, @navroot) is scoped to the subsite rather than the main site root.

  • Root-level services such as @footer, @linklist and @popular-searches resolve to the subsite, so each subsite can carry its own footer, link lists and popular searches.

The logo viewlet is overridden so that, even on a subsite, the site logo always links back to the main site root – giving visitors a way to navigate out of the subsite.

Content Type

Subsite A folderish content item that holds the content of the subsite. It can contain content pages, news folders, media folders, contact folders and the standard Simplelayout blocks.

Configuration Fields

The Subsite carries a Configuration fieldset of optional theming fields. These all use the subsite_ prefix, which is how the @subsite endpoint discovers them.

subsite_logo Image used as the subsite logo.

subsite_mobile_logo Image used as the subsite logo on small viewports.

subsite_primary_color Primary brand color for the subsite (e.g. #ff0000).

subsite_secondary_color Secondary brand color for the subsite.

subsite_base_font Base font for body text.

subsite_heading_font Font for headings.

Additional subsite-scoped settings (preview configuration and popular searches) are provided by a manage behavior and are editable only by users who can manage the subsite.

@subsite

Returns the subsite theming configuration. The endpoint collects every schema field on the subsite whose name starts with subsite_, strips the prefix, and returns the values under a data object together with the subsite title. The frontend uses this to apply per-subsite branding (colors, fonts, logos).

The endpoint is registered on the Subsite itself. When the current context is a page inside a subsite, the component resolves the request to the owning subsite’s @subsite URL.

GET /Plone/my-subsite/@subsite HTTP/1.1
Host: localhost:8080
Accept: application/json

Response structure:

{
    "@id": "http://localhost:8080/Plone/my-subsite/@subsite",
    "data": {
        "title": "My Subsite",
        "primary_color": "#ff0000",
        "secondary_color": "#00ff00",
        "logo": null,
        "mobile_logo": null,
        "base_font": null,
        "heading_font": null
    }
}

The keys inside data correspond to the configuration field names with the subsite_ prefix removed (subsite_primary_color becomes primary_color), plus the subsite title. Each value is the standard REST serialization of the field, so image fields (logo, mobile_logo) serialize to their usual image representation when set. Fields are only included when the requesting user has read permission for them.

Expansion behaviour

@subsite is also an expandable component, but it does not embed its full data when expanded. For caching reasons the expanded component only returns its @id and a hint to fetch it separately:

GET /Plone/my-subsite?expand=subsite HTTP/1.1
Host: localhost:8080
Accept: application/json

Expanded component:

{
    "@components": {
        "subsite": {
            "@id": "http://localhost:8080/Plone/my-subsite/@subsite",
            "data": {
                "msg": "Not extandable for caching reasons, please call @id in a separate request."
            }
        }
    }
}

The subsite component is only present when the current context is a subsite or lives inside one. On the plain Plone site root it is absent. When the context is a page inside a subsite, the component’s @id points at the owning subsite’s @subsite endpoint. The frontend should follow that @id in a separate request to read the actual configuration, which keeps the per-page content responses cacheable.

Root-level services on a Subsite

Because a Subsite is a navigation root, the navigation-root services documented on the Navigation page resolve to the subsite when called on or inside it:

  • @navroot returns the subsite as the navigation root.

  • @footer returns the subsite’s footer fields (schema fields prefixed footer_).

  • @popular-searches returns the subsite’s popular searches, each linking to an @es-search query scoped to the subsite.

Integration Guide

Applying subsite branding

JavaScript:

async function fetchSubsiteConfig(subsiteUrl) {
    const response = await fetch(`${subsiteUrl}/@subsite`, {
        headers: { 'Accept': 'application/json' }
    });
    const data = await response.json();
    return data.data;
}

const config = await fetchSubsiteConfig('http://localhost:8080/Plone/my-subsite');
document.documentElement.style.setProperty('--primary', config.primary_color);
document.documentElement.style.setProperty('--secondary', config.secondary_color);

Resolving the subsite from any page

When rendering an arbitrary page, read the subsite component to find the owning subsite, then fetch its configuration:

JavaScript:

async function resolveSubsite(pageUrl) {
    const page = await (await fetch(`${pageUrl}?expand=subsite`, {
        headers: { 'Accept': 'application/json' }
    })).json();

    const component = page['@components'].subsite;
    if (!component) {
        return null;  // not inside a subsite
    }

    const config = await (await fetch(component['@id'], {
        headers: { 'Accept': 'application/json' }
    })).json();
    return config.data;
}

Python:

import requests

page = requests.get(
    'http://localhost:8080/Plone/my-subsite/some-page',
    params={'expand': 'subsite'},
    headers={'Accept': 'application/json'},
).json()

component = page['@components'].get('subsite')
if component:
    config = requests.get(
        component['@id'],
        headers={'Accept': 'application/json'},
    ).json()
    print(config['data']['primary_color'])