MishkaCmsMigration.Sources.WordPress.Content (Mishka CMS Migration v0.0.1)

Copy Markdown View Source

A WordPress post's or page's content.raw as its item's content pieces (MishkaCmsMigration.Source.Item): %{html: ...} for what is converted here, and %{name: ..., attrs: ..., raw: ...} for a block or shortcode left to migration:content:convert (MishkaCmsMigration.Events), in the order they come. call/2 reads the content with LazyHTML, an HTML5 parser, so its markup is never edited as text; only a shortcode, which is text, is read with a pattern.

Blocks

A block is what its <!-- wp:name {attrs} --> and <!-- /wp:name --> comments enclose, or a <!-- wp:name {attrs} /--> alone. A core block (core/…, the name without a namespace) becomes its own saved markup, which is already semantic HTML: headings, lists, quotes, tables, <pre><code>, <figure> with its <img> and <figcaption>, a gallery's figures inside one <figure>, <hr>. The markup loses WordPress's class and style, which no theme here reads, and the block comments; the blocks inside it are converted the same way. Some take a shape of their own:

  • core/columns and core/media-text — a <div class="grid gap-6 md:grid-cols-N">, one column per inner block, and core/group a <div class="grid gap-6">;
  • core/embed — a <figure> with the player of a YouTube or Vimeo video (below), or with a link to its address for any other;
  • core/html — the author's markup as it is, classes kept, without its comments;
  • core/shortcode — its shortcodes, below;
  • core/block, a reusable block — the content of the reusable block it names, read from WordPress with the fetch call/2 is handed (/wp/v2/blocks) and converted the same way; a reusable block inside it, or one WordPress does not answer, is a piece named core/block with its "ref".

A block of another namespace (acme/callout) and a block with no saved markup, which WordPress draws as the page is served (core/latest-posts), are pieces named by the block, with its attrs and its markup, comments included, as raw.

Classic content and shortcodes

What sits outside any block, all of a post written in the classic editor, has its paragraphs made as WordPress makes them when it shows the post: a blank line ends one, a line break inside one is a <br>, and a block element (a heading, a list, a figure) stands on its own. Its shortcodes, and those of a core/shortcode block, become HTML:

  • [caption] — a <figure> with the image and its caption;
  • [gallery ids="…"] — a <figure> of a figure per image, each image read from WordPress (/wp/v2/media) and linked to its file (link="file"), to nothing (link="none") or to its attachment page;
  • [embed] — a <figure> with the player of the YouTube or Vimeo video it encloses, or with a link to any other address;
  • [video src="…"] — a <video> with its controls.

Any other shortcode, and a gallery none of whose images WordPress answers, is a piece named by the shortcode (no namespace, so never mistaken for a block), with its attributes and its text as raw. A shortcode inside a block other than core/shortcode is left as text.

A YouTube or Vimeo address alone on its line, which WordPress embeds by itself (oEmbed), is the video's player too; any other address on its line stays text.

Images and links keep the addresses WordPress gave them. The links to the site's own posts are named in the item's links, the links to its pages in its pages, and the files of its media library they show or link to in its media (MishkaCmsMigration.Sources.WordPress), for the target to point them at where the posts, the pages and the files move.

An address that runs code or carries a document of its own is dropped, its element kept without it, wherever the content has it (pieces/1): a javascript: or vbscript: address, and a data: one but an image's src, in an href, src, action, formaction, poster or xlink:href, read as a browser reads it, whatever its case and the spaces, tabs and line breaks in it.

Players

A YouTube video (youtube.com/watch?v=, youtu.be/, /embed/, /shorts/, /live/) is played by https://www.youtube-nocookie.com/embed/<id>, which sets no cookie until it plays, and a Vimeo video (vimeo.com/<id>, its unlisted vimeo.com/<id>/<hash>, a channel's, and player.vimeo.com/video/<id>) by https://player.vimeo.com/video/<id>: an <iframe> with a title, 560 by 315, lazily loaded and allowed to go full screen. An <iframe> the author wrote, in a core/html block or in classic content, keeps its own address. The targets that write the content allow the players' origins on the site (MishkaCmsMigration.Targets.Helpers.Frames).

Summary

Types

A converted node: a LazyHTML.Tree node, or {:piece, piece} where a piece is left to migration:content:convert.

How call/2 reads more of WordPress: the rows of a REST route ("/wp/v2/blocks", "/wp/v2/media") with these ids, by id, or %{} when it cannot.

Functions

The blocks at the top of raw, an HTML string, or of a block's inner nodes, as {:block, name, attrs, inner, raw} with the name's namespace ("core/navigation-link"), its attributes as the block comment's JSON says them, the nodes it encloses (nil for a block with no end comment) and its source nodes. What lies between blocks is dropped. For a reader of a block tree other than content, such as a block navigation (MishkaCmsMigration.Sources.WordPress.Navigation).

The pieces raw is made of, reading the reusable blocks and the gallery images it names with fetch.

A <figure> with the player of the YouTube or Vimeo video at url (see "Players"), or with a link to any other address.

raw converted as call/2 converts it, as nodes, for a converter that places the converted content inside markup of its own before it makes the pieces (pieces/1), such as the text of an Elementor widget (MishkaCmsMigration.Sources.WordPress.Elementor).

The content pieces of nodes, in order: the HTML around each {:piece, piece}, and the piece, without an address that runs code or carries a document of its own (javascript:, vbscript:, and data: but an image's).

Types

content_node()

@type content_node() :: LazyHTML.Tree.html_node() | {:piece, map()}

A converted node: a LazyHTML.Tree node, or {:piece, piece} where a piece is left to migration:content:convert.

fetch()

@type fetch() :: (String.t(), [pos_integer()] -> %{optional(pos_integer()) => map()})

How call/2 reads more of WordPress: the rows of a REST route ("/wp/v2/blocks", "/wp/v2/media") with these ids, by id, or %{} when it cannot.

Functions

blocks_in(raw)

@spec blocks_in(String.t() | [LazyHTML.Tree.html_node()]) :: [tuple()]

The blocks at the top of raw, an HTML string, or of a block's inner nodes, as {:block, name, attrs, inner, raw} with the name's namespace ("core/navigation-link"), its attributes as the block comment's JSON says them, the nodes it encloses (nil for a block with no end comment) and its source nodes. What lies between blocks is dropped. For a reader of a block tree other than content, such as a block navigation (MishkaCmsMigration.Sources.WordPress.Navigation).

call(raw, fetch)

@spec call(String.t(), fetch()) :: [map()]

The pieces raw is made of, reading the reusable blocks and the gallery images it names with fetch.

embed(url)

@spec embed(String.t()) :: LazyHTML.Tree.html_node()

A <figure> with the player of the YouTube or Vimeo video at url (see "Players"), or with a link to any other address.

nodes(raw, fetch)

@spec nodes(String.t(), fetch()) :: [content_node()]

raw converted as call/2 converts it, as nodes, for a converter that places the converted content inside markup of its own before it makes the pieces (pieces/1), such as the text of an Elementor widget (MishkaCmsMigration.Sources.WordPress.Elementor).

pieces(nodes)

@spec pieces([content_node()]) :: [map()]

The content pieces of nodes, in order: the HTML around each {:piece, piece}, and the piece, without an address that runs code or carries a document of its own (javascript:, vbscript:, and data: but an image's).