WP Manifestindependent plugin directory
manifest / updates / data-migrator-plugin

Data Migrator

Securely migrate status-preserving WordPress posts and lightweight page structures, terms, metadata, and media between sites through authenticated REST endpoints and a resumable background queue.

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/mubeenhassan-dev/data-migrator-plugin/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Data Migrator is a custom WordPress plugin for migrating data directly from one WordPress website to another through authenticated REST API communication.

The plugin is designed around a Source / Destination architecture: the old website exposes protected read-only migration endpoints, while the new website creates resumable migration jobs, imports data in batches, tracks mappings, and provides live progress and migration history.

Current version: 0.8.3
Requires WordPress: 6.0+
Requires PHP: 7.4+
License: GPL-2.0-or-later

Current migration support

Users

Data Migrator can migrate WordPress user accounts while preserving the user's existing login password.

The Users migration includes:

  • Username and email address
  • Display name and nicename
  • Website URL and registration date
  • User status
  • Current-site roles and capabilities
  • User level
  • Non-authentication profile/user metadata
  • The existing WordPress user_pass hash
  • Source-to-destination user ID mappings

The password is never requested, exposed, or transferred in plaintext. The destination stores the source WordPress password hash without hashing it again, allowing migrated users to continue signing in with the same password.

For security, the following authentication state is intentionally not migrated:

  • Active WordPress sessions
  • session_tokens
  • WordPress Application Passwords
  • Password reset/activation keys
  • Network super-admin grants
  • Data Migrator's own internal metadata

When Users and Posts are migrated together, Users run first so imported posts can map back to their original migrated authors. A configurable destination-author fallback remains available when an author cannot be mapped.

WooCommerce Products

WooCommerce Products are a first-class migration type. WooCommerce must be active on both the source and destination websites.

The administrator can choose between two product-data modes:

  • Products & variations only — recommended: recreates the standard WooCommerce catalog without copying arbitrary plugin/custom metadata. It includes product type, name/description, SKU, prices and sale dates, inventory/stock settings, tax/shipping fields, dimensions, purchase note, catalog visibility, categories, tags, product brands when registered, shipping class, global/custom attributes, default attributes, featured/gallery images, downloadable files, upsells, cross-sells, grouped-product relationships, and variable-product variations.
  • Full Product Data: includes everything above plus non-core product and variation metadata/custom fields, term metadata, and registered custom product taxonomies. Custom meta keys are preserved exactly. Plugin-specific data is most useful when the same extension/custom code exists on the destination.

Simple products are imported directly. Variable products use a separate paginated Variations stage so large catalogs do not embed every variation in a parent-product response. When the selected source products have no variations, the Variations stage is omitted.

Product images, gallery images, product-category thumbnails, and local downloadable files reuse the shared Media queue. After media has finished, Product setup connects migrated attachments, downloads, upsells/cross-sells, grouped children, category thumbnails, and variable-product synchronization.

Source-to-destination mappings and SKU matching make repeat migrations duplicate-safe. Existing products can be updated or skipped through the shared duplicate strategy.

Product reviews/orders/customers are not part of the Products migration in this release. Extension-specific product types can be recreated only when the extension providing that type is available on the destination; Full Product Data preserves their non-core metadata but cannot reproduce behavior for a missing extension.

Posts

Posts can preserve:

  • Title
  • Content
  • Excerpt
  • Slug
  • Original WordPress status
  • Publish and modified dates
  • Categories and tags
  • Author mapping
  • Featured images
  • Embedded media and galleries
  • Attachments
  • Public custom fields
  • Common Yoast SEO, Rank Math, and AIOSEO metadata

Supported post statuses include Published, Draft, Pending Review, Private, and Scheduled.

Pages

Pages intentionally use a lightweight structural migration because the destination website may use a different theme, builder, or page design.

The page importer migrates:

  • Title
  • Slug
  • Original date
  • Original status
  • Parent/child relationships

It does not replace destination page content, Gutenberg blocks, Elementor/design data, templates, SEO metadata, or custom fields. When a previously imported page is updated, existing destination page content remains intact.

Terms and media

Post migrations include the supporting taxonomy stages for categories and tags, including category parents.

Media options include:

  • Media used by selected posts — recommended
  • Featured images only
  • Entire source Media Library
  • Skip media

Media processing uses batching, duplicate detection, mappings, progress tracking, and background workers.

Migration scope and stages

The worker builds the stage sequence from the import types actually selected by the administrator. Unselected migration types are not processed or displayed as fake stages.

Examples:

