Migration overhaul: guided wizard, migration key, and complete-site export (comments, menus, identity, CPT taxonomies)#8
Conversation
…rt, and hardening - Tools → EmDash Migration wizard: preflight health checks (permalinks, REST loopback, Authorization header, app passwords, reachability) with actionable fixes, one-click migration key, deploy-to-Cloudflare branch, honest what-gets-migrated overview - Migration key: programmatic revocable Application Password packaged as em1.<base64url(JSON)> so users never type URLs or credentials - New GET /menus endpoint matching EmDash importMenusFromPlugin() format - Per-post permalink for redirect maps; WPML/Polylang locale + translation_group; i18n info in probe/analyze - Security: require export capability (was edit_posts), remove file_path from media responses, unserialize with allowed_classes=false, 403 via rest_authorization_required_code() - Release workflow: installable zip on tags and PR artifacts
…aid plan The blog-cloudflare template ships a worker_loaders binding (Dynamic Workers, EmDash's plugin sandbox), which the Cloudflare API rejects with HTTP 400 on the Free plan (upstream emdash issues #31/#149). Warn users under the deploy button and point to the free-plan workaround.
- New /comments endpoint: approved and pending comments, paginated oldest-first, with author, email, plain-text body, UTC date, threading, and status. - /options now includes custom_logo_url and site_icon_url so EmDash can take over the site logo and favicon. - Wizard overview lists comments (with count) and site identity as included.
…ate CPT taxonomies /taxonomies now includes each taxonomy's registered post types (object_type) and singular label. EmDash uses this to create matching taxonomy definitions scoped to the right collections during import, instead of skipping custom post type taxonomies as "missing".
…ependent review)
| $response = wp_remote_get(rest_url('emdash/v1/header-check'), [ | ||
| 'timeout' => 10, | ||
| 'sslverify' => false, | ||
| 'headers' => ['Authorization' => 'Basic ' . base64_encode('emdash:header-check')], |
There was a problem hiding this comment.
This is very much outside my wheelhouse, but would sending a Basic auth header not risk actually triggering application password checks even on a public route, which would then fail as it's invalid? It would be safer to use Bearer, which the agents inform me is ignored by application password
There was a problem hiding this comment.
(or even a custom type, like Authorization: EmDashCheck test)
There was a problem hiding this comment.
You're right, and it's worse than a risk — two concrete failure modes: if the site happens to have a user named emdash, Application Password validation rejects the whole REST request (401) and the wizard falsely reports "your server strips the Authorization header"; and security plugins (Wordfence & co.) can count the fake Basic credentials as a failed login attempt from the site's own loopback IP.
Went with your custom-scheme suggestion in bf3539d: the probe now sends Authorization: EmDashCheck header-check. Skipped Bearer deliberately — JWT/OAuth plugins hook Bearer the same way Application Passwords hook Basic, so a custom scheme is the only value nothing tries to authenticate. Since we only test whether the header string survives the server config, any value works.
While in there I also added a getallheaders() fallback to the check endpoint: under mod_php the header can be visible to Apache without being mirrored into $_SERVER['HTTP_AUTHORIZATION'], which would have made the probe a false negative on setups where Application Passwords actually work.
…t fake Basic credentials Review feedback from emdash-cms#8: a Basic header with dummy credentials triggers Application Password validation — if a user named 'emdash' exists the whole REST request 401s (false "header stripped" result), and security plugins can count it as a failed login. Bearer risks the same collision with JWT/OAuth plugins, so use "EmDashCheck header-check" — we only test whether the header string survives the server config. The endpoint now also falls back to getallheaders(): under mod_php the header can be visible to the Apache API without being mirrored into $_SERVER['HTTP_AUTHORIZATION'], which would have made the custom-scheme probe a false negative.
…d readme.md Upstream PR emdash-cms#4 renamed readme.txt to readme.md and reformatted it as Markdown. Our changelog, wizard/comments/menus features, endpoint docs, and the release-download install step are now folded into readme.md in the new format; readme.txt is gone.
What does this PR do?
This PR turns the exporter plugin into a guided, complete-site migration tool. The goal: a WordPress user should be able to move their whole site to EmDash in minutes, without reading docs, and without discovering missing content after the switch.
It is the plugin half of a two-part effort; the EmDash core counterpart (import-side) is emdash-cms/emdash#1830.
Guided migration wizard (UX)
Authorizationheader pass-through, Application Passwords availability, public reachability. Each check explains how to fix a failure — the top support issues (no routes mapping in lokal installations #5, Doesn't seem to detect sites managed with Local #7 territory) are diagnosed up front instead of failing later with cryptic errors.em1.copy-paste string. On the EmDash side, pasting the key starts the import directly — no manual URL/username/password juggling.Complete-site export (new/extended endpoints)
/comments(new): approved and pending comments with authors, dates, threading, and status — paginated. Comments were previously silently left behind./options(extended): custom logo and site icon with resolved URLs, so EmDash can take over title, tagline, logo, and favicon instead of keeping starter-template placeholders./taxonomies(extended): each taxonomy now carries its singular label and registered post types (object_type), so EmDash can auto-create custom post type taxonomies (e.g. acompanytaxonomy on acompanyCPT) scoped to the right collections, instead of dropping them as "missing"./menus(new): navigation menus with hierarchy and theme locations.permalinkfor SEO-preserving redirect maps, and WPML/Polylang info (per-postlocale/translation_group, site-leveli18n) for multilingual migrations.Hardening
exportcapability (previouslyedit_postswas enough).Type of change
Related issues
?rest_route=fallback); this PR contributes the diagnosis UX (preflight checks) that surfaces those situations early.post_typesper taxonomy, core auto-creates the definitions). Route/template creation for CPTs remains an EmDash-side topic.Testing
Tested end-to-end against two live WordPress sites: a small business site (posts/pages/CPT with ACF + Yoast) and a large blog (~1.8k posts, 3.5k media files, 300+ comments, Rank Math, custom taxonomies). Content, comments, menus, taxonomies, SEO fields, and site identity all arrive in EmDash.
Checklist
readme.txtchangelog updated (1.2.0)AI-generated code disclosure