Skip to content
Dee Wan 1.0 is not yet released (pre-MVP). See the 1.0 product boundary.

Edit and publish content

This guide covers items in Content: creating, saving, publishing, translating, reviewing, recovering and removing them. Pages have their own editor; see the page builder. Blog posts live under Posts; see Blog sites. Field types and their rules are in models and fields. What a role may do is in people and access.

Open Content in the sidebar and pick a model. The list shows each item’s title, slug, status and language.

  • Search content… finds saved titles, names, addresses and rich-text bodies, including unpublished drafts. Results show the matching field and a short excerpt. Markup, HTML attributes and JSON keys are not searched. Model, language, stage and field-access rules apply before matches and counts.
  • Stage filters by workflow stage. Delivery filters by Live or Not live.
  • Fields filters and sorts one model’s list by what its fields hold. It is described under Filter and sort by fields below.
  • Open an item with Edit in its row menu. Duplicate copies it as a new draft.
  • A single-entry model opens its one item directly. See single-entry models.

The first search prepares older saved content automatically in small batches. Keep the search open while it loads; change or clear the search to cancel it. A failed request leaves a retryable error, never a zero-result claim. Queries take up to 200 characters. This searches current saved content; public website search still searches published content separately.

The message You can read this content but not change it. It belongs to someone else, or your role grants reading only. explains why you cannot edit the item.

The admin list GET /api/content_instances?q=... searches current saved text. Existing where, model, language, state, field filters, sorting and pagination still apply. Shared fields can match a secondary language through their primary saved value. The primary source must also be readable; hidden fields or unreadable primary entries do not contribute matches. A matching body adds search_match: { field, label, excerpt } to its row. The count endpoint and matching bulk selections use the same readable matches. The query is at most 200 characters, including its normalized case form; % and _ are literal text.

An older or changed value may need derived text. A bounded read can prepare small sets itself; otherwise 409 editorial_search_preparing means the search has no complete count yet. Call POST /api/content_instances/search/prepare with the same filters as query parameters until data.more is false, then repeat the original read. Each call prepares at most 16 values and normally at most 1 MiB, reading a larger value alone. The CMS does this automatically and stops when the query changes or a request fails. No content or publication state changes. Derived fragments are internal and are not part of exports or generic API includes. /first rejects q; use the paginated list. The management listEntries contract remains separate and does not accept q.

With one model open, Fields opens a dialog. Add up to four conditions. Choose All conditions (AND) or Any condition (OR) for each group. Add group nests a group, up to three levels including the outer group. For example, match a gold tier AND either more than 100 views OR an empty date. Matches use the latest saved values, including drafts. Saved views, matching bulk selections, counts and pages keep the same groups. Older flat filters still mean AND.

Field type Conditions
Text is, contains (ignoring case), is empty, is not empty
Choice is any of the options you tick, is empty, is not empty
Yes/no is yes or no, is empty, is not empty
Number is, is more than, is at least, is less than, is at most, is empty, is not empty
Date is, is after, is on or after, is before, is on or before, is empty, is not empty
Link to other items includes any of the items you pick, is empty, is not empty; Linked value filters a configured readable display field
Point within radius, within area, is empty, is not empty
  • Numbers compare as numbers, so 9 is less than 10. Ordinary dates compare by day. Timezone-aware dates compare their stored UTC instants.
  • Sort by orders one text, choice, yes/no, number or date field, ascending or descending. A single-target, single-value relation can sort by its configured readable scalar display field; a multi-value relation cannot. A point field sorts by distance from the latitude and longitude you enter. Missing or unreadable values come last in both directions; ties keep a fixed order between pages.
  • Linked value uses the display field’s text, choice, yes/no, number or date conditions. Only readable related entries and fields contribute. Shared references from another site support membership conditions, not display-value filtering or sorting.
  • Point radii use latitude, longitude and metres. An area uses south, west, north and east bounds; west greater than east crosses the antimeridian. Edges are included.
  • Rich text, media, components and JSON fields cannot be filtered or sorted. The dialog lists them under Not available here with the reason.
  • A field your role cannot read is not offered. It is also not used if a saved view names it.
  • A field that is shared between languages is read from the main language’s item; a language-specific field is read from the item in that language.
  • The conditions and the sort are part of the address, so they survive a reload and can be shared as a link. Select all N matching, the counts and the pages all use the same conditions. The list needs one model: with all models shown, Fields is not offered.

