Responsible Unit¶
The Responsible Unit feature allows assigning an organizational ContentPage as the responsible unit for content pages. It supports inheritance through the content tree, so a responsible unit set on a section-level page automatically applies to all its children. The feature provides REST API endpoints for frontend consumption and a viewlet for the HTML view.
Overview¶
A responsible unit is a ContentPage from the organisation area of the site. The feature supports:
Assigning a responsible unit to any page that has the behavior enabled
Automatic inheritance through the content tree (children inherit from their nearest ancestor)
Overriding the inherited value by setting a different responsible unit on a child page
Blocking inheritance entirely so a page (and its descendants) have no responsible unit
REST API endpoints to retrieve the responsible unit and its back-references
A viewlet displaying all pages that reference the current page as their responsible unit
Schema Fields¶
- responsible_unit
A relation field to select a ContentPage as the responsible organizational unit. The reference browser widget restricts selection to ContentPage objects. Optional field.
- responsible_unit_blocked
A boolean checkbox to block responsible unit inheritance from parent pages. When checked, the page and its descendants will not inherit a responsible unit from ancestors. Default:
False.
Inheritance Logic¶
The responsible unit for a given page is determined by walking up the content tree from the current page towards the navigation root:
If the current page has a
responsible_unitrelation set, that value is returnedIf the current page has
responsible_unit_blockedchecked (and no own relation),Noneis returned – inheritance stopsOtherwise, the same check is repeated on the parent page
The walk stops at the navigation root, which is never checked itself
This means:
Set a responsible unit on a section-level page, and all descendant pages inherit it automatically
Any descendant can override by setting its own responsible unit
Any descendant can block inheritance entirely by checking the blocked flag
If a page has both a relation AND the blocked flag, the relation takes precedence (the blocked flag only stops inheritance from parents)
REST API¶
GET @responsible¶
Retrieves the responsible unit for the current page. Walks up the content tree to find the nearest ancestor with a responsible unit set and returns the full serialization of the responsible organization page.
http
GET /Plone/topics/my-page/@responsible HTTP/1.1
Host: localhost:8080
Accept: application/json
curl
curl -i -X GET http://localhost:8080/Plone/topics/my-page/@responsible -H "Accept: application/json"
python-requests
requests.get('http://localhost:8080/Plone/topics/my-page/@responsible', headers={'Accept': 'application/json'})
Response when a responsible unit is found:
{
"@id": "http://localhost:8080/Plone/topics/my-page/@responsible",
"items": [
{
"@id": "http://localhost:8080/Plone/organisation/department-a",
"@type": "ContentPage",
"title": "Department A",
"UID": "abc123..."
}
]
}
Response when no responsible unit is set or inheritance is blocked:
{
"@id": "http://localhost:8080/Plone/topics/my-page/@responsible",
"items": []
}
The items array contains at most one element: the full JSON serialization of the responsible ContentPage. The serialization includes all standard fields of the ContentPage.
GET @responsible-backreferences¶
Returns all pages that reference the current page as their responsible unit. Only pages with direct relations are returned (not pages that inherit via the content tree). Results are sorted alphabetically by title.
http
GET /Plone/organisation/department-a/@responsible-backreferences HTTP/1.1
Host: localhost:8080
Accept: application/json
curl
curl -i -X GET http://localhost:8080/Plone/organisation/department-a/@responsible-backreferences -H "Accept: application/json"
python-requests
requests.get('http://localhost:8080/Plone/organisation/department-a/@responsible-backreferences', headers={'Accept': 'application/json'})
Response:
{
"@id": "http://localhost:8080/Plone/organisation/department-a/@responsible-backreferences",
"items": [
{
"@id": "http://localhost:8080/Plone/topics/environment",
"@type": "ContentPage",
"title": "Environment",
"UID": "def456..."
},
{
"@id": "http://localhost:8080/Plone/topics/traffic",
"@type": "ContentPage",
"title": "Traffic",
"UID": "ghi789..."
}
]
}
Results are filtered by view permission and effective/expiration dates. Anonymous users will not see pages they do not have permission to view or pages that are inactive.
Back-References Viewlet¶
A viewlet rendered below the content body displays all pages that reference the current page as their responsible unit. The viewlet is only visible when at least one back-reference exists. Links are sorted alphabetically and point directly to the referencing pages.
Caching¶
The Responsible Unit feature integrates with the caching system using cache tags, following the same pattern as the @banner endpoint.
Cache Tag Headers¶
When the endpoints are called by anonymous users, the responses include cache tag headers for CDN invalidation:
@responsible endpoint:
responsible– Generic tag for all responsible-related contentThe UID of the responsible organization page (when one is found)
@responsible-backreferences endpoint:
responsible-backreferences– Generic tag for back-reference contentThe UID of the current context page
Integration Guide¶
Fetching the Responsible Unit¶
JavaScript:
async function fetchResponsibleUnit(pageUrl) {
const response = await fetch(`${pageUrl}/@responsible`, {
headers: {
'Accept': 'application/json'
}
});
const data = await response.json();
if (data.items.length > 0) {
return data.items[0];
}
return null;
}
// Usage
const responsible = await fetchResponsibleUnit('http://localhost:8080/Plone/topics/my-page');
if (responsible) {
console.log(responsible.title); // "Department A"
}
Python:
import requests
response = requests.get(
'http://localhost:8080/Plone/topics/my-page/@responsible',
headers={'Accept': 'application/json'}
)
items = response.json()['items']
if items:
print(items[0]['title'])
Fetching Back-References¶
JavaScript:
async function fetchBackreferences(orgPageUrl) {
const response = await fetch(`${orgPageUrl}/@responsible-backreferences`, {
headers: {
'Accept': 'application/json'
}
});
const data = await response.json();
return data.items;
}
// Usage
const pages = await fetchBackreferences('http://localhost:8080/Plone/organisation/department-a');
pages.forEach(page => {
console.log(page.title);
});
Python:
import requests
response = requests.get(
'http://localhost:8080/Plone/organisation/department-a/@responsible-backreferences',
headers={'Accept': 'application/json'}
)
for item in response.json()['items']:
print(item['title'])
File Locations¶
Behavior:
wcs/backend/responsible/behavior.pyREST API endpoints:
wcs/backend/responsible/restapi.pyViewlet:
wcs/backend/responsible/viewlet.pyViewlet template:
wcs/backend/responsible/templates/backreferences.ptZCML configuration:
wcs/backend/responsible/configure.zcmlTests:
wcs/backend/tests/test_responsible.py