=== ZHBackup – Restore & Migration ===
Contributors: zainhassandeveloper
Tags: backup, import, export, migration, database
Requires at least: 5.8
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 2.2.0
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Full-site WordPress backup, restore, and migration with structured database packages and chunked server-side imports.

== Description ==

**ZHBackup – Restore & Migration** is a full-site WordPress backup and restore plugin built for real migration workflows. It packages your database, media, plugins, themes, selected `wp-content` folders, root files, and optional custom root folders, then restores them through a chunked process designed to avoid request timeouts.

The database is stored as a structured package instead of one large SQL replay file, so large page-builder data, post meta, options, and other oversized values can be restored in smaller, traceable steps. Live activity logs show the progress of exports, imports, database rows, large values, file restore, validation, and cleanup.

After a restore, ZHBackup lets you review the site first and run stale-file cleanup separately from the restore process. This keeps restore completion predictable while still giving you a controlled way to remove old plugins, uploads, themes, and selected root-folder files that are not present in the backup.

**Key Features:**

* **Full-Site Backups** — Back up the database, uploads, plugins, themes, mu-plugins, selected `wp-content` folders, root files, and optional custom root folders.
* **Structured Database Package** — Export database schemas, row chunks, and large values separately for reliable restores of Elementor/page-builder data and other large rows.
* **Chunked Export and Restore** — Process database and file operations in small AJAX steps to reduce timeout and memory-limit issues.
* **Browser Upload or Server Restore** — Upload backups through the browser in resumable chunks, or place backups on the server via FTP/SFTP and restore from a table of available files.
* **Flexible Archive Formats** — Create standard `.zip` backups or optimized `.zhbackup` archives.
* **Live Activity Logs** — Follow professional export/import logs with database checkpoints, row counts, large-value restore events, file progress, validation, and cleanup status.
* **Post-Restore Cleanup** — Save a cleanup plan after restore and run stale-file cleanup manually after reviewing the restored site.
* **Serialized-Safe Find & Replace** — Update URLs safely across serialized PHP data, JSON-escaped URLs, and Elementor widget data.
* **Secure Backup Storage** — Store backups outside the webroot when available, with `.htaccess`, `web.config`, and `index.php` protections when stored inside the site.
* **Retention Policies** — Automatically clean up older backups by count or age using WP-Cron.
* **No Required Account** — Use all backup, restore, migration, and cleanup features without a license key or forced signup.

**WordPress.org Compliant:**

* No obfuscated or encoded PHP
* Proper sanitization for all inputs
* Escaped outputs throughout
* Nonces for all form submissions
* Capability checks on every action
* No hidden external API calls
* No telemetry without explicit consent

== Installation ==

1. Upload the `zhbackup` folder to `wp-content/plugins/`.
2. Activate the plugin through the **Plugins** menu in WordPress.
3. Navigate to **ZHBackup** in the admin sidebar to start creating backups.

**Alternatively:**

1. Go to **Plugins → Add New** in your WordPress admin.
2. Search for "ZHBackup – Restore & Migration".
3. Click **Install Now**, then **Activate**.

== Frequently Asked Questions ==

= Does this plugin work on shared or managed hosting? =

Yes. ZHBackup uses chunked export, upload, import, and restore steps so each request stays small. The database is also restored table-by-table from a structured package instead of replaying one large SQL file.

= How large of a site can this handle? =

There is no fixed size limit in the plugin. Very large sites depend on available disk space, PHP limits, and database performance, but ZHBackup is designed to process large file sets and databases in small resumable steps.

= How does ZHBackup handle large Elementor or page-builder data? =

Large database values are stored separately from normal row chunks and restored in smaller append operations. This helps preserve large `_elementor_data`, post meta, options, and other oversized rows during restore.

= What is the difference between `.zip` and `.zhbackup`? =

