fix(site-exporter): include plugins/themes/uploads in export and activate themes on import#1140
fix(site-exporter): include plugins/themes/uploads in export and activate themes on import#1140superdav42 merged 2 commits intomainfrom
Conversation
Two notices were rendering above page content on PHP 8.1+ environments: 1. Domain_Mapping::replace_url() — when the filter chain produced a relative URL (no host) or a null/false value, parse_url() returned null and preg_quote(null, '#') triggered the deprecation. Guard the URL early and bail before passing a null host into preg_quote(). 2. wu_create_customer() — when called without an email key, wp_parse_args defaulted it to false, which then reached sanitize_email() at the second call site. Both sanitize_email() calls now require a string. Adds three regression tests for the replace_url guards: - null URL - empty string URL - relative (host-less) URL
…vate themes on import
Customers reported that exporting a site with the "Include Plugins",
"Include Themes" and "Include Uploads" toggles produced ZIPs containing
only the database dump — the plugin, theme, and upload directories were
silently dropped. Importing the resulting ZIP into a fresh WordPress
install therefore could not reproduce the original site.
Root cause: `Helpers\runcommand()` and `Helpers\launch_self()` in
`inc/site-exporter/mu-migration/includes/helpers.php` gated the WP-CLI
path on `PHP_SAPI === 'cli'`, but the `wp-cli/wp-cli` Composer package
is autoloaded at all times. In PHPUnit (and any other PHP CLI process
that loads our autoloader without booting WP-CLI), this caused
`\WP_CLI::runcommand()` to be invoked against a non-bootstrapped WP-CLI,
which raised "Undefined constant WP_CLI_ROOT". The exception was caught
upstream and the export silently continued without producing the
intermediate users.csv / tables.sql / meta.json files, so the zipper
saw an empty fileset and emitted a database-only archive. The same
trap also disabled `theme enable` during import, leaving imported
themes on disk but never activated.
Fix:
- Replace `PHP_SAPI === 'cli'` with `defined('WP_CLI') && WP_CLI` in
both `runcommand()` and `launch_self()` so the polyfill engages
whenever WP-CLI is not actively running. This matches the pattern
used elsewhere in the codebase (`Site_Exporter::setup`,
`trait-wp-cli`, `external-cron-manager`).
- Add a `theme enable` branch to the polyfill that calls WordPress core
`switch_theme()` so imported themes activate in web/AJAX context.
- Move the intermediate users/tables/meta files from CWD to
`sys_get_temp_dir()`. CWD-relative writes silently failed on
locked-down hosts (producing the "DB-only export" symptom) and
polluted the WordPress root or plugin source tree on aborted runs.
- Pass explicit `$enclosure`/`$escape` arguments to `fputcsv()` and
`fgetcsv()` to silence the PHP 8.4 deprecation notice that was
flooding test logs and to keep the CSV format stable across
PHP 9.
Tests:
- `Site_Exporter_Zip_Contents_Test`: drives `ExportCommand::all()` with
redirected fixtures and asserts the resulting ZIP contains plugin,
theme, and upload entries (was producing a DB-only ZIP before the
fix).
- `Site_Exporter_Round_Trip_Test`: exports then extracts the package
and checks plugin/upload trees round-trip byte-for-byte.
- `Site_Exporter_Import_Move_Test`: exercises the private movers in
`ImportCommand` (`move_uploads`, `move_themes`) and verifies the
`theme enable` polyfill calls `switch_theme()`.
All 64 site-exporter tests pass after the fix; reverting any single
change makes the corresponding test fail.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThis PR combines defensive input validation across customer creation and domain mapping with significant robustness improvements to the Site Exporter module, including CSV parameter hardening for PHP 8.4 compatibility, temporary file isolation, WP-CLI runtime detection, and comprehensive test coverage. ChangesInput Validation Hardening
Site Exporter Robustness & Testing
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔨 Build Complete - Ready for Testing!📦 Download Build Artifact (Recommended)Download the zip build, upload to WordPress and test:
🌐 Test in WordPress Playground (Very Experimental)Click the link below to instantly test this PR in your browser - no installation needed! Login credentials: |
Summary
Bugs fixed
Files changed
Tests added
VerificationAll 64 site-exporter tests pass; pre-existing tests are unchanged. Test plan for reviewers
aidevops.sh v3.14.78 plugin for OpenCode v1.14.33 with claude-sonnet-4-6 spent 2d 10h on this as a headless worker. Merged via PR #1140 to main. |
1 similar comment
Summary
Bugs fixed
Files changed
Tests added
VerificationAll 64 site-exporter tests pass; pre-existing tests are unchanged. Test plan for reviewers
aidevops.sh v3.14.78 plugin for OpenCode v1.14.33 with claude-sonnet-4-6 spent 2d 10h on this as a headless worker. Merged via PR #1140 to main. |
|
Performance Test Results Performance test results for a290e4c are in 🛎️! URL:
|
Summary
inc/site-exporter/mu-migration/includes/helpers.phpusedPHP_SAPI === 'cli', which is true under PHPUnit (and any other CLI-mode PHP that autoloadswp-cli/wp-cliwithout bootstrapping WP-CLI). That sent calls into a non-bootstrapped\WP_CLI::runcommand()and raisedUndefined constant WP_CLI_ROOT, which the upstream try/catch swallowed — leaving the export with no users.csv/tables.sql/meta.json and no plugin/theme/upload paths, so the zipper produced a DB-only archive. The same trap killedtheme enableon import, leaving imported themes on disk but never activated.defined('WP_CLI') && WP_CLIcheck (matchingSite_Exporter::setup,trait-wp-cli, andexternal-cron-manager), adds atheme enablebranch to the polyfill, and moves intermediate users/tables/meta files from CWD tosys_get_temp_dir()so locked-down hosts no longer silently drop them.Bugs fixed
runcommand()/launch_self()SAPI-only gate engaged\WP_CLIwithout a live WP-CLI runtime; intermediate files were never written and the zipper saw an empty fileset.Helpers\runcommand('theme enable', ...)had no polyfill in web/AJAX context.fputcsv()/fgetcsv()$escapeparameter, which would also break round-tripping CSVs in PHP 9.Files changed
inc/site-exporter/mu-migration/includes/helpers.php— fix WP-CLI gate inruncommand()andlaunch_self(), addtheme enablepolyfill.inc/site-exporter/mu-migration/includes/commands/class-mu-migration-export.php— write intermediate files to system temp dir; explicitfputcsv()enclosure/escape.inc/site-exporter/mu-migration/includes/commands/class-mu-migration-import.php— explicitfgetcsv()enclosure/escape to match.Tests added
tests/WP_Ultimo/Site_Exporter_Zip_Contents_Test.php— drivesExportCommand::all()with redirected plugin/theme/upload fixtures and asserts the resulting ZIP contains the right entries (wp-content/plugins/<slug>/<file>,wp-content/themes/<slug>/style.css,wp-content/uploads/2024/01/photo.jpg, etc.). Includes a regression test for the existingwu_site_exporter_plugin_exclusion_listfilter (ultimate-multisitemust be excluded, other plugins preserved).tests/WP_Ultimo/Site_Exporter_Round_Trip_Test.php— exports a fixture site, then extracts the produced ZIP viaHelpers\extract()and asserts plugin/upload trees round-trip byte-for-byte.tests/WP_Ultimo/Site_Exporter_Import_Move_Test.php— exercises the private import movers (move_uploads,move_themes) and confirms the newtheme enablepolyfill callsswitch_theme().Reverting any single change in
helpers.phpmakes the corresponding test fail with the originalUndefined constant WP_CLI_ROOTerror or with an unswitched theme.Verification
All 64 site-exporter tests pass; pre-existing tests are unchanged.
Test plan for reviewers
Sites → Export, tick Include Plugins, Include Themes, Include Uploads, submit synchronously.unzip -l export.zip— confirm the listing contains entries underwp-content/plugins/,wp-content/themes/<slug>/, andwp-content/uploads/. Before this PR the listing only contained the.csv,.sql, and.json.wp-content/plugins, the imported theme is active (not just present), and uploaded media is reachable from the Media library.aidevops.sh v3.14.78 plugin for OpenCode v1.14.33 with claude-sonnet-4-6 spent 2d 10h on this as a headless worker.
Summary by CodeRabbit
Release Notes
Bug Fixes
Tests