Packages
phoenix_kit
1.7.77
1.7.210
1.7.209
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/modules/publishing/schemas/publishing_post.ex
defmodule PhoenixKit.Modules.Publishing.PublishingPost do
@moduledoc """
Schema for publishing posts within a group.
Each post belongs to a group and has versions with per-language content.
Supports both slug-mode and timestamp-mode URL structures.
## Status Flow
- `draft` - Not visible to public
- `published` - Live and visible
- `archived` - Hidden but preserved
## Data JSONB Keys
- `allow_version_access` - Whether older versions are publicly accessible
- `featured_image` - Featured image reference (media UUID or URL)
- `tags` - List of tag strings
- `seo` - SEO metadata map (og_title, og_description, og_image, etc.)
"""
use Ecto.Schema
import Ecto.Changeset
alias PhoenixKit.Modules.Publishing
@primary_key {:uuid, UUIDv7, autogenerate: true}
@foreign_key_type UUIDv7
@type t :: %__MODULE__{
uuid: UUIDv7.t() | nil,
group_uuid: UUIDv7.t(),
slug: String.t(),
status: String.t(),
mode: String.t(),
primary_language: String.t(),
published_at: DateTime.t() | nil,
post_date: Date.t() | nil,
post_time: Time.t() | nil,
created_by_uuid: UUIDv7.t() | nil,
updated_by_uuid: UUIDv7.t() | nil,
data: map(),
inserted_at: DateTime.t() | nil,
updated_at: DateTime.t() | nil
}
schema "phoenix_kit_publishing_posts" do
field :slug, :string
field :status, :string, default: "draft"
field :mode, :string, default: "timestamp"
field :primary_language, :string, default: "en"
field :published_at, :utc_datetime
field :post_date, :date
field :post_time, :time
field :data, :map, default: %{}
belongs_to :group, PhoenixKit.Modules.Publishing.PublishingGroup,
foreign_key: :group_uuid,
references: :uuid,
type: UUIDv7
belongs_to :created_by, PhoenixKit.Users.Auth.User,
foreign_key: :created_by_uuid,
references: :uuid,
type: UUIDv7
belongs_to :updated_by, PhoenixKit.Users.Auth.User,
foreign_key: :updated_by_uuid,
references: :uuid,
type: UUIDv7
has_many :versions, PhoenixKit.Modules.Publishing.PublishingVersion, foreign_key: :post_uuid
timestamps(type: :utc_datetime)
end
@doc """
Changeset for creating or updating a publishing post.
"""
def changeset(post, attrs) do
post
|> cast(attrs, [
:group_uuid,
:slug,
:status,
:mode,
:primary_language,
:published_at,
:post_date,
:post_time,
:created_by_uuid,
:updated_by_uuid,
:data
])
|> validate_required([:group_uuid, :status, :mode, :primary_language])
|> validate_inclusion(:status, Publishing.Constants.post_statuses())
|> validate_inclusion(:mode, Publishing.Constants.valid_modes())
|> maybe_require_slug()
|> validate_length(:slug, max: Publishing.Constants.max_slug_length())
|> validate_length(:primary_language, max: Publishing.Constants.max_language_code_length())
|> maybe_require_timestamp_fields()
|> unique_constraint([:group_uuid, :slug], name: :idx_publishing_posts_group_slug)
|> unique_constraint([:group_uuid, :post_date, :post_time],
name: :idx_publishing_posts_group_date_time_unique,
message: "a post already exists at this date and time"
)
|> foreign_key_constraint(:group_uuid, name: :fk_publishing_posts_group)
|> foreign_key_constraint(:created_by_uuid, name: :fk_publishing_posts_created_by)
|> foreign_key_constraint(:updated_by_uuid, name: :fk_publishing_posts_updated_by)
end
@doc "Check if post is published."
def published?(%__MODULE__{status: "published"}), do: true
def published?(_), do: false
@doc "Check if post is a draft."
def draft?(%__MODULE__{status: "draft"}), do: true
def draft?(_), do: false
# Data JSONB accessors
@doc "Returns whether older versions are publicly accessible."
def allow_version_access?(%__MODULE__{data: data}),
do: Map.get(data, "allow_version_access", false)
@doc "Returns the featured image reference."
def get_featured_image(%__MODULE__{data: data}), do: Map.get(data, "featured_image")
@doc "Returns the post tags."
def get_tags(%__MODULE__{data: data}), do: Map.get(data, "tags", [])
@doc "Returns SEO metadata."
def get_seo(%__MODULE__{data: data}), do: Map.get(data, "seo", %{})
defp maybe_require_slug(changeset) do
if get_field(changeset, :mode) == "slug" do
validate_required(changeset, [:slug])
else
changeset
end
end
defp maybe_require_timestamp_fields(changeset) do
if get_field(changeset, :mode) == "timestamp" do
validate_required(changeset, [:post_date, :post_time])
else
changeset
end
end
end