{"id":334055,"date":"2026-07-23T11:35:17","date_gmt":"2026-07-23T11:35:17","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/miroir\/"},"modified":"2026-07-23T15:12:11","modified_gmt":"2026-07-23T15:12:11","slug":"miroir-local-sync","status":"publish","type":"plugin","link":"https:\/\/ur.wordpress.org\/plugins\/miroir-local-sync\/","author":16611973,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.5.0","stable_tag":"0.5.0","tested":"7.0.2","requires":"6.0","requires_php":"8.1","requires_plugins":null,"header_name":"Miroir Local Sync","header_author":"Sumotori","header_description":"Synchronisation bidirectionnelle entre un environnement local et un ou plusieurs sites en production (push, pull, search-replace, multi-sites). Le m\u00eame plugin joue le r\u00f4le d'orchestrateur c\u00f4t\u00e9 local et d'Agent c\u00f4t\u00e9 distant.","assets_banners_color":"2c2f69","last_updated":"2026-07-23 15:12:11","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":54,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.4.11":{"tag":"0.4.11","author":"tommybordas","date":"2026-07-23 11:34:53"},"0.5.0":{"tag":"0.5.0","author":"tommybordas","date":"2026-07-23 15:12:11"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon.svg":{"filename":"icon.svg","revision":3619915,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3619915,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3619915,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.4.11","0.5.0"],"block_files":[],"assets_screenshots":{"screenshot-1.jpg":{"filename":"screenshot-1.jpg","revision":3620183,"resolution":"1","location":"assets","locale":"","width":1079,"height":591},"screenshot-2.jpg":{"filename":"screenshot-2.jpg","revision":3620183,"resolution":"2","location":"assets","locale":"","width":1079,"height":1426},"screenshot-3.jpg":{"filename":"screenshot-3.jpg","revision":3620183,"resolution":"3","location":"assets","locale":"","width":1079,"height":902}},"screenshots":{"1":"Dashboard: connected sites and one-click deployment shortcuts.","2":"Comparison screen with per-component diff and data-loss warnings.","3":"Global settings and system status."}},"plugin_section":[],"plugin_tags":[153,16271,4155,19979,1558],"plugin_category":[59],"plugin_contributors":[250689],"plugin_business_model":[],"class_list":["post-334055","plugin","type-plugin","status-publish","hentry","plugin_tags-database","plugin_tags-deployment","plugin_tags-migration","plugin_tags-staging","plugin_tags-sync","plugin_category-utilities-and-tools","plugin_contributors-tommybordas","plugin_committers-tommybordas"],"banners":{"banner":"https:\/\/ps.w.org\/miroir-local-sync\/assets\/banner-772x250.png?rev=3619915","banner_2x":"https:\/\/ps.w.org\/miroir-local-sync\/assets\/banner-1544x500.png?rev=3619915","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/miroir-local-sync\/assets\/icon.svg?rev=3619915","icon":"https:\/\/ps.w.org\/miroir-local-sync\/assets\/icon.svg?rev=3619915","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/miroir-local-sync\/assets\/screenshot-1.jpg?rev=3620183","caption":"Dashboard: connected sites and one-click deployment shortcuts."},{"src":"https:\/\/ps.w.org\/miroir-local-sync\/assets\/screenshot-2.jpg?rev=3620183","caption":"Comparison screen with per-component diff and data-loss warnings."},{"src":"https:\/\/ps.w.org\/miroir-local-sync\/assets\/screenshot-3.jpg?rev=3620183","caption":"Global settings and system status."}],"raw_content":"<!--section=description-->\n<p>Miroir keeps a local WordPress environment and one or more remote sites in sync, with an interface designed for non-technical users.<\/p>\n\n<p>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.<\/p>\n\n<p>Features:<\/p>\n\n<ul>\n<li>Push (local to production) and Pull (production to local) of the database.<\/li>\n<li>Safe search-replace that handles PHP-serialized data, escaped JSON (Gutenberg blocks) and <code>wp_options<\/code>.<\/li>\n<li>Automatic snapshot and rollback before a database import.<\/li>\n<li>Comparison screen with a per-component diff and data-loss warnings.<\/li>\n<li>File deployment of specific themes and plugins.<\/li>\n<li>Editorial content merge by post type (GUID-matched, no deletion; WooCommerce excluded).<\/li>\n<li>One-click deployment shortcuts.<\/li>\n<li>REST (chunked) and SFTP transport.<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Install and activate Miroir on the remote (production) site.<\/li>\n<li>Install and activate Miroir on the local site.<\/li>\n<li>On the local site, open Miroir, add the remote URL and approve the connection.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20replace%20a%20backup%20plugin%3F\"><h3>Does this replace a backup plugin?<\/h3><\/dt>\n<dd><p>No. Miroir is a development and deployment tool, complementary to a general backup solution.<\/p><\/dd>\n<dt id=\"is%20woocommerce%20supported%3F\"><h3>Is WooCommerce supported?<\/h3><\/dt>\n<dd><p>Yes for full database sync. Per-type content merge intentionally excludes WooCommerce products and orders.<\/p><\/dd>\n<dt id=\"will%20a%20push%20delete%20data%20on%20the%20target%3F\"><h3>Will a push delete data on the target?<\/h3><\/dt>\n<dd><p>A full database push replaces the target database, so rows that exist only on the\ntarget are removed. The comparison screen warns about this before you confirm, and\na snapshot is taken so a rollback is possible. Content merge (by post type) and file\ndeployment are additive and never delete.<\/p><\/dd>\n<dt id=\"what%20if%20my%20host%20has%20no%20ssh%2Fsftp%3F\"><h3>What if my host has no SSH\/SFTP?<\/h3><\/dt>\n<dd><p>Everything works over the REST API (chunked transfer). SFTP is optional and only\nspeeds up file transfers on hosts that provide SSH.<\/p><\/dd>\n<dt id=\"are%20my%20credentials%20safe%3F\"><h3>Are my credentials safe?<\/h3><\/dt>\n<dd><p>Yes. Application Passwords and SFTP secrets are encrypted at rest (AES-256-GCM) and\nnever logged in clear text. The agent only answers users with <code>manage_options<\/code>.<\/p><\/dd>\n<dt id=\"will%20large%20sites%20time%20out%3F\"><h3>Will large sites time out?<\/h3><\/dt>\n<dd><p>The database search-replace is resumable (processed in time-bounded chunks), file\ntransfers are chunked, and native mysqldump\/mysql are used when available.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.5.0<\/h4>\n\n<ul>\n<li>Internationalization: all source strings are now in English (the WordPress.org standard), so the plugin can be translated on translate.wordpress.org and the directory page reads consistently with the code.<\/li>\n<li>Bundled French translation (fr_FR) in \/languages: the admin interface and the activity log display in French on French sites out of the box.<\/li>\n<li>Loads the bundled text domain on init (load_plugin_textdomain) so translations work on WordPress 6.0\u20136.6; 6.7+ auto-loads them.<\/li>\n<li>Included a translation template (miroir-local-sync.pot) for further locales.<\/li>\n<\/ul>\n\n<h4>0.4.11<\/h4>\n\n<ul>\n<li>Progress transients: the literal prefix sumotori_sync_prog_ is now visible at every set_transient() call site (no more dynamic keys), so all options\/transients are verifiably prefixed.<\/li>\n<li>Renamed the JS localized object MiroirSync to sumotori_sync_cfg (consistent unique prefix).<\/li>\n<li>Hardened storage confinement: chunk writes (Agent) and downloads (Transport) now refuse any path outside the protected storage folder under wp_upload_dir() (uploads\/miroir-local-sync-{key}\/). No data is ever written inside the plugin folder.<\/li>\n<\/ul>\n\n<h4>0.4.10<\/h4>\n\n<ul>\n<li>Removed ini_set('display_errors') from the AJAX guard (no error-reporting changes in production code).<\/li>\n<li>Progress transient key is now force-prefixed with sumotori_sync_ in the push\/pull setters.<\/li>\n<\/ul>\n\n<h4>0.4.9<\/h4>\n\n<ul>\n<li>Storage folder in uploads now uses the plugin slug prefix (miroir-local-sync-).<\/li>\n<\/ul>\n\n<h4>0.4.8<\/h4>\n\n<ul>\n<li>Portability: use WP_PLUGIN_DIR and get_theme_root() instead of hardcoded WP_CONTENT_DIR paths. Correct readme contributor.<\/li>\n<\/ul>\n\n<h4>0.4.7<\/h4>\n\n<ul>\n<li>Renamed to Miroir Local Sync (slug miroir-local-sync) for a distinctive, descriptive name.<\/li>\n<\/ul>\n\n<h4>0.4.6<\/h4>\n\n<ul>\n<li>Text domain aligned to the plugin slug (miroir). Plugin Check fixes: escaped CTA button attribute, translators comment, composer.json kept in the package.<\/li>\n<\/ul>\n\n<h4>0.4.5<\/h4>\n\n<ul>\n<li>Security hardening: search-replace unserialize restricted to stdClass (no object injection); transfer chunks now capped (size guard against disk-fill); manual connection validates the URL like the auto flow.<\/li>\n<li>i18n: Push\/Pull button label no longer hardcoded after an operation.<\/li>\n<\/ul>\n\n<h4>0.4.4<\/h4>\n\n<ul>\n<li>After synchronizing a component, its row status flips from \"modifi\u00e9\" to \"\u00e0 jour\" (kept visible until the next refresh).<\/li>\n<\/ul>\n\n<h4>0.4.3<\/h4>\n\n<ul>\n<li>The site \"\u22ef\" menu now closes on outside click or Escape.<\/li>\n<li>Removed the per-component \"D\u00e9tails\" button (the (i) panel covers it).<\/li>\n<\/ul>\n\n<h4>0.4.2<\/h4>\n\n<ul>\n<li>Comparer: clickable (i) on each component reveals file\/size details below the row; \"N components up to date\" collapsible to find any already-synced component.<\/li>\n<li>Per-component detail now includes total size (LOCAL\/PROD files + size).<\/li>\n<\/ul>\n\n<h4>0.4.1<\/h4>\n\n<ul>\n<li>Per-component \"Synchroniser\" button on the Comparer page: push a single plugin\/theme to prod in delta, with a live per-phase spinner.<\/li>\n<li>Component versions shown per side (LOCAL vX \u00b7 PROD vY); file-count detail moved to an (i) tooltip to keep rows clean.<\/li>\n<li>Consistent \"Synchroniser\" terminology across components and database.<\/li>\n<li>Manifest hardening: version \"0\" handled, null-safe count\/size reads.<\/li>\n<\/ul>\n\n<h4>0.4.0<\/h4>\n\n<ul>\n<li>Delta deployment: one-click shortcuts now send only changed files (hash diff vs the remote), ~90% less data on the wire.<\/li>\n<li>Comparer page redesigned into per-category cards \u2014 including in the sync and expanding the detail are now decoupled (inspect without checking).<\/li>\n<li>Local and remote analysis run in parallel (~2x faster), with live per-phase progress.<\/li>\n<li>Lightweight component signature (version + size + file count) for an instant overview; exact per-file hashing kept for drill-down and deployment.<\/li>\n<li>Dashboard redesigned: centered layout, compact collapsible \"add a site\" tile, denser sections.<\/li>\n<li>Fixes: shortcut edit\/delete (id casing), Comparer scope now actually filters, raised memory limit for heavy operations.<\/li>\n<\/ul>\n\n<h4>0.3.0<\/h4>\n\n<ul>\n<li>Editorial content merge by post type.<\/li>\n<li>One-click deployment shortcuts.<\/li>\n<li>Per-component file deployment and one-click agent update.<\/li>\n<\/ul>\n\n<h4>0.2.0<\/h4>\n\n<ul>\n<li>File synchronization, SFTP transport, per-component diff, AJAX progress.<\/li>\n<\/ul>\n\n<h4>0.1.0<\/h4>\n\n<ul>\n<li>Initial connection flow, database push\/pull, search-replace engine.<\/li>\n<\/ul>","raw_excerpt":"Bidirectional sync between a local WordPress and one or more production sites: push, pull, search-replace, per-component file and content deployment.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/334055","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=334055"}],"author":[{"embeddable":true,"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/tommybordas"}],"wp:attachment":[{"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=334055"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=334055"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=334055"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=334055"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=334055"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/ur.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=334055"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}