A view is a named setup of one model’s content list: the stage and delivery filters, field conditions, the search term, which columns show and in what order, and one sort (by a column or by a field). Open it from Views, beside the list’s other buttons.

  • Personal and shared. Save as new view… keeps a view for you. A shared view is seen by everyone who can read the model. Creating, changing or deleting a shared view, and setting the site default, needs the permission to apply model changes (model:apply). Reading a model is enough to make personal views and to use shared ones.
  • Save. Opening a view applies its filters, columns and sort. Changing the list marks the view Modified, and Save replaces it. If someone changed the view after you opened it, you can Use their version, or Keep my changes and save. Save as view keeps the current list as a new view without touching the open one.
  • Columns and sort… chooses which columns show, their order, and sorting by Created or Updated. Columns can be the built-in ones or any text, number, yes/no, choice, date or relationship field of the model. Relationship columns show readable labels and a count. Open a cell to inspect up to 20 related entries; each opens its editor. Names and counts follow your current access.
  • Defaults. In the ⋯ menu, Make my default and Make site default pick the view a model opens with. Your default wins over the site default. A default applies when you open a model whose address has no filters and names no view, so a link that carries filters, such as a dashboard tile, is never replaced. A shared view can be your default; only a shared view can be the site default.
  • Who sees what. A view stores filters, columns and a sort, never content. Each time it is read, your own access is checked again: columns, field conditions and a field sort that use a field your role cannot read are left out and the view says how many parts are ignored without naming the fields, and the rows are the ones your role may read, as in any list. Saving a view stores the columns currently visible to you.
  • Fields that no longer exist. A column, condition or sort on a field that was deleted, or whose type no longer allows it, is left out of the list. The view still opens and is not rewritten. A notice names the removed fields, and Remove them and save repairs the view in one action for people allowed to change it. Everyone else is told to ask someone who manages models. Columns hidden by your role are not removed.
  • Limits. A name has up to 80 characters and is unique among your views of the model, and among the shared ones. Each person keeps up to 25 personal views per model, and a model has up to 25 shared views. A view has up to 40 columns and a search term of up to 200 characters. Deleting a view also removes the defaults that name it.

Views are an admin preference. They change nothing that is published, nothing in the public API, and need no generation or deployment.

  1. Pick the model and press Create content.
  2. Fill in the fields. A new item starts in the site’s default language. If your role cannot create that language, it starts in the first language your role can create. The language list offers only languages your role can create.
  3. Press Create draft. The screen says Content created as a draft.

Creating is refused for a language the site does not have (unknown_locale). If the language is removed while the item is being created, nothing is written (language_changed).

Each language has its own address. Leave Address empty to make one from the title. The rules are in item address.

In a rich-text field, choose Vendor snippet (under More on phones) to insert a provider’s HTML, CSS and JavaScript. Give it an accessible title, then paste each part into its labelled code editor. The editors provide syntax highlighting, line numbers, bracket matching, search and undo/redo.

Use Format document from the editor’s right-click menu or visible actions menu, or press Shift+Alt+F. Prettier formats locally in your browser. Formatting does not save or run the code. Undo restores the previous text; invalid code stays unchanged with an explanation. Tab moves to the next control.

Preview snippet runs the widget inside its isolated frame. Save the entry to keep it; publish separately to show it on the site. The provider handles submitted booking or form data.

Saving and publishing are separate steps. Saving never makes anything public.

Press Save. Each save writes a new version of the item.

  • The Unsaved badge shows edits that are not saved yet.
  • What you type while a save is running is not part of that save. It stays Unsaved until you save again.
  • Leaving with unsaved edits asks first: Keep editing or Discard and leave.
  • Preview here can show unsaved edits in the site’s design without saving them, where the site’s renderer supports it. See Preview unsaved changes.
  • A field rule or a plugin check can refuse a save. The editor names the field and keeps your edit. See plugin content checks.

An integration can propose field changes for you to review. Proposed changes shows the source, requester, date, language and affected fields. Select a field label to jump to and focus that field.

Apply needs you to save or discard your local edits first. It uses the ordinary version-checked save and your current permissions. Dismiss leaves the entry unchanged; the integration can propose again. If the entry changed after the proposal, the proposal is stale. Dismiss it or ask for a new proposal.

By default, a draft may leave required fields empty. A new draft may also have no address. Invalid values, broken references, duplicate unique values and invalid component structure still refuse a save. An existing address cannot be cleared. A model’s Draft validation setting can require complete values on every save instead.

Publishing always checks the exact saved version in every affected language: required fields, address rules, references, plugin checks and required review tasks must pass. This also applies to publication overrides, imports, plugins and scheduled releases. A complete draft in English does not make an incomplete Thai translation publishable.

The Publishing panel shows the moves your role may take. You never see a move that would be refused.

  • With the review flow off, Publish makes the saved version public at once.
  • A move or a publish saves unsaved edits first. If that save is refused, nothing moves.
  • Unpublish takes the item out of public view. It stays as a draft. Nothing is deleted.

The review flow is a site setting: Settings → Workflow → Enable review flow.

With review on, an item moves through the site’s review stages, for example Submit for review, Approve, Send back and Publish. Each move can carry a note for the next person.

The Dashboard lists Waiting for review and Sent back. A row says what you can do, or who it is waiting for. Open the row to act. The move itself happens in the editor.

A role with the override permission (content:publish_override) sees an Override box. No built-in role holds it. A superadmin can always use this override.

  1. Press Publish all languages now (or Publish now on an item with one language).
  2. Write the reason under Why is this necessary?. It is required.
  3. Confirm.

The override applies to the whole translation group. The activity shows it as Publication override. It still checks the role’s publish and unpublish language limits. It is refused (locale_denied, naming the languages) only when it would change a language outside them. A language that already serves the version being published, or is already offline on an unpublish, does not count. Unpublish all languages now works the same way, but it does not save unsaved edits first.

Before an unpublish, the dialog checks what links to the item.

  • It names how many items link here, and how many of them are live. Their published copies keep the link, so those links will break.
  • Items in Trash that link here are marked In Trash. Links only in a draft are marked draft only.
  • If the list changes before you confirm, the move is refused with What links to this changed. Check the list again.