Both formats can contain the same backup data. `.zip` is a standard archive format. `.zhbackup` is ZHBackup's optimized archive format for this plugin. Both support the structured database package.

= Can I restore a backup uploaded by FTP or File Manager? =

Yes. Upload a `.zip` or `.zhbackup` file to the backup storage directory shown on the Import screen. It will appear in the server backup table, where you can restore it directly.

= Where are backups stored? =

The plugin automatically selects the most secure location. It prefers a directory outside the webroot (not accessible via browser), but falls back to `wp-content/zhbackup-backups/` if needed, where files are protected by `.htaccess` rules.

= Can I include custom folders from the WordPress root? =

Yes. The export screen can include optional first-level folders from the WordPress root. WordPress core folders such as `wp-admin`, `wp-includes`, and `wp-content` are handled separately or excluded automatically.

= Can I import a backup from a different domain? =

Yes. After importing, use the built-in **Find & Replace** tool to update old URLs to your new domain. It handles serialized data safely, including Elementor widget data.

= What is post-restore cleanup? =

Restore copies files from the backup first. If older files or folders still exist on the destination site, ZHBackup saves a cleanup plan. After you verify the restored site, go to **ZHBackup → Cleanup** to remove stale files that are not present in the backup.

= Will cleanup fail the restore if file permissions block deletion? =

No. Cleanup is manual and best-effort. Permission-related deletion failures are logged as warnings so the restore can remain complete and you can review any paths that need manual attention.

= Is the email signup required? =

No. The email signup on the Settings page is completely optional and can be dismissed permanently. The plugin is fully functional without it.

= Is the `.zhbackup` format free to use? =

Yes. The `.zhbackup` export format is included for free and does not require any license key or activation page.

= Does this plugin make external API calls? =

Yes, but only for the optional newsletter signup on the Settings page after an administrator explicitly enters an email address and submits the form. No data is sent anywhere during backup, restore, migration, import, export, or find-and-replace operations.

= Will importing overwrite my existing site? =

Yes. Importing a backup restores the backed-up database and files over the current site. Always create a fresh backup of the current site before importing.

== External services ==

ZHBackup includes an optional newsletter signup form on the plugin Settings page.

This feature connects to an external service hosted by Hassan Zain at `hassanzain.com`. It is used only to add the submitted email address to the ZHBackup newsletter or product updates list.

Data is sent only when an administrator manually submits the optional signup form. The plugin does not contact this service during normal backup, restore, migration, import, export, or find-and-replace usage.

When the form is submitted, the plugin sends:

* Email address entered in the signup form
* Site URL (`home_url()`)
* Plugin slug (`zhbackup`)
* Plugin version

Terms of service: https://hassanzain.com/terms-and-conditions
Privacy policy: https://hassanzain.com/privacy-policy

== Privacy ==

ZHBackup does not send telemetry or usage analytics during normal backup, restore, migration, import, export, or find-and-replace operations.

The only optional external data transfer is the administrator-initiated newsletter signup described above.

== Screenshots ==

1. Export screen for choosing backup components, wp-content folders, optional folders and files, and the backup archive format.
2. Existing backups table with backup contents, file size, date, download, restore, and delete actions.
3. Import screen with chunked browser upload plus server-side backup restore from uploaded `.zip` or `.zhbackup` files.
4. Find & Replace screen for serialized-safe URL replacement across WordPress database tables.
5. Restore Cleanup screen for reviewing and running pending stale-file cleanup after a restore.
6. Settings screen showing storage security status, backup engine details, and server health checks.


== Changelog ==

