# `MishkaCmsCore.Runtime.PageSource`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/page_source.ex#L5)

The rows a page shows, read once for the request and handed to everything that draws them: the
head (`MishkaCmsCore.Runtime.Seo`), the frame (`MishkaCmsCore.Runtime.SiteLayout`) and the page's
own blocks.

A page that shows a record is drawn by three readers: the plugin on `Seo.event/0` for its head,
the plugin on `SiteLayout.event/0` for its languages and its bar, and the blocks of its template
for the record itself. Each of them asked the database for the same rows on its own. A plugin on
`event/0` reads them once, before the other two run, and every one of them is handed what it read.

## The event

`MishkaCmsWeb.RuntimeLive` calls `assign_source/1` in every `handle_params/3`, once the page, its
language, `@query_params` and the visitor are known, and before the frame and the head. It fires
`event/0` with the map `given/2` builds and assigns the answer as `@page_source`:

  * `page`, `section`, `record`, `extra`, `params`, `language`, `site_id`: as
    `MishkaCmsCore.Runtime.SiteLayout` hands them to its plugins. Read only.
  * `current_user`: the signed-in visitor, or `nil` for a guest. The rows are read as them.
  * `held`: what this LiveView's previous address read, for the same visitor, or `nil`. A live
    patch that stays on the same record (another page of a list, another version) takes from it
    what the new address did not change, rather than reading it again.
  * `source`: `nil`.

A plugin answers for the pages it owns, matched as a frame plugin matches them, by putting its
rows under `source`, and hands every other page back untouched, before anything is read. What it
puts there is its own: core never looks inside it. It reaches, unchanged:

  * the plugins on `Seo.event/0`, as `source` in the map they are handed (`Seo.given/2`);
  * the plugins on `SiteLayout.event/0`, as `source` (`SiteLayout.given/2`);
  * the page's template, as `@page_source`, which hands it to its blocks:

        <.component component_name="doc-section" page_source={assigns[:page_source]} … />

`{:reply, :halt, given}` keeps later plugins from answering for a page this one owns. A plugin
that raises, or an event whose plugins cannot run, leaves the page with no source, and every
reader reads for itself as it does without one.

## The rules a source keeps

  * IT IS READ AS THE VISITOR, through the public actions and the policies the blocks read with:
    the same locks, the same lock inherited from what a record is filed under, the same page lock.
    It is never broader than what that visitor may see, so a reader handed it shows nothing a
    read of its own would not have. A read made as the system belongs only where nothing of it
    reaches the visitor: the lock a record's page refuses the visitor by.
  * IT IS THE VISITOR'S: it is read for one visitor (`visitor_id/1`) and says which, and a block
    handed one read for somebody else reads for itself.
  * IT LIVES FOR ONE ADDRESS: it is read again in every `handle_params/3`, and lives in the
    LiveView's assigns and nowhere else. A task a block starts is handed what it needs as an
    argument, not the LiveView.
  * A BLOCK STILL STANDS ON ITS OWN. On the builder's canvas, in a preview, on a page whose
    template does not hand it over, and for any read it was not given, a block reads as it
    always did. One handed a source takes from it only rows read for exactly what it would read
    itself.

`page_source` is handed only to a component that declares the attribute
(`MishkaCmsCore.Runtime.LiveViewHelpers`): one an admin keeps on an older kit is drawn as if the
template did not name it, rather than with the rows in its global attributes.

# `accept`

```elixir
@spec accept(term()) :: term()
```

The `source` of the map a plugin on `event/0` returned; `nil` for anything but a map.

# `assign_source`

```elixir
@spec assign_source(Phoenix.LiveView.Socket.t()) :: Phoenix.LiveView.Socket.t()
```

The page's source, assigned to `socket` as `@page_source`.

For `MishkaCmsWeb.RuntimeLive`, which calls it in `handle_params/3` once the page, its language,
`@query_params` and `@current_user` are assigned, and before `SiteLayout.assign_frame/1` and the
head, which are handed it. The source this socket held is handed to the plugins as `held` only
when it was read for the same visitor.

# `event`

```elixir
@spec event() :: String.t()
```

The event a page's rows are read through, once per `handle_params/3`.

# `given`

```elixir
@spec given(map(), keyword()) :: map()
```

The map `event/0` is called with for `page`, before any plugin has answered. See the moduledoc
for each key.

`page` is the `:page` section of the page module's `__page_information__/0`, with `language` the
language of this response. Options: `:site_id`, `:current_user`, `:extra` (the page's `extra`),
`:params` (`@query_params`) and `:held`.

# `source`

```elixir
@spec source(map(), keyword()) :: term()
```

What `page` is read from: `given/2`, then the plugins on `event/0`, then `accept/1`. Takes the
options `given/2` takes.

# `visitor_id`

```elixir
@spec visitor_id(term()) :: term()
```

Whom a source is read for: the signed-in visitor's id, or `nil` for a guest. A plugin keeps it
in what it answers, and a block compares it with its own `current_user`'s before taking a row.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