An item and its languages form one translation group. Each language is saved separately.

In the Language panel, press Start a {language} translation. The new language starts empty. Fill it in and save.

  • Shared (non-translatable) fields are edited in the primary language. The panel says how many there are.
  • When a language is not live yet, the panel says what visitors get instead, for example Not live in Thai yet. Visitors see English. See languages.

The Publishing panel has two scopes:

Scope Buttons Effect
This language Publish {language}, Unpublish {language}, a move with · {language} Only this language changes. Other languages stay as they are.
All languages Publish all languages, Unpublish all languages, a move with · All languages Every language of the group moves. A move between two unpublished stages, such as Approve · All languages or Send back · All languages, leaves a language that is already published live and Published. Take it off with Unpublish {language}.

Other language-only actions:

  • Move this language to Trash removes one language. The language that holds the item’s history cannot go alone; move the whole item instead.
  • Restore {language} only in History restores one language from an older version.

A role limited to some languages can act only in those languages. An action that spans languages is refused in its entirety. See limit a role to some fields or languages.

Press Assign (or Reassign) in the Publishing panel.

  • Pick one Assignee and an optional Due date. The date is today or later.
  • The assignee must be able to move the item on from its current stage.
  • One assignment covers the item and all its languages.
  • The person you assign gets a notification in the admin; see feedback and notifications. The panel shows Due {date} and, after the due date, Due {date} · Overdue.
  • A workflow move clears the assignment. Clear assignment removes it by hand.
  • Deactivated or No access next to an assignee means that person cannot act now.

Review tasks keeps work separate from the item’s workflow assignment. Under Language assignment, choose an owner and due date for the current language, then Save. Other languages and the whole-item assignment keep their owners. Only eligible people are offered; simultaneous changes are refused rather than overwritten.

Choose the task language before its owner. Changing language refreshes the eligible people; All languages offers only people who can read every language of the entry.

For a task, enter Task, choose Owner, language and optional Due, then Add task. Keep Required before publishing on when publication depends on it. The owner must be able to read that entry and language. The owner or an authorized assigner can Complete it. Reopen starts another review; Remove removes the requirement.

Completion records the content reviewed. Changing that language or shared content shows Review changed content and blocks publishing until the task is completed again. Changing only another language does not invalidate it. A task for All languages covers every affected language. When editing a future version, completion refers to that version’s content, not the ordinary draft.

The Feedback section in the side column holds notes from the people reviewing this entry. It is private to the admin. It is not the public comments feature and never reaches the site.

  • Type a note and press Add feedback. A note is 1 to 2000 characters, and an entry holds at most 500.
  • Each note shows who wrote it, when, and the version it was written on. When the entry has a newer version, the note says Written on an older version (version {version}, now version {current}).
  • Resolve closes a note and hides it. Show {count} resolved lists closed notes, and Reopen opens one again.
  • Reading follows read access to the entry. Writing and resolving need update access to it. A role limited to its own items can write only on its own items. Someone who can read but not update sees the notes without the box.
  • A note belongs to the item and the language you wrote it in.

Choose Field before adding feedback to anchor a thread to a field, or leave Whole entry selected. Reply keeps the same field and language; Resolve closes the thread and its replies. Version labels show when a note predates later edits.

Open Notify and select people or editorial teams. Choices include only people who can currently read the thread’s entry, field and language. Up to 20 mentions can notify up to 50 people; a team grants no content access. The notification opens the relevant entry and language. Access is checked again when it is read, so a revoked reader sees no hidden title or field.

People with site-grant permission can open Editorial teams under Review tasks to create or edit a site’s teams and members. Task assignments, completions and mentions appear in the notification bell alongside workflow notifications.

If notifications cannot be loaded, use Try again in the notification panel. Existing notifications stay visible if a refresh fails.

The bell in the header lists notifications that need your attention. The badge counts unread notifications.

Kind You get it when
Assignment Someone assigns the item to you. Assigning yourself, or saving the same assignee again, sends nothing.
Review request The item, or one of its languages, moves into a review stage. It goes to the person named as next to act. With no one named, it goes to everyone who can move the item on from that stage and may review the moved language, up to 50 people, never the person who made the move.
Return for changes An item, or one of its languages, in a review stage goes back to the starting stage. It goes to the person named as next to act, otherwise to the author of that language, if the author can still read it.
  • Each notification names the item and its current review state, not the state it was created in, and links to it. A notification about one language names it (Language: th), shows that language’s state and opens that language.
  • If you can no longer read the item or that language, because your role changed or it moved to Trash, the notification reads An entry you were notified about is no longer available to you. It shows no title and no link.
  • Mark read, Mark unread and Mark all read change only your own list.
  • A notification is written together with the move or assignment that causes it. A move that fails sends nothing, and a retried move sends nothing twice.
  • The editor, the management API and plugin commands notify the same way, because they share one path. A move of one language notifies like a move of the whole item.
  • An import is quiet by default. Whoever runs an import can choose to notify with Notify reviewers about review changes in this import on the run screen; the choice is stored on the job and needs the same data:import permission. Carried history, such as a carried assignment, never notifies.
  • Email is optional. When the install has mail configured (see people and access) and FRONTEND_URL is set, the person also gets a short email with a link and no content. A failed email is not retried. The notification in the admin exists either way.
  • There are no reminders and no chat.

