-
Notifications
You must be signed in to change notification settings - Fork 0
Publishing
Editing can never change the live page by accident. That is not a workflow preference, it is the reason the content is stored in two columns rather than one.
atelier_pages carries draft_content and published_content as separate JSON columns.
- Editing writes the draft. Every change in the builder, immediately.
-
Publishing copies draft to published, stamps
published_at, and snapshots a revision. - The public route reads published and nothing else. There is no code path where a draft reaches a visitor.
An unpublished page 404s rather than rendering, so a half-finished page cannot leak by somebody guessing the URL.
The builder writes the draft on every change. That is deliberate: a client who loses ten minutes of work to a closed tab does not trust the tool again.
It has one consequence worth knowing. Two people editing the same page overwrite each other, silently, with no conflict warning. Fine for one editor, and worth saying out loud before you put two people in the panel.
The badge in the builder toolbar and the Pages table shows one of three states:
| Badge | Means |
|---|---|
| Draft | Never published. The public URL 404s. |
| Published | Live, and the draft matches what is live. |
| Unpublished changes | Live, and the draft has moved on. Publish to catch up. |
The third is the one most CMSs get wrong by not showing it. It is computed by comparing the two columns, so it cannot drift out of sync with reality.
Publish is in the builder toolbar and on the page settings screen. It copies the draft
across, sets published_at, and writes a revision.
Unpublish is on the page settings screen, behind a confirmation. The page 404s
immediately and keeps all of its content, so it is a way to take something down rather than
lose it. Page::unpublish() does the same from code.
The builder's toolbar has a link that opens the draft in a new tab. It is a
temporarySignedRoute, so you can send it to a client with no panel login.
- Expires after
atelier.preview.link_expiry_minutes, 24 hours by default. - Carries
noindex, nofollowas both a meta tag and anX-Robots-Tagheader. - Sends
Cache-Control: no-store, so no proxy keeps a copy. - Renders the draft in whichever locale the editor is on.
- Uses a relative signature, so the link still validates when you reach the site through
127.0.0.1, a tunnel, or any host that is notAPP_URL.
There is no permanent, non-expiring link. Every share expires.
Every publish snapshots the tree that went live into atelier_page_revisions, with who
published it.
$page->revisions; // newest first
$page->revisions()->count();
$page->restoreRevision($revision); // copies it back into the draftSnapshots hold the published tree, not the draft, so a revision is always something that was once live. That is what "put it back" means to the person asking for it.
restoreRevision() writes the draft, deliberately not the published column. Restoring
is an undo you then look at and publish, not a silent change to the live site.
Old snapshots are pruned on every publish, keeping the newest atelier.revisions.keep
(20 by default). Set it higher for a site that publishes rarely, lower for one that
publishes constantly.
tinker or your own code, and there is no screen for browsing or comparing them. Which
means the delete confirmation in the builder is telling the truth: deleting a section is not
reversible until you have published at least once since.
- No cache to clear. Pages render per request, and the sitemap is built from two queries, so publishing takes effect immediately with nothing to invalidate.
- No scheduled publishing. There is no publish-at date. It is on the roadmap and is not built.
- No approval workflow. Anyone who can reach the page in the panel can publish it. Restrict that with Filament policies if a client needs it.
use Safi\Atelier\Models\Page;
$page = Page::create([
'title' => 'Pricing',
'draft_content' => [[
'id' => 'b_hero',
'type' => 'hero',
'attributes' => ['heading' => ['en' => 'Pricing', 'ar' => 'الأسعار']],
'children' => [],
]],
]);
$page->setSlugs(['en' => 'pricing', 'ar' => 'as3ar']);
$page->publish();Useful for seeders and for standing up a client site with its skeleton already in place.
setSlugs() generates a slug from the title when you pass an empty string, and writes a 301
from the old URL whenever a slug changes.
Building
Running a site
Reference