MishkaCmsMigration.Targets.CmsPage (Mishka CMS Migration v0.0.1)

Copy Markdown View Source

The "page" items of a source written as the CMS's own pages (MishkaCmsCore.Runtime.Page), through the :create and :update an admin's own page form runs, on the run's site and as the admin who pressed Import: the admin needs the core's admin:dashboard:runtime:page, and the core's own rules decide the rest, such as that a path is unique within a site and that a page's template must compile.

An item's title is the page's title, its content pieces, converted, the page's template (below), and the page is in the language the item names (a site that runs Polylang or WPML names one), joined as one group with the pages that are its other editions, whatever order they are imported in, or, for an item that names none, in the language the site leads with (MishkaCmsMigration.Targets.Helpers.Editions). An item in a language the site does not publish in waits for an admin's decision, and so does one whose language a source that runs a language plugin does not say, in the dry run too. It is drawn in the site's site-main layout (MishkaCmsCore.Runtime.SiteLayout.layout_id/1), between the header and the footer every page of the site shares, as the pages an extension provisions are. The core names the page after its title, as it does for a page typed in the form.

Its path

The page's path is the path of its address at the source, without the host and the trailing /: http://old.example/about/team/ is /about/team, so a page under its parent keeps the nested path and needs nothing from the parent's record. A page that has no address of its own yet, such as a draft whose "link" is /?page_id=30, takes the one WordPress would give it, from the "permalink_template" it answers (MishkaCmsMigration.Sources.WordPress keeps it in extra) with the page's slug in it. The core serves only ASCII paths, so a segment that is not made of letters, digits and - _ . (a slug written in another script, which WordPress keeps percent-encoded) is decoded and transliterated the way the core slugs a name, /وردپرس being /wrdprs, and the report says so ("path_made_ascii"). The old address is not redirected.

The source's front page is the page whose address is /, and its posts page the page at the address of the posts: neither is told apart, since the address says it. A page whose address the site already answers waits for an admin's decision ("slug_taken", MishkaCmsMigration.MigrationItem's Decide…), also when the site's page is not live: the site has a page at the same path, such as its home at /, or one whose path captures the language answers the same public address, such as the site's /:lang/about for /about or the Blog's /:lang/blog for /blog. Addresses are compared as the core's own check compares them (MishkaCmsCore.Runtime.PageEditions.urls/3 with the primary language unprefixed), so the page the core would refuse to put live beside the site's is asked about first, never failed. The site's pages that could answer are read in one query: those at the items' paths and addresses, and those whose path captures the language. Nothing is ever written over a page an admin made. Connect to the existing record makes the item that page, kept as it is, and the pages under it are written as the walk writes any child. Import as new with another slug writes the item as a page of its own, its path's last part followed by its source id, /contact-29 for /contact and /home-2 for a front page whose slug is home, and the report says so ("another_slug"). A page under one that waits is skipped, as the walk does for any item whose parent was not imported, naming that page ("parent_undecided: <parent>").

Status

By the run's pages choice (MishkaCmsMigration.MigrationRun's options):

  • "publish" — a page WordPress has published is live at its path (active); a draft, and any other, is a page that is not live;
  • "draft" (the default) — every page is a page that is not live, at the path it would have, so nothing goes live until an admin publishes it.

The core has no page pending review, private, behind a password or scheduled, so "pending", "private", a page with a "password" (in its extra) and "future" are never live, and the report says so for each (note/2: "draft_from_pending", "draft_from_private", "draft_from_password", "draft_from_future"), in the dry run too.

Its template

