# `MishkaCmsCore.Runtime.Resources.Validations.SameSiteReference`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/resources/__validations/same_site_reference.ex#L5)

Refuses a record that names, by id, a record of another site.

The record named must belong to the site the record being written is stored with — on a create
given a tenant, the tenant, unless the action bypasses multitenancy; otherwise its `site_id`; for
a record with no site of its own (a join row), the site of the parent named in `:own` — or, with
`shared?: true`, to no site. A record that belongs to no site names only shared records. A record
of another site and a record that does not exist are refused with the same message, so the answer
does not tell a caller which ids exist elsewhere. A record the site could name but that is
archived is refused with "That <label> is archived. Restore it first."

Checked whoever writes, the system included: the rule is about the two records, not the actor.
Nothing is checked when the id is absent or unchanged.

Each refusal is written in the Gettext locale of the process the action runs in, `errors` domain,
with the `:label` translated in the same domain; a request with no locale reads it in English.

A user is named the same way: an account of the record's site, or with `shared?: true` a master
(a user with no site). Read users with `read_action: :related`, which reaches every account.

`shared?: :master` also asks who writes: a record with no site is named only by a master
(`MishkaCmsCore.Accounts.Helper.master_user?/1`) or by the system, a call with no actor made with
`authorize?: false`. A site's user, whatever it holds and with `authorize?: false` too, gets the
refusal a record of another site gets. Declare it with `before_action?: true`, so the one asked is
the actor the action runs as.

`MishkaCmsCore.Runtime.Resources.Validations.TenantIsolation` holds a write to the caller's
tenant and refuses shared records; this holds one record to another.

## The record named

With `moves: true` and a `:label`, it guards the other end of the same links: an update may
change the record's `site_id` only while every record naming it could still name it there. The
records naming it are found through every `SameSiteReference` on the resources of the host's Ash
domains, archived rows included, and through the versions that name an account as the one who
wrote them. A record with no site of its own, or kept outside Postgres, is taken to be on another
site. Refused with "Records outside that site still use this <label>, so it cannot move there.",
or for a move to no site "Records that cannot use a shared <label> still use this one, so it
cannot become shared."

## Options

  * `:field` — required. The attribute holding the id.
  * `:resource` — required. The resource the id names. It has a `site_id`. For an id that names
    one of several resources, `{kind_field, %{kind => resource}}`: the record's `kind_field` picks
    the resource, and a kind the map does not hold is refused. The id is then checked again when
    only the kind changes.
  * `:label` — required. What the named record is called in the message, such as `"layout"`.
  * `:shared?` — whether a record with no site may be named: `true`, `false`, or `:master` for
    only when a master or the system writes. Defaults to `false`.
  * `:read_action` — the read that reaches every site's rows. Defaults to `:read_any`. With a kind
    map, every resource in it has this read.
  * `:own` — for a record with no `site_id`: `{field, resource}` or `{field, resource, read_action}`
    naming the parent whose site the record has. Its read action defaults to `:read_any`.

## Usage

    validate {SameSiteReference,
              field: :layout_id, resource: MishkaCmsCore.Runtime.Layout, label: "layout",
              shared?: true}

    validate {SameSiteReference,
              field: :tag_id, resource: MyApp.Tag, label: "tag", own: {:post_id, MyApp.Post}}

    validate {SameSiteReference,
              field: :document_id,
              resource: {:section, %{page: MishkaCmsCore.Runtime.Page,
                                     layout: MishkaCmsCore.Runtime.Layout}},
              label: "document", shared?: true}

    validate {SameSiteReference,
              field: :author_id, resource: MishkaCmsCore.Accounts.User, label: "author",
              shared?: true, read_action: :related}

    validate {SameSiteReference,
              field: :user_id, resource: MishkaCmsCore.Accounts.User, label: "user",
              shared?: :master, read_action: :related},
             before_action?: true

    validate {SameSiteReference, moves: true, label: "layout"}

---

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