Selected import Stages
Users Users
Pages Pages
Products (simple only) Products → Media → Product setup
Products (with variations) Products → Variations → Media → Product setup
Users + Pages Users → Pages
Posts, media skipped Terms → Posts → Finalize → Verify
Posts with media Terms → Posts → Media → Finalize → Verify
Users + Products + Posts + Pages Users → Products → Variations* → Terms → Posts → Pages → Media → Product setup → Finalize → Verify

* The Variations stage is omitted when no selected source products have variations.

Source and destination modes

The plugin can run in:

  • Source mode — exposes authenticated migration endpoints
  • Destination mode — connects to a source and imports data
  • Both mode — enables both responsibilities on the same installation

The responsibilities remain separated internally even when Both mode is selected.

Connection flow

The destination uses a single first-time Save & Connect action.

  1. Enter the source URL, WordPress username, and Application Password.
  2. Click Save & Connect.
  3. Data Migrator performs a lightweight authenticated /status API health check only.
  4. Credentials are encrypted and stored only if that check succeeds.
  5. After a connection is saved, the fields are locked and Test Connection becomes available.

Connection testing does not fetch products, posts, pages, users, media, or migration totals. Product/WooCommerce capability and counts are checked only when Products is selected while creating a migration job.

Forget connection uses a custom confirmation modal and removes the saved username and encrypted Application Password. The source URL remains available for convenience.

Users endpoint security

User migration uses dedicated source endpoints:

/wp-json/data-migrator/v1/users/status
/wp-json/data-migrator/v1/users

The Users endpoints:

  • Require an authenticated administrator with manage_options
  • Use the dedicated status endpoint for permission/count preflight
  • Return user data in batches
  • Mark responses as non-cacheable
  • Never put password hashes into migration activity logs
  • Never persist password hashes in migration queue payloads

HTTPS and local development

Public source websites must use HTTPS for Users export.

Supported examples:

https://oldsite.com  →  https://newsite.com
https://oldsite.com  →  http://newsite.local
http://oldsite.local →  http://newsite.local

HTTP user export is allowed only for recognized local-development sources. WordPress Application Password authentication must also be available on that local source; for normal WordPress local development this commonly means setting WP_ENVIRONMENT_TYPE to local or using local HTTPS.

Do not enable relaxed local/private URL or SSL-verification settings on production destinations.

Background processing

Data Migrator uses:

  1. Action Scheduler when its API is available
  2. WordPress Cron as a fallback
  3. Live-page worker requests to improve throughput while the migration dashboard is open

Migration state is stored in the database, so refreshing or reopening the admin page does not lose the job.

The migration system is designed to remain:

  • Resumable
  • Batch-based
  • Duplicate-safe
  • Mapping-aware
  • Memory-conscious
  • Traceable through activity logs

Live migration dashboard

While a migration is active, the dashboard shows:

  • Overall progress
  • Live elapsed time
  • Current stage
  • Current item/operation
  • Imported count
  • Updated count
  • Skipped count
  • Failed count
  • Recent activity
  • Pause / Resume / Cancel controls when appropriate

The elapsed timer runs client-side every second and is synchronized against the server's elapsed duration. It no longer waits for a worker/status request before visually updating.

When a migration becomes Completed, Failed, or Cancelled, live polling and activity DOM updates stop.

Migration History

Migration History is intentionally separate from the live processing dashboard.

The history table displays completed and previous migration records without starting the live poller. Each row provides View logs, which opens a custom log browser modal.

The logs modal includes:

  • Scope-aware title such as Log #27 - Users + Posts Import
  • Total log count
  • Matching-result count
  • Search
  • Level filter
  • Log type filter
  • 25 / 50 / 100 per-page selection
  • AJAX pagination with First / Previous / numbered pages / Next / Last
  • Browser-local timestamps
  • One controlled modal-body scroll region

Logs are loaded on demand only when the modal is opened.

History cleanup and safe rollback

Data Migrator treats history deletion and imported-content rollback as two different operations.

  • Delete history removes one terminal migration record together with its logs, queue records, and rollback audit data. Destination content is not deleted.
  • Clear history performs the same history-only cleanup for all completed, failed, and cancelled migrations while active migrations remain untouched.
  • Rollback content selectively removes destination objects that the selected migration actually created. Rollback runs through a background queue (Action Scheduler when available, WordPress Cron fallback) while AJAX only monitors deleted, protected/shared, failed, and remaining counts.

