# 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. ```http GET /Plone/my-subsite/@subsite HTTP/1.1 Host: localhost:8080 Accept: application/json ``` **Response structure:** ```json { "@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: ```http GET /Plone/my-subsite?expand=subsite HTTP/1.1 Host: localhost:8080 Accept: application/json ``` **Expanded component:** ```json { "@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](navigation.md) 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. ### @popular-searches on a Subsite Returns the popular search terms configured for the subsite. Each item links to a search within the subsite. ```http GET /Plone/my-subsite/@popular-searches HTTP/1.1 Host: localhost:8080 Accept: application/json ``` **Response structure:** ```json { "@id": "http://localhost:8080/Plone/my-subsite/@popular-searches", "items": [ { "title": "search 1", "@id": "http://localhost:8080/Plone/my-subsite/@es-search?SearchableText=search 1" }, { "title": "search 2", "@id": "http://localhost:8080/Plone/my-subsite/@es-search?SearchableText=search 2" } ] } ``` When no popular searches are configured, `items` is an empty array. ## Integration Guide ### Applying subsite branding **JavaScript:** ```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:** ```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:** ```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']) ```