MishkaCmsCore.Runtime.PageDraft (Mishka CMS Core v0.0.1)

Copy Markdown View Source

An unpublished working copy of a runtime MishkaCmsCore.Runtime.Page.

A draft holds the same editable fields as a Page plus:

  • source_page_id — the live Page it edits, or nil for a brand-new draft-only page.
  • preview_token — an unguessable token for the shareable /_preview/:token URL.
  • status (:draft / :published) and applied_at — set when the draft is published.

Publishing is explicit (:publish / :master_publish): it writes the draft's fields to the live Page (creating it when source_page_id is nil, otherwise updating all but the page's events, infos and helpers), which runs the normal Page compile pipeline, then marks the draft published. See MishkaCmsCore.Runtime.Resources.Changes.PublishPageDraft.

Mirrors MishkaCmsCore.Runtime.Page's conventions (tenant-scoped, master/tenant action pairs) but carries no version history of its own — the live Page already versions on publish.

Upgrading across 2026-08-01

This resource and its two siblings — MishkaCmsCore.Runtime.LayoutDraft and MishkaCmsCore.Runtime.ErrorPageDraft — used AshArchival until 2026-08-01. Discarding a draft set archived_at instead of removing the row, and a base_filter_sql "(archived_at IS NULL)" kept it out of every read.

Removing the extension made discard a real delete, and took the base filter with it. Any row still carrying an archived_at therefore became visible again — a draft its author threw away is back in the picker, publishable over the live page, and reachable at its preview token. That happens on git pull, before any migration runs.

If your database predates that date, run the purge BEFORE the migration that drops the column, because afterwards a discarded draft is indistinguishable from live work:

mix mishka_cms_core.purge_draft_archival --dry-run
mix mishka_cms_core.purge_draft_archival

See Mix.Tasks.MishkaCmsCore.PurgeDraftArchival for the release equivalent and the exact rows it matches. A database created after that date needs nothing; the task is a no-op there.

Summary

Functions

Generate an unguessable URL-safe preview token.

Validates that the keys in the provided input are valid for at least one action on the resource.

Same as input/1, except restricts the keys to values accepted by the action provided.

Types

t()

@type t() :: %MishkaCmsCore.Runtime.PageDraft{
  __lateral_join_source__: term(),
  __meta__: term(),
  __metadata__: term(),
  __order__: term(),
  active: term(),
  aggregates: term(),
  applied_at: term(),
  calculations: term(),
  content: term(),
  description: term(),
  events: term(),
  extra: term(),
  format: term(),
  helpers: term(),
  id: term(),
  infos: term(),
  inserted_at: term(),
  label: term(),
  language: term(),
  language_group_id: term(),
  layout: term(),
  layout_id: term(),
  name: term(),
  path: term(),
  permissions: term(),
  precompile: term(),
  preview_token: term(),
  priority: term(),
  resource_links: term(),
  schema: term(),
  section: term(),
  seo_tags: term(),
  site: term(),
  site_id: term(),
  source_hash: term(),
  source_page: term(),
  source_page_id: term(),
  state: term(),
  status: term(),
  template: term(),
  title: term(),
  updated_at: term()
}

Functions

default_short_name()

generate_token()

Generate an unguessable URL-safe preview token.

input(opts)

@spec input(values :: map() | Keyword.t()) :: map() | no_return()

Validates that the keys in the provided input are valid for at least one action on the resource.

Raises a KeyError error at compile time if not. This exists because generally a struct should only ever be created by Ash as a result of a successful action. You should not be creating records manually in code, e.g %MyResource{value: 1, value: 2}. Generally that is fine, but often with embedded resources it is nice to be able to validate the keys that are being provided, e.g

Resource
|> Ash.Changeset.for_create(:create, %{embedded: EmbeddedResource.input(foo: 1, bar: 2)})
|> Ash.create()

input(opts, action)

@spec input(values :: map() | Keyword.t(), action :: atom()) :: map() | no_return()

Same as input/1, except restricts the keys to values accepted by the action provided.

primary_key_matches?(left, right)