Starting with v0.8.0, every imported object receives a migration ownership audit record. Objects that already existed and were only updated or skipped are recorded as protected rather than owned. If an object created by one migration is later reused by another migration, rollback treats it as shared and leaves it in place.

Rollback is dependency-aware: product variations are removed before products, posts/pages before media and taxonomies, and users last. Taxonomy terms are deleted only when they are no longer assigned, and user accounts are protected when deleting them would be unsafe. The feature is deliberately a safe delete of migration-created objects, not a snapshot restore of pre-existing objects.

Starting with v0.8.2, rollback execution is independent of the Migration History page. Starting a rollback queues a dedicated background worker. If Action Scheduler is available it is used automatically; otherwise Data Migrator schedules WordPress Cron events. The modal polls read-only rollback status through AJAX, so it can be closed and reopened without stopping the rollback. The worker processes one dependency-aware batch at a time, uses a migration-specific lock to avoid concurrent deletion workers, and retries unexpected worker failures with a bounded retry count.

Migrations created before v0.8.0 do not have reliable ownership records, so the UI marks them Rollback unavailable instead of guessing and risking deletion of pre-existing content.

Duplicate handling

Data Migrator stores source-to-destination mappings so repeat migrations do not blindly create duplicate content.

For Users, the importer can update or skip an existing account. Identity-conflict protection prevents an unsafe merge when the source username and source email independently match different destination accounts.

For Products, Posts, and Pages, mappings allow future runs to update previously migrated records according to the selected duplicate strategy. Products can additionally match an existing non-variation destination product by SKU when no Data Migrator mapping exists.

Installation

  1. Install and activate Data Migrator on both WordPress websites.
  2. On the old website, go to Data Migrator → Settings and choose Source or Both.
  3. Create a WordPress Application Password for the account Data Migrator will use.
  4. Use an administrator account when migrating Users. If migrating Products, activate WooCommerce on both websites.
  5. On the new website, choose Destination or Both.
  6. Open Data Migrator → Import Data.
  7. Enter the source URL, username, and Application Password.
  8. Click Save & Connect.
  9. Select Users, Products, Posts, Pages, or a combination.
  10. Configure the relevant filters/options and click Create migration job.

For maximum throughput on a local-development destination, keep the live migration page open while the job runs.

Rollback is different: once queued, rollback processing runs in the background and does not require the Migration History modal or browser tab to remain open. Action Scheduler is used when available, otherwise WordPress Cron processes rollback batches.

REST API

Namespace:

/wp-json/data-migrator/v1/

Current source endpoints include:

GET /status
GET /summary
GET /posts
GET /pages
GET /terms
GET /authors
GET /media
GET /media/{id}
GET /users/status
GET /users
GET /products/status
GET /products
GET /product-variations

Endpoints are protected by WordPress authentication/capability checks and source-side rate limiting.

Security design

Data Migrator is built to avoid moving secrets through the browser unnecessarily.

  • Saved source Application Passwords are encrypted using keys derived from WordPress authentication salts.
  • Saved credentials are not returned through AJAX after connection.
  • Connection-changing requests use non-cacheable admin AJAX behavior.
  • Public Users export requires HTTPS.
  • Password hashes are restricted to the administrator-only Users endpoint.
  • Password hashes are not included in logs or persistent queue job payloads.
  • Session tokens and Application Passwords belonging to migrated users are excluded.
  • Source REST requests and migration creation are rate-limited.
  • Migration locks prevent duplicate active migrations of the same protected type, including Products.
  • Product API responses are non-cacheable; Full Product Data is available only through authenticated migration endpoints.
  • Product custom metadata is never loaded during Save & Connect/Test Connection.

Project structure

data-migrator/
├── data-migrator.php
├── README.md
├── admin/
│   ├── class-admin.php
│   ├── css/
│   ├── js/
│   └── views/
├── includes/
│   ├── source/
│   ├── destination/
│   ├── class-database.php
│   ├── class-job-repository.php
│   ├── class-logger.php
│   ├── class-mapping-repository.php
│   ├── class-migration-guard.php
│   └── ...
├── languages/
└── uninstall.php

Development rules

When extending the plugin:

  • Maintain backward compatibility where practical.
  • Do not expose saved credentials or user password hashes to admin JavaScript.
  • Keep source and destination responsibilities separate.
  • Keep migrations resumable and batch-based.
  • Avoid loading large source datasets into memory.
  • Preserve source-to-destination mappings.
  • Log migration activity without logging authentication secrets.
  • Do not clone user sessions or API credentials.
  • Use WooCommerce CRUD setters for standard product fields; reserve direct meta copying for Full Product Data custom metadata.
  • Keep product/variation imports paginated and media references in the shared Media queue.
  • Add new migration types as scope-aware stages rather than forcing unrelated stages through every job.