Unsaved workflow edits are protected when leaving Settings or replacing or closing the editor. On mobile, Save and Cancel stay available while scrolling. If workflows cannot be loaded, choose Try again; a failed refresh keeps the current draft.

Settings → Workflow → Workflows lists the site’s named workflows. A workflow is the set of states an item moves through and the moves between them. The site starts with one, marked Site default.

  • New workflow starts from a copy of the site default. Give it a name. Add states, mark exactly one as the Starting state, mark the ones that publish, and add the moves. A move can need Approvers, Publishers or people who can unpublish.
  • A workflow has at most 16 states and 80 moves, and a site has at most 20 workflows. A state key is lowercase letters, digits and underscores, starting with a letter. The starting state must be able to reach a state that publishes.
  • Edit changes names, states and moves. Delete is offered only for a workflow no model uses and no item is attached to. The site default cannot be deleted.
  • Workflow for each model gives each model one workflow, or the site default. An item uses the workflow it is already attached to, then its model’s workflow, then the site default. Changing a model affects new items.
  • Settings → History records creating, changing, deleting and assigning a workflow.
  • If someone else changes, deletes or starts using a workflow while you save, the save is refused with workflow_changed, workflow_in_use or workflow_assigned. Nothing is written and no item loses its review state. Reload and try again. An item move that was checked against an older version of the workflow is refused the same way (workflow_changed).

A change that would leave items in a state that no longer applies is refused until you review a plan. This covers removing a state that items are in, and giving a model another workflow while its items are in review.

  • Review how entries move lists the states, how many items are in each, and some of the items. Choose a new state for each. For a model change you can also choose Leave on their current workflow.
  • Move entries and apply moves the items one by one and lists each result: Moved, Refused, Changed meanwhile or Unchanged. Each move is recorded in the item’s history.
  • A reviewed move cannot publish or unpublish an item. Move those items with their ordinary buttons first.
  • A request moves 25 items. The screen repeats it until none are left. The edit or the model change is applied only when no item remains. If one could not be moved, nothing else changes. Fix it and apply again.
  • While items are attached to a workflow, its starting state and the publishing flag of its states cannot change.

The first person to open an item in a language holds it for editing. Others see {name} is editing this. They can read along: the form follows the holder’s unsaved work, refreshed every 10 seconds.

  • Also open: {people}. Shared fields and relations save for every language. names people working on other languages of the same item.
  • You are editing this in another tab. means your own other tab holds it.
  • The hold ends about two minutes after the holder’s tab stops renewing it, for example when the tab is closed or hidden.

Press Take over to edit. The hold moves at once.

  • The previous holder sees {name} took over. Their next save or move is refused.
  • Their unsaved changes are kept as their own recovery draft. They get them back when they hold the item again.
  • Holding or taking over a language needs your role to change that language (or to create it, when the item has no row in it yet). A role that edits only its own items must own that language’s row. Otherwise the server refuses (locale_denied or not_owner).
  • While someone else holds an item, saves, moves, assignment, restore, the override and Trash from other sessions are refused. Plugins, imports, scheduled publication and spreadsheet apply are not blocked by a hold.

Unsaved work autosaves a moment after you stop typing. It is one shared draft per item and language, written by the holder.

  • People who may read the item see it after a reload, on any device. They see only the fields they may read, also in a conflict. A role that reads only its own items sees only drafts of the language rows it owns.
  • Read access is checked on its own, apart from the right to change. A role that may change an item but not read that row or language can still write the draft. It never sees the draft it replaces, and a conflict answer carries no draft.
  • An autosave or a discard after someone took over is refused and changes nothing. A refused autosave becomes your recovery draft. A refused discard says Not discarded — taken over. Your work is kept. What you typed stays recoverable as your recovery draft.
  • A draft is never a version. Save turns it into a version and clears the draft.
  • A draft is kept 30 days after its last change.
  • A new item, before its first save, keeps your own private draft.

The status next to Save says what happened:

Status Meaning
Saving draft… The draft is being saved to the server.
Draft saved The draft is saved on the server.
Offline — kept here Kept on this device. Sent on reconnection or Retry.
Draft not saved The write failed. Press Retry.
Changed on another device Press Choose to pick a version.
Not saved — taken over Someone took over. Your work is your recovery draft.
You see What happened What to do
Someone else saved this content Another save landed first. Your edits are still in the form. Load theirs, or Overwrite with mine.
Unsaved changes from {age} A draft you never saved. Restore them or Discard.
Unsaved changes from an older version Someone saved after your draft was made. Compare, then Apply my changes or Discard.
Another device kept different unsaved changes {age}. Choose which to keep. Two devices wrote different drafts. Use the other device’s or Keep this one.
Your own unsaved changes Your work was kept when someone took over. Restore them or Discard.
An old draft was removed The draft passed 30 days. The saved version is on screen.

Open History from the editor. It lists versions oldest first and marks the version being edited and the version the public sees. Step with Previous revision and Next revision. Each version shows its fields, links and images. Reading history changes nothing.

Expand grouped fields to read nested blocks. Rich text appears as readable text; exact stored values remain under Technical details. Shared fields remain visible when you switch languages; language-specific links follow the selected language.

