=== Portamo Backup ===
Contributors:      wpmonodev
Tags:              backup, migration, restore, database, woocommerce
Requires at least: 6.4
Tested up to:      7.1
Requires PHP:      7.4
Stable tag:        1.0.10
License:           GPL-2.0-or-later
License URI:       https://www.gnu.org/licenses/gpl-2.0.html

Backup your whole site and restore it again — any size, free. Chunked and resumable, so it finishes on cheap shared hosting too.

== Description ==

Portamo Backup takes a complete snapshot of your site — database and files — and puts it back when something goes wrong. No size limit, no time limit, no upsell standing between you and your own data.

Most backup plugins fail on the same thing: a big site on a small server. Portamo Backup is built around that problem. The database is streamed row by row, files are walked in batches, and everything runs on a background queue that survives a 30-second PHP timeout. Close the browser tab — the job keeps going.

= What the free version does =

* **Back up your entire site** — full site, database only, or files only. Any size. No cap.
* **WooCommerce data only** — on a store, a fourth scope narrows the snapshot to the tables WooCommerce writes to plus the uploads folder and `wc-logs`. Much smaller and quicker to move than a full-site archive when all you need is the shop.
* **Restore any snapshot this site created** — also any size, also free, forever. Your backups are never held hostage.
* **Rollback guard** — a database snapshot is taken before every restore. If the restore fails or you cancel it, one click puts the database back the way it was.
* **Import a backup from another site** — any size, no cap. URLs and serialized data are rewritten automatically, so widget settings, ACF fields and page-builder layouts survive the move.
* **Cancel, pause and resume** — stop a job mid-flight and pick it up exactly where it stopped.
* **Adaptive Dual-Core engine** — detects your server at runtime. PHP 7.4 gets small sequential chunks that survive 64 MB shared hosts; PHP 8.0+ gets larger chunks and memory-efficient streaming. Nothing to configure.
* **Diagnostics** — one page showing which engine is running, queue health, disk space, and whether the storage directory passes a write probe.
* **Restore history** — every restore attempt is logged with a 7-day success rate, so you know your backups are actually restorable.
* **Email and Slack notifications** on success or failure, with quiet hours.

= What Pro adds =

The free version is a complete backup and restore tool on its own. Pro is for people who need automation and off-site copies:

* **Scheduled automatic backups** — daily, weekly, monthly or a custom cron rule, with retention policy and run history.
* **Cloud storage** — Google Drive and Dropbox over OAuth 2.0 using your own app credentials, with resumable chunked uploads. Pull a snapshot back from its cloud copy after the local archive has been pruned.
* **Token-based site migration** — issue a one-time signed token on the source site, paste it on the destination, walk away. The pull completes itself even on hosts with no reliable cron.
* **AES-256-GCM encryption** — seal every archive with a master password before it leaves your server.
* **WooCommerce order journal** — captures every order the moment it happens and keeps it outside the snapshot. Restoring an older backup replays the orders placed since, so rolling a shop back never drops revenue. HPOS and legacy order storage both supported.

