MishkaCmsCoreWeb. Live. Admin. Components. ExtensionInstallerPanel
(Mishka CMS Core v0.0.1)
Copy Markdown
View Source
Setting an extension up on a site: the form, the report it leaves on screen afterwards, and the three places a dashboard puts them.
Written once here because it was written twice: the blog's card and the document's differed in a noun, two flash sentences and a DOM-id prefix, and each carried its own copy of picking a site, refusing a picker with no permission, and wording a run that changed nothing.
Wiring
One call in mount/3, two event clauses, one tag:
socket = ExtensionInstallerPanel.setup(socket, app: :mishka_blog, noun: "blog")
def handle_event("extension_install", params, socket),
do: {:noreply, ExtensionInstallerPanel.install(socket, chosen_site(socket, params), socket.assigns.may_provision?)}
def handle_event("extension_site", params, socket),
do: {:noreply, ExtensionInstallerPanel.choose(socket, chosen_site(socket, params))}
<ExtensionInstallerPanel.first_run_screen :if={@showing_installer?} setup={@extension_install} … />
<ExtensionInstallerPanel.doctor_button setup={@extension_install} id="blog-kit-upgrade" … />
<ExtensionInstallerPanel.doctor_modal setup={@extension_install} kit={@kit_upgrade} … />A dashboard draws the card itself nowhere. Until the site has been set up it IS the screen, under
first_run_screen/1; afterwards it is the Repair half of doctor_modal/1, which the header icon
opens. A set-up site's dashboard is the extension's own work and nothing else.
Which site it runs against is the dashboard's answer, not this module's — a tenant admin has one
and a master picks from the ones their page offered — which is the same question
MishkaCmsCoreWeb.Live.Admin.Components.KitUpgradePanel already takes that way.
The report is the point
An admin pressed a button and is owed the answer, not a flash that the next click clears: what arrived, what was written under another name because the one it wanted was taken, and what was left alone because something of theirs holds it. A page whose PATH is held is the one that needed saying — it was not written, no later run will write it, and only the admin can decide what to do about it.
It never writes the second time by itself
Pressing it again is safe and is meant to be: the run adds only what is missing, so an admin who fixed a clash can press it again and get the page they were missing without losing anything they have edited.
Summary
Functions
The mark in a dashboard's header saying this extension has not been set up on this site, and the way back to the installer once the admin has skipped past it.
Remembers which site the card is about, and clears a report that was about another one.
The skipped pages somebody ELSE holds — the ones no later run will write, and the only ones an admin has to answer for.
Puts the dashboard back, for an admin who came to look rather than to install.
The icon beside a dashboard's Settings button that opens doctor_modal/1.
Everything an admin does to an extension on a site that already has it, behind one icon.
Whether this site has never been set up with this extension — which is what makes the menu open on the installer instead of the dashboard.
The whole screen an extension shows instead of its dashboard, until it has been set up here.
Runs the install for site_id and leaves the report on the card.
What one run did, worded from what happened rather than from what was asked for.
Opens the installer over the dashboard — what the badge does.
One line per page written under a name of its own, for the card to list.
Puts the card's state on the socket.
Whether this dashboard should draw the installer instead of itself.
One line per page left alone, saying which of the two kinds of skip it was.
The site the card starts on, before anybody has touched the picker.
Functions
The mark in a dashboard's header saying this extension has not been set up on this site, and the way back to the installer once the admin has skipped past it.
Drawn only when there is something to do about it: an extension already installed here needs no badge, and an admin who may not install one is not offered a button that would refuse them.
Attributes
setup(:map) (required)id(:string) (required)allowed?(:boolean) - Defaults tofalse.
@spec choose(Phoenix.LiveView.Socket.t(), String.t() | :none | :refused) :: Phoenix.LiveView.Socket.t()
Remembers which site the card is about, and clears a report that was about another one.
The skipped pages somebody ELSE holds — the ones no later run will write, and the only ones an admin has to answer for.
Not answered?/1: that asks whether the site serves something at the address, which is true of
our own page on a second run and of a stranger's page on the first. The difference is the mark a
provisioned page carries — see MishkaCmsCore.Runtime.PageProvision.stamp/3 — and it is why that
mark exists at all.
@spec close(Phoenix.LiveView.Socket.t()) :: Phoenix.LiveView.Socket.t()
Puts the dashboard back, for an admin who came to look rather than to install.
The icon beside a dashboard's Settings button that opens doctor_modal/1.
Never drawn with badge/1 and never instead of it: a site that has not been set up has nothing
to compare and is offered the installer, and a site that has been set up has nothing to be warned
about and is offered the check. Which of the two a header carries is which of the two is true.
Attributes
setup(:map) (required)id(:string) (required)allowed?(:boolean) - Defaults tofalse.
Everything an admin does to an extension on a site that already has it, behind one icon.
A dashboard is for the extension's own work, and a set-up site needs none of this until the day it does — so it is a modal rather than two cards under the tables, which is what this was: one card to install on a site that already had it, and another to compare, both drawn for ever on a dashboard where both were nearly always nothing to do.
One site picker for the whole modal. Choosing in it is itself the check — see
MishkaCmsCoreWeb.Live.Admin.Components.KitUpgradePanel — and the dashboard's kit_check clause
hands the same site to choose/2, so the Repair below writes to the site whose standings are on
screen rather than to whichever one the page opened on.
When the extension answers MishkaCmsCore.Runtime.ShareCard.targets_event/0 under its app's
name, the modal ends with a button that draws its own share cards on that site again
(MishkaCmsCoreWeb.Live.Admin.Components.ShareCardRedraw).
Attributes
id(:string) (required)install_id(:string) (required)setup(:map) (required)kit(:map) (required)sites(:list) - Defaults to[].site_name(:string) - Defaults tonil.allowed?(:boolean) - Defaults tofalse.title(:string) (required)
@spec first_run?(Phoenix.LiveView.Socket.t()) :: boolean()
Whether this site has never been set up with this extension — which is what makes the menu open on the installer instead of the dashboard.
Read from the site's own settings, so it survives a restart and answers per site: the same admin may run a set-up blog on one and meet the installer on another.
A site that HAS been set up answers false for ever after, and the card is still there to press —
pressing it again adds only what is missing, which is how an admin repairs a site that lost a page.
The whole screen an extension shows instead of its dashboard, until it has been set up here.
A dashboard for an extension a site has never installed is a page of zeroes over pages that do not exist, and nothing on it says why or what to do. This says both, and the thing to press is the only thing on it.
Not now is a real answer: the dashboard opens, empty, and the badge in its header brings this
back. Nothing is written by saying no — the site simply stays as it is.
Attributes
setup(:map) (required)id(:string) (required)sites(:list) - Defaults to[].allowed?(:boolean) - Defaults tofalse.title(:string) (required)blurb(:string) (required)
@spec install( Phoenix.LiveView.Socket.t(), String.t() | :none | :refused, boolean(), map() ) :: Phoenix.LiveView.Socket.t()
Runs the install for site_id and leaves the report on the card.
allowed? is the permission the dashboard already computed. Every refusal is worded here, so
every extension says the same thing.
What one run did, worded from what happened rather than from what was asked for.
A run that finds everything already there is a success with nothing to add, not a failure.
@spec open(Phoenix.LiveView.Socket.t()) :: Phoenix.LiveView.Socket.t()
Opens the installer over the dashboard — what the badge does.
One line per page written under a name of its own, for the card to list.
@spec setup(Phoenix.LiveView.Socket.t(), keyword()) :: Phoenix.LiveView.Socket.t()
Puts the card's state on the socket.
:app— the extension's OTP app, which is the key its plugin answers to.:noun— what the card calls this extension in a sentence: "blog", "documentation".
@spec showing_installer?(Phoenix.LiveView.Socket.t()) :: boolean()
Whether this dashboard should draw the installer instead of itself.
The first visit to an extension nobody has set up on this site, or any visit where the admin asked for it from the badge. Pinned once either is true, so a render caused by something else does not change what the admin is looking at.
One line per page left alone, saying which of the two kinds of skip it was.
A page whose own row is already there is the ordinary case of a run repeated. A page whose NAME or PATH a DIFFERENT page holds was never written and no later run will write it — and those are the ones the admin has to decide about.
@spec start_on(Phoenix.LiveView.Socket.t(), String.t() | nil) :: Phoenix.LiveView.Socket.t()
The site the card starts on, before anybody has touched the picker.
Without it first_run?/1 has no site on the first render and every dashboard would look set up.