MishkaBlog.Plugins.PageSeo (Mishka Blog v0.0.1)

Copy Markdown View Source

Gives each post, category, tag, blogger and team its own head on the blog page that serves it, through the runtime:page:seo mishka_installer hook MishkaCmsWeb.RuntimeLive fires in handle_params/3 (see MishkaCmsCore.Runtime.Seo for the map it is handed and what the head builds from it).

A page is matched by the kind of record it says it shows (record, its extra's "record"): "blog:post", "blog:category", "blog:tag", "blog:author" or "blog:team", the kinds MishkaBlog.Plugins.PageRecords names. Its path may be anything that captures the slug. Every other page is handed back untouched before anything is read. A kind has one owner, so the answer for one of these halts the chain: no later plugin is asked.

Everything returned is a default: what the page sets in its own SEO — a title, a description, a tag, structured data — wins, and may name the variables below, {{post.title}} | Blog.

What is read

Nothing, on a page a visitor reads: MishkaCmsWeb.RuntimeLive hands it the rows the page read once for its head, its frame and its blocks, as the visitor (source, MishkaBlog.PageSource), and the record below is the one there, with what that read loaded. A record the visitor's read left out is answered with its lock alone, read here as the system, so the address answers them 404 and nothing else of it reaches them. An editor's preview and a share card hand no rows, and the record is read here, once: a post, a category or a tag as the system, and a blogger or a team, which carry no lock of their own, as a guest, through the public action.

A post, "blog:post"

The post the slug capture names in the page's language, published — read through :public_list as blog-post-detail reads it, on the page's site, with its authors, categories, tags and its other published editions:

  • title, description, image — the post's title, excerpt and featured image;
  • type — "article";
  • tags — author (the authors' names), a twitter:creator with the X account of the first author, in the authors' order, who has one, article:published_time, article:modified_time, article:section (the first category), one article:tag per tag, the pairs Slack and Discord show under a link — twitter:label1/twitter:data1, then twitter:label2/ twitter:data2: "Written by" the authors' names, "Reading time" the post's, each only when known and numbered from 1 — and a rel="alternate" link per language the post is published in among the site's languages, with x-default at the site's primary one, when there are two or more. The post's own seo_tags come last and replace any of these they name;
  • schema — the post's own schema when it holds one, else a BlogPosting whose author is a Person per author — their name, their address on the site's "blog:author" page and sameAs their social links — whose articleSection is the first category, and whose publisher is the site's Organization by its @id, <site address>/#organization; then a BreadcrumbList, Home → Blog → the post;
  • vars — post.title, post.excerpt, post.image, post.author, post.published_at, post.modified_at, post.url, post.collection, post.tags, and post.rating (the readers' average, one decimal) with post.rating_count once a reader has rated it.

card — what its share card shows (MishkaCmsCore.Runtime.ShareCard): the first category above the title, the authors' photos and names, their teams, the date and the reading time, over the featured image.

A category, "blog:category"

The active category the slug capture names in the page's language: its name and description, its own seo_tags, its own schema (else a CollectionPage) and a BreadcrumbList, Home → Blog → the category. vars — collection.name, collection.description, collection.url. Its share card says "Category" above the name.

A tag, "blog:tag"

The tag the slug capture names in the page's language: its name and description, its own seo_tags, a CollectionPage and a BreadcrumbList, Home → Blog → the tag. A public tag says robots noindex, follow, unless its own seo_tags hold a robots; the sitemap lists exactly the tags that say index (MishkaBlog.Plugins.SitemapUrls). vars — tag.name, tag.description, tag.url. Its share card says "Tag" above the name.

A blogger, "blog:author"

The blogger the slug capture names: their name, their bio as plain text, their avatar, a twitter:creator with their X account when they have one, a ProfilePage in the language of the page whose mainEntity is the Person — with sameAs their social links and knowsAbout their expertise — and a BreadcrumbList, Home → Blog → the blogger. vars — author.name, author.bio, author.image, author.url. Its share card says "Author" above the name, with their photo and their teams. A blogger is the same row in every language, so the slug alone names them.

A team, "blog:team"

The active team the slug capture names: its name, description and avatar, a ProfilePage in the language of the page whose mainEntity is the Organization with its members as Persons, and a BreadcrumbList, Home → Blog → the team. vars — team.name, team.description, team.url. Its share card says "Team" above the name, with the members' photos and how many they are.

Where "Blog" points

The breadcrumb's middle item is the blog's home, read off the page's own path: the post page's parent, the parent of every other kind's parent — /blog/:slug and /blog/tag/:slug both lead to /blog, /:lang/blog/:slug to /:lang/blog in the page's language.

What every matched page returns

permissions — what the record opens with: named, MishkaCmsWeb.RuntimeLive opens the address only for whoever holds one of them, and the head keeps it out of an index. A tag opens with its own; a category with its own, else its nearest parent category's; a post with its own, else its categories' (MishkaBlog.Resources.Calculations.EffectivePermissions).

var_docs lists the page's variables in the current locale, whether or not this request filled them. Called with no params — an editor asking which variables a page has — that is all it changes. A slug that names no row a reader may open changes nothing else.

A module because a mishka_installer hook is one. Registered in MishkaBlog.Application.

Summary

Functions

Returns a specification to start this module under a supervisor.

Callback implementation for MishkaInstaller.Event.Hook.enable/0.

Callback implementation for MishkaInstaller.Event.Hook.get/0.

Optional. See MishkaInstaller.Event.Hook.health_check/0. Override to report health.

Callback implementation for MishkaInstaller.Event.Hook.register/0.

Callback implementation for MishkaInstaller.Event.Hook.restart/0.

Callback implementation for MishkaInstaller.Event.Hook.start/0.

Callback implementation for MishkaInstaller.Event.Hook.stop/0.

Types

error_return()

@type error_return() ::
  {:error, [%{action: atom(), field: atom(), message: String.t()}]}

okey_return()

@type okey_return() :: {:ok, struct() | map() | module() | [any()]}

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

config()

@spec config() :: keyword()

config(key)

@spec config(atom()) :: any()

enable()

@spec enable() :: okey_return() | error_return()

Callback implementation for MishkaInstaller.Event.Hook.enable/0.

get()

@spec get() :: keyword()

Callback implementation for MishkaInstaller.Event.Hook.get/0.

health_check()

@spec health_check() :: :ok | {:degraded, term()} | {:error, term()}

Optional. See MishkaInstaller.Event.Hook.health_check/0. Override to report health.

on_dependency_error(error)

@spec on_dependency_error(term()) :: term()

Optional. See MishkaInstaller.Event.Hook.on_dependency_error/1. Override to react.

register()

@spec register() :: okey_return() | error_return()

Callback implementation for MishkaInstaller.Event.Hook.register/0.

restart()

@spec restart() :: okey_return() | error_return()

Callback implementation for MishkaInstaller.Event.Hook.restart/0.

start()

@spec start() :: okey_return() | error_return()

Callback implementation for MishkaInstaller.Event.Hook.start/0.

start_link(args \\ [])

stop()

@spec stop() :: okey_return() | error_return()

Callback implementation for MishkaInstaller.Event.Hook.stop/0.

unregister()

@spec unregister() :: okey_return() | error_return()

Callback implementation for MishkaInstaller.Event.Hook.unregister/0.