=== Migvora — WordPress Backup, Restore & Migration ===
Contributors: migvora
Tags: backup, restore, migration, google drive, clone
Requires at least: 5.9
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 2.11.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Backup Everything. Restore Anytime. Move Anywhere.

== Description ==

**Migvora** — Her şeyi yedekle. İstediğin zaman geri yükle. İstediğin yere taşı.

* Full site backup (database + uploads, themes, plugins) or database / files only, as one portable `.migvora` file.
* Keep backups on the server or send them to your own Google Drive with one click.
* Import on any WordPress site — even a brand-new one — from an uploaded file or straight from Google Drive.
* Domains, paths and serialized data are adapted automatically (http → https, sub-folders, different table prefix).
* Safe by design: the import goes into temporary tables and is switched in at the very end; a restore point is created first.
* Chunked processing: no timeouts on large sites, resume after a closed browser tab.

== Installation ==

1. Upload the `migvora` folder to `/wp-content/plugins/` (or Plugins → Add New → Upload the zip).
   If the old "WP Smart Full Migration / BCYBackup" plugin is installed, deactivate it first. Backups are always kept; if you also delete it, its settings are removed, so re-check Migvora → Settings (and reconnect Google Drive) afterwards.
2. Activate the plugin through the **Plugins** menu in WordPress.
3. Navigate to **Migvora** in the admin sidebar.

== Frequently Asked Questions ==

= Does it support multisite? =
Not in the current version. Multisite is planned for a future release.

= Where are backup files stored? =
In `wp-content/wpsfm-storage/backups/` (outside the plugin folder, so updating or re-installing the plugin never deletes backups). The folder is protected from direct HTTP access via `.htaccess` and `web.config`, and backup names carry a random suffix for servers that ignore `.htaccess` (nginx).

= What happens to my site if an import fails? =
Nothing. The backup is imported into temporary `wsft_*` tables; the live tables are replaced in one atomic `RENAME TABLE` only at the very end. Any failure before that drops the temporary tables and leaves the site as it was. A restore point (database, or database + files) is also created before every import.

= Why do I have to log in again after an import? =
Users, passwords and sessions come from the imported site. Log in with the credentials of the site the backup was taken from.

= Can I import backups made with 1.x? =
Yes. 1.x stored every "%" in the database as a random placeholder; the importer repairs this. 1.x also skipped one file in every folder whose name contained "cache" (e.g. `polylang/src/integrations/cache/load.php`), so plugins restored from a 1.x backup may need to be reinstalled. Take new backups with 2.x.

= Do scheduled backups run if nobody visits the site? =
WordPress runs scheduled tasks on page visits. On low-traffic sites, add the cron job shown on the Schedule page (wget … wp-cron.php) in your hosting panel so backups start on time.

= I forgot the backup password =
It cannot be recovered: the password is not stored anywhere and the backup is unreadable without it. Keep it in a password manager.

= How does site-to-site migration work? =
On the new (target) site: Migvora → Import → "Create Migration Key". On the old (source) site: Migvora → "Migrate to Site", paste the key, Start. The target must be reachable from the internet; the source can be a local site. When it is done, log in to the target with the source site's username and password.

= How does Google Drive work? =
Click "Google Drive'ı Bağla" (Settings, Backup or Import page) and pick your Google account. Backups chosen for Drive go to the "Migvora" folder of that account. On a new site, install the plugin, connect the same account on the Import page and import any listed backup. The plugin can only see files it created itself.

= The backup is bigger than my upload limit =
Uploads are sent in small chunks, so `upload_max_filesize` does not matter. You can also copy the `.migvora` file to `wp-content/wpsfm-storage/backups/` with FTP/SSH.

== Changelog ==