A page is compiled as HEEx, in which {...} and <%= ... %> are Elixir the page runs, so the content, which is only text and markup, is written so that it cannot be either:

  • it is read and written back with an HTML parser (LazyHTML), so its tags are balanced and the < of every text and attribute value is &lt;;
  • the <% that is left, in a script, a style or a comment, whose text is not escaped, is written &lt;%, so no EEx tag opens;
  • all of it sits inside one <div phx-no-curly-interpolation>, which makes HEEx read every { under it as text, however deep. A { in a paragraph, in a code block, or in the raw text of a block nobody converted is shown as it was.

The builder opens the page with that div as its root. An element WordPress had, with an attribute name HEEx reads as code (:if, or one in braces), is refused by the core's compile check with its own sentence, and the item fails with it. A deployment that turned hand-written templates off (config :mishka_cms_core, :page_code, :off) refuses every page the same way.

Its pictures and files

Its "media" name the addresses of its content that are files of the source, and each src, href, video poster and srcset candidate of them points at the Media address of the file the same run, or an earlier one, wrote for it (MishkaCmsMigration.Targets.CmsMedia.relinked/3), a size WordPress made of a file too. The address of a file that was not written is kept as it is, and so is every other link; an srcset candidate of such a file is named in the report ("srcset_file_missing").

Its "pages" name the links of its content that point at a page of the source, each by its href as written and the address WordPress gives that page (the WordPress source finds them, MishkaCmsMigration.Sources.WordPress). Each such link points at the path that page is written at instead (relinked/3), however the link was written, and keeps its ?query and #fragment: http://old.example/about/team/#staff becomes /about/team#staff, and a link to a page an admin decided to import as new beside the site's own ("slug_taken") its other path, /contact-29. It is the path the run writes the page at, so a page that links to one listed after it points at it too. A page with no address of its own yet, such as a draft, and any other link, such as a category's or a feed's, is kept as it is. The same links in a post point at the same paths (MishkaCmsMigration.Targets.BlogPost).

Its SEO

Its "seo", what Yoast SEO or Rank Math kept for it (the WordPress source reads it), is the page's seo_tags (MishkaCmsMigration.Targets.Helpers.SeoTags): its title, description, robots, share image, as the Media file it was imported as, and a canonical address on another site.

Its videos

A YouTube or Vimeo player its content frames (the WordPress source makes one of each embed) is allowed on the site before the page is written, in an import only, as the admin (MishkaCmsMigration.Targets.Helpers.Frames), and the report names them ("frames_allowed: <origins>"), in the dry run too. For an admin who may not change the site's settings the page waits for an admin's decision ("player_not_allowed"), once its path does not, in the dry run too (waiting/2), also a page an earlier import wrote: Wait for an admin keeps it for a run by one who may, and Import without the player writes it with that player left out ("player_left_out").

Its scripts

A page whose content holds a <script> or an inline event handler (onclick, onerror, ...) waits for an admin's decision ("scripts: <found>", MishkaCmsMigration.Targets.Helpers.Scripts), in the dry run too: Remove them writes it without them ("scripts_removed: <found>"), and Keep them (I trust this content) writes it as it is ("scripts_kept: <found>").

Its Elementor layout

A page laid out with Elementor arrives as the page builder's own elements (MishkaCmsMigration.Sources.WordPress.Elementor), and is written as any other page. One with widgets the CMS has no equivalent for, such as a form, a slider or a map, waits for an admin's decision ("unsupported_widgets: <names>", MishkaCmsMigration.Targets.Helpers.Widgets), once its path does not, in the dry run too (waiting/2), also a page an earlier import wrote: Import with the fallback HTML for those parts writes it with what Elementor drew for them ("fallback_html: <names>", and "no_fallback: <names>" for those it drew nothing for), and Leave the page out leaves it out.

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.

html with each link that pages names, an href as the source wrote it to the "link" and "id" of the page it is (MishkaCmsMigration.Sources.WordPress's pages), pointing at the path the CMS serves that page at, with the link's own ?query and #fragment: the path of its address, in ASCII, followed by -<id> once an admin decided to import that page as new beside the site's page at that path ("slug_taken"), as attrs/2 writes it. A link whose page has no address of its own, and every other link, is kept as it is. The admins' decisions are read from the pages' items (MishkaCmsMigration.MigrationItem) with one query, as context's admin.

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

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

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

The path the page whose address at the source is link is written at, as attrs/2 writes it: the path of its address, in ASCII, followed by -<source id> once an admin decided to import it as new beside the site's page at that path ("slug_taken" in the decisions of item, the page's MishkaCmsMigration.MigrationItem, or nil for a page no run has met). nil for an address with no path of its own, such as a draft's /?page_id=30.

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.

relinked(html, pages, context)

@spec relinked(String.t(), map() | nil, MishkaCmsMigration.Target.context()) ::
  String.t()

html with each link that pages names, an href as the source wrote it to the "link" and "id" of the page it is (MishkaCmsMigration.Sources.WordPress's pages), pointing at the path the CMS serves that page at, with the link's own ?query and #fragment: the path of its address, in ASCII, followed by -<id> once an admin decided to import that page as new beside the site's page at that path ("slug_taken"), as attrs/2 writes it. A link whose page has no address of its own, and every other link, is kept as it is. The admins' decisions are read from the pages' items (MishkaCmsMigration.MigrationItem) with one query, as context's admin.

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.

written_path(link, arg2)

@spec written_path(String.t() | nil, map() | nil) :: String.t() | nil

The path the page whose address at the source is link is written at, as attrs/2 writes it: the path of its address, in ASCII, followed by -<source id> once an admin decided to import it as new beside the site's page at that path ("slug_taken" in the decisions of item, the page's MishkaCmsMigration.MigrationItem, or nil for a page no run has met). nil for an address with no path of its own, such as a draft's /?page_id=30.