Use Compare with to choose another saved version. Changed fields show removed text struck through and added text underlined. The selected version is the result side of the comparison. Use the language buttons to compare translations, search settings and page facts. Only fields and related labels you can currently read are shown. Absent means the field was not stored; Empty means an explicit empty value. A changed link or file is marked even when its visible label stays the same. Comparison never saves, restores or publishes. Load older history first to include older retained versions in the selector.

In a relationship picker, Create {model} opens the ordinary entry editor without leaving your parent draft. The pencil button edits an existing related entry. Child fields, media, translations, validation and save conflicts work as in the full editor. Saving refreshes the selected entry and its label; it does not save the parent. Closing without saving keeps the previous selection and parent edits. If the child has unsaved changes, choose whether to discard them. Publication remains separate.

  • Restore this version loads that version into the editor and saves it as a new version. It covers every language and every field. It needs the restore permission in every language of the item.
  • Restore {language} only restores one language. Other languages and what is live stay as they are. It needs the restore permission in that language.
  • Restoring never publishes. Publish afterwards if you want the public to see it.

Settings → Workflow → Version history → Versions kept per item sets the number. The default is 50. The minimum is 1.

  • Each save or restore of an item deletes its oldest versions beyond that number.
  • The version being edited and every published version are always kept. Future-version heads and history, and the base versions of open proposals, are retained while they are needed.
  • Lowering the number deletes nothing at once. Each item is trimmed at its next save.
  • A deleted version cannot be opened or restored. History says Version {version} is no longer retained — this site keeps a limited number of versions. Showing the nearest one that is.

Press Move to Trash in the editor or the list. This moves every language to Trash. Move this language to Trash removes one language.

  • A live item or language is refused. Unpublish it first.
  • Items in Trash leave lists, search, relation pickers and previews.
  • You need delete access to the model.

Open Trash above the content list. Restore brings the item back as a draft, with its versions, translations, links and media.

  • The dialog lists links that will not work, such as links to items still in Trash or deleted for good.
  • If another item took its address, restore is refused. Change that item’s address first.

Delete permanently removes every language, version and link of the item, and its visitor comments. It asks first. It cannot be undone.

An item or language is deleted for good 30 days after it went to Trash. The Deleted on column shows that date.

  • A scheduled job runs once a day. In the supplied Worker configuration it runs at 03:17 UTC.
  • Each run deletes at most 50 items per site, oldest first. When more are due, the rest wait for the next run.
  • Sites that are deactivated are skipped until they are active again.
  • Restoring before deletion stops it.

Tick rows in the content list. You can act on at most 25 at a time.

  • Move to… a stage, then Move.
  • Publish now and Unpublish now, for roles with the override permission. They need a reason, recorded as an override.
  • Move to Trash.
  • In Trash: Restore and Delete permanently.

Each item is checked on its own, with the same rules as the single action. A refusal does not stop the others, and nothing is rolled back. The result shows {done} updated, {refused} refused. It lists each refused item with its reason, for example not yours, live — unpublish it first or changed by someone else. Languages of one item move together, so a second row of the same item is counted once.

Tick the page checkbox at the top of the list. When more items match your filters than one page shows, a line appears: Select all N matching. Click it to select every match across all pages. Untick a row to leave that item out; the exclusion is kept as you turn pages. Changing the filters, the field conditions, the search or the content type ends the selection.

  • The selection is every item your role can read that matches the filters, field conditions and search at the moment you start. It is fixed then. Items that are added or change later do not join it.
  • A change covers at most 5,000 items. When more match, the list says so and does not offer the selection; narrow the filters first.
  • The available actions are Move, Publish now and Unpublish now (roles with the override permission), Move to Trash, and Edit fields. Edit fields needs the list narrowed to one content type.
  • Items that other content links to are skipped when they would go offline or to Trash. They are listed afterwards as refused; there is no way to approve them in bulk.

Edit fields lists the fields of the content type that can be set to one value for every selected item: text, number, yes/no, date and choice fields. Tick a field, give it a value, and choose Apply to N items.

Fields that are not offered are listed with the reason:

  • Rich text, links, files and structured content are edited one item at a time.
  • A field that must stay different on every item (unique).
  • A field filled in automatically.
  • A private field.
  • A field your role cannot change.

Each item is saved like a normal edit: the same checks, field and language permissions, version history and cache refresh. Each changed item gets one new version. An item that already holds the value is left alone and reported as No change needed.

The change runs in steps of at most 25 items. A bar shows how far it is, and Stop ends it where it is; items not reached are not touched. If you close the page or lose the connection, the list offers Continue next time. After a failed step, Try again continues the same change and does not repeat what was already done. Only the person who started a change can see and continue it.

Every item is checked again when its turn comes, with the same rules as the single action. The result lists each item as:

  • Changed.
  • No change needed.
  • Refused, with the reason, for example not yours or live — unpublish it first.
  • Changed meanwhile: someone saved or moved the item after the list was fixed. Nothing is overwritten.

Use the buttons above the result to show one group.

The Export spreadsheet → Import spreadsheet round trip updates existing items of one model without changing their IDs or addresses. Each changed item gets a new saved version. To create items, upsert or add translations from CSV/XLSX, use Collection files with the modes described below.

  1. Export spreadsheet downloads an Excel workbook, one sheet per language.
  2. Edit cells. Leave the id, slug and current_version_id columns as they are.
  3. Import spreadsheet, choose the .xlsx file, or a .csv file with Language of this CSV.
  4. Check file. Checking writes nothing. It shows rows, changes and blocked rows, with the row and column of each problem.
  5. Apply N changes. Apply works only when no row is blocked, and only on the exact file you checked. Each changed item gets a new version. Nothing is published.

