=== OmniMigrate – Advanced Migration & Automated Backup ===
Contributors: davidshum98
Tags: backup, restore, migration, clone, database
Requires at least: 5.0
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 2.6.3
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Back up, restore and move a whole WordPress site: database and files in one file, streamed, with no ZipArchive and no size limit.

== Description ==

OmniMigrate copies your whole site — database and files — into a single `.omni` file, and puts it back on any other WordPress install. It was written for the sites that usually defeat a backup: several gigabytes, tens of thousands of files, shared hosting with short time limits and little memory.

**No ZipArchive, no temporary copy of your site.** The `.omni` format is written as a stream, file after file, so memory use stays flat whether the site is 50 MB or 4 GB. Everything happens in batches: each batch works for a few seconds, saves where it got to, and the next one carries on. A timeout no longer means starting over.

= What it does =

* **Full backup**: database, plugins, themes, uploads and the rest of wp-content. You can leave out the media library, the plugins or the themes.
* **Database only**: a backup of just the database, which is a tiny fraction of the size, for frequent copies. Restoring one puts the database back and leaves your files alone.
* **Restore**: from a file on your computer or from a backup already on the server.
* **Migration**: restore a backup on a different domain and the URLs are rewritten everywhere, including inside serialized data, so widgets, theme options and page builders keep working.
* **Scheduled backups**: every day, or on the weekday you pick, at the hour you choose, with a switch to turn them on and off and an email when they finish or fail.
* **Retention**: keep only the last N backups and the older ones are deleted on their own.
* **WP-CLI**: `wp omni backup`, `wp omni list`, `wp omni delete` and `wp omni schedule`, so a real cron job or a deploy script can make backups without a browser.
* **Log and diagnostics**: what happened, step by step, plus your server's limits.
* **Translatable**, with a language picker of its own: the plugin's screens can be in a different language from the rest of the site.
* **Light, dark or automatic**, chosen per user and only on this plugin's screen.

= Built for large sites =

* Batches that resume: a cut connection, a proxy timeout or a full disk stop the job, they do not corrupt it.
* Uploads in chunks sized to your server's real limit, so a big file does not die at `upload_max_filesize`.
* While the database is half written, visitors get a maintenance page (503) instead of a broken site, and nothing writes rows that would clash with the copy.
* If a restore is interrupted, the database copy taken before it started is kept, and the admin screen offers to put it back for up to 7 days.
* Only the tables of *this* site are touched: another WordPress sharing the same database is left alone.

= Your data stays on your server =

OmniMigrate does not connect to any external service. Backups are written to `wp-content/omnimigrate`, inside a folder with a random name, protected for Apache and IIS and not listable. There is no telemetry, no account and no remote storage: the file is yours and never leaves the server unless you download it.

== Installation ==

1. Install and activate the plugin.
2. Go to **OmniMigrate** in the admin menu.
3. Press **Create backup**. When it finishes, the file appears in the list, ready to download.

To move a site: make a backup on the old one, download it, install this plugin on the new one and upload the file under **Import**.

== Frequently Asked Questions ==

= Does it work on shared hosting? =

That is what it was written for. Work is split into batches of a few seconds, memory use stays flat, and uploads are cut into chunks that fit your server's limits. Nothing needs shell access, `mysqldump`, ZipArchive or extra PHP extensions.

= How big a site can it handle? =

There is no limit in the plugin. Sites of several gigabytes and tens of thousands of files are the case it was built for. The real limits are your disk space and, when you download, your connection.

= The site I restore has different users =

Yes, and that is the point: a restore puts back the database of the site the backup came from, including its users. When it finishes, log in with a user from that site. The screen says so before you start.

= Can I restore on a different domain? =

Yes. Addresses are rewritten everywhere they appear, including inside serialized data, which is what usually breaks widgets and page builder content when a site is moved by hand.

= Does the plugin add anything outside its own folder? =

It never generates PHP code. Your backups go in `wp-content/omnimigrate`, a folder of its own named after the plugin, along with the plugin's own work files and the two placeholders (an empty `index.php` and a key file that both end in `exit`) that keep that folder from being listed or guessed. It is not inside the uploads folder on purpose: a restore replaces your options table halfway through, so a path that depends on it would move while the restore is still looking for its own state.

Restoring a backup is the one thing that writes elsewhere, and the files are yours, not the plugin's: a full backup includes your `wp-content/mu-plugins` and the drop-ins at the root of `wp-content` (`object-cache.php`, `db.php`, `advanced-cache.php`), and putting them back where they were is what restoring a whole site means. They are held back until the very last step, so they never run while the database is half replaced.

