A site's icons and its web manifest, each its own, answered on the site's own address
(MishkaCmsCoreWeb.SeoController):
/favicon.ico— the file thesite.faviconsetting names (favicon/1);/apple-touch-icon.pngand/apple-touch-icon-precomposed.png— 180×180, the icon a phone puts on its home screen;/site-icon-192.pngand/site-icon-512.png— the icons of the manifest;/site.webmanifest— what a browser installs the site with (manifest/1).
What the icons are drawn from
The site's site.app_icon setting, else its site.favicon — the first that names a picture of
the site's own (MishkaCmsCore.Runtime.ShareCard.site_file/2): a file of its media or under the
host's static folder that is a PNG, JPEG, WebP, GIF or SVG (source/1). An http(s)://
address is never fetched, so a site whose pictures are elsewhere has no icons but its favicon
link. Each icon is the picture fitted whole in its square and centered on transparency, never
stretched (MishkaCmsCore.Runtime.ShareCard.squares/2).
When they are drawn
On the first request for one: every size at once, by libvips in a separate process, as a share
card is drawn. A site draws one at a time; the requests that come meanwhile wait for it and send
what it drew. The files are stored at /uploads/<site_id>/icons/<hash>-<size>.png, hash named
by the picture's bytes, and every later request sends the file. A picture libvips cannot draw is
not tried again until the setting or the file changes. Drawing them fires
"runtime:site:after_icons" with %{site_id:, hash:, files: %{size => path}}; a site's
older icons are deleted then.
What a page reads
Which files the favicon and the picture are, and the picture's hash, are kept in ETS
(MishkaCmsCore.Runtime.States.AssetsState, under {site_id, "site_icon"}) beside the values of
the two settings they were read for. The head of every page reads them from there
(url/2, manifest_url/1); a setting that holds another value — on any node — has the files
read again. A media file replaced in place is caught by MishkaCmsCore.Runtime.MediaSiteIcons,
and saving either setting by MishkaCmsCore.Runtime.SettingSiteIcons: both forget/1 it.
The manifest
name is the site's name in its primary language (MishkaCmsCore.Runtime.Seo.site_name/2),
start_url is /, display is browser and icons are the 192×192 and 512×512 PNGs. Three
more keys are there only for a site that set them, on its "App & home screen" settings, and are
left out of the manifest of one that did not: short_name (short_name/1, which a browser
replaces with name when it is missing), theme_color (theme_color/1) and background_color
(background_color/1). A plugin on "runtime:site:manifest" is handed
%{site: site, manifest: manifest} and returns the map with its manifest: another display,
a lang.
The three settings are read from the settings cache, as every setting is, so a change shows on
the next request. A page's head names the theme color too, as a theme-color meta tag
(MishkaCmsCore.Runtime.Seo), for a site with no icons as well.
SiteIcon.url(site.id, 180) #=> "/apple-touch-icon.png?v=4be1f0a3c2d9e8b7"
Summary
Types
A file of the site's own: where it is on disk and its type, read off its first bytes.
The picture the icons are drawn from, and the hash their names carry.
Functions
The color shown behind the icon while the installed app opens: the site's
site.background_color setting, nil while it is empty or holds anything but a hex color
(color?/1).
Whether value is a hex color a manifest and a theme-color meta tag take: #rgb or #rrggbb,
in either case, and nothing around it.
color?/1, or empty: how site.theme_color and site.background_color are validated.
The folder the icons of the site site_id are written to.
The event fired after a site's icons are drawn: "runtime:site:after_icons".
The site's favicon as a file of its own: %{path:, type:}, type one of
MishkaCmsCore.Runtime.ShareCard.picture_type/1's. nil for no favicon, and for one that is an
address elsewhere or a file the site does not own.
The PNG of the site's icon of size on disk, drawn now when it is not there yet:
{:ok, %{path:, hash:}}, hash the one its address carries (url/2). :error for a site with
no picture to draw it from, and for a picture libvips could not draw.
Drops what is kept of the icons of the site site_id — of every site for nil — so the next
request reads its settings and files again, and draws its icons again when their picture
changed.
forget/1 for the site site_id when its favicon or the picture of its icons is the media file
relative_path, /uploads/<site_id>/media/…; anything else is left as it is.
The site's web manifest: %{"name", "start_url", "display", "icons"}, and "short_name",
"theme_color" and "background_color" when the site set them, as a plugin on manifest_event/0
leaves it, with string keys, ready to be encoded. nil for a site with no icons.
The event a plugin changes a site's manifest on: "runtime:site:manifest".
The site's /site.webmanifest, for a site with icons; nil for one with none.
The path the icon of size is served at, with no hash.
The settings the icons are read from: ["site.app_icon", "site.favicon"].
The name the site is given under its home-screen icon: its site.app_short_name setting, nil
while it is empty.
Whether value fits site.app_short_name: at most short_name_max/0 characters.
The longest site.app_short_name a site may save, in characters: 30.
Every size an icon is drawn at, in pixels: [180, 192, 512].
The picture the site's icons are drawn from: its app icon, else its favicon, when it is a
drawable file of its own. nil when neither is.
The color of the browser's toolbar on phones and of the installed app's title bar: the site's
site.theme_color setting, nil while it is empty or holds anything but a hex color
(color?/1).
The address the site's icon of size is served at, with the hash of the picture it is drawn
from: "/apple-touch-icon.png?v=…" for 180, "/site-icon-192.png?v=…". nil for a site with
no picture to draw it from.
Types
A file of the site's own: where it is on disk and its type, read off its first bytes.
The picture the icons are drawn from, and the hash their names carry.
Functions
The color shown behind the icon while the installed app opens: the site's
site.background_color setting, nil while it is empty or holds anything but a hex color
(color?/1).
Whether value is a hex color a manifest and a theme-color meta tag take: #rgb or #rrggbb,
in either case, and nothing around it.
color?("#0b0a14") #=> true
color?("red") #=> false
color?/1, or empty: how site.theme_color and site.background_color are validated.
The folder the icons of the site site_id are written to.
@spec drawn_event() :: String.t()
The event fired after a site's icons are drawn: "runtime:site:after_icons".
The site's favicon as a file of its own: %{path:, type:}, type one of
MishkaCmsCore.Runtime.ShareCard.picture_type/1's. nil for no favicon, and for one that is an
address elsewhere or a file the site does not own.
@spec file(String.t() | nil, pos_integer()) :: {:ok, %{path: Path.t(), hash: String.t()}} | :error
The PNG of the site's icon of size on disk, drawn now when it is not there yet:
{:ok, %{path:, hash:}}, hash the one its address carries (url/2). :error for a site with
no picture to draw it from, and for a picture libvips could not draw.
@spec forget(String.t() | nil) :: :ok
Drops what is kept of the icons of the site site_id — of every site for nil — so the next
request reads its settings and files again, and draws its icons again when their picture
changed.
forget/1 for the site site_id when its favicon or the picture of its icons is the media file
relative_path, /uploads/<site_id>/media/…; anything else is left as it is.
The site's web manifest: %{"name", "start_url", "display", "icons"}, and "short_name",
"theme_color" and "background_color" when the site set them, as a plugin on manifest_event/0
leaves it, with string keys, ready to be encoded. nil for a site with no icons.
@spec manifest_event() :: String.t()
The event a plugin changes a site's manifest on: "runtime:site:manifest".
The site's /site.webmanifest, for a site with icons; nil for one with none.
@spec path(pos_integer()) :: String.t()
The path the icon of size is served at, with no hash.
@spec settings() :: [String.t()]
The settings the icons are read from: ["site.app_icon", "site.favicon"].
The name the site is given under its home-screen icon: its site.app_short_name setting, nil
while it is empty.
Whether value fits site.app_short_name: at most short_name_max/0 characters.
@spec short_name_max() :: pos_integer()
The longest site.app_short_name a site may save, in characters: 30.
@spec sizes() :: [pos_integer()]
Every size an icon is drawn at, in pixels: [180, 192, 512].
The picture the site's icons are drawn from: its app icon, else its favicon, when it is a
drawable file of its own. nil when neither is.
The color of the browser's toolbar on phones and of the installed app's title bar: the site's
site.theme_color setting, nil while it is empty or holds anything but a hex color
(color?/1).
@spec url(String.t() | nil, pos_integer()) :: String.t() | nil
The address the site's icon of size is served at, with the hash of the picture it is drawn
from: "/apple-touch-icon.png?v=…" for 180, "/site-icon-192.png?v=…". nil for a site with
no picture to draw it from.