Title: Miroir Local Sync
Author: Tommy Bordas
Published: <strong>جولای 23, 2026</strong>
Last modified: سپتامبر 3, 2026

---

جستجوی افزونه‌ها

![](https://ps.w.org/miroir-local-sync/assets/banner-772x250.png?rev=3619915)

![](https://ps.w.org/miroir-local-sync/assets/icon.svg?rev=3619915)

# Miroir Local Sync

 توسط [Tommy Bordas](https://profiles.wordpress.org/tommybordas/)

[دانلود](https://downloads.wordpress.org/plugin/miroir-local-sync.1.4.4.zip)

 * [جزئیات](https://fa-af.wordpress.org/plugins/miroir-local-sync/#description)
 * [نقد و بررسی‌ها](https://fa-af.wordpress.org/plugins/miroir-local-sync/#reviews)
 *  [نصب](https://fa-af.wordpress.org/plugins/miroir-local-sync/#installation)
 * [توسعه](https://fa-af.wordpress.org/plugins/miroir-local-sync/#developers)

 [پشتیبانی](https://wordpress.org/support/plugin/miroir-local-sync/)

## توضیحات

Miroir keeps a local WordPress environment and one or more remote sites in sync,
with an interface designed for non-technical users.

The same plugin embeds two roles: an orchestrator on the local site and an agent
on each remote site. They communicate over the WordPress REST API (authenticated
with Application Passwords) and, optionally, SFTP.

Features:

 * Push (local to production) and Pull (production to local) of the database.
 * Safe search-replace that handles PHP-serialized data, escaped JSON (Gutenberg
   blocks) and `wp_options`.
 * Automatic snapshot and rollback before a database import.
 * Comparison screen with a per-component diff and data-loss warnings.
 * File deployment of specific themes and plugins.
 * Editorial content merge by post type (GUID-matched, no deletion; WooCommerce 
   excluded).
 * One-click deployment shortcuts.
 * REST (chunked) and SFTP transport.
 * Live data protection on push: WooCommerce orders, customers, users, comments 
   and reviews on the target keep their current state.
 * Scheduled sync (WP-Cron): refresh your local copy from production automatically.
 * WP-CLI commands (`wp miroir status|compare|push|pull`) for CI/CD and system crons.
 * Webhook notifications (Slack, Discord, custom endpoint) in addition to email.
 * CSV export of the activity log.
 * File rollback: themes and plugins are snapshotted before deployment and can be
   restored, alongside database snapshots, from the Snapshots view.
 * Streaming database import (constant memory) and one-pass search-replace, safe
   on hosts without shell access.
 * SSH host key pinning on first SFTP pairing, and checksum-verified transfers.
 * Activity log with per-site filters and pagination; keyboard- and screen-reader-
   friendly screens.

### External services

Miroir only ever talks to servers you configure yourself. **No data is sent to Sumotori
or to any service belonging to the plugin’s author.** The plugin contains no telemetry,
no analytics, no licence check and no phone-home of any kind. If you pair no site
and configure no SFTP host or webhook, Miroir makes no outbound request at all.

**1. The remote WordPress sites you pair with**

Address: the URL you type yourself when adding a site (for example `https://example.
com`). There is no default and no suggested host.

What is sent: your WordPress username and Application Password for that site (HTTP
Basic Auth, over the connection scheme of the URL you gave); database dumps produced
from the current site; ZIP archives of the themes, plugins or media folders you 
selected; JSON bundles of the post types you chose to merge; and, when you ask for
it, a ZIP of the Miroir plugin itself in order to update the remote agent.

What is received: the remote site’s status, its list of themes, plugins, media and
content, its database dump when you pull, and the result of each operation.

When: only on an explicit action of yours (a push, a pull, a comparison, a deployment
shortcut, an agent update) or on the schedule you enable yourself in Settings  Scheduled
sync. Nothing is transmitted in the background otherwise.

This exchange is the entire purpose of the plugin: the site you pair with is your
own, and the terms that apply are those of its host.

**2. An SFTP host (optional)**

If, and only if, you fill in the SFTP settings of a profile, Miroir connects to 
the host, port and account you entered, to deposit or fetch the transfer files (
database dumps, ZIP archives) instead of passing them through the REST API. The 
host key is pinned on first connection and a changed key aborts the transfer. Leave
these fields empty and no SFTP connection is ever made.

**3. A notification webhook (optional)**

If you fill in a webhook URL in Settings, Miroir sends one HTTP POST to that address
after each real push or pull. The body is JSON and contains only: the source site
address, the event name, the operation type, the target site name, whether it succeeded,
and the list of steps with their status. No credentials and no site content are 
included. Leave the field empty and nothing is sent.

## عکس‌های صفحه

[⌊Dashboard: connected sites and one-click deployment shortcuts.⌉⌊Dashboard: connected
sites and one-click deployment shortcuts.⌉[

Dashboard: connected sites and one-click deployment shortcuts.

[⌊Comparison screen with per-component diff and data-loss warnings.⌉⌊Comparison 
screen with per-component diff and data-loss warnings.⌉[

Comparison screen with per-component diff and data-loss warnings.

[⌊Global settings and system status.⌉⌊Global settings and system status.⌉[

Global settings and system status.

## نصب

 1. Install and activate Miroir on the remote (production) site.
 2. Install and activate Miroir on the local site.
 3. On the local site, open Miroir, add the remote URL and approve the connection.

## سوالات متداول

### Does this replace a backup plugin?

No. Miroir is a development and deployment tool, complementary to a general backup
solution.

### Is WordPress Multisite supported?

Not yet. Miroir targets standard (single-site) WordPress installs on both ends: 
the database engines (full dump/import, search-replace) are not designed for the
shared tables of a network. To prevent any data loss, Miroir disables its operations
when it detects a Multisite network. Syncing with multiple _separate_ production
sites is fully supported.

### Is WooCommerce supported?

Yes for full database sync. Per-type content merge intentionally excludes WooCommerce
products and orders.

### Can I push my local database without overwriting new orders on the shop?

Yes. Enable “Protect live data on the target” in the push wizard (on by default 
when WooCommerce is active): orders (HPOS), order items, sessions, analytics lookups,
users and comments/reviews are excluded from the push, so the live shop keeps them.
Note: if the site still stores orders in wp_posts (legacy mode, HPOS disabled), 
those specific rows cannot be protected at table level.

### Can syncs run automatically?

Yes. Settings  Scheduled sync lets you run a pull (or push) hourly, twice daily,
daily or weekly via WP-Cron, with email/webhook notification. For large sites, prefer
a real system cron calling WP-CLI, e.g.:

    ```
    0 3 * * * cd /path/to/site && wp miroir pull --profile=<id> --db --yes
    ```

### Is there a WP-CLI integration?

Yes: `wp miroir status` (connected sites), `wp miroir compare --profile=<id>` (data-
loss alerts), `wp miroir push|pull --profile=<id> [--db --themes --plugins --media][--
dry-run] [--protect] [--yes]`.

### Will a push delete data on the target?

A full database push replaces the target database, so rows that exist only on the

target are removed. The comparison screen warns about this before you confirm, and
a snapshot is taken so a rollback is possible. Content merge (by post type) and 
file deployment are additive and never delete.

### What if my host has no SSH/SFTP?

Everything works over the REST API (chunked transfer). SFTP is optional and only

speeds up file transfers on hosts that provide SSH.

### Are my credentials safe?

Yes. Application Passwords and SFTP secrets are encrypted at rest (AES-256-GCM) 
and
 never logged in clear text. The agent only answers users with `manage_options`.

### Will large sites time out?

The database search-replace is resumable (processed in time-bounded chunks), file

transfers are chunked, and native mysqldump/mysql are used when available.

### Are my dumps reachable from the web?

Miroir stores its dumps and snapshots in a folder under `wp-content/uploads` whose

name carries a random key, protected by an `.htaccess` file and an `index.php`. 
On Apache that is enough. **On nginx the `.htaccess` is ignored**, so the files 
would be downloadable by anyone who guessed the address. The system status screen
warns you when it detects nginx. Add this to the server configuration, on both sites:

    ```
    location ~* /uploads/miroir-local-sync-[^/]+/ { deny all; return 404; }
    ```

A dump contains everything the database contains — password hashes, e-mail
 addresses,
orders. This block is not optional on nginx.

## نقد و بررسی‌ها

نقد و بررسی‌ای برای این افزونه یافت نشد.

## توسعه دهندگان و همکاران

“Miroir Local Sync” نرم افزار متن باز است. افراد زیر در این افزونه مشارکت کرده‌اند.

مشارکت کنندگان

 *   [ Tommy Bordas ](https://profiles.wordpress.org/tommybordas/)

[ترجمه “Miroir Local Sync” به زبان شما.](https://translate.wordpress.org/projects/wp-plugins/miroir-local-sync)

### علاقه‌ مند به توسعه هستید؟

[کد را مرور کنید](https://plugins.trac.wordpress.org/browser/miroir-local-sync/),
را بررسی کنید [مخزن SVN](https://plugins.svn.wordpress.org/miroir-local-sync/), 
یا مشترک شوید [گزارش توسعه](https://plugins.trac.wordpress.org/log/miroir-local-sync/)
توسط [RSS](https://plugins.trac.wordpress.org/log/miroir-local-sync/?limit=100&mode=stop_on_copy&format=rss).

## گزارش تغییرات

#### 1.4.4

 * Fixed: on some shared hosts, a database push failed at the import step with “
   mysql import failed (-1):” and no explanation, and the rollback then failed the
   same way — leaving an operation that looked half-done although nothing had been
   written. The native `mysql` client on those hosts exits with a status PHP cannot
   even read, and says nothing on its error output. That is not an SQL error: the
   plugin now recognises it, falls back to its own PHP import engine (which works
   there), and only treats a failure as fatal when the client actually reported 
   something. Warnings such as “Using a password on the command line interface can
   be insecure” no longer count as failures either.

#### 1.4.3

 * Fixed, and it could damage a production site: the content merge (“Sync pages”,“
   Sync posts”, and the grouped sync) wrote the source site’s addresses into the
   target. A page built in Gutenberg carries absolute URLs inside its blocks — column
   background images, galleries, internal links — and they were copied verbatim.
   A page pushed from a local site landed on production asking for `https://your-
   site.local/wp-content/uploads/...`: the file was there under the right domain,
   but the address pointed at a domain that only exists on the developer’s machine,
   so nothing displayed — and on an HTTPS site the browser blocked the request as
   mixed content. Full database push and pull have always rewritten these addresses;
   the content merge did not. It now applies the same rewriting, in both directions,
   before writing anything, and the report says how many addresses were rewritten.

#### 1.4.2

 * Fixed: on the confirmation screen, a scope carried over from the comparison (
   shown as chips) was sent empty when clicking Send or Fetch, and the operation
   was refused with “Choose at least one thing to sync” although four chips were
   displayed. The scope is now read correctly whatever form it takes.
 * Ticking “Database” on the comparison screen now ticks and greys out every content
   type below it: they are included in the database sync, so the grouped content
   sync is disabled and says so. Unticking the database gives the boxes their previous
   state back.

#### 1.4.1

 * Snapshots have their own tab, next to Sites, Logs and Settings, with a site switcher
   when several sites are paired. They were only reachable from the “⋯” menu of 
   a site card, where they got lost among connection and maintenance actions.
 * That menu is now grouped: connection actions first, then the lock release, then
   deletion.

#### 1.4.0

A performance release: nothing moves on screen, but what was slow is now fast, and
the operations that used to give up on large sites finish. **Update the Agent on
every paired site** to get compressed dumps and 4 MB transfer chunks. A local site
on 1.4.0 keeps working with a remote site still on 1.3.x: it simply falls back to
the smaller chunks and to uncompressed dumps.

Speed

 * Miroir no longer loads on pages that have nothing to do with it. Every page of
   your site, front end included, was compiling 474 KB of Miroir’s code, and every
   admin screen just over 1 MB — for code that only runs on Miroir’s own screens
   and on the REST routes the Agent answers. That is now 55 KB, loaded where it 
   is actually used.
 * A database transfer sends less, in far fewer round trips. The dump is compressed
   before it leaves, and the two sides agree on the largest chunk the target’s PHP
   will accept — typically 4 MB, where it used to be a fixed 512 KB whatever the
   server could take. Pushing a 100 MB database went from 202 requests to 6, pulling
   one from 201 to 14. A 100,000-row table that produced a 10.8 MB dump now produces
   4.6 MB, or 0.5 MB once compressed.
 * Search-replace and export walk each table by its primary key instead of counting
   rows from the beginning. On a large `wp_posts`, every slice made MySQL go through
   again everything it had already passed, so each slice was slower than the one
   before; the cost is now flat, and an interrupted run resumes exactly where it
   stopped.
 * Content sync and the comparison screen ask the database far less. Matching an
   editorial item on the target cost up to eight full scans of the posts table and
   now costs none — the identifiers are read once, in a single pass. Table lists,
   column lists and the manifest are computed once per request instead of once per
   use.
 * The activity log no longer keeps a full copy of every report. A report carrying
   hundreds of entries is stored as a summary, and its detail row says the list 
   was truncated instead of presenting it as complete.

Long operations

 * A long import no longer looks dead while it works. Streaming a dump into `mysql`,
   or walking thousands of files, could go minutes without a sign of life — long
   enough for the screen to declare the operation stalled while it was in fact running.
   Both ends now report progress as they go, and the lock is refreshed throughout.
 * Comparing a large media library no longer recomputes every checksum. Hashes are
   kept between runs, and when the cache fills, the ones left unused longest go 
   first.
 * A file list cut short for size says so. The file-by-file diff used to show the
   first few hundred files as if that were all of them.

Fixes

 * A dump exported through a pipe was announced as “0 B” on the transfer screen 
   although it was complete.
 * A gzip-compressed dump arriving on a server without the zlib extension is now
   refused before anything is written, instead of failing half-way through the import.
   A compressed dump truncated in transit is reported as incomplete, rather than
   as an import that succeeded.
 * The scope of the last sync, on a site card, is recorded as data instead of as
   a finished sentence: it now reads in your own language, whatever language the
   sync ran in.
 * “Loading file details…” and the whole WP-CLI output stayed in English whatever
   the site language. They are translated.

Under the hood

 * A build script that refuses to package a release whose three version numbers 
   disagree, a PHP_CodeSniffer ruleset the code passes clean, a GitHub Actions pipeline,
   and an integration suite that exercises export, import, search-replace, snapshots
   and the REST loop against a real WordPress and a real MySQL.

#### 1.3.2

 * The comparison screen no longer pre-ticks “Database”: nothing is selected until
   you choose, or the last scope you launched on that site is restored. The database
   card stays open regardless, so the comparison is always visible.
 * Launching with nothing selected is now refused with a clear message, instead 
   of silently falling back to a full database sync.

#### 1.3.1

 * Each theme or plugin row now has a “File details” button that lists, right under
   the row, which files would be sent, which extra files on the target would be 
   deleted when you deploy, and which differ. The file-by-file diff existed but 
   nothing led to it; the row also says the gap at a glance (“36 more files in LOCAL”).
   The confirmation of a deployment now announces the real number of deletions.
 * The “(root files)” pseudo-component is named for what it is — files at the root
   of the folder, outside any theme or plugin — and its details now render instead
   of coming back empty.
 * The content-merge preview button is labelled “Preview” instead of a bare “i”.

#### 1.3.0

Ten things a user expects to see at a glance, found by re-reading every screen through
a user’s eyes.

 * Each site card now says when you last synced, in which direction, and whether
   it succeeded — not only when the connection was last tested. Failed operations
   are now filed under their site in the log.
 * The confirmation screen says what will happen before you click: how many tables
   of the target are replaced and their size, which groups are kept as they are 
   by the live-data protection, whether files are sent, and that the site stays 
   reachable meanwhile.
 * After a successful sync, a “Roll back to the previous state” button opens the
   Snapshots view with that restore point already selected.
 * The database card no longer claims “93 items modified on the target” while every
   row says both sides are identical: it only counts rows where the target really
   is newer, and names the types. A legend explains the Δ column.
 * The comparison table now says, for each content type, which side holds the most
   recent version — amber when the target is newer (syncing would overwrite it),
   green when the source is, grey when both match — with the date of the last change
   under each counter.
 * One sentence on the database card explains that including the database replaces
   it entirely, while the per-row “Sync” buttons only merge. Users and comments 
   are marked “protected by default”.
 * An (i) button on each content row previews what a merge would do — how many items
   to create or update, and which — without changing anything.
 * The media card says what sending would do (“0 files to send · PROD has 5 more,
   sending never deletes anything”) instead of two contradictory verdicts.
 * The log shows the cause of a failure (step and error) in an expandable row, and
   exports both to CSV.
 * Shortcuts list what they deploy and show their last run and, when scheduled, 
   the next one.
 * The Snapshots view says how many are kept and what each one was taken before (
   push or pull, and its scope). A snapshot just taken can no longer be pruned by
   the operation that created it.
 * Notices from other plugins no longer pile up at the top of the Miroir screen.
 * The comparison table now says, for each content type, which side holds the most
   recent version — “PROD has the most recent version” (amber: syncing would overwrite
   it) or “LOCAL has the most recent version” (green: safe). Each counter shows 
   when that side last changed. The previous badge only said that the target had
   changed, and the explanation was hidden in a tooltip.
 * Notices from other plugins no longer pile up at the top of the Miroir screen.

#### 1.2.0

 * Fixed: a failed import that used a different table prefix no longer leaves its
   half-imported tables behind after the rollback. Only tables the import itself
   created under the source prefix are removed — never anything that existed before,
   so a second WordPress install sharing the database is safe.

This release repairs the safety net a database push relies on, closes the ways an
operation could run twice or write outside its own folder, and finishes the screens
that had been left half-wired. **Update the Agent on every paired site**: reliable
rollback, file restore and single-pass search-replace all need 1.2.0 on both ends,
and Miroir now says so on the operation screen when the remote side is too old.

Safety net

 * Automatic rollback after a failed push works again. The import renames the storage
   folder to match the target’s own key, so the snapshot taken minutes earlier was
   no longer where the rollback looked for it: every restore ended on “Snapshot 
   not found” and the target stayed on the half-written database. The Agent now 
   returns the snapshot’s absolute path, and the restore falls back to searching
   the renamed folder.
 * A failed push now also puts the files back. Deployment takes a snapshot of the
   components it is about to replace, and the rollback restores them. Until now 
   a theme overwritten halfway through an operation stayed overwritten, with nothing
   on screen saying so; when the components are too large to archive beforehand,
   the report says that plainly instead of implying a rollback that will not happen.
 * An import that stops halfway no longer blocks its own rollback. A dump is replayed
   statement by statement, and mysqldump writes its data inside `LOCK TABLES … WRITE`
   blocks: an import that failed inside one of them left the connection holding 
   the lock, so the restore that followed died on its first `DROP TABLE` — the safety
   net gave way at the exact moment it was needed. Table locks are now released 
   whatever happens, and new dumps no longer contain any.
 * New Snapshots screen, per site: the restore points held on each side, their date
   and size, with Restore (confirmation by site name) and Delete. Snapshots existed
   but there was no way to see or use one outside an automatic rollback.
 * A push whose two sites have nothing to rewrite is refused before the snapshot,
   not after the import. The operation used to run to the end and leave the target
   carrying the local addresses.
 * Search-replace applies every address in a single pass. Replacing pairs one after
   another could rewrite a URL twice and produce “example.local.local”; the same
   value was also counted twice in the report.
 * A search-replace that cannot write a row no longer reports success. Failures 
   are counted, and past a threshold the operation stops and says how many rows 
   still carry the old address — so the rollback can still be triggered.
 * `wp_options` is processed in the first slice of a search-replace. Between the
   end of the import and the end of a resumable search-replace, the target was serving
   the local site’s addresses for minutes; that window is now seconds.
 * The plugin’s own options are left out of the search-replace. They were already
   kept out of the import, but not out of the rewrite that follows: on a pull the
   reversed pairs rewrote the remote site’s address stored in the profile, so the
   very next call — the `/unlock` that ends the operation — went to the wrong host.
   The pairing came back as an authentication error and the remote site stayed locked
   until the lock expired. A filter, `sumotori_sync_search_replace_skip_options`,
   lets an add-on protect its own options the same way.

Refusing what should never have been accepted

 * A table prefix or a table list that does not match the expected shape is refused,
   and the answer says what was wrong. A crafted request could previously name tables
   outside the current prefix, contradict the live-data protections, or make the
   import rename tables it should never have touched. Nothing is renamed unless 
   the dump really contains that prefix’s `options` and `posts` tables.
 * Every file path used by a transfer is checked against the plugin’s own storage
   folder before anything is read, written or deleted.
 * Incoming archives are bounded: an archive that announces more data than the disk
   can take, or whose compression ratio no real set of files could produce, is refused
   before extraction rather than filling the disk.
 * Symbolic links are no longer followed when building an archive. A link pointing
   outside the site could pull an arbitrary part of the disk into a deployment; 
   links are now skipped, counted, and named in the result message.
 * SFTP transfers carry a sha256 checksum, verified on arrival. A truncated or corrupted
   deposit is refused, and nothing is imported.
 * The SSH host key of an SFTP server is pinned on first connection. A key that 
   changes afterwards aborts the transfer and says what it may mean; the profile’s
   SFTP settings offer “Forget the fingerprint” for a legitimate server change.
 * Stored credentials are re-encrypted with a derived key bound to the plugin version.
   Existing profiles are migrated transparently the first time they are read — nothing
   to redo. When the site’s WordPress salts have changed since pairing, the message
   now says the credentials can no longer be decrypted and that the site must be
   paired again, instead of reporting a connection failure no amount of retrying
   could fix.
 * Pairing over `http://` is refused, because the application password would travel
   in clear text. A local address is still accepted, and a checkbox lets you accept
   the risk explicitly; sites already paired over http keep working and are flagged
   on the Sites screen.

One operation at a time

 * Two operations can no longer run on the same site at once. Starting a second 
   push while one is running is refused, naming the operation that holds the lock
   and how long it has been running, instead of two imports interleaving on the 
   same database.
 * The lock records which operation owns it: only that operation releases it, and“
   Force unlock” now clears both ends. An interrupted operation no longer blocks
   the site until a transient expires.
 * The lock survives the import that used to erase it. It is now kept, along with
   your profiles, settings, shortcuts and logs, when the incoming database replaces`
   wp_options`.
 * A long import keeps the lock alive while it works, so a slow operation is no 
   longer declared dead and restarted underneath itself.

Database engine

 * Databases of any size import without loading the dump into memory. The dump is
   read and replayed statement by statement; a 2 GB dump no longer needs 2 GB of
   memory on a host without `mysqldump`. Dumps using `DELIMITER` or MySQL conditional
   comments are read correctly.
 * An import that fails on a statement says which one: its rank in the dump and 
   its first characters, instead of a bare SQL error with no way to find the cause.
 * Note: **database views and triggers are not transferred.** They were being dumped
   as if they were tables, which produced a dump that could not be imported at all—
   and therefore a snapshot that could not be restored. Views are now left out and
   named in the report, so you can recreate them on the other side.
 * Note: **a table prefix that does not end in an underscore is not reconciled.**
   Tables are then imported under their source names and the operation says so, 
   rather than renaming tables on a guess.

Content merge

 * An item can no longer be given the type of another. Matching is now constrained
   to the post type, so a custom post type sharing an identifier with a post is 
   skipped and named, instead of quietly turning one into the other.
 * Modification dates survive the merge. Items arrived stamped with the time of 
   the sync, which made every later comparison show the target as “recently edited”.
 * Featured images follow. When the media does not exist on the other side, the 
   item is named in the report and you are told to sync media first — instead of
   arriving silently without its image.
 * Items whose author does not exist on the target are named, with the account they
   were reassigned to.
 * Note: **metadata whose key starts with an underscore is still transferred by 
   default.** That is where ACF, Meta Box and Yoast keep their field references,
   and excluding it would break those merges. Settings  Protected metadata offers
   an allowlist, or full exclusion, for sites that need it.

Screens

 * An operation that stops answering says so. The progress card used to spin forever,
   with no way to tell whether the operation was still running: it now reports the
   silence after 90 seconds, stops polling, and points to the Logs. Losing the network
   shows an inline error instead of a browser alert box.
 * The content selection now works in both directions. The checkboxes were displayed
   on a pull, but no button ever ran the selection.
 * A batch sync that fails on one type says “Finished with errors: 1 of 3”, marks
   the failing row and re-enables the button, instead of concluding with a check
   mark.
 * An operation whose comparison could not be run no longer starts on trust. The
   screen states that the two sites could not be compared, offers to analyse them
   there and then, and requires the site name to be typed before running without
   a loss check.
 * Confirmation prompts say what is deployed, where, what is lost and what is not,
   instead of asking a generic question.
 * Updating the Agent no longer reloads the page: the version badge updates in place
   and the result message stays readable.
 * Logs: a Site column, filters by level and by site, pages of 50, and a “Clear 
   the log” action. The CSV export now opens with its accents intact in Excel.
 * Accessibility: contrast raised on badges and secondary labels, the information
   button is a 24 px target with a visible focus ring, live regions announce progress
   without re-reading the whole card, and keyboard focus returns where it was after
   a refresh.
 * Step names in reports and e-mails are translated (“Connection”, “Search-replace”)
   instead of showing their internal identifier.
 * On nginx, the system status screen warns that the `.htaccess` protecting the 
   storage folder has no effect, and gives the `location` block to add (see the 
   FAQ).
 * An invalid notification e-mail or webhook URL is now refused with an explanation,
   instead of being saved as a notification that would never be sent. Deleting a
   site also disarms any schedule pointing at it.

WP-CLI and scheduled runs

 * `wp miroir push|pull` runs the same data-loss check as the screen and asks for
   confirmation, listing what it found. `--yes` keeps its meaning (warn, log and
   continue), `--strict` makes those warnings fatal, and `--no-loss-check` skips
   the check outright.
 * Command-line and scheduled runs take the same lock as the interface, send the
   same notifications, and name their steps in plain words.

#### 1.1.24

 * Fixed: media with accented filenames arrived on the target under a mangled name
   and returned 404, even though the file was there. Archive entry names were being
   re-decoded as CP437 on extraction, turning “d’écran” into “dΓÇÖ╠ücran”. Names
   are now read as the raw UTF-8 they were written in.
 * Fixed: macOS stores filenames in decomposed Unicode while the database records
   the composed form. The two resolve alike on macOS but not on Linux, so accented
   media could land under a name their own URL did not match. Archive entries are
   now normalized.
 * The media card no longer reports “up to date” when both sides hold the same number
   of files but a different total size — a sign that files were replaced, which 
   an additive transfer does not carry over.
 * Logs: the date column no longer wraps onto two lines, and the header names the
   timezone so an hour is never misread on a site left on UTC.
 * Completed the French translation.

#### 1.1.23

 * Content syncs now name what they touched — created and updated items are listed
   by title, in the result message and in the log, instead of bare counts.
 * When the target holds items the source does not, they are now named and explained.
   A content sync merges and never deletes, so that gap could never close; the screen
   showed an unexplained difference that repeated syncing would never resolve.
 * Fixed: a content sync could create a duplicate of a page or post instead of updating
   it. Items are matched on a normalized identifier because search-replace rewrites
   GUIDs with the site URL, but the merge still compared the raw value and so failed
   to find the existing item. The duplicate was then invisible — it collapsed into
   one index entry — leaving a permanent, unexplained gap in the counts.
 * Duplicates already present on the target are now detected and named, with the
   reason and what to do about them.

#### 1.1.22

 * A category no longer claims to be “up to date” while the rows inside it report
   content edited on the target. It now says how many items were modified there,
   so the summary and the detail agree.
 * The comparison header states how old the figures are, and says “analyzed just
   now” after a refresh — previously nothing distinguished freshly computed numbers
   from cached ones, so refreshing looked like it did nothing.
 * Following the refresh link without JavaScript now really re-runs the analysis
   instead of re-reading the cache.
 * Shortened the badge legend to a single line.

#### 1.1.21

 * The amber badge on the comparison table now explains itself. Its tooltip states
   the consequence — these items were changed directly on the target, syncing this
   way would overwrite them — and a legend under the table says the badge is a warning,
   not an error, and that the item count can read 0 while the content itself differs.

#### 1.1.20

 * Fixed: deleting components on the target always failed with a bare “Error” and
   left the button stuck on “Operation in progress…”. The request was sent malformed,
   so WordPress never routed it to the plugin.
 * Fixed: selecting several components at once sent them as a single run-together
   name, so none matched and nothing was deleted.
 * An expired session during an action now says so, instead of showing an error 
   with no explanation.

#### 1.1.19

 * Fixed: a full database push could leave the target unreachable, every later connection
   being refused, and the automatic rollback unable to run. WordPress only attempts
   application-password authentication when the site has recorded that it uses them;
   that record travelled with the database, so a source that had never created one
   switched the feature off on arrival and locked the target out even though the
   credential itself was intact. The setting is now preserved along with the identity.
 * Fixed: the identity was restored while the object cache still described the pre-
   import database, so part of the restore silently did nothing. The cache is now
   cleared before restoring, not only after.

#### 1.1.18

 * Updating the remote Agent now shows what it is doing — checking, packaging, sending(
   with a byte counter), installing, verifying — instead of a frozen button. The
   plain link still works without JavaScript.

#### 1.1.17

 * Sync several content types at once: tick Posts, Pages or any custom type in the
   comparison and run them with a single button, one after another, with per-type
   progress and result. Each type still transfers only what actually changed.

#### 1.1.16

 * A content sync now always states its outcome, including “already up to date: 
   nothing to transfer”. It previously reloaded the screen unchanged, which read
   as a failure.
 * Reworded the target-activity badge so it reads as what it is — a warning that
   the target was edited recently — rather than a number of items waiting to be 
   synced.

#### 1.1.15

 * Themes and plugins present on the target but absent from this site can now be
   deleted: tick them in the comparison, then confirm. Never implicit — a sync still
   adds and updates only. Active themes and plugins are refused, and deletion is
   confined to the theme and plugin directories.
 * The confirmation step no longer asks for the scope a second time: it states what
   was chosen and offers a way back to the comparison to change it.

#### 1.1.14

 * Content sync is now differential: the plugin asks the target for a compact index
   and transfers only the items that are missing or older there, instead of the 
   whole content type. Matching ignores the site part of the GUID, which search-
   replace rewrites.
 * The per-type sync button no longer appears only when the counts differ: two sites
   can hold the same number of pages with different content, and the button was 
   simply missing.

#### 1.1.13

 * New “Reconnect” action on a site: re-runs the pairing without deleting the profile,
   so a broken credential no longer forces you to recreate the site and lose the
   deployment shortcuts pointing at it.

#### 1.1.12

 * The comparison screen now shows editorial activity on the target: how many items
   of each content type changed there in the last 7 days, and when. Counts alone
   never revealed that a sync was about to overwrite fresher work.
 * When an import invalidates the connection credential, the failure now says so
   and how to recover, instead of a bare 401 on the next step.

#### 1.1.11

 * Rollback is no longer given up on the first “busy” answer: it waits for the operation
   still running on the target, and the lock now reports which operation holds it
   and for how long.
 * Live-data protection works across different table prefixes: the agent translates
   the incoming patterns to its own prefix instead of silently protecting nothing.
 * Database reads are ordered by primary key: LIMIT/OFFSET without ORDER BY could
   skip or duplicate rows in exports and in the resumable search-replace.
 * Live-data protection extended to download permissions, saved payment tokens and
   the Action Scheduler queue.
 * SFTP transfers verify the transferred size on both upload and download, like 
   the REST transport already did.

#### 1.1.10

 * Security: the plugin’s own working folder (database snapshots, transfers) is 
   no longer swept into a “media” push. Full SQL dumps — password hashes, e-mails,
   orders — were being copied onto the target site. Folders left by earlier installs
   are excluded too.
 * Search-replace now covers every URL variant: http and https, protocol-relative//
   host, and www/non-www. Only one form was handled, so a migration silently left
   the others pointing at the source site.

#### 1.1.9

 * Selective rollback: a failed push no longer restores the tables it had deliberately
   protected. Those tables were never touched by the import, so replaying them from
   the snapshot rolled them BACK, destroying orders and accounts created during 
   the operation. The snapshot stays complete; only the restore is filtered.

#### 1.1.8

 * “Protect live data on the target” is now checked by default in the push form,
   whether or not WooCommerce is installed: overwriting the target’s users, comments
   and orders should be a deliberate choice. This aligns the UI with WP-CLI (–protect
   was …

## اطلاعات

 *  Version **1.4.4**
 *  Last updated **57 دقیقه پیش**
 *  Active installations **کمتر از 10**
 *  WordPress version ** 6.0 یا بالاتر **
 *  Tested up to **7.1**
 *  PHP version ** 8.1 یا بالاتر **
 *  Language
 * [English (US)](https://wordpress.org/plugins/miroir-local-sync/)
 * Tags
 * [database](https://fa-af.wordpress.org/plugins/tags/database/)[deployment](https://fa-af.wordpress.org/plugins/tags/deployment/)
   [migration](https://fa-af.wordpress.org/plugins/tags/migration/)[staging](https://fa-af.wordpress.org/plugins/tags/staging/)
   [sync](https://fa-af.wordpress.org/plugins/tags/sync/)
 *  [نمایش پیشرفته](https://fa-af.wordpress.org/plugins/miroir-local-sync/advanced/)

## امتیازها

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/miroir-local-sync/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/miroir-local-sync/reviews/)

## مشارکت کنندگان

 *   [ Tommy Bordas ](https://profiles.wordpress.org/tommybordas/)

## پشتیبانی

چیزی برای گفتن دارید؟ نیاز به کمک دارید؟

 [مشاهده انجمن پشتیبانی](https://wordpress.org/support/plugin/miroir-local-sync/)