Guide 05 · Developer docs

Content types and custom entities

Which built-in type fits which purpose, how categories, tags, slugs, custom URLs and SEO settings behave across them, how revisions and publishing gate what the public sees, how to model a domain the platform has no type for, and where uploaded files live.

L1–L3
Unit 03 · Revisions, publishing and approval

Revisions and the active revision

A versioned record keeps its content in revisions, versions and drafts, and one version at a time is the active revision that every layout area getter reads. This page covers the kinds of revision, the save actions that create them, and what a template sees.

Who this is for. Developers writing templates for pages, blog posts, book pages and other versioned records, and anyone explaining to an author why a saved change is not on the site. After this page you can say which revision a template renders and which save action puts a change live.

Content lives in revisions

Pages, blog posts, books, units and book pages, FAQ pages and questions, custom entity records, product and category descriptions, forms and global components are versioned. The record carries what is not content, such as its slug and its published date; its content is stored in revisions. One revision at a time is the active revision, the one visitors see.

A template does not fetch the active revision itself. On a versioned record every layout area getter reads it for you: $page->getAreaText('intro') is $page->getActiveRevision()->getAreaText('intro'), and the shorthand {$page->intro} (Twig {{ page.intro }}) is the shortest way to the same value. Each new revision copies the whole area graph, components included, so a component placed in a layout area belongs to the revision whose area holds it, and an earlier revision keeps the content it was saved with. How a value is tied to its layout area, and what renaming or retiring a layout area does to stored content, is on Where content is stored, and what a change does to it.

Template code
{* the shorthand: the house style *}
{$page->intro}

{* these two are the same call: the record's getter reads its active revision *}
{$page->getAreaText('intro')}
{$page->getActiveRevision()->getAreaText('intro')}
{# the shorthand: the house style #}
{{ page.intro }}

{# these two are the same call: the record's getter reads its active revision #}
{{ page.getAreaText('intro') }}
{{ page.getActiveRevision().getAreaText('intro') }}

Some templates assign the revision to a variable once, {$activeRevision = $page->getActiveRevision()}, and read from that. It is the same value written out longer; name the revision only when you need the revision object itself.

Versions and drafts

Every revision has a type, version or draft. A save that does not name a type writes a version.

  • A version is a saved state of the record that can be made the active revision. The active revision need not be the newest version: a version saved without activating it, or a rollback to an earlier one, leaves the active revision on an older version.
  • A draft is one author's work in progress. Each user has at most one draft per record (per branch, once an approval workflow applies): saving a draft again updates the one you have instead of adding another. A draft can never become the active revision; an attempt fails with "A draft cannot be set as active revision".

Drafts are short-lived: when a record is saved, the drafts on the same branch that the save does not update are deleted.

The save actions

The edit screen of a versioned record offers these buttons:

ButtonWhat it does
Update versionSaves the changes into the revision being edited.
Save new versionSaves the changes as a new version. The active revision stays where it was.
Save and activateSaves a new version and makes it the active revision.
Create branchStarts a branch. Offered only once an approval workflow applies to the record (see Approval workflows: steps, transitions and approvals).

"Save and activate" is withheld, leaving only "Save new version", when the record has an approval workflow assigned or the user lacks publish access on the record. Which of the two is listed first follows the setting "Activate new revisions by default" (Globals.admin.defaultActivate), which is on by default.

Saved is not live

"Save new version" stores the change and leaves visitors on the old active revision. Until someone activates the new version the site does not change, which is the commonest reason behind "I saved it and nothing happened". The other reason, an empty or future published date, is on Published, active and draft: the two gates.

What a template sees

  • A visitor sees the active revision of a published record, and nothing else. A draft never renders.
  • getLastRevision() returns the newest revision by id. That can be a version nobody activated or someone's draft, so it belongs in a revision history for authors, never in a template that renders content. On the docs' own sandbox page the newest revision was 3424 while the active revision was still 3277: saved, not live.
  • A backend user can render a revision other than the active one by adding ?revision=<id> to the record's URL, for that request only. See Previewing unpublished content.

The API exposes the same two revisions as activeRevision and lastRevision on a versioned record; how an integration reads and writes them is on Versioned content and the live revision.

Go deeper

Was this page helpful?

Your answer goes to the docs team.