= 2.11.0 =
* Free and Premium are now separate: cloud destinations (Google Drive, Amazon S3 / S3-compatible, FTP/SFTP), site-to-site transfer, scheduled backups, pre-update backups, Telegram & e-mail reports, partial restore, share links and encrypted backups moved to the Migvora Premium add-on.
* Free version: manual backups stored on this server, imports up to 1 GB.
* New Premium badges and upgrade cards where Premium features used to be.

= 2.10.1 =
* Telegram notifications now come from the official @MigvoraBot: "Connect Telegram" opens the bot, the admin taps Start, done — no bot token, no chat ID. The bot token lives only on the relay (migvora-telegram-relay); the site keeps an encrypted random link key that can only message the chat that connected it. Works with private chats, groups and channels; /stop in the bot or "Disconnect" in the plugin turns it off. If the bot is blocked, the plugin notices and shows "Connect" again.
* The own-bot option of 2.10.0 was removed.

= 2.10.0 =
* FTP, FTPS (explicit TLS) and SFTP as a backup destination for manual and scheduled backups, with import straight from the server, delete, and retention for scheduled backups. Uploads are resumable (pieces appended to "<file>.part", sized to the connection speed, renamed when complete; after a dropped connection the upload continues from the size on the server). Downloads use ranged reads. Backups go to "folder/site-name/", so one account can serve many sites. The password is stored encrypted.
* SFTP uses curl when it was built with SSH support, otherwise the PHP ssh2 extension; the System page shows what the server supports.
* Telegram notifications for scheduled backups (success and/or failure), next to e-mail: paste the bot token, "Find chat ID" picks up the chat that last wrote to the bot, "Send test message". Works with private chats, groups and channels. The token is stored encrypted.

