PhoenixKitPhotos.Bulk (PhoenixKit Photos v0.3.0)

Copy Markdown View Source

What can be done to many files at once. A person gets here through the timeline's selection and an agent through the API tools (PhoenixKitPhotos.API); both take the same two steps, so an agent cannot do what a person's screen would refuse.

plan/4 reads and writes nothing: it checks every file for the person acting and says which can go ahead and which cannot, and why. run/2 does what a plan says. Nothing here trusts a client's list: a file the person may not read counts as one that does not exist, and a plan's files are read from the database, never taken from the request.

ActionNeedsDoes
:downloadreadnothing here: PhoenixKitPhotos.Web.Download streams the plan
:linksreadnothing here: the page lists the addresses
:copyread, and upload in the target librarya new file in the target library
:moveedit, and upload in the targetcopy, then the original to the trash
:tageditadds tags to metadata["tags"], or removes them
:trasheditStorage.trash_file/1, which :restore undoes
:restoreeditStorage.restore_file/1

Nothing here deletes for good: a trashed file comes back with :restore until the site's trash retention empties it. A move leaves the original in the trash, and the new file has all it had (date, place, tags, title and description), since after the retention it is the only one left. An image that has been edited stays where it is: its unedited original is not something a new library could hold. Copying someone else's file brings only what core gives a stranger (Storage.link_copy/3), so not their tags, their place or a date they set by hand.

Files here are the timeline's: photos and videos. :trash and :move ask the host's removal guard (config :phoenix_kit, :file_removal_guard), and anything it says is in use stays.

Summary

Functions

The action named by a client's string, or nil.

A hash of the values a plan would touch, for a confirmation that must fail when they changed: the same for the same data, whatever order the ids came in, and untouched by what the action does not depend on (a new tag does not void a confirmed trash).

The most files one action takes.

Checks ids for action by auth.

What to tell a person about a file that could not be included.

Does what the plan says. Each file stands alone: one that fails is reported and the rest go on. :on_progress is called with how many are done and how many there are.

Tags as a list: from a list or from text with commas, trimmed, without doubles, at most 20 of at most 60 characters.

Types

action()

@type action() :: :download | :links | :copy | :move | :tag | :trash | :restore

plan()

@type plan() :: %{
  action: action(),
  auth: PhoenixKit.Users.Auth.Scope.t() | nil,
  files: [PhoenixKit.Modules.Storage.File.t()],
  refused: [refusal()],
  target: PhoenixKit.Modules.Storage.Library.t() | nil,
  add: [String.t()],
  remove: [String.t()]
}

reason()

@type reason() ::
  :not_found
  | :not_allowed
  | :same_library
  | :already_there
  | :not_trashed
  | :trashed
  | :no_target
  | :edited
  | :in_use

refusal()

@type refusal() :: %{id: String.t(), name: String.t() | nil, reason: reason()}

Functions

action(name)

@spec action(term()) :: action() | nil

The action named by a client's string, or nil.

fingerprint(plan)

@spec fingerprint(plan()) :: String.t()

A hash of the values a plan would touch, for a confirmation that must fail when they changed: the same for the same data, whatever order the ids came in, and untouched by what the action does not depend on (a new tag does not void a confirmed trash).

limit()

@spec limit() :: pos_integer()

The most files one action takes.

plan(action, ids, auth, params \\ %{})

@spec plan(action(), [String.t()], PhoenixKit.Users.Auth.Scope.t() | nil, map()) ::
  plan()

Checks ids for action by auth.

params holds what the action needs: "library" (a uuid) for :copy and :move, "add" and "remove" (lists of tags, or text with commas) for :tag.

reason_text(arg1)

@spec reason_text(atom()) :: String.t()

What to tell a person about a file that could not be included.

run(plan, opts \\ [])

@spec run(plan(), keyword()) :: %{
  action: action(),
  done: [String.t()],
  failed: [refusal()],
  created: [String.t()]
}

Does what the plan says. Each file stands alone: one that fails is reported and the rest go on. :on_progress is called with how many are done and how many there are.

tags(value)

@spec tags(term()) :: [String.t()]

Tags as a list: from a list or from text with commas, trimmed, without doubles, at most 20 of at most 60 characters.