Durable key/value CMS settings, multitenant like MishkaCmsCore.Runtime.Layout (global? true):
a row with site_id = nil is a GLOBAL setting (one value for every site, master-managed); a row
with a site_id is a PER-SITE setting owned by that tenant (site title, SEO, …). Both live in the
one cms_settings table, keyed by [:key, :site_id].
Values are stored as JSON maps; the facade MishkaCmsCore.Settings wraps/unwraps scalars and the
ETS cache MishkaCmsCore.Runtime.States.SettingsState serves them lock-free (site value → global
default) on every request. Reads are open (non-secret config); a tenant writes only its own rows
(the tenant :set auto-stamps site_id), the master writes any site or the global via :master_set.
That split is enforced by the policies below, not by convention or by the caller. A write takes
the permission the setting's definition declares, or admin:dashboard:setting when it declares
none (MishkaCmsCore.Runtime.Resources.Checks.SettingPermission). Because every site's rows share
one table, that permission alone cannot decide a write — every site's settings admin holds it — so
a second policy pins the row to actor.site_id unless the actor is a master. Write through
MishkaCmsCore.Settings.put_as/4 to get that; put/3 is the trusted infra path for boot seeding
and passes authorize?: false, which skips all of it.
PubSub topics: a nil site_id publishes to "setting:updated" only; a uuid site_id publishes to
both "setting:updated" AND "setting:{uuid}:updated", so a master watching the global topic sees
every change while a tenant watches only its own.
See MishkaCmsCore.Settings, MishkaCmsCore.Runtime.States.SettingsState,
MishkaCmsCore.Runtime.Layout (the global? true reference) and
MishkaCmsCore.Runtime.Resources.Validations.TenantContext.
Summary
Types
@type t() :: %MishkaCmsCore.Runtime.Setting{ __lateral_join_source__: term(), __meta__: term(), __metadata__: term(), __order__: term(), aggregates: term(), calculations: term(), category: term(), created_at: term(), description: term(), editable?: term(), id: term(), key: term(), site: term(), site_id: term(), source: term(), updated_at: term(), value: term() }
Functions
Validates that the keys in the provided input are valid for at least one action on the resource.
Raises a KeyError error at compile time if not. This exists because generally a struct should only ever
be created by Ash as a result of a successful action. You should not be creating records manually in code,
e.g %MyResource{value: 1, value: 2}. Generally that is fine, but often with embedded resources it is nice
to be able to validate the keys that are being provided, e.g
Resource
|> Ash.Changeset.for_create(:create, %{embedded: EmbeddedResource.input(foo: 1, bar: 2)})
|> Ash.create()
Same as input/1, except restricts the keys to values accepted by the action provided.