PhoenixKitStaff.Attachments (PhoenixKitStaff v0.6.0)

Copy Markdown View Source

Folder-scoped media attachments for a staff person, backed by core PhoenixKit.Modules.Storage (the same per-resource-folder convention phoenix_kit_catalogue/phoenix_kit_locations use — no module-owned table, no migration).

Each person owns a deterministic root folder staff-person-<uuid> for generic files, with a nested Images subfolder (parent_uuid = the root) for images — i.e. all of a person's files in one folder, images in a folder inside it. Folders are resolved by name on every read (never cached on Person, so an admin renaming/deleting the folder in /admin/media can't strand a dangling uuid) and created lazily on first upload. The [:name, :parent_uuid] unique index in core makes find-or-create race-safe.

Files live in core phoenix_kit_files under the folder; uploading/browsing is done by MediaSelectorModal (scoped to the folder), so this module only resolves folders, lists their files, (un)links picked files, and removes them — mirroring PhoenixKitCatalogue.Attachments' write semantics (soft-trash a sole-owner file, unlink a shared one). It never hard-deletes a possibly-shared asset.

Summary

Functions

Ensures file_uuid is attached to folder_uuid: a no-op if already home there (the modal's scoped uploads land here directly); adopts an orphan file as home; otherwise adds a FolderLink so a file picked from elsewhere appears here without being moved from its owner.

The person's avatar File struct, or nil if unset / missing / trashed.

Thumbnail URL for the person's avatar (or nil).

The person's avatar file uuid (from metadata), or nil.

Clears the person's avatar pointer.

Removes a file from folder_uuid. If the file's home is this folder and it is not linked elsewhere → soft-trash it (recoverable in the media trash). If it is also linked elsewhere → promote a link to home (keeps it alive). If it is here only via a FolderLink → just drop the link. Mirrors core's non-destructive convention; never hard-deletes a shared asset.

Public download URL for a file (nil-safe).

Find-or-create the folder for kind, returning {:ok, uuid} or {:error, reason}. Race-safe: a lost create (unique [:name, :parent_uuid]) re-resolves the winner. Call when an action needs the folder to exist (opening the picker / handling a selection).

Heroicon name for a file based on its Storage type / mime.

Resolves the folder uuid for kind (:files → root, :images → the nested Images subfolder) without creating it. Returns the uuid or nil (used on render so viewing a tab doesn't spawn empty folders).

Human-readable byte count. Nil-safe.

Whether the file with this uuid is an image (by Storage file_type).

Files attached to folder_uuid (home-folder files plus those linked in via FolderLink), newest first, excluding trashed. :only narrows by type: :images (file_type == "image"), :non_images (everything else), or :all (default). Defensive — keeps a tab showing only its own kind even if a stray file of the other kind landed in the folder.

Permanently purges a person's media — deletes the root folder and its whole subtree (the nested Images folder + every file, including bucket copies) via core's cascading delete_folder_completely/1. Best-effort: logs and returns :ok on any failure so it never blocks a person deletion. Call only on a permanent delete (soft-trash keeps the files).

Deterministic root folder name for a person's files.

Points the person's avatar at file_uuid (server-owned metadata write). Refuses a trashed person ({:error, :person_trashed}) — a removed person shouldn't gain a new profile photo; clearing the avatar stays unguarded.

Thumbnail URL for an image file, falling back to the original (nil-safe).

Functions

attach(file_uuid, folder_uuid)

@spec attach(binary(), binary()) :: :ok

Ensures file_uuid is attached to folder_uuid: a no-op if already home there (the modal's scoped uploads land here directly); adopts an orphan file as home; otherwise adds a FolderLink so a file picked from elsewhere appears here without being moved from its owner.

avatar_file(person)

The person's avatar File struct, or nil if unset / missing / trashed.

avatar_url(person)

@spec avatar_url(PhoenixKitStaff.Schemas.Person.t()) :: String.t() | nil

Thumbnail URL for the person's avatar (or nil).

avatar_uuid(arg1)

@spec avatar_uuid(PhoenixKitStaff.Schemas.Person.t()) :: binary() | nil

The person's avatar file uuid (from metadata), or nil.

clear_avatar(person)

@spec clear_avatar(PhoenixKitStaff.Schemas.Person.t()) ::
  {:ok, PhoenixKitStaff.Schemas.Person.t()} | {:error, term()}

Clears the person's avatar pointer.

detach(file_uuid, folder_uuid)

@spec detach(binary(), binary() | nil) :: :ok | {:error, term()}

Removes a file from folder_uuid. If the file's home is this folder and it is not linked elsewhere → soft-trash it (recoverable in the media trash). If it is also linked elsewhere → promote a link to home (keeps it alive). If it is here only via a FolderLink → just drop the link. Mirrors core's non-destructive convention; never hard-deletes a shared asset.

download_url(file)

@spec download_url(map()) :: String.t() | nil

Public download URL for a file (nil-safe).

ensure_folder(person_uuid, atom, actor_uuid)

@spec ensure_folder(binary(), :files | :images, binary() | nil) ::
  {:ok, binary()} | {:error, term()}

Find-or-create the folder for kind, returning {:ok, uuid} or {:error, reason}. Race-safe: a lost create (unique [:name, :parent_uuid]) re-resolves the winner. Call when an action needs the folder to exist (opening the picker / handling a selection).

file_icon(arg1)

@spec file_icon(map()) :: String.t()

Heroicon name for a file based on its Storage type / mime.

folder_uuid(person_uuid, atom)

@spec folder_uuid(binary(), :files | :images) :: binary() | nil

Resolves the folder uuid for kind (:files → root, :images → the nested Images subfolder) without creating it. Returns the uuid or nil (used on render so viewing a tab doesn't spawn empty folders).

format_file_size(bytes)

@spec format_file_size(integer() | nil) :: String.t()

Human-readable byte count. Nil-safe.

image?(file_uuid)

@spec image?(binary()) :: boolean()

Whether the file with this uuid is an image (by Storage file_type).

list_files(folder_uuid, opts)

@spec list_files(
  binary() | nil,
  keyword()
) :: [PhoenixKit.Modules.Storage.File.t()]

Files attached to folder_uuid (home-folder files plus those linked in via FolderLink), newest first, excluding trashed. :only narrows by type: :images (file_type == "image"), :non_images (everything else), or :all (default). Defensive — keeps a tab showing only its own kind even if a stray file of the other kind landed in the folder.

purge_person_media(person_uuid)

@spec purge_person_media(binary()) :: :ok

Permanently purges a person's media — deletes the root folder and its whole subtree (the nested Images folder + every file, including bucket copies) via core's cascading delete_folder_completely/1. Best-effort: logs and returns :ok on any failure so it never blocks a person deletion. Call only on a permanent delete (soft-trash keeps the files).

root_folder_name(person_uuid)

@spec root_folder_name(binary()) :: binary()

Deterministic root folder name for a person's files.

set_avatar(person, file_uuid)

@spec set_avatar(PhoenixKitStaff.Schemas.Person.t(), binary()) ::
  {:ok, PhoenixKitStaff.Schemas.Person.t()} | {:error, term()}

Points the person's avatar at file_uuid (server-owned metadata write). Refuses a trashed person ({:error, :person_trashed}) — a removed person shouldn't gain a new profile photo; clearing the avatar stays unguarded.

thumb_url(file)

@spec thumb_url(map()) :: String.t() | nil

Thumbnail URL for an image file, falling back to the original (nil-safe).