While a restore is running, the plugin also writes one row in the options table of your database: it is the only plugin left active until the restore ends, which is what lets it show visitors the maintenance page and keep the site answering on its own address while the database is half replaced. When the restore finishes, or is undone, that row goes and your plugins and theme come back.

= What happens to my backups if I delete the plugin? =

They stay. Uninstalling removes the plugin's own settings and its work files, but never a `.omni` file: it may be the only copy of your site. Delete them yourself from `wp-content/omnimigrate`.

= What is a database-only backup for? =

Your content changes every day; your files hardly ever do. A database-only backup is small enough to make daily or even hourly, and it is what you need to undo a bad edit, a plugin that mangled your options or a failed update. On a typical site it is under 1 MB against several hundred MB for a full backup.

Restoring one replaces the database and **does not touch your files**. Keep making full backups too: a database alone will not bring a site back if you lose the server.

= What happens if I set a maximum number of backups and leave scheduled backups on? =

They work together, and nothing grows without limit: after each backup the oldest ones are deleted until only the maximum remain.

Two are never deleted: the one that was just made, and **the most recent complete backup**. That second rule matters if you schedule database-only backups, which are a tiny fraction of the size. Without it, a daily database-only backup with the maximum set to 3 would push your last complete backup out in three days, and you would be left with nothing to rebuild the site from.

= Do scheduled backups really run on their own? =

They are started by WP-Cron, which only runs when somebody visits the site. On a site with visitors that is enough. On a quiet one, ask your host for a real cron job calling `wp-cron.php` every few minutes, or use WP-CLI, which does not depend on visits at all:

    0 3 * * * cd /path/to/site && wp omni backup --quiet

A large backup is made in pieces, so it may take several runs to finish; each run carries on where the last one stopped, and a backup you start by hand is never interrupted by a scheduled one.

= Can I have the plugin in a different language from the site? =

Yes. Under **Settings** there is a **Language** picker that only affects this plugin's screens, messages and emails; the rest of WordPress stays as it is. It is there for anyone who looks after sites in a language that is not their own: the client's site stays in their language and this screen stays in yours.

The picker only appears once there is more than one language installed for the plugin. Translations are installed by WordPress from translate.wordpress.org, like any other plugin; none are bundled here. If you want to use your own, drop the `.mo` into `wp-content/languages/plugins/`, where it survives plugin updates.

The logs under **View logs and diagnostics** are always in English, whatever language the screens are in, so they can be pasted into the support forum.

= Can I change how long each batch runs? =

Every batch is one HTTP request. Copying files asks for 45 seconds where your `max_execution_time` is unlimited or 180 seconds or more, and 15 seconds where it is tighter; every other step uses 15 seconds. Longer batches mean fewer requests, and so less time spent loading WordPress and your plugins, but each request lasts longer. To choose yourself:

`add_filter( 'omnimigrate_batch_seconds', function ( $seconds, $copying_files ) {
	return $copying_files ? 90 : $seconds;
}, 10, 2 );`

Keep it under whatever your web server allows, not just PHP: nginx waits 60 seconds by default. Going over is not dangerous — the progress of a backup is saved every ten seconds, so a request that is cut off only loses the seconds since the last save — but the job will keep being interrupted.

= I use nginx =

nginx ignores `.htaccess`. The backup folder has a random name, so it cannot be guessed, but it is better to block it in the server config as well. The plugin shows you the exact line.

== Screenshots ==

1. The plugin screen: back up, restore, retention and the list of backups.
2. Creating a backup, with progress and what is being copied.
3. Restoring a backup: what is about to happen, and the confirmation it asks for.
4. The log and the diagnostics of your server.

== Changelog ==

= 2.6.3 =
* Every value read from a request is now sanitized where it is read, as well as validated where it is used. This covers the whole plugin, not only the places reported in review.
* The folders of the site a backup came from are recorded with `wp_normalize_path()` instead of replacing separators by hand.
* No change in what the plugin does.

= 2.6.2 =
* Housekeeping only: the `Author URI` header has been removed. Nothing the plugin does has changed.

= 2.6.1 =
* The plugin is now called OmniMigrate. It used to carry a name that belongs to a company the author works for but does not own, which could suggest the plugin was that company's product. It is a personal project, so the name, the folder, the admin screen and everything inside the code have been changed to match.
* Backups made by earlier versions are kept. Their folder is moved to `wp-content/omnimigrate` on the first load, and the `.lyn` files they contain are still listed, downloaded and restored exactly as before. New backups are written as `.omni`.
* Settings and the schedule are stored under the new prefix, so a site that ran an earlier version has to set its schedule again.