What the file holds:

  • Only the items you may read and change. A role limited to its own items exports only those. A row for anyone else’s item is blocked as not found.
  • Only the fields your role may both read and change, and one sheet per language your role may read. A file exported by a field-limited role can be checked and applied without the other columns. Their stored values are kept. A file that adds such a column back, or a sheet in a language your role cannot read, is blocked.
  • Each sheet’s slug is that language’s own address.
  • A change in a language your role may read but not change is blocked with locale_denied at its row and column. A shared (non-translatable) field is stored in the primary language, so changing it on any sheet counts as a change in the primary language.
  • In this update-only round trip, values for a translation that does not exist are blocked with spreadsheet_locale_missing. Add it in the editor first, or use Collection files to add it with the required language and field grants.
  • A rule problem on a field or language your role cannot see is shown without naming that field or language.
  • Relation cells contain JSON references, for example [{"model":"author","id":"entry_id"}] or [{"model":"author","slug":"jane","lang":"en"}]. References must name existing entries in this site and fit the field’s target models and cardinality.
  • Media cells contain JSON references such as [{"id":"media_id"}] or [{"storage_key":"folder/image.png"}], with optional crop and focal. They select existing library files; they do not upload a file or accept an arbitrary URL.
  • Component cells contain the same JSON values as a collection file, including nested and repeatable component values. Keep a structured cell blank to preserve its value; enter JSON null to clear a value or link list. Empty required values are allowed in draft mode; strict models and publication require them. Shared link or media values must agree across language sheets.
  • A row whose item changed after the export is blocked. Export again.
  • A workbook exported before each sheet carried its own address can be blocked with spreadsheet_slug_mismatch on the other languages’ sheets. Export again.
  • Formulas are refused. A file is at most 10 MiB, with at most 20,000 rows per sheet.

If the apply stops part-way, the screen says Apply stopped and which row. Rows saved before it are kept. Export again before you retry.

A whole-site export is a different, operator task. See import and migration.

Use Collection files to import JSON, CSV or XLSX and create or update items of one model, including components, related items, media references and translations. It is separate from the update-only spreadsheet round trip above, whole-site copy and migration from another CMS. Open it from the model’s content list. The button shows for roles that can read the model and create or update it.

Download collection saves a .json file of the items your role may read: the languages, fields, related items and media you may read, and only your own items when your read grant is limited to those. A model with many items downloads in parts (Download part 2, and so on); each part imports on its own.

For CSV/XLSX, use field keys as column headers, alongside id, slug and current_version_id where needed. Choose Language of this CSV for a CSV; workbook sheet names identify the site languages. Cells for components, JSON, relations, media and points contain JSON values, not flattened subfield columns. Timezone-aware date cells also use JSON, for example {"datetime":"2026-11-01T06:30:25.123Z","timezone":"America/New_York"}; datetime is the UTC instant, not the local clock time. Rows with the same match across language sheets form one translated item; shared values must agree. Spreadsheet selection starts with update-by-ID: choose a different mode and match explicitly to create or upsert. The same checks, per-row permissions and 500-entry limit apply to all three formats.

{
"format": "dee-wan-collection",
"version": 1,
"model": "article",
"entries": [
{
"id": "…",
"current_version_id": "…",
"primary_lang": "en",
"slugs": { "en": "hello", "th": "sawasdee" },
"locales": {
"en": { "title": "Hello", "faqs": [{ "question": "Why?" }] },
"th": { "title": "สวัสดี" }
},
"relations": { "related": [{ "model": "author", "id": "…" }] },
"locale_relations": {
"th": { "tags": [{ "model": "tag", "slug": "news", "lang": "th" }] }
},
"media": { "cover": [{ "id": "…", "storage_key": "…", "crop": null }] }
}
]
}
  • locales holds field values as the item stores them: nested components, JSON and rich text keep their structure. A field left out is not touched. null clears it.
  • relations names related items as { "model", "id" }, or { "model", "slug" } with an optional lang (default: the site’s main language). A relation with different links in each language goes under locale_relations. The model is always written, and it must be the model of the item found. A field that accepts one model refuses another.
  • media names files already in this site’s library by id or storage_key. Importing a reference never uploads or copies a file. crop and focal are kept when written.
  • slugs holds each language’s address. id and current_version_id are only read when matching by id.
  • A file is at most 10 MiB, 500 entries and 32 levels of nesting. Apply saves 20 rows per request and the screen continues until none are left.

Choose what the file does and how existing items are found.

Mode A row with no matching item A row with a matching item
Create Created. Refused (collection_match_exists).
Update Refused (spreadsheet_row_not_found). Updated if it differs.
Create or update Created. Updated if it differs, otherwise unchanged.

Match on ID, Address (slug) or a field of the model that is set to be unique. The slug is read in the entry’s primary_lang. ID matches existing items only, so create and upsert by ID never make a new item; match on the address or a unique field to create. A row with no value to match on, two rows with the same value, two rows for one item, or a value that more than one item holds are all refused before anything is written (collection_match_missing, collection_duplicate_match, collection_duplicate_target, collection_match_ambiguous).

  1. Check file. Checking writes nothing. It shows rows to create, rows to update, unchanged rows, kept rows and blocked rows, with the row, language and field of each problem.
  2. Apply changes. Apply works only when no row is blocked, only on the exact file you checked, and only while the model’s fields and rules are still the ones you checked against (collection_definitions_changed: check again).

