Database storage layer for the Publishing module.
Provides CRUD operations for publishing groups, posts, versions, and contents via PostgreSQL with Ecto.
Summary
Functions
Clears a specific url_slug from all content rows of a post. Returns cleared language codes.
Counts posts by primary language status for a group.
Counts primary language status from an already-loaded list of posts. Avoids re-querying when posts are already available.
Creates content for a version/language.
Creates a publishing group.
Creates a post within a group.
Creates a new version for a post.
Creates a new version by cloning content from a source version.
Deletes a group and all its posts (cascade).
Hard-deletes a post and all its versions/contents (cascade).
Finds content by a previous URL slug (stored in data.previous_url_slugs JSONB array).
Finds content by URL slug across all versions in a group.
Finds a post by date and time (timestamp mode, matches hour:minute only).
Gets content for a specific version and language.
Gets a group by UUID.
Gets a group by slug.
Gets the latest version for a post.
Gets a post by group slug and post slug.
Gets a timestamp-mode post by date and time.
Gets a post by UUID with preloads.
Gets a specific version by post and version number.
Lists all content rows for a version.
Lists all groups ordered by position.
Lists available languages for a version.
Lists posts in a group, optionally filtered by status.
Lists all posts in a group in listing format (excerpt only, no full content).
Lists posts in slug mode (ordered by slug asc).
Lists posts in timestamp mode (ordered by date/time desc).
Lists all posts in a group with their latest version metadata.
Lists all versions for a post, ordered by version number.
Updates all posts in a group to use the given primary language.
Gets the next version number for a post.
Reads a full post with its latest version and content for a specific language.
Reads a timestamp-mode post by date and time instead of slug.
Soft-deletes a post by setting status to 'archived'.
Updates content.
Bulk-updates the status of all content rows for a version, excluding a specific language.
Updates a publishing group.
Updates a post.
Updates a version.
Upserts content by version_id + language.
Upserts a group by slug.
Functions
Clears a specific url_slug from all content rows of a post. Returns cleared language codes.
Counts posts by primary language status for a group.
Returns %{current: n, needs_migration: n, needs_backfill: n} where:
current— primary_language matches the global settingneeds_migration— primary_language is set but differs from globalneeds_backfill— primary_language is nil
Counts primary language status from an already-loaded list of posts. Avoids re-querying when posts are already available.
Posts can be DB structs or legacy maps (with :primary_language key).
Creates content for a version/language.
Creates a publishing group.
Creates a post within a group.
Creates a new version for a post.
Creates a new version by cloning content from a source version.
Creates a new version row and copies all content rows from the source. Wrapped in a transaction for atomicity.
Returns {:ok, %PublishingVersion{}} or {:error, reason}.
Deletes a group and all its posts (cascade).
Hard-deletes a post and all its versions/contents (cascade).
Finds content by a previous URL slug (stored in data.previous_url_slugs JSONB array).
Finds content by URL slug across all versions in a group.
Finds a post by date and time (timestamp mode, matches hour:minute only).
Gets content for a specific version and language.
Gets a group by UUID.
Gets a group by slug.
Gets the latest version for a post.
Gets a post by group slug and post slug.
Gets a timestamp-mode post by date and time.
Truncates seconds from the input time since URLs use HH:MM format only, and new posts are stored with seconds zeroed. For legacy posts with non-zero seconds, falls back to hour:minute matching.
Gets a post by UUID with preloads.
Gets a specific version by post and version number.
Lists all content rows for a version.
Lists all groups ordered by position.
Lists available languages for a version.
Lists posts in a group, optionally filtered by status.
Lists all posts in a group in listing format (excerpt only, no full content).
Always uses Mapper.to_listing_map/4 which strips content bodies and includes
only excerpts. Designed for caching in :persistent_term where data is copied
to the reading process heap — keeping entries small matters.
Lists posts in slug mode (ordered by slug asc).
Lists posts in timestamp mode (ordered by date/time desc).
Lists all posts in a group with their latest version metadata.
Returns a list of legacy-format maps suitable for listing pages.
Lists all versions for a post, ordered by version number.
Updates all posts in a group to use the given primary language.
Returns {:ok, count} with the number of updated posts.
Gets the next version number for a post.
Reads a full post with its latest version and content for a specific language.
Returns a map suitable for the legacy mapper or nil if not found.
Reads a timestamp-mode post by date and time instead of slug.
Soft-deletes a post by setting status to 'archived'.
Updates content.
Bulk-updates the status of all content rows for a version, excluding a specific language.
Updates a publishing group.
Updates a post.
Updates a version.
Upserts content by version_id + language.
Upserts a group by slug.