MishkaCmsMigration.Sources.WordPressFile (Mishka CMS Migration v0.0.1)

Copy Markdown View Source

The WordPress export file source ("wordpress_file"): a site read from the file WordPress makes under Tools ▸ Export (WXR), for a site whose REST API cannot be reached or signed in to. It gives the same items as the WordPress source (MishkaCmsMigration.Sources.WordPress), so a site moved from its export lands in the CMS as it does from its REST API, but for what an export does not carry (below), and a run of either finds what a run of the other wrote (both read from the same host).

It reads a file (file?/0): the New import form asks for the export, an .xml file, and no connection keys (form_fields/0 is empty), and the run keeps the file until it ends (MishkaCmsMigration.MigrationRun's "The source's file"); its path is the connection's "file". The file is streamed (MishkaCmsMigration.Sources.WordPress.Export), never read whole, and one that declares a document type, entities or anything it would fetch is refused.

host/1 is the host of the export's <link>, the site's home, which is also where its files are downloaded from (MishkaCmsMigration.Targets.CmsMedia). connect/2 reads the whole file once, refusing with one of these reasons:

  • :no_file — the connection names no file, or none is there;
  • :unreadable_file — the file is not a WordPress export: not XML, not an <rss> channel with a wp:wxr_version and a <link>, broken off, or one that declares a document type.

While it reads, it keeps what the items point at: the authors by login, the categories and tags by slug, how many published posts each term has, and the id, slug and address of each post and page, the address, alt text and sizes of each file, and the content of each reusable block. That is how it answers the reads the WordPress source's normalize/3 makes with its connection's read (the blocks and gallery images of a post's content, the posts, pages and files its links point at), from the file instead of the site. extra/1 keeps the site's name, tagline and home.

What it reads

kinds/0 is the WordPress source's, in its order. list/3 reads the file again for each page of 100 rows of a kind, from the top, and stops at the row after the page's last; the cursor is the next page's number. Each row is the shape the REST API answers for that kind, built from the export, and normalize/3 is the WordPress source's own, so its items carry the same attrs, extra and content, converted the same way:

  • setting — the site's name and tagline, the channel's title and description, read once when the file is opened;
  • stylesheet — Elementor's global colours and fonts, read once when the file is opened from the settings (_elementor_page_settings) of its kit, the elementor_library item whose _elementor_template_type is kit, as the REST API's /elementor/v1/globals answers them (MishkaCmsMigration.Sources.WordPress.ElementorKit.from_settings/1). An export does not say which kit is active, so of two the one made last is read; an export with none has no row;
  • user — each wp:author: its id, login (username), display name (name), first and last name, email, and a slug made of the login the way the CMS slugs a name;
  • category and tag — each wp:category and wp:tag: its id, name, slug, description, how many published posts carry it (count) and, for a category, its parent's id;
  • media — each attachment item: its title, slug, address (source_url, the wp:attachment_url), type (mime_type, from its file name), alt text, caption, description, the post it was uploaded to, its dates, and its media_details (_wp_attachment_metadata, its size in bytes and the address of each size WordPress made of it);
  • page and post — each page and post item, but the trash: title, content, excerpt, slug, status, address (link), author's id, dates, featured file (_thumbnail_id), template, its other meta under meta, and a page's parent, menu order and permalink_template (its parent's address, or the home, with %pagename%/), a post's categories and tags by id, whether it is sticky and its format;
  • comment — each wp:comment of an item that is not in the trash, approved or held for moderation, with its post, parent, author, text, status, type and dates;
  • post_redirect — each post again, its id, title, status, address and modified time, its old address being the path of its <link>;
  • menu — each nav_menu term (wp:term, which an export may list twice) with its nav_menu_item items, whose own fields are their meta (_menu_item_type, _menu_item_object, _menu_item_object_id, _menu_item_menu_item_parent, _menu_item_url, _menu_item_target), read in one pass (MishkaCmsMigration.Sources.WordPress.menus/4). An item with no label of its own takes the title of the page or post, or the name of the category or tag, it points at, and its address too; one whose page, post or term the export does not carry is invalid, as WordPress counts it. Each wp_navigation post is a block menu too, as the REST API answers it.

Languages

An export of a site that runs Polylang or WPML carries the language of each entry and term, and which entries and terms are editions of one another, and a page, a post, a category and a tag row carries them as Polylang Pro's REST API does, lang and translations (every edition's id by language, the row's own among them), which the WordPress source reads into the item (MishkaCmsMigration.Sources.WordPress, "Languages"):

  • Polylang keeps them as its own taxonomies, which an export carries as terms (wp:term): an entry names its language term (its slug is the code) and its post_translations term, and each post_translations and term_translations term describes its group as a serialized map of code to id. A term's language is its code in the term_translations group that names it; a term in none says no language;
  • WPML, with its WPML Export and Import, keeps them in an entry's meta and a term's meta (_wpml_import_language_code, the code, and _wpml_import_translation_group, a value every edition of one entry shares; https://wpml.org/documentation/translating-your-contents/multilingual-content-import/). These are not kept in meta.

extra/1 keeps which plugin the export came from as "languages" ("polylang" or "wpml"), as the WordPress source does for a site.

Where it differs from the REST API

An export carries less than the REST API answers, and some of it otherwise:

  • the site's logo and icon are options, which an export does not carry, so only its name and tagline are read;
  • an author has no bio, website, avatar, nickname or roles, so a blogger made from one has none, and its slug is made of its login rather than WordPress's own user_nicename;
  • a term has no link, so a menu item of a category or a tag has WordPress's plain address of it (/?cat=<id>, /?tag=<slug>), which WordPress answers whatever its permalinks;
  • a menu's theme locations are the theme's, which an export does not carry, so the main menu is the first made;
  • a draft, pending or scheduled entry WordPress kept no GMT date for has no date_gmt (the export gives only the site's own time, not its timezone), and no entry has a generated_slug;
  • meta holds every meta field of the entry the export carries (custom fields among them, such as subtitle), where the REST API answers only those a plugin registered;
  • an export of posts alone (Tools ▸ Export ▸ Posts) carries the authors, files and comments of its posts but no categories, tags or pages, so its posts come without their categories and tags; an export of all content carries both.

MishkaCmsMigration.Source.Item.hash/1 covers extra, so an item read from an export and later from the REST API, or the other way round, is updated once.

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.