Packages
phoenix_kit
2.52.2
2.64.0
2.63.0
2.62.2
2.62.1
2.62.0
2.61.0
2.60.3
2.60.2
2.60.1
2.60.0
2.59.0
2.58.0
2.57.1
2.57.0
2.56.1
2.56.0
2.55.1
2.55.0
2.54.2
2.54.1
2.54.0
2.53.0
2.52.2
2.52.1
2.52.0
2.51.0
2.50.0
2.49.1
2.49.0
2.48.0
2.47.0
2.46.0
2.45.0
2.44.0
2.43.1
2.43.0
2.42.1
2.42.0
2.41.6
2.41.4
2.41.3
2.41.2
2.41.1
2.41.0
2.40.1
2.40.0
2.39.0
2.38.1
2.38.0
2.37.5
2.37.4
2.37.3
2.37.2
2.37.1
2.37.0
2.36.1
2.36.0
2.35.0
2.34.0
2.33.0
2.32.1
2.32.0
2.31.1
2.31.0
2.30.0
2.29.1
2.29.0
2.28.2
2.28.1
2.28.0
2.27.2
2.27.1
2.27.0
2.26.1
2.26.0
2.25.0
2.24.0
2.23.3
2.23.2
2.23.1
2.23.0
2.22.24
2.22.23
2.22.22
2.22.21
2.22.20
2.22.19
2.22.18
2.22.17
2.22.16
2.22.15
2.22.14
2.22.13
2.22.12
2.22.11
2.22.10
2.22.9
2.22.8
2.22.7
2.22.6
2.22.5
2.22.4
2.22.3
2.22.2
2.22.1
2.22.0
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.0
2.19.0
2.18.1
2.18.0
2.17.0
2.16.0
2.15.1
2.15.0
2.14.2
2.14.1
2.14.0
2.13.19
2.13.18
2.13.17
2.13.16
2.13.15
2.13.13
2.13.12
2.13.11
2.13.10
2.13.9
2.13.8
2.13.7
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.0
2.10.0
2.9.0
2.8.1
2.8.0
2.7.0
2.6.0
2.5.0
2.4.0
2.3.0
2.2.0
2.1.0
2.0.1
2.0.0
1.7.236
1.7.235
1.7.234
1.7.233
1.7.232
1.7.231
1.7.230
1.7.229
1.7.228
1.7.227
1.7.226
1.7.225
1.7.224
1.7.223
1.7.222
1.7.221
1.7.220
1.7.219
1.7.218
1.7.217
1.7.216
1.7.215
1.7.214
1.7.213
1.7.212
1.7.211
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/storage/schemas/library.ex
defmodule PhoenixKit.Modules.Storage.Library do
@moduledoc """
A storage library: a partition of the file store (V202).
Every file, media folder and folder link belongs to exactly one library.
**System** libraries are site-wide and managed by admins; everything that
existed before V202 lives in the default one, **Media**
(`PhoenixKit.Modules.Storage.Libraries.media_uuid/0`). **User** libraries
(V203) belong to a user, are `private`, and may have members
(`PhoenixKit.Modules.Storage.LibraryMember`). A trashed user library may
outlive its owner (`owner_uuid` NULL) until it is purged.
`key_prefix` is the first segment of the object key for files stored in the
library from now on. `nil` keeps the historical layout, which is what Media
uses, so nothing about an existing install's keys changes.
Go through `PhoenixKit.Modules.Storage.Libraries` rather than this schema.
"""
use Ecto.Schema
use PhoenixKit.SchemaPrefix
import Ecto.Changeset
@primary_key {:uuid, UUIDv7, autogenerate: true}
@foreign_key_type UUIDv7
@kinds ~w(system user)
@visibilities ~w(site private)
@type t :: %__MODULE__{
uuid: UUIDv7.t() | nil,
name: String.t() | nil,
kind: String.t(),
owner_uuid: UUIDv7.t() | nil,
visibility: String.t(),
key_prefix: String.t() | nil,
slug: String.t() | nil,
settings: map(),
is_default: boolean(),
trashed_at: DateTime.t() | nil,
storage_profile_uuid: UUIDv7.t() | nil,
variant_set_uuid: UUIDv7.t() | nil,
inserted_at: DateTime.t() | nil,
updated_at: DateTime.t() | nil
}
schema "phoenix_kit_storage_libraries" do
field :name, :string
field :kind, :string, default: "system"
field :visibility, :string, default: "site"
field :key_prefix, :string
# The URL name (`/admin/media/library/<slug>`); nil for the default
# library, which is the bare `/admin/media`. Kept when the library is
# renamed, so its links keep working.
field :slug, :string
field :settings, :map, default: %{}
field :is_default, :boolean, default: false
field :trashed_at, :utc_datetime
# Where the library's bytes live and which sizes its uploads get (V205).
# nil means the Default of each; set through `Storage.Profiles` and
# `Storage.VariantSets`, never cast by the changesets here.
field :storage_profile_uuid, UUIDv7
field :variant_set_uuid, UUIDv7
belongs_to :owner, PhoenixKit.Users.Auth.User,
foreign_key: :owner_uuid,
references: :uuid
timestamps(type: :utc_datetime)
end
@doc "A new system library: a name, a URL slug and an object-key prefix."
def create_system_changeset(library, attrs) do
library
|> cast(attrs, [:name, :key_prefix, :slug])
|> put_change(:kind, "system")
|> put_change(:visibility, "site")
|> validate_name()
|> validate_required([:key_prefix, :slug])
|> validate_format(:key_prefix, ~r/\A[a-z0-9][a-z0-9_-]{0,63}\z/)
|> validate_format(:slug, ~r/\A[a-z0-9]+(?:-[a-z0-9]+)*\z/)
|> validate_length(:slug, max: 64)
|> unique_constraint(:key_prefix, name: :phoenix_kit_storage_libraries_key_prefix_index)
|> unique_constraint(:slug, name: :phoenix_kit_storage_libraries_owner_slug_index)
end
@doc """
A new user library: a name, its owner, a URL slug (unique among the
owner's libraries) and an object-key prefix. Always `private`.
"""
def create_user_changeset(library, attrs) do
library
|> cast(attrs, [:name, :owner_uuid, :key_prefix, :slug, :is_default])
|> put_change(:kind, "user")
|> put_change(:visibility, "private")
|> validate_name()
|> validate_required([:owner_uuid, :key_prefix, :slug])
|> validate_format(:key_prefix, ~r/\A[a-z0-9][a-z0-9_-]{0,63}\z/)
|> validate_format(:slug, ~r/\A[a-z0-9]+(?:-[a-z0-9]+)*\z/)
|> validate_length(:slug, max: 64)
|> unique_constraint(:key_prefix, name: :phoenix_kit_storage_libraries_key_prefix_index)
|> unique_constraint(:slug, name: :phoenix_kit_storage_libraries_owner_slug_index)
|> unique_constraint(:is_default, name: :phoenix_kit_storage_libraries_default_index)
end
@doc """
The slug a name gives: lower-case letters and digits, hyphen-separated
(`"Brand Assets 2026"` → `"brand-assets-2026"`). Letters outside ASCII
are dropped with their accents where they have one; a name with nothing
left is `"library"`.
"""
@spec slugify(String.t()) :: String.t()
def slugify(name) do
name
|> String.normalize(:nfd)
|> String.replace(~r/\p{Mn}/u, "")
|> String.downcase()
|> String.replace(~r/[^a-z0-9]+/, "-")
|> String.trim("-")
|> String.slice(0, 58)
|> String.trim("-")
|> case do
"" -> "library"
slug -> slug
end
end
@doc "Renames a library. Its kind, owner and key prefix never change here."
def rename_changeset(library, attrs) do
library
|> cast(attrs, [:name])
|> validate_name()
end
defp validate_name(changeset) do
changeset
|> update_change(:name, &String.trim/1)
|> validate_required([:name])
|> validate_length(:name, min: 1, max: 255)
|> validate_inclusion(:kind, @kinds)
|> validate_inclusion(:visibility, @visibilities)
|> unique_constraint(:name,
name: :phoenix_kit_storage_libraries_owner_name_index,
message: "is already the name of another library"
)
end
end