Release history

0.8.3

  • Removed the legacy WordPress readme.txt file from the plugin package.
  • README.md is now the single bundled project documentation file.
  • No migration behavior changed in this maintenance release.

0.8.2

  • Moved rollback execution from browser-driven AJAX loops to a resumable background queue.
  • Uses Action Scheduler automatically when available, with WordPress Cron fallback.
  • AJAX now starts rollback and monitors progress only; leaving the page does not stop deletion processing.
  • Added persistent rollback state, queue engine/heartbeat details, safe worker locking, delayed retries, and bounded failure handling.
  • Added compatibility recovery for interrupted v0.8.0/v0.8.1 rollback queues.
  • Prevents history deletion while a background rollback is active and cancels rollback actions when history is intentionally deleted.

0.8.1

  • Fixed WooCommerce variation attribute assignment so migrated variations are automatically connected to their correct attribute combinations.
  • Custom/local attributes preserve exact option labels instead of slugifying them.
  • Global taxonomy attributes resolve destination taxonomy and term mappings before saving variation data.
  • Full Product Data excludes raw attribute_* variation meta because variation attributes are now migrated structurally.

0.8.0

  • Added migration ownership auditing for reliable rollback decisions.
  • Added selective Safe Rollback from Migration History with AJAX batch progress.
  • Added rollback protection for pre-existing and later-shared destination objects.
  • Added dependency-aware deletion for users, products/variations, posts, pages, media, taxonomies, and WooCommerce attributes.
  • Added per-run Delete history and bulk Clear history actions that remove history data only.
  • Older migrations remain visible but are not automatically rollbackable because they were created before ownership tracking existed.

0.7.1

  • Added immediate visual feedback while a migration job is being prepared.
  • The Create migration job button now shows an animated Preparing migration… state as soon as it is clicked.
  • Added an inline preparation panel explaining that source access, selected content, totals, and prerequisites are being checked.
  • Added a slower-request message for larger source sites so queue setup does not appear frozen.
  • Preparation failures are displayed beside the migration action and the form is restored for retry.

0.7.0

  • Added first-class WooCommerce Products migration with dedicated Products, Variations, Media, and Product setup stages.
  • Added Products & variations only and Full Product Data modes.
  • Added standard WooCommerce product fields, categories/tags/brands, shipping classes, attributes, images, downloads, related products, stock/pricing, and variable-product variations.
  • Added full custom product/variation metadata, term metadata, and registered custom product-taxonomy migration when Full Product Data is selected.
  • Added WooCommerce source/destination preflight checks, product migration locking, progress/history support, and duplicate-safe SKU/mapping behavior.
  • Added product-specific REST endpoints: /products/status, /products, and /product-variations.
  • Product APIs use pagination and no-cache responses; Save & Connect remains a lightweight /status check.
  • Product media uses the existing shared Media queue and finalization resolves attachment IDs, local download URLs, product relationships, and category thumbnails.

0.6.8

  • Refreshed and expanded public project documentation.
  • Added a GitHub-focused README.md describing the current architecture and migration flows.
  • Corrected the v0.6.4/v0.6.5 changelog grouping in the public documentation.
  • No migration behavior changed in this documentation release.

0.6.7

  • Added migration-scope titles to History log modal headings, e.g. Log #27 - Users + Posts Import.

0.6.6

  • Removed nested scrolling from the History logs modal and moved scrolling to one controlled body region.

0.6.5

  • Made elapsed migration time tick every second independently of server polling.

0.6.4

  • Added AJAX log pagination, total/matching counts, search, level/type filters, and per-page controls.

0.6.3

  • Made migration stages scope-aware.
  • Stopped polling terminal migrations.
  • Reworked Migration History around an on-demand logs modal.
  • Converted displayed migration/log times to the viewer's browser timezone.

0.6.2

  • Fixed terminal-state live controls remaining visible.

0.6.1

  • Standardized the Migration History live-control button and fixed hero heading contrast.

0.6.0

  • Added first-class WordPress Users migration with password-hash preservation, roles/capabilities, profile metadata, author mapping, security restrictions, and user migration locks.

Earlier versions established the REST migration architecture, background queue, Posts/Pages/Media support, saved connection flow, duplicate protection, migration locks, and rate limiting.

License

Data Migrator is licensed under the GNU General Public License v2 or later (GPL-2.0-or-later).