Working Copy#
Note
This feature is available only on Plone 5 or greater.
Plone has a working copy feature provided by the core package plone.app.iterate
.
It allows the users to create a working copy of a published or live content object, and work with it until it is ready to be published without having to edit the original object.
This process has several steps in its life cycle.
Create working copy (a.k.a., check-out)#
The user initiates the process and creates a working copy by checking out the content:
http
POST /plone/document/@workingcopy HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl
curl -i -X POST http://nohost/plone/document/@workingcopy -H "Accept: application/json" --user admin:secret
httpie
http POST http://nohost/plone/document/@workingcopy Accept:application/json -a admin:secret
python-requests
requests.post('http://nohost/plone/document/@workingcopy', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
…and receives the response:
HTTP/1.1 201 Created
Content-Type: application/json
Location: http://localhost:55001/plone/document
{
"@id": "http://localhost:55001/plone/copy_of_document"
}
Get the working copy#
A working copy has been created and can be accessed querying the content:
http
GET /plone/document/@workingcopy HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl
curl -i -X GET http://nohost/plone/document/@workingcopy -H "Accept: application/json" --user admin:secret
httpie
http http://nohost/plone/document/@workingcopy Accept:application/json -a admin:secret
python-requests
requests.get('http://nohost/plone/document/@workingcopy', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
…and receives the response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"working_copy": {
"@id": "http://localhost:55001/plone/copy_of_document",
"created": "1995-07-31T13:45:00+00:00",
"creator_name": "admin",
"creator_url": "http://localhost:55001/plone/author/admin",
"title": "Test document"
},
"working_copy_of": null
}
The GET
content of any object also states the location of the working copy, if any, as working_copy
:
http
GET /plone/document HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl
curl -i -X GET http://nohost/plone/document -H "Accept: application/json" --user admin:secret
httpie
http http://nohost/plone/document Accept:application/json -a admin:secret
python-requests
requests.get('http://nohost/plone/document', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json
{
"@components": {
"actions": {
"@id": "http://localhost:55001/plone/document/@actions"
},
"aliases": {
"@id": "http://localhost:55001/plone/document/@aliases"
},
"breadcrumbs": {
"@id": "http://localhost:55001/plone/document/@breadcrumbs"
},
"contextnavigation": {
"@id": "http://localhost:55001/plone/document/@contextnavigation"
},
"navigation": {
"@id": "http://localhost:55001/plone/document/@navigation"
},
"navroot": {
"@id": "http://localhost:55001/plone/document/@navroot"
},
"types": {
"@id": "http://localhost:55001/plone/document/@types"
},
"workflow": {
"@id": "http://localhost:55001/plone/document/@workflow"
}
},
"@id": "http://localhost:55001/plone/document",
"@type": "Document",
"UID": "SomeUUID000000000000000000000001",
"allow_discussion": false,
"contributors": [],
"created": "1995-07-31T13:45:00+00:00",
"creators": [
"test_user_1_"
],
"description": "",
"effective": null,
"exclude_from_nav": false,
"expires": null,
"id": "document",
"is_folderish": false,
"language": "",
"layout": "document_view",
"lock": {
"created": "1995-07-31T17:30:00+00:00",
"creator": "admin",
"creator_name": "admin",
"creator_url": "http://localhost:55001/plone/author/admin",
"locked": true,
"name": "iterate.lock",
"stealable": false,
"time": 807211800.0,
"timeout": 4294967280,
"token": "0.12345678901234567-0.98765432109876543-00105A989226:1630609830.249"
},
"modified": "1995-07-31T17:30:00+00:00",
"next_item": {},
"parent": {
"@id": "http://localhost:55001/plone",
"@type": "Plone Site",
"description": "",
"title": "Plone site",
"type_title": "Plone Site"
},
"previous_item": {},
"relatedItems": [],
"review_state": "private",
"rights": "",
"subjects": [],
"table_of_contents": null,
"text": null,
"title": "Test document",
"type_title": "Page",
"version": "current",
"working_copy": {
"@id": "http://localhost:55001/plone/copy_of_document",
"created": "1995-07-31T13:45:00+00:00",
"creator_name": "admin",
"creator_url": "http://localhost:55001/plone/author/admin",
"title": "Test document"
},
"working_copy_of": null
}
The GET
content of any a working copy also returns the original as working_copy_of
:
http
GET /plone/copy_of_document HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl
curl -i -X GET http://nohost/plone/copy_of_document -H "Accept: application/json" --user admin:secret
httpie
http http://nohost/plone/copy_of_document Accept:application/json -a admin:secret
python-requests
requests.get('http://nohost/plone/copy_of_document', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json
{
"@components": {
"actions": {
"@id": "http://localhost:55001/plone/copy_of_document/@actions"
},
"aliases": {
"@id": "http://localhost:55001/plone/copy_of_document/@aliases"
},
"breadcrumbs": {
"@id": "http://localhost:55001/plone/copy_of_document/@breadcrumbs"
},
"contextnavigation": {
"@id": "http://localhost:55001/plone/copy_of_document/@contextnavigation"
},
"navigation": {
"@id": "http://localhost:55001/plone/copy_of_document/@navigation"
},
"navroot": {
"@id": "http://localhost:55001/plone/copy_of_document/@navroot"
},
"types": {
"@id": "http://localhost:55001/plone/copy_of_document/@types"
},
"workflow": {
"@id": "http://localhost:55001/plone/copy_of_document/@workflow"
}
},
"@id": "http://localhost:55001/plone/copy_of_document",
"@type": "Document",
"UID": "SomeUUID000000000000000000000002",
"allow_discussion": false,
"contributors": [],
"created": "1995-07-31T13:45:00+00:00",
"creators": [
"test_user_1_"
],
"description": "",
"effective": null,
"exclude_from_nav": false,
"expires": null,
"id": "copy_of_document",
"is_folderish": false,
"language": "",
"layout": "document_view",
"lock": {
"locked": false,
"stealable": true
},
"modified": "1995-07-31T17:30:00+00:00",
"next_item": {},
"parent": {
"@id": "http://localhost:55001/plone",
"@type": "Plone Site",
"description": "",
"title": "Plone site",
"type_title": "Plone Site"
},
"previous_item": {},
"relatedItems": [],
"review_state": "private",
"rights": "",
"subjects": [],
"table_of_contents": null,
"text": null,
"title": "Test document",
"type_title": "Page",
"version": "current",
"working_copy": {
"@id": "http://localhost:55001/plone/copy_of_document",
"created": "1995-07-31T13:45:00+00:00",
"creator_name": "admin",
"creator_url": "http://localhost:55001/plone/author/admin",
"title": "Test document"
},
"working_copy_of": {
"@id": "http://localhost:55001/plone/document",
"title": "Test document"
}
}
Check-in#
Once the user has finished editing the working copy and wants to update the original with the changes, they would check in the working copy:
http
PATCH /plone/copy_of_document/@workingcopy HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl
curl -i -X PATCH http://nohost/plone/copy_of_document/@workingcopy -H "Accept: application/json" --user admin:secret
httpie
http PATCH http://nohost/plone/copy_of_document/@workingcopy Accept:application/json -a admin:secret
python-requests
requests.patch('http://nohost/plone/copy_of_document/@workingcopy', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
…and receives the response:
HTTP/1.1 204 No Content
The working copy is deleted afterwards as a result of this process.
The PATCH
can also be issued in the original (baseline) object.
Delete the working copy (cancel check-out)#
If you want to cancel the check-out and delete the working copy (in both the original and the working copy):
http
DELETE /plone/copy_of_document/@workingcopy HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl
curl -i -X DELETE http://nohost/plone/copy_of_document/@workingcopy -H "Accept: application/json" --user admin:secret
httpie
http DELETE http://nohost/plone/copy_of_document/@workingcopy Accept:application/json -a admin:secret
python-requests
requests.delete('http://nohost/plone/copy_of_document/@workingcopy', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
and receives the response:
HTTP/1.1 204 No Content
When a working copy is deleted using the normal DELETE
action, it also deletes the relation and cancels the check-out.
That is handled by plone.app.iterate
internals.