= 2.6.0 =
* The plugin no longer generates any PHP file. The two files it used to write into `wp-content/mu-plugins` are gone: the "fast mode" has been dropped altogether, and what the recovery file did is now done by writing one row in the options table, which is the one table a restore never lets go of. If an older version left one of those files behind, it is deleted on update.
* Backups move from `wp-content/uploads/lyn-backups` to `wp-content/omnimigrate`, a folder of its own named after the plugin. Whatever was in the old location is brought across on its own, together with the state of an unfinished restore and its undo copy. If it cannot be moved (a different disk) the screen says so and tells you where it is.
* Classes and AJAX actions take the plugin's full prefix (`OmniMigrate_*`, `omnimigrate_*`). Your settings and your schedule are untouched: those already used the long prefix.
* Uploaded chunks are now saved with WordPress's own `wp_handle_upload()` instead of moving the temporary file directly.
* Fixed: table and column names were quoted with an escape that does not escape the backtick, which is the character that closes an identifier in MySQL. A table whose name contained one — and a table name can come from inside a backup file — produced invalid SQL. There is now a single quoting helper and every query uses it.
* The per-batch time limit is only lifted where work actually takes time, not in the helper that merely reads it, so a diagnostics request no longer changes the timeout of the page it runs on.
* Backups of large sites are quick again. Every batch is one request, and every request loads WordPress with all of your plugins: on a site with fourteen of them that is about six seconds per batch. Copying files now asks for a 45-second batch where your server clearly allows it, and keeps the old 15 seconds where it does not, so that boot is paid for fewer times. Restoring never needed this, because from the snapshot step onwards only this plugin is active.
* A backup saves its progress every ten seconds while a batch runs, not only when the batch ends. If the server cuts the request off halfway, the retry carries on from there instead of copying that batch again.
* While a restore is abandoned, visitors get the maintenance page instead of a blank page: the site has no theme and only this plugin active until the restore finishes or is undone, so it cannot render anything. An abandoned restore that never touched the database now undoes itself on the next visit to the admin area.
* On Windows and IIS the login screen could be caught by the maintenance page, leaving the owner with no way in while a restore was half done.

= 2.5.5 =
* The plugin's own icon in the admin menu and at the top of its screen, instead of the generic ones.
* A backup of the database alone now says so in its file name and carries a "DB only" label in the list, so it cannot be mistaken for a full one.
* The backups table has fixed columns: a longer size no longer widened its column and pushed that row's buttons out of line with the rest.
* The SIZE heading sits over its value instead of to the left of it.
* The restore screen was still in Spanish whatever the chosen language, as were the buttons of a row added right after making a backup. Every string in the admin script now goes through the translation files.

= 2.5.4 =
* Dark mode: the text of a dropdown stayed black after opening its list and moving the pointer away. WordPress sets the colour again for every state of a select, so the plugin now does the same.
* Checkboxes are square, with the same width and height whatever the surrounding layout does.

= 2.5.3 =
* Dark mode: fixed dropdown labels turning almost black when hovered, and the nginx notice in the logs panel being unreadable. Both were WordPress rules winning over the plugin's.
* The tick inside a checked box is drawn as a centred image instead of two rotated borders, so it can no longer be clipped.

= 2.5.2 =
* Fixed checkboxes showing two ticks: WordPress draws its own and it was added on top of the plugin's.
* Fixed the number and email fields, and the dropdowns, showing with a white background in dark mode. WordPress styles them too and its rules were winning.
* The confirmation dialog for a restore is blue, like the button that opens it. Red is kept for deleting.
* Buttons keep their white label after being clicked, instead of turning blue.
* Fields and dropdowns are taller, rounder and lighter on the eye.
* The language picker says "English" instead of "English (United States)".

= 2.5.1 =
* The whole screen now follows the plugin's language, not just its own strings: weekday and month names used to stay in the site's language, so an English screen said "Every Week viernes at 5:00 am".
* The date of a backup that has just been made is written in the same format as the rest of the table. Before, the new row used a fixed Y-m-d H:i:s.
* Inputs, dropdowns and checkboxes are drawn by the plugin, so they look the same in every browser and in both themes.
* The schedule reads in one line: Every [Week] [Friday] at [05:00], with the on/off switch next to the heading and the four exclusions laid out two by two.
* The log files are trimmed when they pass 256 KB, keeping the most recent lines. They used to grow forever.

