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 awp:wxr_versionand 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
titleanddescription, 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, theelementor_libraryitem whose_elementor_template_typeiskit, as the REST API's/elementor/v1/globalsanswers 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 aslugmade of the login the way the CMS slugs a name; - category and tag — each
wp:categoryandwp:tag: its id, name, slug, description, how many published posts carry it (count) and, for a category, its parent's id; - media — each
attachmentitem: its title, slug, address (source_url, thewp:attachment_url), type (mime_type, from its file name), alt text, caption, description, the post it was uploaded to, its dates, and itsmedia_details(_wp_attachment_metadata, its size in bytes and the address of each size WordPress made of it); - page and post — each
pageandpostitem, but the trash: title, content, excerpt, slug, status, address (link), author's id, dates, featured file (_thumbnail_id), template, its other meta undermeta, and a page's parent, menu order andpermalink_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:commentof 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_menuterm (wp:term, which an export may list twice) with itsnav_menu_itemitems, 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. Eachwp_navigationpost 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 itslanguageterm (its slug is the code) and itspost_translationsterm, and eachpost_translationsandterm_translationsterm describes its group as a serialized map of code to id. A term's language is its code in theterm_translationsgroup 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 inmeta.
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 agenerated_slug; metaholds every meta field of the entry the export carries (custom fields among them, such assubtitle), 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.
Optional. See MishkaInstaller.Event.Hook.on_dependency_error/1. Override to react.
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.
Callback implementation for MishkaInstaller.Event.Hook.unregister/0.
Types
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec config() :: keyword()
@spec enable() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.enable/0.
@spec get() :: keyword()
Callback implementation for MishkaInstaller.Event.Hook.get/0.
Optional. See MishkaInstaller.Event.Hook.health_check/0. Override to report health.
Optional. See MishkaInstaller.Event.Hook.on_dependency_error/1. Override to react.
@spec register() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.register/0.
@spec restart() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.restart/0.
@spec start() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.start/0.
@spec stop() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.stop/0.
@spec unregister() :: okey_return() | error_return()
Callback implementation for MishkaInstaller.Event.Hook.unregister/0.