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.