Gives each document and each page of one its own head on the documentation page that serves it,
through the runtime:page:seo mishka_installer hook MishkaCmsWeb.RuntimeLive fires in
handle_params/3 (see MishkaCmsCore.Runtime.Seo for the map it is handed and what the head
builds from it).
A page is matched by the kind of record it says it shows (record, its extra's "record"):
"docs:document", a document's front page, or "docs:page", one page of a document — the kinds
MishkaDocument.Plugins.PageRecords names. Its path is a document's own address,
/chelekom/docs and /chelekom/docs/*section (MishkaDocument.Address), which names that
document; or anything that captures workspace, and *section for a page, whose slug names a
document with no address of its own. Every other page is handed back untouched before anything
is read. A kind has one owner, so the answer
for one of these halts the chain: no later plugin is asked.
What is read
Nothing, on a page a visitor reads: MishkaCmsWeb.RuntimeLive hands it the rows the page read
once for its head, its frame and its blocks, as the visitor (source,
MishkaDocument.PageSource): the active document whose own address the page is at, else the one
the workspace capture names, in the page's language; its published versions; the approved pages
of the one shown — the version shown is the one ?v= names, else the one marked latest, else the
newest published — and the page at the address the section capture holds, headless/switch,
with its own text and lock. A slug names only a document with no address of its own: one that
has an address is read there and nowhere else, so /docs/<its slug> names nothing. A version's
front page is at the document's own address and nowhere under it (MishkaDocument.Address), so a
section naming its slug names nothing; a group is only a heading in the contents, and nothing is
named at its slug either — unless it has a page of its own (group_page), which is named there
as any page is.
A document or a page the visitor's own read left out is answered with its lock alone, read as the system, so the address answers them 404 and nothing of it reaches them.
An editor's preview and a share card hand no rows, and the same document, versions and page are
read here, once, through :public_list as the system (MishkaDocument.PageSource.workspace/3).
What it returns
Every value is a default: what the page sets in its own SEO — a title, a description, a tag,
structured data — wins, and may name the variables below, {{doc.title}} · {{workspace.name}}.
title,description— the document's name and description on its front page, whether it draws the page its version marks as its front page there or the landing; the page's title and the start of its text on one of its pages;type—"article";tags— the document's or the page's ownseo_tags; a page that holds no robots meta of its own takes its document's, as a CMS page takes its layout's;schema— its ownschemawhen it holds one, else aTechArticle; then aBreadcrumbList, Documentation → the document → the page. TheTechArticle'sdescriptionis the description meta the record stores, else the one above; itsimageis{{page.image}}; itsauthoris thePersonthe page'sauthormeta names, else its document's, and left out when neither names one; itspublisheris the site, theOrganizationat<site>/#organization, with thesite.logosetting as itslogo. A record whose robots meta (tags) saysnoindexornonehas noTechArticle: a stored robots meta ofnoindex, followkeeps a page that is not ready yet out of an index, and out of the sitemap (MishkaDocument.Plugins.SitemapUrls);url— the canonical address: the document's own address where it has one, with the page's under it, so the head names the one address a reader and a crawler are sent to; else the address asked;vars—doc.title,doc.description,doc.url,doc.version,doc.group,doc.published_at,doc.modified_at(the front page or the page of it this address shows; a page's, like itsTechArticle'sdateModified, is when it last changed —MishkaDocument.Resources.Calculations.LastModified), andworkspace.name,workspace.description,workspace.url(the document);card— what its share card (MishkaCmsCore.Runtime.ShareCard) shows besides the title and description: "Documentation" above a document's front page, the document's name above one of its pages —Guide · Navigationunder a group — and the version, on the front page the one a reader lands on;var_docs— those variables in the current locale, whether or not this request filled them. Called with noparams— an editor asking which variables a page has — that is all it changes.
permissions is not a default: the document's on its front page; on one of its pages, the page's
own, else its nearest parent page's, else its document's
(MishkaDocument.Resources.Calculations.EffectivePermissions). MishkaCmsWeb.RuntimeLive opens
the address only for whoever holds one of them, after the page's own, and the head keeps it out
of an index. That is why a record the visitor's read left out has its lock read as the system: a
read as the visitor leaves a locked record out for exactly the visitor the page has to refuse.
What plugins add to it
The answer is handed to the document:page:seo mishka_installer event (event/0) through
MishkaInstaller.Event.Hook.call/3, and what it replies is the answer; a plugin may give the page
a share card of its own — image with card: false — or change any other default. A plugin that
opts in with call/2 is handed, as meta:
%{
site_id: "…",
document: %{
name: "Guide",
slug: "guide",
path: "/guide/docs",
language: "en",
description: "How to use it.",
url: "https://example.com/guide/docs"
},
version: "2.0.0",
page: %{
slug: "breadcrumb",
title: "Breadcrumb",
description: "The start of its text.",
path: "navigation/breadcrumb",
parents: [%{slug: "navigation", title: "Navigation", group: true}]
},
url: "https://example.com/guide/docs/breadcrumb",
contents: [
%{title: "Breadcrumb", slug: "breadcrumb", group: false, current: true},
%{title: "Menu", slug: "menu", group: false, current: false}
]
}The answer keeps redraw as core handed it (MishkaCmsCore.Runtime.Seo.given/2): true when a
forced look at the share card asks for the head (MishkaCmsCore.Runtime.ShareCard.specs/3). A
plugin that draws a card of its own draws it again over its old file then.
page is nil on a document's front page, and version when it has no published version. A
description is the description meta the document or the page stores, else the one the head
is given here.
parents are the pages and groups the page is filed under, nearest last. contents are up to
seven entries of the page's level of the contents, in their order, the page among them marked
current; on a front page, the document's top level, its version's front page marked. A plugin
replies with the answer:
use MishkaInstaller.Event.Hook, event: "document:page:seo"
def call(seo, %{page: %{title: title}}), do: {:reply, %{seo | title: title <> " · Docs"}}
def call(seo, _meta), do: {:reply, seo}A capture that names nothing a reader may open changes nothing else but card, false: the page
answers 404 as it does without this, and has no share card to draw. A note is drawn inside its
page and has no address of its own, so its own SEO is not read here.
A module because a mishka_installer hook is one. Registered in MishkaDocument.Application.
Summary
Functions
Returns a specification to start this module under a supervisor.
Callback implementation for MishkaInstaller.Event.Hook.enable/0.
The mishka_installer event a documentation page's answer is handed to: "document:page:seo".
Callback implementation for MishkaInstaller.Event.Hook.get/0.
Optional. See MishkaInstaller.Event.Hook.health_check/0. Override to report health.
Optional. See MishkaInstaller.Event.Hook.on_dependency_error/1. Override to react.
Callback implementation for MishkaInstaller.Event.Hook.register/0.
Callback implementation for MishkaInstaller.Event.Hook.restart/0.
Callback implementation for MishkaInstaller.Event.Hook.start/0.
Callback implementation for MishkaInstaller.Event.Hook.stop/0.
Callback implementation for MishkaInstaller.Event.Hook.unregister/0.
Types
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec config() :: keyword()
@spec enable() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.enable/0.
@spec event() :: String.t()
The mishka_installer event a documentation page's answer is handed to: "document:page:seo".
@spec get() :: keyword()
Callback implementation for MishkaInstaller.Event.Hook.get/0.
Optional. See MishkaInstaller.Event.Hook.health_check/0. Override to report health.
Optional. See MishkaInstaller.Event.Hook.on_dependency_error/1. Override to react.
@spec register() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.register/0.
@spec restart() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.restart/0.
@spec start() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.start/0.
@spec stop() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.stop/0.
@spec unregister() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.unregister/0.