Each row is written like a manual edit: the same validation, history, cache clearing and audit. Nothing is published.

What is blocked: a field or language that does not exist; a relation or media reference that names nothing, an item in Trash, an item of another site or the wrong model (collection_relation_missing, collection_relation_in_trash, collection_relation_wrong_model, collection_media_missing); a value the editor would refuse; a relation written in locales, or the reverse (collection_field_reference); a model you cannot create, read or update; a translation the item does not have yet when your role may not create that language or change that field (locale_denied, field_denied); and a changed field or language your role may not write (field_denied, locale_denied). A field or language you did not change is not checked against your write grants.

If a row’s current_version_id is not the item’s current version and the row differs from the item, the item is kept as it is (spreadsheet_version_conflict). The other rows still apply. Export again to change a kept item. If you leave out current_version_id, the row is compared with the item as it is when you apply. A version from another site’s file is ignored when the id is not the matched item’s.

Apply reports each row as created, updated, unchanged, kept or refused; a refused row shows the reason. It never claims the whole file applied as one step. If the screen says the last step could not be confirmed, choose Resume. Rows already written read as unchanged. Retrying the same file never creates a second item: a created row is remembered for 30 days by the file and its row number, and after that a create refuses the existing item and an upsert reads it as unchanged.

Apply works only on a file that was checked, and it keeps the version each item had when you checked. An item that changed after the check is kept (spreadsheet_version_conflict) and the editor’s change stays. A check that is older than 30 days, or an apply that skips the check, is refused with spreadsheet_not_checked: check the file again.

A retry never overwrites an edit made after the file’s first run. An item the file created and someone then edited reads as unchanged (already created by this file). An item the file updated and someone then edited is kept (spreadsheet_version_conflict). A translation the file started is not written again, so later edits to it stay.

A row that gives a language the existing item does not have yet starts that translation inside the item’s translation group, with its address (slugs), field values, components, related items and media references. This needs the update grant on the model, the create grant for that language and write access to the fields. Without them the row is blocked (locale_denied, field_denied). A language that is not set up for the site is blocked as an unknown language. Invalid supplied values and addresses already used by another item block the row (field_validation_failed, slug_reserved). Missing required values block saves only for strict models; publication always requires them.

AI generation needs the ai:generate permission and an AI provider set up for the installation. Without a provider, generation is refused. Each site has an hourly allowance, 250 requests by default.

  • Text fields. A field set up for AI generation in the model shows a Generate with AI button. AI prompt not configured means the field has no prompt.
  • Rich text. AI Generate opens a prompt. Write what you want and press Generate.
  • Components with A/B variants. Generate variants with AI adds variants.

In text fields and rich text, the answer is shown for review first. Nothing changes until you press Apply. In a text field, Apply replaces the field. In rich text, Apply inserts at the cursor. An answer over the field’s limit is truncated, and the panel says so before you apply.

Images are generated in the media library, see media. The content planner writes whole drafts.

Manage files in the media library, and set up languages.

Open AI translation updates in the entry sidebar. Opt in, select existing target languages, then allow replacement of existing text, including manual translations. The source is saved first. AI translates only changed translatable text and rich-text fields into selected languages and saves one draft version through normal permissions and version checks. A save that changes no source text makes no AI request. Other fields and languages stay unchanged. Clearing source text clears that selected translated field without inventing replacement text. Switching source language resets consent. Failure keeps the source save and reports the translation failure. Publishing remains a separate step.

Page settings also offer AI translation updates for existing composed-page languages. Select targets and explicitly allow replacement, including manual wording. The source saves first. AI compares previous and current source text with each target’s own text; it updates only existing text fields. Each target keeps its section IDs, order, layout, media, links, code, component pins, shared references and facts. Rich paragraph formatting and link destinations stay. Shared and component-pinned content is excluded. Targets save sequentially through ordinary page permissions and version/facts checks. If the page changes after the source save, review the latest page before retrying translation. Each target reports success, no corresponding change, or failure. An unchanged source or a source with no previous or current text makes no AI request. Clearing source wording can remove corresponding target wording. Source page, language or site changes reset consent. Translation failure keeps the source save; typing during translation stays in the source editor. Publication remains separate.

Open the compact Entry folders control on Content to browse one folder or Unfiled, across models or within the selected model. The folder filter stays in the address and survives reload and Back. Folder counts include only entries you can read; translations count as one logical entry.

People with site:settings choose New folder to create a folder, or select a folder and choose Edit to rename it or change its parent. Cancel closes the form without saving. They can also delete empty folders. Names contain 1–80 characters; sibling names are unique ignoring case. A site can hold 500 folders with five levels of nesting. Move live entries and child folders out before deleting a folder. The confirmation shows entries in Trash that will become Unfiled if restored; their placement history stays. Creating or renaming a folder shows a success message.