= 2.5.0 =
* Light, dark or automatic theme for the plugin's screen, chosen by each user and remembered. It is applied before the page paints, so there is no white flash on the way in.
* Scheduled backups can now run on a weekday of your choice ("every Friday at 05:00"), and have an on/off switch: turning them off no longer loses the day and the hour you had set.
* The settings are laid out in two columns, general on the left and the schedule on the right.
* Retention never deletes the most recent complete backup. Before, a daily database-only backup with retention set to 3 pushed the last complete backup out in three days, and nothing said so.
* The nginx notice moved from the top of the screen into the logs and diagnostics panel, where it belongs: it is read once and applied on the server.
* The WordPress admin footer is hidden on this screen.
* The language picker no longer offers "same as WordPress": it always names a language, and the plugin ships in English.
* `wp omni schedule` takes `--on`, `--off` and `--day`.

= 2.4.0 =
* The whole plugin can be translated: screens, messages, confirmation dialogs, emails and the WP-CLI output, 342 strings in all.
* A language picker in **Settings**, for this plugin alone, so you can look after a site in one language while its screens stay in yours.
* The settings area redesigned: fields on a grid with their labels, scheduled backups in a panel of their own, and the long explanation about cron folded away.
* Dates and times now follow the formats set in **Settings &rarr; General**, instead of a fixed day/month/year.
* The maintenance page shown to visitors during a restore is no longer in a fixed language.
* Fixed: the warning listing what a backup could not copy depended on matching Spanish words, and would have gone silent once translated.
* The logs stay in English on purpose, so they can be shared on the support forum.

= 2.3.0 =
* WP-CLI commands: `wp omni backup` (with `--only-db` and the same exclusions), `wp omni list`, `wp omni delete` and `wp omni schedule`.
* Database-only backups, by hand or scheduled: a fraction of the size, for frequent copies. Restoring one leaves your files untouched.
* Scheduled backups: daily or weekly, at the hour you choose, with the same exclusions as a manual backup and an optional email when they finish or fail. They use the same engine, so a large site is backed up in pieces and carried on across runs.

= 2.2.0 =
* Much faster on large sites: a single progress request at a time, batches without pauses, file lists resumed by byte position and streamed copies.
* Fast mode: this plugin's AJAX requests load only this plugin, with an automatic fallback to the normal path if anything fails. (Removed since: the plugin no longer writes any PHP of its own outside its folder.)
* Restore: extraction with a folder cache, database batches by time, and upload chunks sized to the server's real limit.
* The plugin's own confirmation dialogs, and a checkbox to confirm a restore.
* Nothing runs on the public side of the site any more. (Since the fast mode was removed, there is one exception: while a restore is running, the plugin reads one row that WordPress has already loaded, and no more.)

= 2.1.3 =
* Backups, SQL dumps, token and logs moved to a folder with a random name, with Apache and IIS protection.
* Safe retries: per-step locking and progress stored on the server, for both backup and restore.
* Truncated or damaged backups are rejected instead of restored.

= 2.1.2 =
* Fixed restoring onto a different site: files were extracted one folder too deep.
* The restore keeps this plugin active and never overwrites itself.

= 2.1.1 =
* URL rewriting is now safe with serialized data.
* Protection against Zip-Slip when extracting.
* Fixed timeouts when uploading files larger than 1 GB.

== Upgrade Notice ==

= 2.6.2 =
Nothing to do. This release only drops a header; the plugin behaves exactly as 2.6.1.

= 2.6.1 =
The plugin is now called OmniMigrate. Your existing backups are moved across and still restore; only the schedule has to be set again.

= 2.6.0 =
Your backups move to `wp-content/omnimigrate` on their own. The plugin no longer writes any PHP file outside its own folder.

= 2.5.4 =
Fixes dropdown text going black in dark mode after using the list.

= 2.5.3 =
Fixes unreadable text in dark mode and the clipped tick on checkboxes.

= 2.5.2 =
Fixes the double tick on checkboxes and the white fields in dark mode.

= 2.5.1 =
Tidier controls, the schedule on one line, and dates and weekdays that follow the plugin's language.

= 2.5.0 =
Dark mode, backups on the weekday you choose, and retention that never deletes your last complete backup.

= 2.4.0 =
The plugin can now be translated, it has a language picker of its own, and the settings area has been redesigned.

= 2.3.0 =
Backups can now run on their own, there are database-only backups, and WP-CLI commands.

= 2.2.0 =
Large sites are much faster, and restores are safer to retry.
