Staging (Working Copies)¶
Staging lets editors edit a copy of already-published content without touching the live version. A working copy is created next to the live baseline, edited in isolation, and then either applied back onto the baseline or discarded.
Overview¶
When a baseline is staged:
A working copy is cloned into the same parent container as the baseline.
The working copy is put into the Arbeitskopie (working copy) workflow state, so it is never publicly visible (see Website Workflow).
The baseline and working copy are linked to each other. A baseline can have only one working copy at a time.
Editing happens on the working copy. The baseline stays live and unchanged.
Internal links and Simplelayout block references inside the working copy are rewritten so the copy is self-contained.
When the editor is done, the working copy is either applied (its changes are merged back into the baseline and the working copy is removed) or discarded (the working copy is removed and the baseline is left untouched).
Working copies and their baselines are protected: they cannot be moved, deleted, or copied through the normal content actions while the staging relationship exists.
Staging actions¶
Staging is driven by three browser actions on the staged content. Each accepts an
Accept: application/json request header and then returns a JSON object with the
URL the client should navigate to, instead of issuing an HTTP redirect.
Create a working copy¶
Call create_working_copy on the baseline. It clones the baseline into the parent
container and returns the working copy URL. If a working copy already exists for
this baseline, the request fails with 400 Bad Request.
POST /Plone/my-page/create_working_copy HTTP/1.1
Host: localhost:8080
Accept: application/json
Response:
{
"redirect_url": "http://localhost:8080/Plone/my-page-1"
}
Apply a working copy¶
Call apply_working_copy on the working copy. Its changes are merged back into the
baseline, a review-history entry (“Updated from working copy”) is added to the
baseline, the working copy is removed, and the baseline URL is returned. Calling
this on something that is not a working copy fails with 400 Bad Request.
An optional version-comment form value is recorded as the comment on the
baseline’s review-history entry.
POST /Plone/my-page-1/apply_working_copy HTTP/1.1
Host: localhost:8080
Accept: application/json
Response:
{
"redirect_url": "http://localhost:8080/Plone/my-page"
}
Discard a working copy¶
Call delete_working_copy on the working copy. The working copy is removed, the
baseline is left unchanged, and the baseline URL is returned. Calling this on
something that is not a working copy fails with 400 Bad Request.
POST /Plone/my-page-1/delete_working_copy HTTP/1.1
Host: localhost:8080
Accept: application/json
Response:
{
"redirect_url": "http://localhost:8080/Plone/my-page"
}
Driving the actions from JavaScript¶
async function createWorkingCopy(baselineUrl, token) {
const response = await fetch(`${baselineUrl}/create_working_copy`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
return data.redirect_url; // navigate the editor here
}
async function applyWorkingCopy(workingCopyUrl, token) {
const response = await fetch(`${workingCopyUrl}/apply_working_copy`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
return data.redirect_url; // back to the (now updated) baseline
}
How staged content appears¶
A working copy is an ordinary content object living next to its baseline, so it is fetched, navigated, and edited through the standard content endpoints. It carries the Arbeitskopie workflow state, which keeps it out of public/anonymous views.
For folderish content, the working-copy folder serialization is enriched so that a client editing the copy also sees the baseline’s existing children. When listing the items of a working-copy folder, the response merges:
the items that physically live in the working copy (newly added or modified pages, served from the working-copy path), and
the baseline’s existing items (served from the baseline path), so the editor sees the full structure rather than only the staged subset.
GET /Plone/folder-workingcopy?include_items=true HTTP/1.1
Host: localhost:8080
Accept: application/json
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"@id": "http://localhost:8080/plone/folder-workingcopy",
"@type": "Folder",
"title": "My Folder (Working Copy)",
"is_folderish": true,
"items_total": 4,
"items": [
{
"@id": "http://localhost:8080/plone/folder-workingcopy/new-page",
"@type": "Document",
"title": "New Page",
"review_state": "private"
},
{
"@id": "http://localhost:8080/plone/folder-workingcopy/modified-page",
"@type": "Document",
"title": "Modified Page",
"review_state": "private"
},
{
"@id": "http://localhost:8080/plone/folder/existing-page",
"@type": "Document",
"title": "Existing Page",
"review_state": "published"
},
{
"@id": "http://localhost:8080/plone/folder/another-page",
"@type": "Document",
"title": "Another Page",
"review_state": "published"
}
]
}
In the example, new-page and modified-page are served from the working-copy
path (/plone/folder-workingcopy/...), while existing-page and another-page
are surfaced from the baseline path (/plone/folder/...). This lets a frontend
render the complete folder while the editor works on the copy.
Availability¶
Staging is only offered while the content is in a state where a working copy is
allowed. The set of states that do not permit a working copy is configured
through the registry record wcs.backend.workflow.non_working_copy_states. Content
whose current state is listed there cannot be staged.