MishkaCmsCore.Runtime.Resources.Validations.PreventMasterLanguageDeletion (Mishka CMS Core v0.0.1)

Copy Markdown View Source

Refuses to destroy the original edition of a translated record while translations of it are live.

A translated record is several rows sharing a language_group_id, and the group's key is the original's own id — so the original is the row where id == language_group_id, or where there is no group at all, and every other row in the group points at it. Destroying the original first would leave those pointing at nothing, so this says no until they are gone. A translation itself is free to go at any time.

It queries changeset.resource, so the same validation guards every resource that carries the pattern — MishkaCmsCore.Runtime.Page, MishkaBlog.Collection, MishkaBlog.Tag, MishkaBlog.BlogPost, MishkaDocument.Workspace, and anything added later.

Put it on every destroy action, the archiving ones and the permanent ones:

destroy :destroy do
  validate MishkaCmsCore.Runtime.Resources.Validations.PreventMasterLanguageDeletion
end

destroy :permanent_destroy do
  validate MishkaCmsCore.Runtime.Resources.Validations.PreventMasterLanguageDeletion
end

Both kinds wait for the same thing, a translation that is still live. An archived one is in nobody's way: MishkaCmsCore.Runtime.Resources.Changes.PreventOrphanedTranslationRestore keeps it from coming back under an original that is not there. To remove a whole group, destroy the translations first, then the original.

It holds for every caller, authorize?: false included, and a check that cannot be answered refuses too.

Plugin event

Each refusal for live translations fires runtime:language:delete_refused with %{resource, id, site_id, language, translations, message}: the original, its language, the languages of its live translations, and the message the refusal carries.

The refusal

An Ash.Error.Changes.InvalidAttribute on :language_group_id whose message is message/2: the original's language, the languages still live, and what to do. The API returns that message as it is.

The message is written in the Gettext locale of the process the action runs in, errors domain, since the language names inside it are translated too. A request with no locale reads it in English. Its private_vars hold live_translations_of: id, the original's id, which is how MishkaCmsCore.LanguageEditions.refused_originals/1 finds it among an action's errors.

Summary

Functions

What refusing an original says: its own language, the languages of its live translations, and what to do.

Functions

message(language, translations)

@spec message(String.t() | atom() | nil, [String.t() | atom() | nil]) :: String.t()

What refusing an original says: its own language, the languages of its live translations, and what to do.

message("en", ["fr", "fa"])
#=> "This is the original English version. Its translations in French and Persian are still
#    live, so it can't be deleted yet. Archive or delete those translations first, then this one."