= 2.2.0 =
* Fixed an invalid SQL statement (SHOW INDEX ... ORDER BY) that logged database errors on every export, could break export start, and left tables dumped without a stable order.
* Greatly improved export speed on large databases by switching from OFFSET to keyset (seek) pagination and buffering row writes, so large tables (e.g. postmeta with ~1M rows) no longer slow down as the export progresses.
* Greatly improved import speed on large databases by restoring rows as batched multi-row statements instead of one query per row.
* Fixed a "You do not have permission" error reported at the end of a same-site import, caused by the users/usermeta table swap invalidating the current login session. The importing admin's session is now re-established automatically after the swap.
* Fixed the import progress bar jumping backward during the file restore; progress now only moves forward.
* Redesigned post-restore Cleanup to be safe and reviewable. It now scans for top-level folders that are not part of the backup and lists them for review; nothing is deleted until you select items and confirm. This removes the previous automatic deletion that could remove wanted files.
* Fixed imports that could break a site by mixing two versions of a plugin or theme (for example a missing module class fatal). Each plugin/theme is now restored as a clean, complete copy and swapped into place atomically, keeping the live site loadable throughout the restore.

= 2.1.0 =
* Fixed unreliable database restores when importing onto an existing/live site. The structured restore now rebuilds each table under an isolated temporary name and swaps it into place with an atomic rename, so core tables (options, users, usermeta) stay intact and queryable throughout the chunked import. This resolves intermittent 403 "permission" / "invalid nonce" errors and stalled imports caused by core tables being dropped mid-restore between requests.
* Fixed file-only and folder-only exports producing an empty archive. Selecting components without the database (for example only specific wp-content folders or only root files) now correctly packages the chosen files.
* Fixed a fatal error ("critical error" page) that could occur during exports and on normal page loads. The scheduled cleanup task used a namespaced helper as a string callback, which PHP resolved in the global namespace and could not find, aborting the request. The callback is now fully qualified.
* Improved database view restoration to drop and recreate views against the final tables after the table swap, instead of attempting an invalid table drop.
* Improved post-restore validation so a fully restored site is no longer reported as a hard failure for advisory issues; failed data rows and homepage checks are now surfaced as warnings in the activity log.

= 2.0.0 =
* Added a structured database package that exports schema files, table row chunks, and separate large-value files for reliable full-site restores.
* Added table-by-table database restore with checkpoints, validation, large-value restoration, URL migration support, and detailed activity logs.
* Added optional custom root folder backup and restore for first-level folders in the WordPress root.
* Added manual post-restore cleanup so administrators can review the restored site before removing stale files.
* Added a redesigned admin experience with cleaner backup options, optional item grouping, compact backup tables, icon tooltips, and professional live logs.
* Improved server-side import with a table of available backups and one-click restore actions.
* Improved backup content selection for database, root files, wp-content folders, root folders, archive format, and additional database tables.
* Improved archive support for both `.zip` and `.zhbackup` formats.

= 1.0.0 =
* Initial public release.
* Full site backup with chunked AJAX export and import.
* Support for `.zip` and `.zhbackup` backup formats.
* Chunked, resumable browser uploads for large imports.
* Server-side import from backups uploaded via FTP/SFTP.
* Serialized-safe find and replace with Elementor support.
* Automatic secure storage with `.htaccess`, `web.config`, and `index.php` protection.
* Backup retention policies (by count or age).
* ZipArchive with PclZip fallback.
* Optional, dismissible email signup with no forced account requirement.
* WordPress.org-compliant release with no license activation system.

== Upgrade Notice ==

= 2.2.0 =
Faster exports and imports on large databases, a safer scan-and-review cleanup that never auto-deletes, a fix for the end-of-import permission error, and reliable plugin/theme restores that no longer mix versions or break the site mid-import.

= 2.1.0 =
Reliability update: same-site/live restores now use atomic table swaps to prevent mid-import failures, file-only and folder-only exports are fixed, a fatal "critical error" from the cleanup task is resolved, and post-restore validation no longer fails a successfully restored site.

= 2.0.0 =
Major backup and restore upgrade with structured database packages, optional root folder support, manual cleanup, improved logs, and a redesigned admin interface.

= 1.0.0 =
Initial release. Install and start backing up your site.
