The buttons plugins add to a document's admin screen — the Sections screen of a workspace,
/admin/dashboard/document/sections/:workspace_version_id — through the
document:workspace:admin_actions mishka_installer event. Each button opens the plugin's own
LiveComponent in the screen's modal, one at a time.
The event
The screen fires it through actions/2, which calls MishkaInstaller.Event.Hook.call/3, once it
has loaded its document and again when another version is chosen — only for a reader who may
manage that workspace (admin:dashboard:document:workspace); anyone else is shown no button and
no plugin is asked. Each plugin is handed:
%{
site_id: "…",
workspace: %{id: "…", slug: "chelekom", path: "/chelekom/docs", name: "Chelekom"},
version: %{id: "…", version_number: "0.0.9"},
actor: %MishkaCmsCore.Accounts.User{},
actions: []
}version is nil for a workspace with no version, and path is nil for a document read at
its default address. The plugins are asked in the screen's process, so a plugin answers from what
it has at hand and leaves slow work to its component. A plugin appends its actions to actions
and replies with the map:
use MishkaInstaller.Event.Hook, event: "document:workspace:admin_actions"
def call(%{workspace: %{slug: "handbook"}, actions: actions} = state),
do: {:reply, %{state | actions: actions ++ [action()]}}
def call(state), do: {:reply, state}An action
%{
id: "handbook-export",
label: gettext("Export"),
icon: "hero-arrow-down-tray",
component: MyPlugin.ExportPanel,
assigns: %{format: :pdf}
}:id— lower-case letters, digits,-and_, starting with a letter or a digit; the first action with an id is kept.:label— the button's words, already in the reader's language.:icon— a Heroicon class name (hero-…), ornil.:component— aPhoenix.LiveComponentmodule.:assigns— a map with atom keys, handed to the component.
An action of any other shape is left out. An event that cannot run shows no button.
The component
Rendered inside the modal, which the screen opens only for a reader who may manage the workspace,
with the action's :assigns and these, which it cannot override:
:id (component_id/1), :site_id, :workspace, :version and :current_user. While its
modal is open, each message the screen does not handle itself is handed to it as
update(%{info: message}, socket): a component that subscribes to a PubSub topic hears it
there. Closing the modal removes it.
Summary
Types
One button on the screen and the component its modal draws.
What the screen tells a plugin, and its component, about the document on screen.
Functions
The actions the plugins add for context (context/2) and actor, in the order the plugins
answered; [] when the event cannot run.
The assigns action's component is rendered with, besides its module and component_id/1: the
action's own, then the screen's context and current_user over them.
The id the screen renders action's component under, and sends its updates to.
What the screen tells a plugin about workspace and its version (nil for none): its site,
and the fields of each named in the moduledoc.
The event's name.
Types
@type action() :: %{ id: String.t(), label: String.t(), icon: String.t() | nil, component: module(), assigns: map() }
One button on the screen and the component its modal draws.
@type context() :: %{ site_id: String.t() | nil, workspace: %{ id: String.t(), slug: String.t() | nil, path: String.t() | nil, name: String.t() | nil }, version: %{id: String.t(), version_number: String.t() | nil} | nil }
What the screen tells a plugin, and its component, about the document on screen.
Functions
The actions the plugins add for context (context/2) and actor, in the order the plugins
answered; [] when the event cannot run.
The assigns action's component is rendered with, besides its module and component_id/1: the
action's own, then the screen's context and current_user over them.
The id the screen renders action's component under, and sends its updates to.
What the screen tells a plugin about workspace and its version (nil for none): its site,
and the fields of each named in the moduledoc.
@spec event() :: String.t()
The event's name.