Content spec, version 3¶
The interchange format between an agent and a Drupal site. One JSON object
describes one content entity: a page with its whole paragraph tree, a media
item, a taxonomy term, a menu link, a user account, a block, a redirect.
content_deployment reads and writes it.
Version 3 describes any content entity the field policy allows, in every language it has, and every write lands as a draft for a person to review. A version 2 spec, in one language, and a version 1 spec, which described nodes only, are still valid input.
The shape¶
{
"$schema": "https://project.pages.drupalcode.org/content_deployment/schema/content-spec/v3",
"entity_type": "node",
"bundle": "landing",
"uuid": "0a8ba38b-c932-40ca-bd7f-f28a87686ac8",
"langcode": "en",
"title": "Opening hours",
"path": "/opening-hours",
"field_topics": [{ "name": "Visiting" }],
"children": [
{
"bundle": "section",
"uuid": "67c79be6-36d7-4d1c-9a3d-2d03a8b7000d",
"_text": "When we are open",
"field_heading": "When we are open",
"field_layout": "wide",
"translations": {
"el": { "_text": "Πότε είμαστε ανοιχτά", "field_heading": "Πότε είμαστε ανοιχτά" }
},
"children": [
{
"bundle": "text",
"field_body": { "value": "<p>Daily, 9 to 5.</p>", "format": "rich_html" }
}
]
}
],
"translations": {
"el": { "title": "Ώρες λειτουργίας", "path": "/ores" }
}
}
$schema is informational. It is not checked on input.
The envelope¶
| Key | Meaning |
|---|---|
entity_type |
Required. node, media, taxonomy_term, user, menu_link_content, block_content, redirect, or any other type content-spec:model lists. |
bundle |
Required. The bundle, even for types with one (user, menu_link_content, redirect). |
uuid |
Identity. Required by content-spec:apply; refused by content-spec:create when it already exists. Content references travel by uuid so a spec survives a database clone between environments. |
title |
The entity's label: a node's title, a media item's, term's or user's name, a block's description, a menu link's title. Required where the type has a string label. A redirect has none; its title is derived. |
path |
A custom alias. Pinned on write: Pathauto is told to leave it alone. Omit it and the site generates one, which content-spec:read reports as _path. |
langcode |
The language the spec's top level is written in: the entity's own. Optional on input: the site's default language for a create, the entity's own for an apply, which refuses any other. |
moderation_state |
Descriptive: what content-spec:read saw. Never a request; see Review by default. |
children |
A paragraph tree, when the bundle has exactly one paragraph field. Otherwise name the field. |
translations |
The entity's other languages: see Translations. |
| anything else | A field, by machine name. |
Keys beginning with _ are derived and ignored on write:
| Key | On | Meaning |
|---|---|---|
_text |
paragraphs | The paragraph's own prose, without its descendants', for finding content without parsing markup. |
_path |
the root | An alias the site generated. |
_state |
the root, each translation | revision; published, whether visitors see a published version of that language; pending_draft when the revision read is not the live one; active for an account. |
_structure_only |
the root | The spec was read with --structure-only. Writes refuse it. |
_advisories |
results | Findings that did not block the write. |
_applied |
apply results | Counts per verb: updated, created, moved in, moved out, removed, redirect. |
_illustrations |
illustrate results | Per image slot: where it is, the text used, what was chosen and the runners-up. |
Paragraphs¶
A paragraph is {bundle, uuid?, fields…} nested under its host field.
Identity is the uuid. On apply:
| The paragraph | Becomes |
|---|---|
| is in the tree already | updated in place, and moved if its position changed |
| belongs to another entity's tree | claimed: a move, not a copy |
| has no uuid | created |
| is in the tree and not in the spec | removed from the new revision; earlier revisions keep it |
Moving content between pages is two applies, destination first: the destination claims the subtree by including it with its original uuids, then the source omits it. Applied the other way round, the source removes the paragraphs before the destination can claim them.
Translations¶
translations holds each of the entity's other languages, keyed by langcode,
with the fields that differ by language only. The paragraph tree is the
default language's and is shared by every language, as symmetric paragraph
translation has it: each paragraph carries its own translations, with its
translatable fields, and _text per language.
| In a host's translation | Meaning |
|---|---|
title |
The label in that language. |
path |
The custom alias in that language; _path when generated. |
moderation_state, _state |
Descriptive, as at the top level. |
| a field | A translatable field's value in that language. |
A field every language shares is refused there, naming it; a paragraph field
too: translate each paragraph in its own translations instead. A paragraph
field translated per language, as asymmetric sites have it, gives each
language a tree of its own, which specs do not support yet: specs read and
write the default language's tree, and refuse that field in a translation by
name. content-spec:model marks it asymmetric.
An image or file item in a translation may give only what differs, such as
{"alt": "…"}: the file is shared by every language. A {"name": …}
reference resolves in the language it is written in first, then in the site's
default language, then in any.
On apply, a language the spec lists is written declaratively, as the top level is: a translatable configurable field it omits is emptied. A language the spec does not list is left alone, and a version 2 spec, which lists none, leaves every other language as it is. Nothing removes a translation.
When a language is added, every paragraph in the tree gets that language, a copy of its own values until translated: Entity Reference Revisions does the same whenever a language's revision is made.
What may be described¶
Every configurable field is authorable. Base fields are authorable when they are content someone would set, and not when the system owns them:
| Type | Core base fields a spec may set |
|---|---|
node |
uid (author), created, promote, sticky |
media |
uid, created |
taxonomy_term |
description, parent, weight |
user |
mail, roles, status (active or blocked), timezone, created. Never pass. |
menu_link_content |
link, menu_name, parent, weight, expanded, description |
block_content |
reusable |
redirect |
redirect_source, redirect_redirect, status_code, uid, created |
Base fields that contributed modules add, such as Scheduler's publish_on,
follow the same rule.
Never authored: ids, revision bookkeeping, changed, langcode, a
paragraph's parent pointers, a user's password or login times, a media
thumbnail, and the published flag of anything publishable, which the review
flow governs.
These entity types cannot be a spec's root:
| Type | Instead |
|---|---|
paragraph |
Describe paragraphs in their host's spec. |
file |
content-spec:media-ingest makes files; reference the media item. |
path_alias |
An alias is the path of the entity it belongs to. |
content_moderation_state |
Content moderation manages these. |
crop |
Crops belong to the image widgets that make them. |
search_api_task |
Internal to Search API. |
webform_submission |
Submissions are visitors' data, not site content. |
content-spec:model --entity-type=<type> is the authority for a given site.
On apply, what omission means¶
A configurable field the spec omits is emptied: the spec is the whole
desired state. A base field the spec omits is left as it is:
content-spec:read omits settings-like base fields at their defaults, so a
read spec applied back must not change them.
Values¶
| Field type | Spec form |
|---|---|
string, string_long, email, list_string |
a string |
text, text_long, text_with_summary |
{"value", "format"}; text_with_summary adds "summary" when it has one. A bare string takes the field's default format. |
boolean |
true or false |
integer, list_integer, weight |
a number |
decimal |
a string, to keep precision |
created, changed, timestamp |
ISO 8601 with offset, such as 2026-09-09T11:04:49-07:00 |
datetime |
the stored value, such as 2026-07-14T22:10:00 |
link |
a uri string, or {"uri", "title", "options"} when it has a title or options. A uri to content is entity:node/<uuid>, translated to the local id on write. |
entity_reference to content |
the uuid, {"uuid": …}, or {"name": …} (see below) |
entity_reference to config (roles, views, formats) |
the machine name |
image |
the file uuid, or {"uuid", "alt", "title"} when it has alt or title |
file |
the file uuid, or {"uuid", "description", "display"} |
viewsreference |
{"view", "display", "data"} |
dynamic_entity_reference |
{"entity_type", "uuid"}, or "id" for config |
redirect_source |
a path string, or {"path", "query"} |
geofield |
well-known text |
| anything else with several stored parts | an object naming the parts; with one part, the value alone |
A field holding several values is a list. A field holding one is the value alone.
Names for references¶
A taxonomy reference may be {"name": "Visiting"}. It is matched on the term
name within the vocabularies the field accepts. When no term exists and the
field's handler allows autocreate, the same rule the field's widget follows,
the term is created during the write, and validation says so in advance:
no topics term "Visiting" exists; one will be created. Any other content type
may be referenced by {"name"} too, matched on its label. An ambiguous name is
refused with a request for the uuid.
Review by default¶
Nothing a write does is visible to visitors until a person publishes it.
content-spec:createsaves an unpublished entity.content-spec:applyon a live entity saves a pending revision: visitors keep seeing the current version. Under content moderation that is the workflow's draft state; without it, a core non-default revision. A type without revisions (a redirect) cannot hold a proposal, so its change is live and the result says so. A type without a published state (an account) is simply saved.--publishon either makes the result live at once.content-spec:publishpublishes a draft later, and editors get a Publish action on the page.content-spec:unpublishtakes the live version away from visitors: the workflow's archived state, an unpublished revision, or a blocked account. It asks first. Nothing is ever deleted through these operations.- Every write is a new, labelled revision authored by the robot account,
content_deployment, so reverting one is an ordinary revision revert.
In several languages¶
A draft belongs to one language, as content moderation has it. A write saves one revision per language it changes: the default language's first, carrying everything every language shares, then each other language's. Each starts from that language's newest revision, with the other languages as visitors see them, so one language's draft never carries another's. Before each save the untranslatable fields check runs, which entity saves otherwise skip: changes to what every language shares belong to the default language's revision.
A language's translation is made against the published paragraph tree. A translation written for paragraphs that exist only in the default language's pending draft waits, with an advisory, until that draft is published.
Review records, the notice and Publish are per language: the notice on a
Greek page is about the Greek draft, and publishing it leaves English as
visitors see it. content-spec:publish --langcode=el publishes the Greek
draft. content-spec:unpublish retires every language.
A moderation_state or published flag in a spec is ignored, with an advisory.
An agent that echoed the state it had just read would otherwise publish by
accident.
The robot account¶
Drush commands and changesets write as content_deployment, a blocked account
with no password and no roles, created on first use. Everything they write
belongs to it: author columns, revision history, uploaded files. It cannot log
in.
The review notice¶
Anyone who may edit the entity sees a notice on its full view:
| Viewing | Notice |
|---|---|
| the live version, with a pending draft | A draft is awaiting review; visitors see the current version until it is published. Links to the draft. |
| the draft | This is a draft; visitors still see the published version. Links to the live version. |
| an entity never published | It is an unpublished draft; visitors cannot see it. |
The notice says whether a person has edited the draft since, and what the write
changed. Anyone allowed to publish also gets Publish: a CSRF-protected
link to /content-deployment/review/{entity_type}/{entity_id}/publish. Under
content moderation that takes update access and the workflow's transition to
its published state; without moderation, update access and edit access to the
published flag.
Under content moderation the draft link is the Latest version tab; without it, core's revision view. A record clears itself when the draft is published by any route, reverted, or deleted.
Operations¶
| Command | Does |
|---|---|
content-spec:model [--entity-type] [--bundle] |
The vocabulary: an overview, or one type in full |
content-spec:query [--entity-type] [--bundle] [--search] [--where …] [--with] |
Find entities from a description |
content-spec:read <id\|uuid> [--entity-type] [--published] [--no-text] [--portable] [--structure-only] |
An entity as a spec |
content-spec:create <file\|-> [--dry-run] [--publish] |
Make one |
content-spec:apply <file\|-> [--dry-run] [--publish] |
Change one, declaratively |
content-spec:media-ingest <file\|-> [--alt] [--name] [--bundle] |
Files into media |
content-spec:pending |
Drafts awaiting review |
content-spec:publish <id\|uuid> [--entity-type] [--langcode] |
Make a draft live |
content-spec:unpublish <id\|uuid> [--entity-type] -y |
Take the live version offline |
content-spec:suggest-media --text=… [--for] [--orientation] [--exclude] |
Rank the image library against a text (content_deployment_media) |
content-spec:illustrate <id\|uuid\|file\|-> [--apply] [--publish] |
Fill a page's or a spec's empty image slots (content_deployment_media) |
content-spec:kitchen-sink <bundle\|--all> [--entity-type] [--include-hidden] [--write] [--publish] |
A page with every field filled in, for reviewing the theme |
Every command prints JSON. --entity-type defaults to node.
content-spec:model¶
drush content-spec:model # overview and paragraph vocabulary
drush content-spec:model --entity-type=media --bundle=photo # one type in full
drush content-spec:model --bundle=card # one paragraph bundle
Without options, an overview: the spec conventions, each primary entity type
with its bundles and their field names and types, and the paragraph vocabulary
in full. --entity-type describes one type in full: labels, required flags,
cardinality, allowed values, what each reference accepts, and which taxonomy
fields autocreate. --entity-type=paragraph gives the paragraph vocabulary
alone.
Base fields that are content are reported with "base": true. A bundle with
exactly one paragraph field reports it as children_shorthand, with the
bundles it accepts.
The model lists the site's languages, the default first. Each bundle says
whether it is translatable, and each field of a translated bundle whether it
is translatable. A paragraph field translated per language is also marked
"asymmetric": true, "supported": false.
content-spec:query¶
drush content-spec:query --bundle=landing
drush content-spec:query --search="opening hours"
drush content-spec:query --entity-type=media --bundle=photo --search=harbour --with=field_photo --limit=10
drush content-spec:query --entity-type=user --where="roles = editor"
drush content-spec:query --entity-type=menu_link_content --where="menu_name = main" --sort=weight
drush content-spec:query --entity-type=node --where="created >= 2026-01-01" --count-only
| Option | Meaning |
|---|---|
--search |
Text in the label, every text field, and the names of referenced taxonomy terms, in any language. Text in paragraphs counts for the entity hosting them, through nested paragraphs to the root. On a site with several languages, each row says which it matched_in. |
--where |
field OPERATOR value, repeatable. Operators: =, <>, <, <=, >, >=, CONTAINS, STARTS_WITH, ENDS_WITH, IN, NOT IN, BETWEEN, IS NULL, IS NOT NULL. Paths reach through references, such as field_topics.entity:taxonomy_term.name = Visiting. Dates may be written as dates, booleans as words, and content references as uuids. |
--published, --unpublished |
Filter on state; for users, active or blocked. Without either, both. |
--sort |
field or field:desc. Default changed:desc. |
--limit, --offset |
Paging. Default 50, at most 500. |
--with |
Comma-separated fields to add to each row. References are shown as {uuid, label}. Drush reserves --fields and --include. |
--count-only |
The count without the rows. |
Access checks are off: unpublished content is found, and every row says
whether it is published. Rows carry entity_type, bundle, id, uuid,
label, published, moderation_state, path, created, changed and
author. Media rows add file_url, mime and alt; user rows add mail,
active, roles and last_access; term rows add parents; menu link rows
add menu, parent, link, link_path, weight and enabled. count is
the total that matched, whatever the paging.
content-spec:read¶
drush content-spec:read 1
drush content-spec:read 0a8ba38b-c932-40ca-bd7f-f28a87686ac8
drush content-spec:read 7 --entity-type=media
drush content-spec:read 1 --no-text # without derived _text
drush content-spec:read 1 --published # what visitors see, ignoring a pending draft
drush content-spec:read 1 --portable # names instead of uuids, where unambiguous
drush content-spec:read 1 --structure-only # the shape without the prose
It reads the newest revision, which is the pending draft when there is
one. _state says which.
- Filtered text keeps its
format, and a summary when there is one. - A custom alias is
path; a generated one is_path. - A link keeps its
options, an image itsaltandtitle, a multi-part value all its parts, andentity:node/26is writtenentity:node/<uuid>. childrenis used only when the bundle defines exactly one paragraph field, so a bundle reads the same whatever it holds.--portablenames content references,{"name": …}, where the name is unambiguous within the bundles the field accepts, and keeps the uuid otherwise.--structure-onlyomitstext,text_long,text_with_summaryandstring_longvalues, and adds_structure_only: true. Create and apply refuse such a spec, since apply would empty every field it omits.
content-spec:create¶
drush content-spec:create page.json --dry-run
cat page.json | drush content-spec:create -
drush content-spec:create page.json --publish
Creates an entity, with its whole paragraph tree, in one transaction, and
prints it as read back, assigned uuids included. A spec whose uuid already
exists is refused with a pointer to content-spec:apply, and so is one that
reuses an existing paragraph uuid. An account is saved active unless the spec
says "status": false, with no password, and refused when Drupal would refuse
it: a taken name, a missing address. --dry-run validates and reports the
outcome without writing.
content-spec:apply¶
drush content-spec:apply page.json --dry-run
drush content-spec:apply page.json
drush content-spec:apply page.json --publish
Applies a spec to the entity with its uuid, building on the newest revision,
so a second apply builds on the first draft. Apply never creates: a spec
without a uuid is refused with a pointer to content-spec:create, and one
whose uuid is not on the site is refused. An entity carrying a Layout Builder
layout is refused, since the spec cannot represent it.
--dry-run reports the outcome (pending draft, unpublished draft, published,
or live because the type keeps no revisions) and counts and identities per
verb, then rolls back.
When the alias changes, a redirect from the old alias is created, unless one
from that path is already there. Without path, Pathauto generates the alias.
A pending draft cannot hold an alias: core saves an alias the moment its
entity is saved, whatever the revision. A new path on a live entity waits in
the draft's review record, is reported as the draft's path by
content-spec:read, and is set when the draft is published, by the Publish
action, content-spec:publish or core's moderation form. Applying with
--publish writes the spec's own alias, and drops one a draft was holding.
Each language's draft holds its own: a path under translations waits in
that language's record and is set when that language is published. A
translation that leaves path out leaves its alias, and any it holds, alone.
content-spec:media-ingest¶
drush content-spec:media-ingest photo.jpg --alt="Harbour at dawn"
cat .ingest/report/manifest.json | drush content-spec:media-ingest -
Turns files into media items a spec can reference by uuid. The manifest form
takes a list of {path, alt, name?, bundle?, id?}, or an object with a media
list, and returns it with a uuid on each item.
- Identical bytes ingest once: a media item whose source file has the same SHA-256 is reused, and bytes already here as a file are not copied again. The SHA-256 comes from File Hash, which must be enabled with SHA-256; without it, every ingest makes a new media item.
- Image media require alt text. A manifest that carries a different alt for
bytes already ingested updates it, reported as
alt_updated. - The media type's configuration decides the accepted extensions, the size
limit, which bundle a file becomes when
--bundleis omitted (an image source wins for an image extension), and the upload directory. - Media items are published on arrival and belong to the robot account: an asset shows nothing until a page references it, and that page is the draft.
Upload endpoint¶
content_deployment_export adds two routes for files on an operator's
machine. Both authenticate with basic auth and need the permission Upload
content deployment media.
| Route | Request | Response |
|---|---|---|
POST /content-deployment/media |
multipart file, plus alt, name?, bundle? |
201 with uuid, bundle, name, filename, reused; 422 with errors |
GET /content-deployment/media/{sha256} |
200 with uuid, bundle, name if those bytes are ingested; 404 if not |
content-spec:pending, content-spec:publish, content-spec:unpublish¶
drush content-spec:pending
drush content-spec:publish 0a8ba38b-c932-40ca-bd7f-f28a87686ac8
drush content-spec:unpublish 42 -y
drush content-spec:unpublish 166 --entity-type=user -y
pending lists each recorded draft: the entity and its langcode, the
operation, when it was proposed, what it changed, whether it was edited since
(superseded), whether a version is live, and path and draft_path. There is
one record per language with a draft. Records live in the key-value
collection content_deployment.pending_review. publish publishes the newest
revision. unpublish takes the live version offline and asks first; -y
skips the question.
content-spec:suggest-media and content-spec:illustrate¶
From content_deployment_media.
drush content-spec:suggest-media --text="harbour at dawn"
drush content-spec:suggest-media --for=12 --orientation=landscape
drush content-spec:illustrate 12 # what would be placed; writes nothing
drush content-spec:illustrate 12 --apply # place them, as a draft
drush content-spec:illustrate spec.json > illustrated.json
suggest-media scores every published image's name, alt and title text,
taxonomy terms, and text fields against the words given, by inverse document
frequency over the library's own vocabulary. Terms weigh most, then text, then
the name; two query words side by side in a picture's text earn a bonus. Rows
add score, matched, keywords, description, orientation, width and
height to the query row. The index is cached until any media item or term
changes.
| Option | Meaning |
|---|---|
--text |
What the picture should be about. |
--for |
Instead of --text, an id or uuid whose title and prose are the text. |
--limit |
How many. Default 5. |
--orientation |
landscape, portrait or square. |
--exclude |
Comma-separated media uuids not to offer. |
--min-score |
Below this nothing is offered. Default 1. |
illustrate finds every empty media reference that accepts an image media type
in a spec's tree, matches it against the text around it (its own text counts
double, then its ancestors'), and fills it with the best picture not already
in the spec. A slot that scores below --min-score stays empty. Given an id or
uuid, nothing is written unless --apply is passed, and then the result is a
draft; given a spec, the filled spec is printed. --alternatives sets the
runners-up reported per slot (default 3).
content-spec:kitchen-sink¶
drush content-spec:kitchen-sink landing # print the spec
drush content-spec:kitchen-sink --all --write # a draft for every content type
drush content-spec:kitchen-sink landing --write --publish
Builds a spec for one bundle with every field on its form filled in, and one paragraph of every bundle each paragraph field accepts, nested up to three levels, so one page shows how the theme renders each element. It is read from the content model, so it shows the fields the site has today.
- Text is written in the field's allowed format, or else the lightest enabled format with an editor, using that editor's heading levels and styles, with lists, a table, a blockquote and, where the format embeds media, embedded media in each allowed view mode, at most six per page.
- Pictures (landscape, portrait and square, each with alt text) and a PDF are generated, deterministically, so building again reuses the same media.
- Other references point at the newest published item of an accepted bundle; a taxonomy reference that autocreates names a "Kitchen sink" term. View references use the field's default, else the view most used in that field, else the first preselected view.
- The uuid is derived from the entity type and bundle, so there is one kitchen
sink per bundle on every environment, and
--writecreates it once and updates it after that, as a draft unless--publishis given. - What cannot be filled is left empty and listed in
advisories. --include-hiddenalso fills fields that are not on the form.
Validation¶
Every problem is reported at once, before anything is written:
root: landing.field_sections does not accept "card" (accepts section).
root > field_sections[0]: section.field_layout[0]: "full" is not an allowed value (narrow, wide).
root > field_sections[0]: no field "field_title" on section.
Problems block the write: an unknown field, a bundle a child field does not
accept, a value not in a field's allowed list, more values than the field
holds, a reference that does not resolve, a text format the site lacks, a link
to content that is not there, a name that matches several entities, a
viewsreference value without view. Advisories do not: a required field
left empty (Drupal enforces required in forms, not on save), a term that will
be created, a moderation_state or published flag that was ignored, a change
that is live because its type keeps no revisions.
Per type¶
- node: the paragraph tree under its host fields. An alias comes from the
site's patterns unless
pathpins one. - media:
titleis the name. The source field is{uuid, alt}for an image, the file uuid otherwise, of a filecontent-spec:media-ingestmade. - taxonomy_term:
parentis a term uuid; omit it for a root term. - user:
titleis the username. A new account is active unless the spec says"status": falseand has no password; sign-in takes a one-time link. Account changes are immediate;unpublishblocks. - menu_link_content:
linkpoints at content byentity:node/<uuid>;parentis a plugin id such asmenu_link_content:<uuid>;menu_nameis a menu's machine name. A new link is disabled until published; an edit to a live link is a pending revision, and the menu shows the old link until it is published. - block_content:
titleis the block description. Blocks have no page of their own, so review happens throughcontent-spec:pendingand the block's revisions. - redirect:
redirect_source,redirect_redirectandstatus_code. New redirects are disabled until published. Redirects keep no revisions, so an edit is live.
Removal¶
A paragraph the spec omits leaves the new revision and nothing more. Earlier
revisions still reference it, as when an editor removes a paragraph in the
form, so reverting brings it back, and while the change is a pending draft the
live revision still needs it. Entity Reference Revisions queues paragraphs no
revision references into entity_reference_revisions_orphan_purger, which
cron processes.
Changes from version 2¶
langcodein the envelope, andtranslationskeyed by langcode on the host and on each paragraph, with translatable fields only.- One revision per language a write changes, the default language first.
path, labels such as menu link titles and term names, and image alt text per language;{"name": …}resolved in the spec's language first.- Review records, the notice and Publish per language.
content-spec:queryreports the languages a search matched in;content-spec:modelreports languages and translatability.
Changes from version 1¶
- Any root entity type, not only nodes;
titlemaps to each type's label. - Base fields per the field policy, with omission meaning "leave it".
- Lossless values: link
options, imagealtandtitle, filedescription, textsummary, multi-part values as objects,entity:uris by uuid. {"name"}references, with autocreate.pathpins a custom alias; generated aliases are reported as_path._stateon every spec;_advisoriesand_appliedon results.- Review by default,
--publish, pending, publish and unpublish. Removed paragraphs stay in earlier revisions instead of being deleted.