Pro details and video walkthroughs of the scheduling, cloud and WooCommerce features listed above: [wpmono.com/portamo-backup](https://wpmono.com/portamo-backup/)

= Why "Adaptive Dual-Core"? =

Two execution paths share one interface and are chosen at runtime:

* **Standard Mode (PHP 7.4)** — small chunks, sequential I/O, conservative memory ceilings. Survives shared hosts with 64 MB RAM and 30-second execution limits.
* **Ghost Turbo Mode (PHP 8.0+)** — larger chunks, parallel hash-then-write loops, memory-efficient streaming. Uses what a VPS actually has.

You do not pick one. The plugin looks at the server and decides.

= Privacy =

* Your site's content is never sent anywhere. Backups, database dumps and archives stay on your own server unless you connect a cloud destination yourself (a Pro feature that uses your own Google or Dropbox credentials).
* Backups are written to `/wp-content/uploads/portamo-backup/`, protected by an `.htaccess` rule that blocks direct web access.
* The plugin uses one external service, Freemius, for licensing and updates — see the "External services" section below for exactly what is sent and when.

== External services ==

This plugin uses one third-party service: **Freemius**, the licensing and update
platform that distributes the paid version.

**What it is used for.** Freemius handles Pro licence activation, delivers Pro
updates, and — only if you agree — collects basic environment data that helps us
support the plugin.

**When data is sent, and what.** On activation the plugin shows an opt-in screen
with two buttons:

* **Skip** — no site data is sent. Freemius is still contacted to check whether a
  plugin update is available, and to validate a licence key if you enter one.
* **Allow & Continue** — in addition to the above, your site URL, the
  administrator email address, your WordPress and PHP versions, and the list of
  active plugins and themes are sent to Freemius so we can reproduce and fix
  problems on setups like yours.

Opting in is entirely optional. Every backup, restore and migration feature works
identically whether you opt in or skip, and you can change the choice later from
the plugin's settings.

**The contents of your site are never sent to Freemius** — no files, no database
rows, no backup archives.

* Service provider: Freemius, Inc.
* Endpoint contacted: `api.freemius.com`
* Terms of Service: https://freemius.com/terms/
* Privacy Policy: https://freemius.com/privacy/

== Installation ==

1. In WP-Admin go to *Plugins → Add New*, search for "Portamo Backup", and click **Install Now**. Or upload the ZIP via *Plugins → Add New → Upload Plugin*.
2. Activate. The plugin creates its database table and a protected storage folder on first run.
3. Open the new **Portamo Backup** menu and click **Backup Now**.

No account, no licence key, no registration needed to back up and restore.

== Frequently Asked Questions ==

= Is there a size limit on backups? =

No. Backups are unlimited in the free version. Files are processed in batches and the database is streamed row by row, so a 10 GB site works the same as a 100 MB one — it just takes longer.

= Is there a size limit on restore? =

No. Any backup this site created can be restored at any size, and an archive imported from another site is not size-limited either. We think paywalling disaster recovery is indefensible: your archive is in our own `.wsb` format and no other tool can open it, so locking you out of it would be locking you out of your data.

= Will it work on cheap shared hosting? =

That is what it is designed for. Every step is pure PHP — no `exec`, no shell access, no assumptions about `disk_free_space`. Work is chunked so no single request runs long enough to hit a 30-second timeout, and a restore keeps advancing even after the database import has temporarily broken the REST API.

= Does it work on PHP 7.4? =

Yes. PHP 7.4 runs the Standard engine. You lose nothing in features — only the chunk sizes and a few hot loops differ from the PHP 8.0+ path.

= What happens if a restore fails halfway? =

The rollback guard takes a database snapshot before every restore. If it fails or you cancel, you can revert to the pre-restore state from the Restore History panel. Files already written stay on disk, but the database is fully recoverable.

= Can I move a site from staging to production? =

Yes. Download the snapshot from the source site and import it on the destination — free, at any size. The URL remapper is serialization-aware, so widget settings, ACF fields and page-builder data survive intact. To skip the manual download entirely and pull the site over with a one-time token instead, use Pro.

= Does the free version send data anywhere? =

Your site's content never leaves your server. No files, no database rows, no backup archives are transmitted anywhere.

The plugin does talk to one external service, Freemius, for licence and update checks, and — only if you press "Allow & Continue" on the activation screen — for basic environment data such as your WordPress and PHP versions. Pressing "Skip" declines that, and nothing about how the plugin works changes either way. The full details are in the "External services" section above.

Cloud storage, which does talk to Google or Dropbox, is a Pro feature and only ever runs after you connect an account yourself.

= Is there WP-CLI support? =

Not yet. Version 1.x is REST API and admin UI only.

== Screenshots ==

1. Dashboard — one-click backup, snapshot history, and the Smart Analysis panel with site size and backup health.
2. Snapshot history — every backup with its size, storage location, verification status, and Download / Restore / Delete on each row.
3. Import — drop in a `.wsb` archive from this site or another one, with conflict handling and optional decryption password.
4. Diagnostics — engine tier, background driver, system info and a self-test you can hand to support.
5. Settings — retention, engine tier override and job priority.
6. Restore finished — the site is back, reported straight in the dashboard.

== Changelog ==

= 1.0.10 =
* The temporary must-use plugin a restore used to install is gone. While a restore was running, the plugin wrote a small PHP file into `wp-content/mu-plugins` and deleted it again at the end; that file served the "restore in progress" page and kept the restore moving. It now does both from inside the plugin itself, so nothing is ever written outside the plugin's own uploads folder.
* The "restore in progress" page loads its stylesheet and script as ordinary files instead of writing them into the page, and the licence dialog opener on the Plugins screen is enqueued the way WordPress expects.
* Fixed: the progress bar during a restore could jump backwards — reaching 90%, dropping to around 70%, then climbing again. It reported whichever figure the server sent last, and a tick that arrived without a precise figure fell back to a rough estimate for the current stage. Progress describes work already finished, so it now never decreases.
* Removed a duplicate notification when a restore starts. The confirmation dialog already warns you not to close the tab, and the progress panel is on screen — a third copy of the same message on top of the panel you are trying to read is noise.

= 1.0.9 =
* The WooCommerce backup scope is now part of the free version. On a store you can choose "WooCommerce Data" as a fourth scope and get a snapshot narrowed to the tables WooCommerce writes to plus the uploads folder — a much smaller archive when the shop is all you need to move. The order journal, which records orders placed between snapshots and replays them after a restore, remains part of the paid version and is not included in this package.
* The helper scripts that used to sit in the plugin's `bin` folder have been removed. Nothing in this package is a web-reachable PHP file any more; every file refuses to run unless WordPress loaded it. The work those scripts did has not gone away — the restore driver now runs inside the temporary must-use plugin a restore installs, and the post-restore URL audit is a proper screen under Settings → Diagnostics.
* Added: Settings → Diagnostics now has a post-restore URL audit. After moving a site between domains it scans every table in the database for addresses still pointing at the old site — including tables belonging to page builders, sliders and SEO plugins, which a restore does not rewrite — and can correct them. It reports first and only writes when you confirm.
* Fixed: the URL rewrite tool could be triggered by a link on another website while an administrator was logged in. It is now a form submission protected against that.
* Housekeeping now removes leftover upload staging files automatically. Restoring from an uploaded archive could leave a copy of it on disk; on a site that had imported a few times this was several gigabytes doing nothing.

= 1.0.8 =
* Fixed: the in-page navigation bar listed screens that the free version does not include, so following one of those links reached a permission error instead of a page. The bar now lists only the screens that exist.
* Renamed to Portamo Backup. The previous name overlapped an existing website-preservation service, which could have confused users about who provides what.
* The free version no longer contains any licence check. Features that are not part of this package are simply absent from it rather than present-but-disabled: the paid screens, their menu entries and their form endpoints are all removed from the free build, and nothing in it consults a licence.
* Uploaded archives are now handed to WordPress' own upload handler instead of being moved by hand, so the platform's checks and any host-level upload filters apply.
* Restores refuse to write through a symbolic link that would place a file outside the WordPress install, even when the path inside the archive looks legitimate.
* Schedule identifiers arriving from the browser are now validated before being stored or passed to the scheduler.
* Installs that keep their uploads folder somewhere other than the default are now handled correctly everywhere the plugin resolves that path.
* The helper scripts no longer assume a fixed directory depth or a fixed plugin folder name when locating WordPress, so a renamed plugin directory or relocated wp-content no longer breaks a restore.

= 1.0.7 =
* Fixed: on a site with an active licence, the free build reported itself as the paid one. It kept the paid menu label and took every paid code path, then stopped with "missing view partial" on screens whose files the free build does not include. Paid behaviour now requires the paid files to actually be present, so this combination simply behaves as the free version.

= 1.0.6 =
* Fixed: an upload that kept failing on the same byte range retried forever instead of giving up after five attempts. The resume query reset the attempt counter every time it succeeded, so the counter never climbed past one — a stuck transfer spun as fast as the network allowed, logged the same line dozens of times per second, and grew the debug log into the hundreds of kilobytes.
* Fixed: the delay between retries was calculated and then ignored. A single request kept re-entering the upload immediately, so the intended 5s / 10s / 20s backoff never happened.
* Fixed: the Cloud Debug Log panel had no scrollbar and a long log rendered down over the rest of the page. It relied on a CSS class that is not present in the stylesheet the plugin ships.

= 1.0.5 =
* Fixed: external cron services reported the ping as "Failed (timeout)" whenever a backup or upload was running. The ping was in fact succeeding — it answered in 0.6 seconds when idle but took over 40 while pushing a job forward, which is past the 30-second limit services like cron-job.org enforce. Because those services disable a job that keeps failing, a site could quietly lose its scheduled backups. The ping now answers immediately and continues the work after the response is sent.
* Changed: scheduled backups reach cloud storage considerably sooner. Work was capped at 20 seconds per ping because a client was assumed to be waiting; now that the response goes out first, each ping does far more, so a large archive no longer advances in short slices spread across many pings.

= 1.0.4 =
* Fixed: a backup could finish perfectly and then never be sent to cloud storage, with nothing written to any log. If the request was interrupted in the moments after the archive was sealed and verified — which on a multi-gigabyte site is exactly when it is closest to the host's time limit — the completion event never fired, and the housekeeping pass that should have noticed simply discarded the job. That pass now finishes the interrupted job instead, so the upload still goes out.

= 1.0.3 =
* Fixed: large cloud uploads to Google Drive were abandoned after a transient error. The query that asks Drive where to resume was sent without a Content-Length header, so Drive answered "411 Length Required" and the upload gave up — with every byte already transferred thrown away. Small uploads never hit the error that triggers this path, which is why it only appeared on multi-gigabyte archives.
* Fixed: upload jobs had no concurrency lock, so two background ticks could process the same transfer at once. One tick could report an upload as failed while the other was still uploading it successfully, and their retry counters overwrote each other. Uploads now take the same lock the backup, restore and migration pipelines already used.
* Changed: a failed resume query no longer counts against the transfer's retry budget, and no longer ends the upload. It is retried a few times on its own, then the transfer continues from the last known position.

= 1.0.2 =
* Fixed: weekly schedules always ran at midnight. PHP's "this Monday" resets the time to 00:00, so the hour and minute the operator chose were discarded.
* Changed: the Schedule screen's third option was labelled "Files only" but backed up the uploads folder alone — no plugins or themes. It now reads "Media files only", matching both the Backup Now modal and what it actually does. Existing rules keep working.
* Changed: the Schedule screen said "Server timezone" while reporting the timezone from Settings → General. It now says "Site timezone".

= 1.0.1 =
* Fixed: scheduled backups ran at the wrong hour on any site whose timezone is not UTC. The recurrence was armed with the operator's local time but evaluated against UTC, so a 03:15 backup on a UTC+7 site fired at 10:15.
* Fixed: an order placed in the seconds before a restore could be missed by the WooCommerce order journal, because its capture job was still queued in the database the restore replaced. The queue is now flushed before the restore touches anything.
* Fixed: on a registered Pro install the sidebar showed both "License" and "Account" pointing at the same screen.

= 1.0.0 =
* Initial release.
* Adaptive Dual-Core engine (Standard / Ghost Turbo) selected automatically by PHP version.
* Streaming `.wsb` archive format — chunked and resumable.
* On-demand backups: full site, database only, or files only, at any size.
* Unlimited local restore, with a pre-restore rollback guard.
* Import and restore an archive from another site at any size, with serialization-aware URL remapping.
* Cancel, pause and resume for both backup and restore.
* Diagnostics panel with pipeline health, storage probe and a synthetic self-test.
* Restore history with a 7-day success rate.
* Email and Slack notifications with quiet hours.
* Translation-ready (`portamo-backup` text domain).

== Upgrade Notice ==

= 1.0.10 =
Restores no longer write a temporary file into wp-content/mu-plugins, and the progress bar no longer jumps backwards. Recommended for anyone who restores or migrates sites.

= 1.0.0 =
Initial release.