Open Current folder on an existing entry, then choose a folder and Move entry. The controls and placement history stay collapsed until opened. Moving needs read and update access to that entry, including ownership and entry rules. Every translation shares the primary entry’s folder. A folder move changes no URL, revision or publication pointer. Placement history records moves separately from content revisions; restoring an old content revision keeps the current folder. The latest 100 moves appear in the editor.

Duplicate keeps the source entry’s folder, including for composed pages. A site copy carries empty folders and current placement. History transfer also carries placement events and actor labels. Destination IDs are remapped; folders with the same path of names are reused. Reimport does not move entries created by an earlier job. Undo keeps an entry if someone changed its placement after import.

Model field configuration can limit relation choices with filterChoices, and select options with when. These use the same bounded conditions as field filters: at most four conditions and three group levels. Relation conditions test candidate target fields; select when conditions test sibling fields in the current form. Nested component choices use siblings inside that component instance.

{
"type": "relation",
"targetModel": "article",
"relationType": "manyToMany",
"filterChoices": [
{ "field": "department", "op": "eq", "values": ["$field:department"] }
]
}
{
"type": "select",
"options": [
{ "value": "general", "label": "General" },
{
"value": "sales",
"label": "Sales",
"when": [{ "field": "department", "op": "eq", "values": ["sales"] }]
}
]
}

Operands can use $field:key, $user:id, $user:email, $entry:id, $entry:model, $entry:locale. Prefix a literal dollar value with $$. Missing context gives no matching choices. Unsaved sibling edits recompute the picker. An incompatible saved select value stays visible with a warning and a Clear action; you can also choose a replacement. Save checks selected values again, including retained dependent values; change an incompatible selection before saving. Relation choices also respect the person’s existing read access. A choice rule grants no extra access.

An active plugin can declare an inline item in Blocks → Plugin blocks. Place the caret inside a paragraph and choose its label. The item stays inside that paragraph. Select its visible label to edit the plugin’s declared fields in a dialog; close the dialog to continue writing.

The host draws the controls and preview. Values are structured data with the plugin type and version, not pasted markup. Undo, saved versions, restore, site copy and native import keep the values and references. The public and generated renderers allow inline markup only; a plugin cannot insert a block element into a paragraph. A disabled or unavailable plugin keeps the stored item but does not render it publicly. Existing block items remain available separately.

For a collection, open Collection order and enable Enable manual order. This requires model:apply. Select Use manual order to request it in the list, then move entries with the arrow buttons or drag them before another visible entry. Translations share one position. Filtered, hidden and off-page entries keep their order relative to each other; a move uses the visible entry as its anchor. New groups append; Trash and restore preserve their position. Reordering creates no content version.

Manual order is optional and supports up to 20,000 translation groups per model. Concurrent changes return a conflict: reload before moving again. Public consumers explicitly request order=manual; other sort modes keep their behavior. A field sort cannot be combined with manual order. Cursors bind the order revision, so an old cursor is refused after reordering. Native copies carry the source order for newly copied groups and preserve existing destination groups. Undo restores an imported order flag only if subsequent ordering or membership changes have not superseded it.

Models may group fields under localized headings. The heading and its info control use the admin language, with the site’s default language and original label as fallbacks. Group names do not change saved field keys or content values.

Use Scheduling to add an entry to a release with Version → Independent future version. Give the release a name and time. Open Future versions on the entry and select that release’s version. Each release has its own saved content and history; editing it leaves the ordinary draft and public version unchanged. Different campaigns can prepare different versions of the same entry or composed page.

In the future editor, choose a language, edit fields or the page’s sections and facts, then Save. Future history lists its saved versions. Required review tasks are checked against this future content. Return with Back to entry to edit the ordinary draft.

Preview through this release uses the published site plus earlier scheduled releases, including their publication and unpublication actions, then this release. Composed pages use the preview in their page controls. Choose a release time first; unreadable content stays hidden. It is a private preview and changes no public cache or publication.

The release freezes the exact future revision when its publication command starts. It then becomes read-only. Later ordinary draft edits are preserved. A normal release item still publishes the latest ordinary saved draft. Two releases cannot act on the same entry and overlapping languages at exactly the same time; choose different times.

Open one content type, select entries or Select all matching, then choose Batch AI. Choose text fields and Rewrite, Translate, Terminology, or Summarize. Translation also needs different source and target languages; terminology needs your glossary in Instructions.

Generate proposals freezes the current selection. Later matches are excluded. Generation requires permission to use AI and read the source content; it does not save entries. Each result retains its base revision and original values. Translation creates at most one proposal per selected translation group.

Use Review proposals to compare the original text, edit a proposal, apply it, or reject it. Applying uses your current field, locale and content permissions, validation and version checks. If an entry changed after generation, the proposal is refused as stale. Editing and resolving a proposal compare its revision too, so one reviewer cannot silently overwrite another’s changes. Translation can add a missing configured language through the normal versioned save.

Leaving the page keeps pending work available through Continue. Stop cancels pending generation permanently; it preserves proposals already produced and an in-flight result. Transient provider failures can be retried up to three attempts; committed proposals are not generated again. Reopen Batch AI to review the latest run, or open an entry to see its retained proposals. AI credentials stay on the server.


Unreleased / pre-MVP; no stable version
Source revision: fe23db481c422b40e317b92fc98116b4c0c26a87 (modified checkout)
Build date (UTC): 2026-10-18T09:22:34.630Z
Upgrade target: unavailable: modified checkout. Release notes