MishkaDocument.PageSource (Mishka Document v0.0.1)

Copy Markdown View Source

The rows a documentation page is made of: the document its address names, its published versions, the version a reader is shown and that version's approved pages, the page the address names with its text, and that page's notes.

Read once, for everything that draws them

A document's front page and each of its pages are drawn by the head (MishkaDocument.Plugins.PageSeo), the frame (MishkaDocument.Plugins.PageFrame) and the page's own blocks, doc-sidebar, doc-section, doc-toc and the bar's doc-switcher. Each of them read the same document, the same versions and the same pages on its own. read/1 reads them once, in handle_params/3, through the runtime:page:source event (MishkaDocument.Plugins.PageSource, MishkaCmsCore.Runtime.PageSource), and every one of them is handed what it read: the two plugins as source, the blocks as page_source.

Three reads, each bringing what is joined to it: the document, with its versions and its other editions and theirs (relationships loaded in the same read); the pages of all of those versions the page's readers look in, in one read; and the page the address names, with its text and its notes.

A request is the map core hands any of the three plugins: the site_id, the page and its path, the params (the path's captures and the query), the language of the response, the current_user and, on the event, held.

As the visitor

Every row is read as the visitor, current_user, through the actions and policies the blocks read with (MishkaDocument.Resources.Preparations.OpenToActor): :public_list of the document, of its versions, of the pages of the versions shown, and of the page and its notes. What the visitor may not open is not among them: a locked document, a locked page or one under a locked page, a page whose own page on the site is closed to them, a draft, a version that is not published.

THE SYSTEM READS ONE ROW, AND ONLY WHEN THE VISITOR'S READ FOUND NO DOCUMENT: the document and its versions, as the blocks have always read them (open? says which read found it). Two things need it, and neither shows the visitor anything their own lock does not let them see:

  • the lock. A document the visitor may not open is no row at all when read as them, and the head would hand MishkaCmsWeb.RuntimeLive no permissions to refuse them with. The head takes the lock alone from it, and the address answers 404;
  • a page whose own lock opens for the visitor inside a document whose lock does not. Its pages are still read as the visitor, so only a page they may open is found, and the document is drawn around that page as the blocks draw it.

Keyed to its visitor, and to what it was read for

A source names the site, the visitor (MishkaCmsCore.Runtime.PageSource.visitor_id/1), the language and the document it was read for. A block takes nothing from one read for another site or another visitor, and takes a row only for exactly the read it would make itself: the same document named the same way, the versions of that document, the pages of that version, that page's text and notes. Anything else it reads for itself, so a block an admin pinned to another document, or placed on a page of their own, reads as it always did.

What a block reads of it

A block's code calls nothing of this extension's (MishkaDocument.KitTest), so it reads a source by its shape, which is therefore part of what the kit relies on:

  • rows_of — "mishka_document", which a block asks before anything else;
  • site_id, visitor, language — whom and for what it was read;
  • tried — the documents the address named, {:path, address} or {:slug, slug}, in the order they were looked for, and found_by the one that answered, or nil;
  • workspace — that document; versions — its published versions, newest first;
  • version — the one shown; pages — its approved pages, in their order; pages_by_version — the approved pages of every version of the document, and on a page of it of the version each other edition is on, by the version's id, each in their order;
  • body — the page the address names, every field, with rendered_content; notes — its approved notes, in their order, with their content.

Another page of the same document

A live patch to another page, or to another version, reads only what changed: the event hands back the source the address before read for the same visitor (held), and the document, its versions and, in the same version, its pages are taken from it.

Summary

Functions

The source the page read for request, when it was handed one for this site and visitor; else the rows read here, once (read/1). For the plugins on the frame and the head, which are handed core's source.

A version's pages, without their text, for Ash.load/3 or a read of the pages of several.

The rows of the documentation page request asks for, read as its current_user; nil for a request that names no document, on no site. A document that is not there, or not shown, is a source with no workspace: every reader handed it answers as its own read of it would.

The version a reader is shown among a document's published versions (MishkaDocument.Address.version_shown/2) and its approved pages as a tree — what a page's address is followed through (MishkaDocument.Address.page_at/2), read for that version alone, as the system, as workspace/3 reads; nil when there is none.

A document's published versions, without their pages, for workspace/3 to load.

The document whose own address the page is at, else the one with no address of its own the workspace capture names, in the page's language, on the page's site; nil when the request names none or none is shown. Only fields of it are read, and loads loaded.

Types

named()

@type named() :: {:path, String.t()} | {:slug, String.t()}

t()

@type t() :: %MishkaDocument.PageSource{
  body: struct() | nil,
  editions: [struct()],
  found_by: named() | nil,
  language: String.t() | nil,
  lock: [String.t()],
  notes: [struct()],
  open?: boolean(),
  page: struct() | nil,
  pages: [struct()],
  pages_by_version: %{optional(String.t()) => [struct()]},
  rows_of: String.t(),
  site_id: String.t(),
  tried: [named()],
  version: struct() | nil,
  versions: [struct()],
  visitor: term(),
  workspace: struct() | nil
}

Functions

of(request)

@spec of(map()) :: t() | nil

The source the page read for request, when it was handed one for this site and visitor; else the rows read here, once (read/1). For the plugins on the frame and the head, which are handed core's source.

pages()

@spec pages() :: Ash.Query.t()

A version's pages, without their text, for Ash.load/3 or a read of the pages of several.

read(request)

@spec read(map()) :: t() | nil

The rows of the documentation page request asks for, read as its current_user; nil for a request that names no document, on no site. A document that is not there, or not shown, is a source with no workspace: every reader handed it answers as its own read of it would.

shown(versions, asked, site_id)

@spec shown([struct()], String.t() | nil, String.t()) :: {struct(), [struct()]} | nil

The version a reader is shown among a document's published versions (MishkaDocument.Address.version_shown/2) and its approved pages as a tree — what a page's address is followed through (MishkaDocument.Address.page_at/2), read for that version alone, as the system, as workspace/3 reads; nil when there is none.

versions()

@spec versions() :: Ash.Query.t()

A document's published versions, without their pages, for workspace/3 to load.

workspace(request, fields, loads)

@spec workspace(map(), [atom()], term()) :: struct() | nil

The document whose own address the page is at, else the one with no address of its own the workspace capture names, in the page's language, on the page's site; nil when the request names none or none is shown. Only fields of it are read, and loads loaded.

READ AS THE SYSTEM, for the head of a caller that is no visitor: an editor's preview and a share card (MishkaDocument.Plugins.PageSeo). A page a visitor reads is answered from read/1.

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.