PhoenixKit.Modules.Storage.ImageEditing (phoenix_kit v2.29.1)

Copy Markdown View Source

Editing uploaded images after the fact — crop, rotate, flip, straighten, redact, brightness and contrast (PhoenixKit.Modules.Storage.ImageEdit).

The model

An edited image keeps its uuid: every module that stored the uuid keeps showing it, now edited. Its "original" instance becomes the edited bytes, so every reader of the original — variants, tiles, downloads, public URLs — gets the edit without knowing about it. The unedited original is not destroyed: its instance rows move to a hidden, system-managed child file (backup/1) that is never listed and never served by the public file routes. The owner reaches it through the editor (download, restore, delete) and GET /api/files/:uuid/unedited.

The edit is data (file.edits) and is always applied to the unedited original, so it can be changed or reverted at any time — until the owner deletes the unedited original, which bakes the edit in. With the media setting storage_image_edit_mode set to "replace_original", every save bakes immediately.

While an edit renders

Saving bumps edit_revision and sets edit_state to "pending"; PhoenixKit.Modules.Storage.ApplyImageEditJob renders and swaps. Until it finishes — and if it fails — every variant of the file is served as a neutral placeholder: a half-applied redaction must never show the bytes it hides.

Who may edit

Every function takes opts with :scope (a PhoenixKit.Users.Auth.Scope): the file's owner, an Owner/Admin, or a holder of the "media" permission (the admin media library's) may edit. Trusted internal callers pass system: true instead. Anything else is {:error, :forbidden}.

Summary

Functions

An edited file's hidden unedited backup, or nil.

Whether file is some edited image's hidden backup. Such a file is never served by the public file routes.

The file_name of an edited image's hidden backup.

Whether scope may edit file (see the moduledoc). Also accepts the opts keyword the other functions take.

Deletes the unedited original of an edited file, baking the current edit in. It cannot be undone. The file keeps serving its edited bytes.

Saves an edit of file and queues its rendering. params is anything ImageEdit.normalize/1 accepts; an edit equal to the current one changes nothing.

Whether an edit of file is rendering (or failed); it is served as a placeholder.

Whether file can be edited at all: an active, user-owned still image in a format ImageMagick can write back.

Whether images of mime_type can be edited (ImageMagick writes them back).

Whether file has an unedited backup (its edit can be changed or reverted).

What saving an edit does: "keep_original" (default) keeps the unedited original as a hidden backup; "replace_original" bakes the edit in.

The media setting key for mode/0.

The values mode/0 accepts.

Queues the rendering again for an edit that failed (or seems stuck).

Reverts file to its unedited original and queues the swap back. Nothing to revert is {:error, :not_edited}; an annotated file whose edit moved pixels is {:error, {:annotated, count}}, as for edit/3.

Renders params as a new file (a copy in the same folder, owned by the file's owner, with edited_from_uuid pointing back). The copy is created by a job; {:ok, job} means it was queued. Annotations do not matter — the copy has none. An edit that changes nothing is {:error, :no_edit}.

The instance an edit is applied to: the backup's original when the file has been edited, else the file's own original.

Functions

backup(file)

An edited file's hidden unedited backup, or nil.

backup?(arg1)

Whether file is some edited image's hidden backup. Such a file is never served by the public file routes.

backup_name()

The file_name of an edited image's hidden backup.

can_edit?(file, opts)

Whether scope may edit file (see the moduledoc). Also accepts the opts keyword the other functions take.

delete_unedited_original(file, opts \\ [])

@spec delete_unedited_original(PhoenixKit.Modules.Storage.File.t(), keyword()) ::
  {:ok, PhoenixKit.Modules.Storage.File.t()} | {:error, term()}

Deletes the unedited original of an edited file, baking the current edit in. It cannot be undone. The file keeps serving its edited bytes.

edit(file, params, opts \\ [])

Saves an edit of file and queues its rendering. params is anything ImageEdit.normalize/1 accepts; an edit equal to the current one changes nothing.

Returns {:ok, file} (now pending), or {:error, reason}:

  • :forbidden, :not_editable, :not_found (deleted meanwhile)
  • :not_queued — saved, but the rendering job could not be queued; the file stays a placeholder until retry/2 succeeds
  • {:invalid_edit, reason} from ImageEdit.normalize/1
  • {:annotated, count} — the file has annotations, and the edit changes where pixels are (crop, turn, mirror, straighten differ from the current edit's): they would no longer line up. Redaction and brightness/contrast are allowed; "save as copy" always is.

edit_in_progress?(arg1)

Whether an edit of file is rendering (or failed); it is served as a placeholder.

editable?(file)

@spec editable?(PhoenixKit.Modules.Storage.File.t() | nil) :: boolean()

Whether file can be edited at all: an active, user-owned still image in a format ImageMagick can write back.

editable_mime?(mime_type)

@spec editable_mime?(term()) :: boolean()

Whether images of mime_type can be edited (ImageMagick writes them back).

edited?(arg1)

Whether file has an unedited backup (its edit can be changed or reverted).

mode()

@spec mode() :: String.t()

What saving an edit does: "keep_original" (default) keeps the unedited original as a hidden backup; "replace_original" bakes the edit in.

mode_setting()

The media setting key for mode/0.

modes()

The values mode/0 accepts.

retry(file, opts \\ [])

Queues the rendering again for an edit that failed (or seems stuck).

revert(file, opts \\ [])

Reverts file to its unedited original and queues the swap back. Nothing to revert is {:error, :not_edited}; an annotated file whose edit moved pixels is {:error, {:annotated, count}}, as for edit/3.

save_copy(file, params, opts \\ [])

@spec save_copy(PhoenixKit.Modules.Storage.File.t(), map(), keyword()) ::
  {:ok, Oban.Job.t()} | {:error, term()}

Renders params as a new file (a copy in the same folder, owned by the file's owner, with edited_from_uuid pointing back). The copy is created by a job; {:ok, job} means it was queued. Annotations do not matter — the copy has none. An edit that changes nothing is {:error, :no_edit}.

source_instance(file)

The instance an edit is applied to: the backup's original when the file has been edited, else the file's own original.