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

A new component made from an existing one — the inspector modal's Duplicate.

The copy is written through the resource's own `:master_create` or `:create`, chosen by the actor
the same way `MishkaCmsCore.Runtime.PageBuilder.Helpers.save_section/4` chooses. Nothing is
inserted around them, so a copy is validated, signed, versioned, broadcast and compiled exactly
like a component somebody typed in — and refused the same way: to an actor who may not write the
component, and, for one carrying `helpers`, while the deployment's `:page_code` switch is off.

## What a copy keeps, and what it does not

Every attribute the create action accepts is carried over, read from the action rather than listed
here, so a field added to the resource is copied without anyone remembering this module. Five
things are not the source's to pass on:

  * **The name.** `copy_name/1` — `<name>-copy-<4 characters>`.
  * **The kit.** `extra.ui_kit`, `extra.ui_kit_version` and `extra.ui_kit_digest` go, and
    `extra.copied_from` names the source instead. All three are one stamp —
    `MishkaCmsCore.Runtime.Handlers.UiKitUpgrade` writes them together and reads them together —
    and a copy that kept the fingerprint would be claiming to be what a kit installed. A copy of
    `chelekom-button` still starts with `chelekom-`, and
    `MishkaCmsCore.Runtime.Handlers.UiKitHandler` purges by that prefix on an uninstall or a
    `cleanup: true` reinstall — through `:master_permanent_destroy`, which leaves no archive and
    no version. `copied_from` is what it reads to leave a user's copy alone.
  * **`required`.** It says the CMS cannot run without the component, which is true of the kit's
    icon and of nobody's copy of it.
  * **`precompile`.** A copy is built by the first render that names it, not at every boot.
  * **Its elements' classes.** See below.

## A copy stays where its source is

A master copies any row into the source's own scope: a global row makes a global copy, a site's
row a copy in that site. A site copies only its own rows. A shared row copied into a site would be
a fork its examples cannot find — they say `site="Global"` — so that is refused rather than
guessed at.

## It names itself

A component's examples are real invocations, `<.component component_name="chelekom-card" …/>`,
and so is the occasional template that renders itself. Copied verbatim, the copy's Preview tab and
the builder's Examples modal would draw the ORIGINAL. Every exact `component_name="<source>"` in
the template, the body, the examples and anything under `extra` is rewritten to the copy's name.
The closing quote is part of the match, so `chelekom-button-group` is not a reference to
`chelekom-button`; a computed `component_name={@name}` is not rewritten.

## Its elements are its own

Every minted `pb-xxxxxxxx` class in the copied text is swapped for a fresh one and the source's
CSS for those classes is written under the new ones in the copy's own row — the shared
`MishkaCmsCore.Runtime.PageBuilder.ElementScopes`, which says what a kept class costs.

# `copy_name`

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

`<name>-copy-<suffix>`: four lowercase base32 characters, the base cut so the whole stays within
100 characters.

Already in the form the resource's `slugify(:name)` stores, so the name the self-references are
rewritten to is the name the row is saved under.

# `duplicate`

```elixir
@spec duplicate(MishkaCmsCore.Runtime.Component.t(), map() | nil, keyword()) ::
  {:ok, MishkaCmsCore.Runtime.Component.t()} | {:error, term()}
```

Copy `source` as `actor`, trying a fresh name when the last one was taken.

`:names` is the candidates to try, in order; by default three from `copy_name/1`. Only a taken
name moves on to the next one — any other refusal is returned as it came.

Returns the resource's own `{:ok, copy}` / `{:error, error}`, or `{:error, :out_of_scope}` when
the actor is a site and `source` is not that site's.

---

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