= 2.9.1 =
* Fix (important): tables with a binary primary key (e.g. Wordfence's wp_wffilemods) were exported with rows repeated and others missing, because the resume position was mangled when saved between steps. Import then failed with "Duplicate entry … for key PRIMARY". The position is now stored byte-exact; the domain replacement had the same issue and is fixed too. Older backups with repeated rows import again (the repeats are skipped and logged).
* Fix: domain replacement no longer changes file names that contain the domain ("old.com-logo.svg" stayed "new.com-logo.svg" in the database while the file on disk kept its name → missing logo). Inside file names, folder names, e-mail addresses and other domains ("myold.com", "old.com.tr") the old domain is left alone; URLs and the domain in text are still replaced.
* Fix: after an import, pretty permalinks and custom login URLs (WPS Hide Login …) gave the web server's 404 until Settings → Permalinks was saved. The rewrite rules and the WordPress block of .htaccess are now written right when the import finishes and again on the first request (this failed before on front-end requests).
* New "System" page: checks PHP version, memory_limit, max_execution_time, post_max_size, disk space, write permissions, loopback, WP-Cron, LiteSpeed "noabort", nginx/CloudPanel settings, permalinks and PHP extensions, with copy-paste fixes (php.ini / .user.ini, .htaccess, nginx vhost, cron). One click adds the LiteSpeed rules to .htaccess or refreshes the permalinks. Error messages link to it.
* The switch from a cloud download to the import is now shown on screen.

= 2.9.0 =
* Fix: site-to-site migration (and every backup) now includes wp-content/languages — core, theme and plugin translations, incl. Loco Translate's languages/loco/. A migration to a fresh site no longer loses translations.
* Fix: the target's import during a site-to-site migration is now driven step by step by the source's progress screen (like a normal import in the browser). Hosts that kill background PHP when the connection closes (LiteSpeed without "noabort") can no longer interrupt it; the background runner stays as a fallback.
* An import now says so when files in the backup failed their checksum, instead of only logging it.
* Backup contents without extracting: source site, WordPress/PHP version, tables with sizes, uploads, translations, each theme and plugin.
* Partial restore: restore only the database, only uploads, only translations, or a single theme or plugin. A selected theme/plugin folder is replaced exactly with its backed-up version; a restore point of just those parts is taken first; the rest of the site — and your login — stay untouched.
* Shareable download link: single-use, expires after 1 hour, 24 hours or 3 days, no login needed. A confirmation page keeps link previews (WhatsApp, Slack, mail scanners) from using it up. Links can be revoked on the Backups page.
* Size and time estimate shown before a backup starts (and updated when options change); the time is learnt from this server's own backups. Starting with too little free disk space asks for confirmation.

= 2.8.2 =
* Big downloads (512 MB+) in Chrome / Edge are fetched in 128 MB pieces and written straight to the chosen file, with a progress bar and automatic retries — works on any host, whatever its response size limits or firewall.
* Backups over ~1.9 GB always use the direct link in other browsers too (the self-check is often blocked by host firewalls; PHP streaming cannot work at that size on LiteSpeed). The self-check result is written to the log.

= 2.8.1 =
* Fix: downloading big backups (over ~2 GB) failed with "413 Request Entity Too Large" on LiteSpeed hosts, which cap PHP responses. Backups of 512 MB and more are now sent by the web server itself through a temporary, unguessable link (hard link, no extra disk space, removed after 6 hours); the link is checked first and PHP streaming is used when it does not work.
* Downloads through PHP support HTTP Range, so interrupted downloads can be resumed.

= 2.8.0 =
* Amazon S3 and S3-compatible storage (Cloudflare R2, Wasabi, Backblaze B2, DigitalOcean Spaces, MinIO) as a backup destination for manual and scheduled backups: Signature V4, multipart upload in 16 MB parts (resumable, retried on throttling), import straight from the bucket, delete, retention for scheduled backups. Objects are stored as prefix/site-name/file, so one bucket can serve many sites. The secret key is stored encrypted.
* One-click AWS setup: "Set Up in AWS" opens a CloudFormation quick-create stack in the customer's own account (private encrypted bucket + a key limited to that bucket); pasting the one-line setup code fills in all S3 fields.
* Automatic database backup right before WordPress core, plugin and theme updates (manual, bulk and automatic background updates); one backup per update run, the newest N are kept, the update is never blocked.
* "Undo last import": after an import, admins see a notice with a one-click return to the restore point taken before it (also offered on the import's done screen).
* Maintenance mode during imports: visitors get a short 503 page while an import runs; administrators still see the site.
* Exclusion rules in Settings: folders and file patterns (wildcards, relative to wp-content) and database tables (with their sizes); core tables cannot be excluded. Restore points and pre-update backups ignore the rules.
* Size estimate on Create Backup: database, files, approximate archive size, free disk space and a warning when space may run out.

= 2.7.0 =
* Site-to-site migration ("Migrate to Site"): create a one-time migration key on the target (Import page), paste it on the source; the backup is pushed straight to the target in chunks and imported there automatically — no download, no upload, works from localhost to a live site.
* Signed requests (HMAC-SHA256 + timestamp, replay window 10 min), single-use key valid for 2 hours, chunk size negotiated from the target's POST limit and halved automatically if a proxy refuses it, resumable after network errors.
* Both screens show live progress: sending on the source, receiving + importing on the target; the source follows the target's import until it finishes.
* Background jobs keep the interface language of the admin who started them.

= 2.6.0 =
* One-click restore: "Restore" button on every backup (Backups page and dashboard). Plain backups go straight to the confirmation; encrypted ones ask for the password first.
* Password-encrypted backups (AES-256-GCM, PBKDF2-SHA256 200 000 rounds): option on Create Backup and on scheduled backups. Every 1 MiB chunk is authenticated, so a wrong password or a tampered file is detected; a wrong password is rejected before the import starts.
* The password is never stored; a running job keeps only a key wrapped with the site's secret keys, removed when the job ends. The scheduled-backup password is stored encrypted.
* Encrypted backups are marked 🔒 (local list and Google Drive) and stay encrypted in Drive.
* Path safety check hardened for not-yet-existing folders behind symlinks.

= 2.5.0 =
* New, modern interface: Migvora brand bar with section tabs on every page, card layout, refreshed buttons, forms, tables and badges (scoped to Migvora pages only).
* New dashboard: "Back Up Now" call to action, summary tiles (backups and total size, last backup, automatic backup status, Google Drive), recent backups with one-click download, system status checklist.
* Dates shown as dd.mm.yyyy hh:mm everywhere; relative time follows the plugin language; log list shows dates.
* Responsive down to phone width.

= 2.4.0 =
* Scheduled backups (Migvora → Schedule): daily, every X days, weekly (pick the day) or monthly (pick the date), at a time you choose in the site's timezone; full / database / files; to this server or Google Drive.
* Runs in the background without a browser: short chunks chained by loopback requests, with a WP-Cron safety net. If another operation is running, it retries every 15 minutes.
* Retention: keep the newest N automatic backups (server + Drive); manual backups are never deleted.
* E-mail report after each automatic backup (success and/or failure) to any addresses you enter, with file, size, destination, duration, next run and the error if it failed.
* Built-in SMTP settings (host, port, TLS/SSL, login, encrypted password, sender) used only for Migvora e-mails, plus a "Send Test E-mail" button that shows the SMTP error.
* Imports keep the target site's schedule, e-mail/SMTP and Migvora settings.

= 2.3.0 =
* Interface language: Türkçe / English. TR | EN switch at the top right of every Migvora page (remembered per administrator); site default under Settings → Interface Language ("Automatic" follows the WordPress language).
* All screens, progress messages, errors and dialogs are translated.
* The "Uploading to Google Drive" step is only listed for Drive backups.

= 2.2.0 =
* Renamed to Migvora. New backups use the `.migvora` extension; `.wpsfm` backups (and backups already in Google Drive) keep working.
* Settings, backups, Drive connection and storage of the previous plugin are reused as they are.

= 2.1.0 =
* Google Drive: "Connect Google Drive" (one click, customer picks their Gmail account), back up straight to the customer's own Drive (folder "BCYBackup"), list and import Drive backups on a brand-new site.
* Resumable 8 MB chunk upload and ranged 16 MB download; survives network errors, rate limits (retries) and token expiry.
* Scope drive.file: the plugin only sees files it created. Refresh token encrypted with the site's salts; the target site keeps its own Drive connection after an import.
* OAuth via a small relay you host (migvora-gdrive-relay/), so one client ID works on every customer domain and the client secret never ships in the plugin.

= 2.0.0 =
* Import goes into staging tables and is swapped in atomically; the live site is never left half-imported, the admin session survives the whole import.
* New append-only archive format (wpsfm-v2): export is O(n) instead of O(n²); per-file CRC32; resumable reads and writes. 1.x ZIP backups are still importable.
* Fixed: "%" characters were corrupted in every export ($wpdb->_escape placeholders).
* Fixed: rows could be skipped or duplicated (LIMIT/OFFSET without ORDER BY); now primary-key cursor paging.
* Fixed: plugin/theme files in folders named "*cache*" were dropped from backups.
* Fixed: SQL parser broke on escapes split across 64 KB read chunks.
* Fixed: unchecked export options (uploads/themes/plugins) were ignored.
* Serialized data is rewritten with a parser (no unserialize(): no object injection, objects of missing classes are handled too). URL variants: http/https, protocol-relative, JSON-escaped and URL-encoded; e-mail domains are left alone.
* Binary/BLOB columns exported as hex, generated columns skipped, MySQL 8 collations mapped for MariaDB / MySQL 5.7, named foreign keys handled, rows with id 0 and zero dates kept.
* HTTPS → HTTP moves deactivate force-SSL plugins so the site stays reachable.
* Chunked upload, time-budgeted ticks, file-based job state and lock, resume after a closed browser tab, tick de-duplication, PHP fatal detection.

= 1.0.0 =
* Initial release — full backup, import, serialized-safe domain replacement, job-based chunked processing.
