Version management functions for the Publishing module.
Handles listing, creating, publishing, and deleting versions of publishing posts.
Summary
Functions
Creates a new version of a slug-mode post by copying from the latest version.
Creates a new version from an existing version or blank.
Deletes an entire version of a post.
Gets the published version number for a post.
Returns a %{status, title, url_slug, version} map for the given
group/post/version/language tuple, or nil when any link in the
chain (post → version → per-language content) is missing.
Gets the status of a specific version/language.
Lists version numbers for a post.
Publishes a version, making it the live version for the post.
Unpublishes a post by clearing its active version.
Functions
@spec create_new_version(String.t(), map(), map(), map() | keyword()) :: {:ok, map()} | {:error, any()}
Creates a new version of a slug-mode post by copying from the latest version.
The new version starts as draft with status: "draft". Content and metadata updates from params are applied to the new version.
Note: For more control over which version to branch from, use create_version_from/5.
@spec create_version_from( String.t(), String.t(), integer() | nil, map(), map() | keyword() ) :: {:ok, map()} | {:error, any()}
Creates a new version from an existing version or blank.
Parameters
group_slug- The publishing group slugpost_slug- The post slugsource_version- Version to copy from, ornilfor blank versionparams- Optional parameters for the new versionopts- Options including:scopefor audit metadata
Examples
# Create blank version
iex> Publishing.Versions.create_version_from("blog", "my-post", nil, %{}, scope: scope)
{:ok, %{version: 3, ...}}
# Branch from version 1
iex> Publishing.Versions.create_version_from("blog", "my-post", 1, %{}, scope: scope)
{:ok, %{version: 3, ...}}
@spec delete_version(String.t(), String.t(), integer(), keyword() | map()) :: :ok | {:error, term()}
Deletes an entire version of a post.
Archives the version instead of permanent deletion. Refuses to delete the last remaining version or the live version.
Returns :ok on success or {:error, reason} on failure.
Gets the published version number for a post.
Returns a %{status, title, url_slug, version} map for the given
group/post/version/language tuple, or nil when any link in the
chain (post → version → per-language content) is missing.
Used by the editor and listing views to surface the version's title and URL slug for a specific language without having to load the full version + content rows.
Returns nil (not an {:error, _} tuple) on DB exceptions — the
caller treats absent metadata the same as a missing version, and
the exception is logged for diagnostics.
Gets the status of a specific version/language.
Lists version numbers for a post.
Publishes a version, making it the live version for the post.
- Sets the target version status to "published" and
published_at(if not already set) - Archives the previously published version (status -> "archived")
- Sets
post.active_version_uuidto the target version's UUID
Options
:source_id- ID of the source (e.g., socket.id) to include in broadcasts, allowing receivers to ignore their own messages
Examples
iex> Publishing.Versions.publish_version("blog", "my-post", 2)
:ok
iex> Publishing.Versions.publish_version("blog", "my-post", 2, source_id: "phx-abc123")
:ok
iex> Publishing.Versions.publish_version("blog", "nonexistent", 1)
{:error, :not_found}
Unpublishes a post by clearing its active version.
- Clears
post.active_version_uuid - Sets the previously-active version status to
:target_statusopt (default"draft"; pass"archived"to archive instead). The UI's "archived" status flows through here so the version's row should actually carry"archived", otherwise the UI label and the DB state diverge (the admin listing shows "Archived" but the underlying version is"draft").
Options
:source_id- ID of the source to include in broadcasts:target_status- final status for the previously-active version ("draft"or"archived"; default"draft")
Examples
iex> Publishing.Versions.unpublish_post("blog", post_uuid)
:ok
iex> Publishing.Versions.unpublish_post("blog", post_uuid, target_status: "archived")
:ok
iex> Publishing.Versions.unpublish_post("blog", "nonexistent")
{:error, :not_found}