From 9faaa89e8a265da5cfd8dc8dda25aeefb7744da7 Mon Sep 17 00:00:00 2001
From: Miriam Goldman
Date: Fri, 16 Aug 2024 09:37:27 -0400
Subject: [PATCH] migrate db pro for testing
---
.../wp-migrate-db-pro/class/ClassMap.php | 610 ++
.../class/Common/Addon/Addon.php | 210 +
.../class/Common/Addon/AddonAbstract.php | 136 +
.../Common/Addon/AddonManagerInterface.php | 8 +
.../class/Common/Addon/AddonsFacade.php | 110 +
.../class/Common/BackupExport.php | 167 +
.../class/Common/Cli/Cli.php | 1008 ++++
.../class/Common/Cli/CliManager.php | 24 +
.../class/Common/Cli/Command.php | 226 +
.../Common/Compatibility/Compatibility.php | 357 ++
.../Compatibility/CompatibilityManager.php | 261 +
.../class/Common/Db/MDBWPDB.php | 62 +
.../class/Common/DryRun/DiffEntity.php | 126 +
.../class/Common/DryRun/DiffGroup.php | 75 +
.../class/Common/DryRun/DiffInterpreter.php | 61 +
.../class/Common/DryRun/MemoryPersistence.php | 41 +
.../Common/DryRun/PersistenceInterface.php | 25 +
.../class/Common/Error/ErrorLog.php | 464 ++
.../Common/Error/HandleRemotePostError.php | 50 +
.../class/Common/Error/Logger.php | 143 +
.../Exceptions/EmptyPropertyException.php | 4 +
.../SanitizationFailureException.php | 6 +
.../class/Common/Filesystem/Filesystem.php | 1103 ++++
.../Common/Filesystem/RecursiveScanner.php | 514 ++
.../class/Common/FormData/FormData.php | 277 +
.../class/Common/FullSite/FullSiteExport.php | 280 +
.../class/Common/Helpers.php | 98 +
.../class/Common/Http/Helper.php | 113 +
.../class/Common/Http/Http.php | 186 +
.../class/Common/Http/RemotePost.php | 490 ++
.../class/Common/Http/Scramble.php | 70 +
.../class/Common/Http/WPMDBRestAPIServer.php | 40 +
.../class/Common/MF/Manager.php | 34 +
.../class/Common/MF/MediaFilesAddon.php | 225 +
.../class/Common/MF/MediaFilesLocal.php | 351 ++
.../Common/Migration/FinalizeMigration.php | 283 +
.../class/Common/Migration/Flush.php | 108 +
.../Common/Migration/InitiateMigration.php | 360 ++
.../Common/Migration/MigrationHelper.php | 143 +
.../Common/Migration/MigrationManager.php | 566 ++
.../MigrationPersistence/Persistence.php | 246 +
.../Common/MigrationState/MigrationState.php | 308 +
.../MigrationState/MigrationStateManager.php | 212 +
.../MigrationState/StateDataContainer.php | 33 +
.../class/Common/Multisite/Multisite.php | 235 +
.../class/Common/Plugin/Assets.php | 179 +
.../class/Common/Plugin/Menu.php | 151 +
.../class/Common/Plugin/PluginManagerBase.php | 544 ++
.../class/Common/Profile/ProfileImporter.php | 502 ++
.../class/Common/Profile/ProfileManager.php | 698 +++
.../Common/Properties/DynamicProperties.php | 20 +
.../class/Common/Properties/Properties.php | 113 +
.../class/Common/Queue/Connection.php | 79 +
.../Queue/Connections/ConnectionInterface.php | 65 +
.../Queue/Connections/DatabaseConnection.php | 294 +
.../Queue/Connections/RedisConnection.php | 77 +
.../class/Common/Queue/Cron.php | 194 +
.../ConnectionNotFoundException.php | 9 +
.../Exceptions/InvalidJobTypeException.php | 9 +
.../WorkerAttemptsExceededException.php | 9 +
.../class/Common/Queue/Job.php | 195 +
.../class/Common/Queue/Jobs/WPMDB_Job.php | 26 +
.../class/Common/Queue/Manager.php | 211 +
.../class/Common/Queue/Queue.php | 67 +
.../class/Common/Queue/QueueHelper.php | 229 +
.../class/Common/Queue/QueueManager.php | 64 +
.../class/Common/Queue/Worker.php | 71 +
.../class/Common/Replace.php | 988 ++++
.../Common/Replace/AbstractReplacePair.php | 40 +
.../Common/Replace/CaseInsensitivePair.php | 21 +
.../Common/Replace/CaseSensitivePair.php | 23 +
.../class/Common/Replace/PairFactory.php | 35 +
.../class/Common/Replace/RegexPair.php | 28 +
.../Common/Replace/ReplacePairInterface.php | 12 +
.../class/Common/Sanitize.php | 187 +
.../class/Common/Settings/Settings.php | 129 +
.../class/Common/Settings/SettingsManager.php | 274 +
.../class/Common/Sql/Table.php | 2099 +++++++
.../class/Common/Sql/TableHelper.php | 258 +
.../class/Common/TPF/Manager.php | 33 +
.../Common/TPF/ThemePluginFilesAddon.php | 554 ++
.../Common/TPF/ThemePluginFilesFinalize.php | 335 ++
.../Common/TPF/ThemePluginFilesLocal.php | 333 ++
.../class/Common/TPF/TransferCheck.php | 84 +
.../Abstracts/TransferManagerAbstract.php | 61 +
.../class/Common/Transfers/Files/Chunker.php | 159 +
.../class/Common/Transfers/Files/Excludes.php | 91 +
.../Common/Transfers/Files/FileProcessor.php | 341 ++
.../Files/Filters/FilterInterface.php | 8 +
.../Files/Filters/WPConfigFilter.php | 89 +
.../Common/Transfers/Files/PluginHelper.php | 338 ++
.../Transfers/Files/TransferManager.php | 70 +
.../class/Common/Transfers/Files/Util.php | 802 +++
.../class/Common/UI/Notice.php | 53 +
.../class/Common/UI/TemplateBase.php | 264 +
.../Upgrades/Routines/RoutineInterface.php | 8 +
.../Upgrades/Routines/Routine_2_6_0.php | 48 +
.../Upgrades/Routines/Routine_2_6_2.php | 34 +
.../Upgrades/UpgradeRoutinesManager.php | 123 +
.../class/Common/Util/Singleton.php | 14 +
.../class/Common/Util/Util.php | 1458 +++++
.../class/Common/Util/ZipAndEncode.php | 41 +
.../wp-migrate-db-pro/class/Container.php | 63 +
.../class/Pro/Addon/Addon.php | 95 +
.../class/Pro/Addon/AddonsFacade.php | 110 +
.../wp-migrate-db-pro/class/Pro/Api.php | 223 +
.../class/Pro/Backups/BackupsManager.php | 160 +
.../class/Pro/Beta/BetaManager.php | 490 ++
.../wp-migrate-db-pro/class/Pro/ClassMap.php | 643 +++
.../class/Pro/Cli/Command.php | 228 +
.../class/Pro/Cli/Export.php | 139 +
.../class/Pro/Cli/Extra/ClassMap.php | 69 +
.../class/Pro/Cli/Extra/Cli.php | 1086 ++++
.../class/Pro/Cli/Extra/CliAddon.php | 15 +
.../class/Pro/Cli/Extra/Command.php | 731 +++
.../class/Pro/Cli/Extra/Initialize.php | 14 +
.../class/Pro/Cli/Extra/Manager.php | 51 +
.../class/Pro/Cli/Extra/Setting.php | 297 +
.../Compatibility/Layers/Addons/Addons.php | 25 +
.../Compatibility/Layers/Addons/CLI/Cli.php | 7 +
.../Layers/Addons/CLI/CliAddon.php | 7 +
.../Addons/MF/CliCommand/MediaFilesCli.php | 7 +
.../Layers/Addons/MF/MediaFilesAddon.php | 7 +
.../Layers/Addons/MF/MediaFilesLocal.php | 7 +
.../Layers/Addons/MF/MediaFilesRemote.php | 7 +
.../MST/CliCommand/MultisiteToolsAddonCli.php | 7 +
.../Layers/Addons/MST/MultisiteToolsAddon.php | 7 +
.../Addons/TPF/Cli/ThemePluginFilesCli.php | 7 +
.../Addons/TPF/ThemePluginFilesAddon.php | 7 +
.../Addons/TPF/ThemePluginFilesLocal.php | 7 +
.../Addons/TPF/ThemePluginFilesRemote.php | 7 +
.../Layers/Platforms/AbstractPlatform.php | 16 +
.../Layers/Platforms/Flywheel.php | 15 +
.../Layers/Platforms/PlatformInterface.php | 10 +
.../Layers/Platforms/Platforms.php | 35 +
.../Layers/Platforms/WPEngine.php | 19 +
.../wp-migrate-db-pro/class/Pro/Download.php | 99 +
.../wp-migrate-db-pro/class/Pro/Import.php | 731 +++
.../wp-migrate-db-pro/class/Pro/License.php | 1045 ++++
.../class/Pro/MF/CliCommand/MediaFilesCli.php | 371 ++
.../Pro/MF/CliCommand/MediaFilesCliBar.php | 15 +
.../MF/CliCommand/MediaFilesCliBarNoOp.php | 30 +
.../class/Pro/MF/Manager.php | 33 +
.../class/Pro/MF/MediaFilesRemote.php | 51 +
.../class/Pro/MF/ServiceProvider.php | 68 +
.../MST/CliCommand/MultisiteToolsAddonCli.php | 380 ++
.../class/Pro/MST/Initialize.php | 9 +
.../class/Pro/MST/Manager.php | 69 +
.../class/Pro/MST/MediaFilesCompat.php | 253 +
.../class/Pro/MST/MultisiteToolsAddon.php | 1282 +++++
.../class/Pro/Migration/Connection.php | 10 +
.../class/Pro/Migration/Connection/Local.php | 170 +
.../class/Pro/Migration/Connection/Remote.php | 462 ++
.../class/Pro/Migration/FinalizeComplete.php | 227 +
.../class/Pro/Migration/Flush.php | 43 +
.../class/Pro/Migration/Tables/Local.php | 10 +
.../class/Pro/Migration/Tables/Remote.php | 427 ++
.../class/Pro/Plugin/ProPluginManager.php | 773 +++
.../class/Pro/RegisterPro.php | 176 +
.../RemoteUpdates/RemoteUpdatesManager.php | 226 +
.../class/Pro/TPF/Cli/ThemePluginFilesCli.php | 807 +++
.../Pro/TPF/Cli/ThemePluginFilesCliBar.php | 16 +
.../TPF/Cli/ThemePluginFilesCliBarNoOp.php | 36 +
.../class/Pro/TPF/Manager.php | 33 +
.../class/Pro/TPF/ThemePluginFilesRemote.php | 186 +
.../Files/IncrementalSizeController.php | 165 +
.../class/Pro/Transfers/Files/Payload.php | 492 ++
.../Pro/Transfers/Files/PluginHelper.php | 154 +
.../Files/SizeControllerInterface.php | 51 +
.../Pro/Transfers/Files/TransferManager.php | 420 ++
.../class/Pro/Transfers/Receiver.php | 157 +
.../class/Pro/Transfers/Sender.php | 117 +
.../class/Pro/UI/Template.php | 181 +
.../class/Pro/UsageTracking.php | 414 ++
.../class/Pro/WPMigrateDBPro.php | 28 +
.../class/SetupProviders.php | 52 +
.../wp-migrate-db-pro/class/WPMDBDI.php | 27 +
.../class/WPMDBDI_Config.php | 38 +
.../wp-migrate-db-pro/class/WPMigrateDB.php | 82 +
.../wp-migrate-db-pro/class/autoload.php | 30 +
.../wp-migrate-db-pro/class/deactivate.php | 78 +
.../compatibility/temp-theme/functions.php | 2 +
.../wp-migrate-db-pro-compatibility.php | 46 +
.../wp-migrate-db-pro-compatibility.php-e | 46 +
.../frontend/build/asset-manifest.json | 70 +
.../frontend/build/index.html | 0
.../wp-migrate-db-pro/frontend/build/noop.css | 0
.../wp-migrate-db-pro/frontend/build/noop.js | 0
.../build/static/css/styles.d4d5e222.css | 9 +
.../build/static/js/135.d717e42327db.chunk.js | 1 +
.../build/static/js/288.52a1e53f645f.js | 2 +
.../static/js/288.52a1e53f645f.js.LICENSE.txt | 109 +
.../build/static/js/358.abfefc366410.chunk.js | 2 +
.../js/358.abfefc366410.chunk.js.LICENSE.txt | 5 +
.../build/static/js/406.64efee064556.chunk.js | 1 +
.../build/static/js/537.8c9507b1c32b.chunk.js | 1 +
.../build/static/js/605.86acde37a857.chunk.js | 1 +
.../build/static/js/613.1f032ee72087.chunk.js | 1 +
.../build/static/js/879.fcb218c0be59.chunk.js | 1 +
.../build/static/js/965.124ef43232e7.chunk.js | 1 +
.../build/static/js/main.542cc9adb3fa.js | 1 +
.../build/static/js/styles.cea332651d4d.js | 1 +
.../static/js/wpmdb-runtime.390ece295331.js | 1 +
.../media/action-backup-database.c3c73850.svg | 1 +
.../media/action-export-database.52b56dcc.svg | 1 +
.../media/action-import-database.40875856.svg | 1 +
.../static/media/action-locked.524d8e4b.svg | 1 +
.../static/media/action-pull.cb4ccd00.svg | 1 +
.../static/media/action-push.ebccd3b6.svg | 1 +
.../media/action-search-replace.9c9bf81f.svg | 1 +
.../build/static/media/add-white.972fe87d.svg | 8 +
.../build/static/media/add.0e409332.svg | 1 +
.../static/media/addons-cli.acc2852c.svg | 1 +
.../media/addons-mediafiles.ecedfa39.svg | 1 +
.../media/addons-multisitetools.34c15ecc.svg | 1 +
.../media/addons-other-files.eace2a2c.svg | 3 +
.../media/addons-plugin-files.0d01933a.svg | 3 +
.../addons-theme-plugins-files.cc98a300.svg | 1 +
.../build/static/media/arrow.992a73c2.svg | 9 +
.../build/static/media/arrow.c76607c1.svg | 1 +
.../static/media/backups-none.031287fd.svg | 1 +
.../build/static/media/calendar.2c8b6ac1.svg | 6 +
.../static/media/check-circular.02c190e9.svg | 1 +
.../build/static/media/close.265fcfb5.svg | 3 +
.../build/static/media/danger.e1678f77.svg | 5 +
.../static/media/downCircle.eb822116.svg | 4 +
.../static/media/equalCircle.0d086278.svg | 4 +
.../build/static/media/error.58f4992a.svg | 12 +
.../build/static/media/info.6957118a.svg | 4 +
.../media/license-checked-blue.c304fe20.svg | 6 +
.../static/media/license-checked.44034878.svg | 12 +
.../build/static/media/local.8a615e13.svg | 26 +
.../static/media/mdb-banner.afefa48f.svg | 1 +
.../mdb-branding-transparent.edbb2b6f.svg | 12 +
.../static/media/mdb-medallion.451f212d.svg | 10 +
.../media/migration-failed.5471ea63.svg | 8 +
.../build/static/media/multisite.b8c0ef24.svg | 8 +
.../static/media/oval-close.55372175.svg | 6 +
.../static/media/oval-question.93e3f70e.svg | 6 +
.../static/media/plusCircle.940ad8be.svg | 4 +
.../media/progress-section-check.2e9bfc60.svg | 3 +
.../media/progress-spinner.121400ac.svg | 3 +
.../build/static/media/question.c7be2b1a.svg | 4 +
.../media/rating-star-active.45821ac6.svg | 3 +
.../media/save-profile-btn.cebaf8c1.svg | 6 +
.../media/search-replace-arrow.1795aa05.svg | 3 +
.../search-replace-casesensitive.874c1930.svg | 4 +
.../media/search-replace-regex.3cf90f8f.svg | 4 +
.../static/media/search-replace.92ae08a9.svg | 8 +
.../static/media/singlesite.e8b6fea0.svg | 4 +
.../static/media/status-error.26cffd9f.svg | 4 +
.../build/static/media/subsite.426375ab.svg | 8 +
.../build/static/media/success.27b62f11.svg | 4 +
.../media/testimonial-avatar.309cd834.png | Bin 0 -> 23537 bytes
.../build/static/media/twitter.0843a854.svg | 3 +
.../build/static/media/upCircle.01eb07ce.svg | 4 +
.../build/static/media/video.753d44eb.svg | 3 +
.../build/static/media/warning.57362294.svg | 5 +
.../media/wp-migrate-2-6-0.8d26599e.png | Bin 0 -> 305770 bytes
.../wp-migrate-db-pro/frontend/mdb-2.0.js | 1 +
.../plugin-update/plugin-update-styles.css | 55 +
.../plugin-update-styles.css.map | 1 +
.../plugin-update/plugin-update-styles.scss | 60 +
.../frontend/plugin-update/plugin-update.js | 91 +
.../frontend/public/index.html | 0
.../frontend/public/noop.css | 0
.../wp-migrate-db-pro/frontend/public/noop.js | 0
.../frontend/template/README.md | 68 +
.../frontend/template/gitignore | 23 +
.../frontend/template/public/favicon.ico | Bin 0 -> 3870 bytes
.../frontend/template/public/index.html | 41 +
.../frontend/template/public/manifest.json | 15 +
.../frontend/template/src/App.css | 33 +
.../frontend/template/src/App.js | 28 +
.../frontend/template/src/App.test.js | 9 +
.../frontend/template/src/index.css | 14 +
.../frontend/template/src/index.js | 12 +
.../frontend/template/src/logo.svg | 7 +
.../frontend/template/src/serviceWorker.js | 135 +
.../languages/wp-migrate-db-en.pot | 4956 +++++++++++++++++
.../languages/wp-migrate-db-pt_BR.mo | Bin 0 -> 41365 bytes
.../plugins/wp-migrate-db-pro/php-checker.php | 71 +
.../wp-migrate-db-pro/react-wp-scripts.php | 404 ++
.../wp-migrate-db-pro/setup-mdb-pro.php | 93 +
.../wp-migrate-db-pro/setup-plugin.php | 49 +
.../common/muplugin-failed-update-warning.php | 6 +
.../template/options-page-outdated-wp.php | 23 +
.../template/options-tools-subsite.php | 12 +
.../wp-migrate-db-pro/template/options.php | 24 +
.../template/pro/beta-feedback-reminder.php | 6 +
.../template/pro/beta-welcome.php | 5 +
.../template/pro/block-external-warning.php | 8 +
.../pro/notice-enable-usage-tracking.php | 5 +
.../template/pro/secret-key-warning.php | 5 +
.../wp-migrate-db-pro/vendor/autoload.php | 25 +
.../brumann/polyfill-unserialize/LICENSE | 21 +
.../brumann/polyfill-unserialize/README.md | 153 +
.../polyfill-unserialize/composer.json | 26 +
.../src/DisallowedClassesSubstitutor.php | 162 +
.../polyfill-unserialize/src/Unserialize.php | 34 +
.../vendor/composer/ClassLoader.php | 579 ++
.../vendor/composer/InstalledVersions.php | 313 ++
.../wp-migrate-db-pro/vendor/composer/LICENSE | 21 +
.../vendor/composer/autoload_classmap.php | 10 +
.../vendor/composer/autoload_files.php | 11 +
.../vendor/composer/autoload_namespaces.php | 9 +
.../vendor/composer/autoload_psr4.php | 18 +
.../vendor/composer/autoload_real.php | 48 +
.../vendor/composer/autoload_static.php | 81 +
.../vendor/composer/installed.json | 544 ++
.../vendor/composer/installed.php | 5 +
.../container-interop/LICENSE | 20 +
.../container-interop/README.md | 148 +
.../container-interop/composer.json | 15 +
.../docs/ContainerInterface-meta.md | 114 +
.../docs/ContainerInterface.md | 158 +
.../docs/Delegate-lookup-meta.md | 259 +
.../container-interop/docs/Delegate-lookup.md | 60 +
.../docs/images/interoperating_containers.png | Bin 0 -> 25738 bytes
.../docs/images/priority.png | Bin 0 -> 16252 bytes
.../docs/images/side_by_side_containers.png | Bin 0 -> 16265 bytes
.../Interop/Container/ContainerInterface.php | 14 +
.../Exception/ContainerException.php | 14 +
.../Container/Exception/NotFoundException.php | 14 +
.../vendor/php-di/invoker/CONTRIBUTING.md | 15 +
.../vendor/php-di/invoker/LICENSE | 21 +
.../vendor/php-di/invoker/README.md | 234 +
.../vendor/php-di/invoker/composer.json | 32 +
.../php-di/invoker/doc/parameter-resolvers.md | 109 +
.../php-di/invoker/src/CallableResolver.php | 102 +
.../src/Exception/InvocationException.php | 12 +
.../src/Exception/NotCallableException.php | 34 +
.../NotEnoughParametersException.php | 12 +
.../vendor/php-di/invoker/src/Invoker.php | 95 +
.../php-di/invoker/src/InvokerInterface.php | 28 +
.../AssociativeArrayResolver.php | 32 +
.../ParameterNameContainerResolver.php | 41 +
.../Container/TypeHintContainerResolver.php | 41 +
.../DefaultValueResolver.php | 33 +
.../NumericArrayResolver.php | 33 +
.../ParameterResolver/ParameterResolver.php | 28 +
.../src/ParameterResolver/ResolverChain.php | 54 +
.../ParameterResolver/TypeHintResolver.php | 31 +
.../src/Reflection/CallableReflection.php | 50 +
.../vendor/php-di/php-di/404.md | 3 +
.../vendor/php-di/php-di/CONTRIBUTING.md | 52 +
.../vendor/php-di/php-di/LICENSE | 18 +
.../vendor/php-di/php-di/README.md | 21 +
.../vendor/php-di/php-di/change-log.md | 354 ++
.../vendor/php-di/php-di/composer.json | 53 +
.../vendor/php-di/php-di/couscous.yml | 134 +
.../vendor/php-di/php-di/phpunit.xml.dist | 30 +
.../php-di/src/DI/Annotation/Inject.php | 74 +
.../php-di/src/DI/Annotation/Injectable.php | 62 +
.../php-di/php-di/src/DI/Cache/ArrayCache.php | 61 +
.../vendor/php-di/php-di/src/DI/Container.php | 274 +
.../php-di/php-di/src/DI/ContainerBuilder.php | 234 +
.../vendor/php-di/php-di/src/DI/Debug.php | 25 +
.../src/DI/Definition/AliasDefinition.php | 66 +
.../src/DI/Definition/ArrayDefinition.php | 71 +
.../Definition/ArrayDefinitionExtension.php | 45 +
.../src/DI/Definition/CacheableDefinition.php | 12 +
.../src/DI/Definition/DecoratorDefinition.php | 42 +
.../php-di/src/DI/Definition/Definition.php | 32 +
.../Dumper/ObjectDefinitionDumper.php | 110 +
.../src/DI/Definition/EntryReference.php | 41 +
.../EnvironmentVariableDefinition.php | 112 +
.../Exception/AnnotationException.php | 12 +
.../Exception/DefinitionException.php | 17 +
.../src/DI/Definition/FactoryDefinition.php | 79 +
.../src/DI/Definition/HasSubDefinition.php | 20 +
.../Helper/ArrayDefinitionExtensionHelper.php | 36 +
.../DI/Definition/Helper/DefinitionHelper.php | 17 +
.../EnvironmentVariableDefinitionHelper.php | 52 +
.../Helper/FactoryDefinitionHelper.php | 80 +
.../Helper/ObjectDefinitionHelper.php | 239 +
.../Helper/StringDefinitionHelper.php | 29 +
.../Helper/ValueDefinitionHelper.php | 32 +
.../src/DI/Definition/InstanceDefinition.php | 62 +
.../src/DI/Definition/ObjectDefinition.php | 291 +
.../ObjectDefinition/MethodInjection.php | 77 +
.../ObjectDefinition/PropertyInjection.php | 61 +
.../DI/Definition/Resolver/ArrayResolver.php | 66 +
.../Definition/Resolver/DecoratorResolver.php | 69 +
.../Resolver/DefinitionResolver.php | 35 +
.../Resolver/EnvironmentVariableResolver.php | 61 +
.../Definition/Resolver/FactoryResolver.php | 102 +
.../Definition/Resolver/InstanceInjector.php | 40 +
.../DI/Definition/Resolver/ObjectCreator.php | 187 +
.../Definition/Resolver/ParameterResolver.php | 93 +
.../Resolver/ResolverDispatcher.php | 117 +
.../DI/Definition/Resolver/SelfResolver.php | 42 +
.../DI/Definition/SelfResolvingDefinition.php | 25 +
.../DI/Definition/Source/AnnotationReader.php | 221 +
.../src/DI/Definition/Source/Autowiring.php | 50 +
.../Source/CachedDefinitionSource.php | 82 +
.../DI/Definition/Source/DefinitionArray.php | 130 +
.../DI/Definition/Source/DefinitionFile.php | 55 +
.../DI/Definition/Source/DefinitionSource.php | 23 +
.../Source/MutableDefinitionSource.php | 14 +
.../src/DI/Definition/Source/SourceChain.php | 85 +
.../src/DI/Definition/StringDefinition.php | 79 +
.../src/DI/Definition/ValueDefinition.php | 67 +
.../php-di/src/DI/DependencyException.php | 11 +
.../php-di/src/DI/Factory/RequestedEntry.php | 21 +
.../php-di/php-di/src/DI/FactoryInterface.php | 27 +
.../Invoker/DefinitionParameterResolver.php | 56 +
.../DI/Invoker/FactoryParameterResolver.php | 50 +
.../php-di/php-di/src/DI/InvokerInterface.php | 12 +
.../php-di/src/DI/NotFoundException.php | 11 +
.../php-di/src/DI/Proxy/ProxyFactory.php | 73 +
.../vendor/php-di/php-di/src/DI/Scope.php | 44 +
.../vendor/php-di/php-di/src/DI/functions.php | 163 +
.../vendor/php-di/phpdoc-reader/LICENSE | 16 +
.../vendor/php-di/phpdoc-reader/README.md | 58 +
.../vendor/php-di/phpdoc-reader/composer.json | 26 +
.../src/PhpDocReader/AnnotationException.php | 10 +
.../src/PhpDocReader/PhpDocReader.php | 237 +
.../PhpDocReader/PhpParser/TokenParser.php | 141 +
.../PhpParser/UseStatementParser.php | 58 +
.../vendor/phpoption/phpoption/LICENSE | 201 +
.../vendor/phpoption/phpoption/Makefile | 23 +
.../vendor/phpoption/phpoption/composer.json | 48 +
.../phpoption/src/PhpOption/LazyOption.php | 144 +
.../phpoption/src/PhpOption/None.php | 113 +
.../phpoption/src/PhpOption/Option.php | 403 ++
.../phpoption/src/PhpOption/Some.php | 138 +
.../vendor/psr/container/LICENSE | 21 +
.../vendor/psr/container/README.md | 5 +
.../vendor/psr/container/composer.json | 33 +
.../src/ContainerExceptionInterface.php | 13 +
.../psr/container/src/ContainerInterface.php | 36 +
.../src/NotFoundExceptionInterface.php | 13 +
.../vendor/scoper-autoload.php | 7 +
.../vendor/symfony/polyfill-ctype/Ctype.php | 201 +
.../vendor/symfony/polyfill-ctype/LICENSE | 19 +
.../vendor/symfony/polyfill-ctype/README.md | 12 +
.../symfony/polyfill-ctype/bootstrap.php | 79 +
.../symfony/polyfill-ctype/composer.json | 47 +
.../vendor/vlucas/phpdotenv/LICENSE | 30 +
.../vendor/vlucas/phpdotenv/composer.json | 62 +
.../vendor/vlucas/phpdotenv/src/Dotenv.php | 178 +
.../src/Exception/ExceptionInterface.php | 8 +
.../src/Exception/InvalidFileException.php | 9 +
.../src/Exception/InvalidPathException.php | 9 +
.../src/Exception/ValidationException.php | 9 +
.../vlucas/phpdotenv/src/Loader/Lines.php | 118 +
.../vlucas/phpdotenv/src/Loader/Loader.php | 102 +
.../phpdotenv/src/Loader/LoaderInterface.php | 19 +
.../vlucas/phpdotenv/src/Loader/Parser.php | 219 +
.../vlucas/phpdotenv/src/Loader/Value.php | 73 +
.../vlucas/phpdotenv/src/Regex/Regex.php | 118 +
.../src/Repository/AbstractRepository.php | 161 +
.../src/Repository/Adapter/ApacheAdapter.php | 61 +
.../src/Repository/Adapter/ArrayAdapter.php | 61 +
.../Adapter/AvailabilityInterface.php | 13 +
.../Repository/Adapter/EnvConstAdapter.php | 64 +
.../src/Repository/Adapter/PutenvAdapter.php | 52 +
.../Repository/Adapter/ReaderInterface.php | 15 +
.../Repository/Adapter/ServerConstAdapter.php | 64 +
.../Repository/Adapter/WriterInterface.php | 24 +
.../src/Repository/AdapterRepository.php | 80 +
.../src/Repository/RepositoryBuilder.php | 121 +
.../src/Repository/RepositoryInterface.php | 50 +
.../vlucas/phpdotenv/src/Result/Error.php | 87 +
.../vlucas/phpdotenv/src/Result/Result.php | 65 +
.../vlucas/phpdotenv/src/Result/Success.php | 87 +
.../vlucas/phpdotenv/src/Store/File/Paths.php | 25 +
.../phpdotenv/src/Store/File/Reader.php | 47 +
.../vlucas/phpdotenv/src/Store/FileStore.php | 52 +
.../phpdotenv/src/Store/StoreBuilder.php | 90 +
.../phpdotenv/src/Store/StoreInterface.php | 15 +
.../phpdotenv/src/Store/StringStore.php | 33 +
.../vendor/vlucas/phpdotenv/src/Validator.php | 153 +
.../plugins/wp-migrate-db-pro/version.php | 2 +
.../wp-migrate-db-pro/wp-migrate-db-pro.php | 57 +
476 files changed, 59230 insertions(+)
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/ClassMap.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/Addon.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/AddonAbstract.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/AddonManagerInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/AddonsFacade.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/BackupExport.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/Cli.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/CliManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/Command.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Compatibility/Compatibility.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Compatibility/CompatibilityManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Db/MDBWPDB.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffEntity.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffGroup.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffInterpreter.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/MemoryPersistence.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/PersistenceInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Error/ErrorLog.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Error/HandleRemotePostError.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Error/Logger.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Exceptions/EmptyPropertyException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Exceptions/SanitizationFailureException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Filesystem/Filesystem.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Filesystem/RecursiveScanner.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/FormData/FormData.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/FullSite/FullSiteExport.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Helpers.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Helper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Http.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Http/RemotePost.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Scramble.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Http/WPMDBRestAPIServer.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/MF/Manager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/MF/MediaFilesAddon.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/MF/MediaFilesLocal.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/FinalizeMigration.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/Flush.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/InitiateMigration.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/MigrationHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/MigrationManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationPersistence/Persistence.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/MigrationState.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/MigrationStateManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/StateDataContainer.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Multisite/Multisite.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/Assets.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/Menu.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/PluginManagerBase.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Profile/ProfileImporter.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Profile/ProfileManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Properties/DynamicProperties.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Properties/Properties.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connection.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connections/ConnectionInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connections/DatabaseConnection.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connections/RedisConnection.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Cron.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Exceptions/ConnectionNotFoundException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Exceptions/InvalidJobTypeException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Exceptions/WorkerAttemptsExceededException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Job.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Jobs/WPMDB_Job.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Manager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Queue.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/QueueHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/QueueManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Worker.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Replace.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/AbstractReplacePair.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/CaseInsensitivePair.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/CaseSensitivePair.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/PairFactory.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/RegexPair.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/ReplacePairInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Sanitize.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Settings/Settings.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Settings/SettingsManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Sql/Table.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Sql/TableHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/Manager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesAddon.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesFinalize.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesLocal.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/TransferCheck.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Abstracts/TransferManagerAbstract.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Chunker.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Excludes.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/FileProcessor.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Filters/FilterInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Filters/WPConfigFilter.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/PluginHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/TransferManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Util.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/UI/Notice.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/UI/TemplateBase.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Upgrades/Routines/RoutineInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Upgrades/Routines/Routine_2_6_0.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Upgrades/Routines/Routine_2_6_2.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Upgrades/UpgradeRoutinesManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Util/Singleton.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Util/Util.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Common/Util/ZipAndEncode.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Container.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Addon/Addon.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Addon/AddonsFacade.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Api.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Backups/BackupsManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Beta/BetaManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/ClassMap.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Command.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Export.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/ClassMap.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Cli.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/CliAddon.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Command.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Initialize.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Manager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Setting.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/Addons.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/CLI/Cli.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/CLI/CliAddon.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/MF/CliCommand/MediaFilesCli.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/MF/MediaFilesAddon.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/MF/MediaFilesLocal.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/MF/MediaFilesRemote.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/MST/CliCommand/MultisiteToolsAddonCli.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/MST/MultisiteToolsAddon.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/TPF/Cli/ThemePluginFilesCli.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/TPF/ThemePluginFilesAddon.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/TPF/ThemePluginFilesLocal.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/TPF/ThemePluginFilesRemote.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Platforms/AbstractPlatform.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Platforms/Flywheel.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Platforms/PlatformInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Platforms/Platforms.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Platforms/WPEngine.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Download.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Import.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/License.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCli.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCliBar.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCliBarNoOp.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/Manager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/MediaFilesRemote.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/ServiceProvider.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/CliCommand/MultisiteToolsAddonCli.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/Initialize.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/Manager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/MediaFilesCompat.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/MultisiteToolsAddon.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Connection.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Connection/Local.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Connection/Remote.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/FinalizeComplete.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Flush.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Tables/Local.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Tables/Remote.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Plugin/ProPluginManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/RegisterPro.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/RemoteUpdates/RemoteUpdatesManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCli.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCliBar.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCliBarNoOp.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Manager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/ThemePluginFilesRemote.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/IncrementalSizeController.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/Payload.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/PluginHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/SizeControllerInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/TransferManager.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Receiver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Sender.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/UI/Template.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/UsageTracking.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/Pro/WPMigrateDBPro.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/SetupProviders.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/WPMDBDI.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/WPMDBDI_Config.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/WPMigrateDB.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/autoload.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/class/deactivate.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/compatibility/temp-theme/functions.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/compatibility/wp-migrate-db-pro-compatibility.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/compatibility/wp-migrate-db-pro-compatibility.php-e
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/asset-manifest.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/index.html
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/noop.css
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/noop.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/css/styles.d4d5e222.css
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/135.d717e42327db.chunk.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/288.52a1e53f645f.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/288.52a1e53f645f.js.LICENSE.txt
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/358.abfefc366410.chunk.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/358.abfefc366410.chunk.js.LICENSE.txt
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/406.64efee064556.chunk.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/537.8c9507b1c32b.chunk.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/605.86acde37a857.chunk.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/613.1f032ee72087.chunk.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/879.fcb218c0be59.chunk.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/965.124ef43232e7.chunk.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/main.542cc9adb3fa.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/styles.cea332651d4d.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/wpmdb-runtime.390ece295331.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/action-backup-database.c3c73850.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/action-export-database.52b56dcc.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/action-import-database.40875856.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/action-locked.524d8e4b.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/action-pull.cb4ccd00.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/action-push.ebccd3b6.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/action-search-replace.9c9bf81f.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/add-white.972fe87d.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/add.0e409332.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/addons-cli.acc2852c.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/addons-mediafiles.ecedfa39.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/addons-multisitetools.34c15ecc.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/addons-other-files.eace2a2c.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/addons-plugin-files.0d01933a.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/addons-theme-plugins-files.cc98a300.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/arrow.992a73c2.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/arrow.c76607c1.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/backups-none.031287fd.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/calendar.2c8b6ac1.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/check-circular.02c190e9.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/close.265fcfb5.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/danger.e1678f77.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/downCircle.eb822116.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/equalCircle.0d086278.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/error.58f4992a.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/info.6957118a.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/license-checked-blue.c304fe20.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/license-checked.44034878.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/local.8a615e13.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/mdb-banner.afefa48f.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/mdb-branding-transparent.edbb2b6f.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/mdb-medallion.451f212d.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/migration-failed.5471ea63.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/multisite.b8c0ef24.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/oval-close.55372175.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/oval-question.93e3f70e.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/plusCircle.940ad8be.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/progress-section-check.2e9bfc60.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/progress-spinner.121400ac.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/question.c7be2b1a.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/rating-star-active.45821ac6.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/save-profile-btn.cebaf8c1.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/search-replace-arrow.1795aa05.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/search-replace-casesensitive.874c1930.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/search-replace-regex.3cf90f8f.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/search-replace.92ae08a9.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/singlesite.e8b6fea0.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/status-error.26cffd9f.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/subsite.426375ab.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/success.27b62f11.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/testimonial-avatar.309cd834.png
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/twitter.0843a854.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/upCircle.01eb07ce.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/video.753d44eb.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/warning.57362294.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/build/static/media/wp-migrate-2-6-0.8d26599e.png
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/mdb-2.0.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/plugin-update/plugin-update-styles.css
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/plugin-update/plugin-update-styles.css.map
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/plugin-update/plugin-update-styles.scss
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/plugin-update/plugin-update.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/public/index.html
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/public/noop.css
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/public/noop.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/README.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/gitignore
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/public/favicon.ico
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/public/index.html
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/public/manifest.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/src/App.css
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/src/App.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/src/App.test.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/src/index.css
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/src/index.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/src/logo.svg
create mode 100644 wp-content/plugins/wp-migrate-db-pro/frontend/template/src/serviceWorker.js
create mode 100644 wp-content/plugins/wp-migrate-db-pro/languages/wp-migrate-db-en.pot
create mode 100644 wp-content/plugins/wp-migrate-db-pro/languages/wp-migrate-db-pt_BR.mo
create mode 100644 wp-content/plugins/wp-migrate-db-pro/php-checker.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/react-wp-scripts.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/setup-mdb-pro.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/setup-plugin.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/template/common/muplugin-failed-update-warning.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/template/options-page-outdated-wp.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/template/options-tools-subsite.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/template/options.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/template/pro/beta-feedback-reminder.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/template/pro/beta-welcome.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/template/pro/block-external-warning.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/template/pro/notice-enable-usage-tracking.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/template/pro/secret-key-warning.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/autoload.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/brumann/polyfill-unserialize/LICENSE
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/brumann/polyfill-unserialize/README.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/brumann/polyfill-unserialize/composer.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/brumann/polyfill-unserialize/src/DisallowedClassesSubstitutor.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/brumann/polyfill-unserialize/src/Unserialize.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/ClassLoader.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/InstalledVersions.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/LICENSE
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/autoload_classmap.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/autoload_files.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/autoload_namespaces.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/autoload_psr4.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/autoload_real.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/autoload_static.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/installed.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/composer/installed.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/LICENSE
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/README.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/composer.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/docs/ContainerInterface-meta.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/docs/ContainerInterface.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/docs/Delegate-lookup-meta.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/docs/Delegate-lookup.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/docs/images/interoperating_containers.png
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/docs/images/priority.png
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/docs/images/side_by_side_containers.png
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/src/Interop/Container/ContainerInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/src/Interop/Container/Exception/ContainerException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/container-interop/container-interop/src/Interop/Container/Exception/NotFoundException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/CONTRIBUTING.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/LICENSE
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/README.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/composer.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/doc/parameter-resolvers.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/CallableResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/Exception/InvocationException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/Exception/NotCallableException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/Exception/NotEnoughParametersException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/Invoker.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/InvokerInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/ParameterResolver/AssociativeArrayResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/ParameterResolver/DefaultValueResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/ParameterResolver/NumericArrayResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/ParameterResolver/ParameterResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/ParameterResolver/ResolverChain.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/ParameterResolver/TypeHintResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/invoker/src/Reflection/CallableReflection.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/404.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/CONTRIBUTING.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/LICENSE
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/README.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/change-log.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/composer.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/couscous.yml
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/phpunit.xml.dist
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Annotation/Inject.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Annotation/Injectable.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Cache/ArrayCache.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Container.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/ContainerBuilder.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Debug.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/AliasDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/ArrayDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/ArrayDefinitionExtension.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/CacheableDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/DecoratorDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Definition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Dumper/ObjectDefinitionDumper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/EntryReference.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/EnvironmentVariableDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Exception/AnnotationException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Exception/DefinitionException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/FactoryDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/HasSubDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Helper/ArrayDefinitionExtensionHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Helper/DefinitionHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Helper/EnvironmentVariableDefinitionHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Helper/FactoryDefinitionHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Helper/ObjectDefinitionHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Helper/StringDefinitionHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Helper/ValueDefinitionHelper.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/InstanceDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/ObjectDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/ObjectDefinition/MethodInjection.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/ObjectDefinition/PropertyInjection.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Resolver/ArrayResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Resolver/DecoratorResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Resolver/DefinitionResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Resolver/EnvironmentVariableResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Resolver/FactoryResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Resolver/InstanceInjector.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Resolver/ObjectCreator.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Resolver/ParameterResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Resolver/ResolverDispatcher.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Resolver/SelfResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/SelfResolvingDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Source/AnnotationReader.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Source/Autowiring.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Source/CachedDefinitionSource.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Source/DefinitionArray.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Source/DefinitionFile.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Source/DefinitionSource.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Source/MutableDefinitionSource.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/Source/SourceChain.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/StringDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Definition/ValueDefinition.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/DependencyException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Factory/RequestedEntry.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/FactoryInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Invoker/DefinitionParameterResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Invoker/FactoryParameterResolver.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/InvokerInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/NotFoundException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Proxy/ProxyFactory.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/Scope.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/php-di/src/DI/functions.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/phpdoc-reader/LICENSE
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/phpdoc-reader/README.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/phpdoc-reader/composer.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/phpdoc-reader/src/PhpDocReader/AnnotationException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/phpdoc-reader/src/PhpDocReader/PhpDocReader.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/phpdoc-reader/src/PhpDocReader/PhpParser/TokenParser.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/php-di/phpdoc-reader/src/PhpDocReader/PhpParser/UseStatementParser.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/phpoption/phpoption/LICENSE
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/phpoption/phpoption/Makefile
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/phpoption/phpoption/composer.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/phpoption/phpoption/src/PhpOption/LazyOption.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/phpoption/phpoption/src/PhpOption/None.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/phpoption/phpoption/src/PhpOption/Option.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/phpoption/phpoption/src/PhpOption/Some.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/psr/container/LICENSE
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/psr/container/README.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/psr/container/composer.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/psr/container/src/ContainerExceptionInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/psr/container/src/ContainerInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/psr/container/src/NotFoundExceptionInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/scoper-autoload.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/symfony/polyfill-ctype/Ctype.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/symfony/polyfill-ctype/LICENSE
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/symfony/polyfill-ctype/README.md
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/symfony/polyfill-ctype/bootstrap.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/symfony/polyfill-ctype/composer.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/LICENSE
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/composer.json
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Dotenv.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Exception/InvalidPathException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Exception/ValidationException.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Loader/Lines.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Loader/Loader.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Loader/Parser.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Loader/Value.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Regex/Regex.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/AbstractRepository.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/Adapter/AvailabilityInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/AdapterRepository.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Result/Error.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Result/Result.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Result/Success.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Store/File/Paths.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Store/File/Reader.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Store/FileStore.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Store/StoreInterface.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Store/StringStore.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/vendor/vlucas/phpdotenv/src/Validator.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/version.php
create mode 100644 wp-content/plugins/wp-migrate-db-pro/wp-migrate-db-pro.php
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/ClassMap.php b/wp-content/plugins/wp-migrate-db-pro/class/ClassMap.php
new file mode 100644
index 000000000..fefbc8d20
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/ClassMap.php
@@ -0,0 +1,610 @@
+state_data_container = new StateDataContainer();
+ $this->filesystem = new Common\Filesystem\Filesystem();
+ $this->properties = new Common\Properties\Properties();
+ $this->util = new Common\Util\Util($this->properties, $this->filesystem);
+ $this->WPMDBRestAPIServer = new WPMDBRestAPIServer($this->properties);
+
+ $this->settings = new Settings(
+ $this->util,
+ $this->filesystem
+ );
+
+
+ $this->error_log = new ErrorLog(
+ $this->settings,
+ $this->filesystem,
+ $this->util,
+ $this->properties
+ );
+
+ $this->dynamic_props = new DynamicProperties();
+ $this->scrambler = new Scramble();
+ $this->migration_state = new Common\MigrationState\MigrationState();
+ $this->http = new Common\Http\Http(
+ $this->util,
+ $this->filesystem,
+ $this->scrambler,
+ $this->properties,
+ $this->error_log
+ );
+
+ $this->migration_state_manager = new MigrationStateManager(
+ $this->error_log,
+ $this->util,
+ new Common\MigrationState\MigrationState(),
+ $this->http,
+ $this->properties,
+ $this->state_data_container
+ );
+
+ $this->form_data = new Common\FormData\FormData(
+ $this->util,
+ $this->migration_state_manager
+ );
+
+ $this->http_helper = new Helper(
+ $this->settings,
+ $this->http
+ );
+
+ $this->multisite = new Common\Multisite\Multisite(
+ $this->migration_state_manager,
+ $this->properties,
+ $this->util
+ );
+
+ $this->table_helper = new TableHelper(
+ $this->form_data,
+ $this->migration_state_manager,
+ $this->http
+ );
+
+ //RemotePost
+ $this->remote_post = new RemotePost(
+ $this->util,
+ $this->filesystem,
+ $this->migration_state_manager,
+ $this->settings,
+ $this->error_log,
+ $this->scrambler,
+ $this->properties
+ );
+
+ $this->pair_factory = new PairFactory();
+ // Swap persistence interface at this point to change storage method.
+ $this->diff_interpreter = new DiffInterpreter(new DiffGroup(new MemoryPersistence()));
+
+ $this->replace = new Replace(
+ $this->migration_state_manager,
+ $this->table_helper,
+ $this->error_log,
+ $this->util,
+ $this->form_data,
+ $this->properties,
+ $this->pair_factory,
+ $this->WPMDBRestAPIServer,
+ $this->http_helper,
+ $this->http,
+ $this->diff_interpreter
+ );
+
+ // Notice
+ $this->notice = new Notice();
+
+ $this->full_site_export = new FullSiteExport([
+ new WPConfigFilter(),
+ ]);
+
+ //Table
+ $this->table = new Table(
+ $this->filesystem,
+ $this->util,
+ $this->error_log,
+ $this->migration_state_manager,
+ $this->form_data,
+ $this->table_helper,
+ $this->multisite,
+ $this->http,
+ $this->http_helper,
+ $this->remote_post,
+ $this->properties,
+ $this->replace,
+ $this->full_site_export
+ );
+ $this->profile_importer = new ProfileImporter($this->util, $this->table);
+ // BackupExport
+ $this->backup_export = new BackupExport(
+ $this->settings,
+ $this->filesystem,
+ $this->table_helper,
+ $this->http,
+ $this->form_data,
+ $this->table,
+ $this->properties,
+ $this->migration_state_manager
+ );
+
+ $this->assets = new Assets(
+ $this->http,
+ $this->error_log,
+ $this->filesystem,
+ $this->properties,
+ $this->settings,
+ $this->util
+ );
+
+
+ $this->migration_helper = new MigrationHelper(
+ $this->multisite,
+ $this->util,
+ $this->table,
+ $this->filesystem,
+ $this->properties,
+ $this->settings,
+ $this->assets
+ );
+ //InitiateMigration
+ $this->initiate_migration = new InitiateMigration(
+ $this->migration_state_manager,
+ new Common\MigrationState\MigrationState(),
+ $this->table,
+ $this->http,
+ $this->http_helper,
+ $this->util,
+ $this->remote_post,
+ $this->form_data,
+ $this->filesystem,
+ $this->error_log,
+ $this->properties,
+ $this->migration_helper,
+ $this->backup_export,
+ $this->full_site_export
+ );
+
+ //FinalizeMigration
+ $this->finalize_migration = new FinalizeMigration(
+ $this->migration_state_manager,
+ $this->table,
+ $this->http,
+ $this->table_helper,
+ $this->http_helper,
+ $this->util,
+ $this->remote_post,
+ $this->form_data,
+ $this->properties,
+ $this->migration_helper
+ );
+
+ // MigrationManager
+ $this->migration_manager = new MigrationManager(
+ $this->migration_state_manager,
+ new Common\MigrationState\MigrationState(),
+ $this->table,
+ $this->http,
+ $this->table_helper,
+ $this->http_helper,
+ $this->util,
+ $this->remote_post,
+ $this->form_data,
+ $this->filesystem,
+ $this->error_log,
+ $this->backup_export,
+ $this->multisite,
+ $this->initiate_migration,
+ $this->finalize_migration,
+ $this->properties,
+ $this->WPMDBRestAPIServer,
+ $this->migration_helper,
+ $this->full_site_export
+ );
+
+ // ProfileManager
+ $this->profile_manager = new ProfileManager(
+ $this->http,
+ $this->http_helper,
+ $this->properties,
+ $this->settings,
+ $this->migration_state_manager,
+ $this->util,
+ $this->error_log,
+ $this->table,
+ $this->form_data,
+ $this->assets,
+ $this->WPMDBRestAPIServer,
+ $this->profile_importer
+ );
+
+ // TemplateBase
+ $this->template_base = new TemplateBase(
+ $this->settings,
+ $this->util,
+ $this->profile_manager,
+ $this->filesystem,
+ $this->table,
+ $this->properties
+ );
+
+ // CompatibilityManager
+ $this->compatibility_manager = new CompatibilityManager(
+ $this->filesystem,
+ $this->settings,
+ $this->notice,
+ $this->http,
+ $this->http_helper,
+ $this->template_base,
+ $this->migration_state_manager,
+ $this->util,
+ $this->properties,
+ $this->WPMDBRestAPIServer
+ );
+
+ $this->settings_manager = new SettingsManager(
+ $this->http,
+ $this->settings,
+ $this->migration_state_manager,
+ $this->error_log,
+ $this->http_helper,
+ $this->WPMDBRestAPIServer,
+ $this->util
+ );
+
+ $this->upgrade_routines_manager = new UpgradeRoutinesManager($this->assets, $this->profile_manager);
+
+
+ $this->plugin_manager_base = new PluginManagerBase(
+ $this->settings,
+ $this->assets,
+ $this->util,
+ $this->table,
+ $this->http,
+ $this->filesystem,
+ $this->multisite,
+ $this->properties,
+ $this->migration_helper,
+ $this->WPMDBRestAPIServer,
+ $this->http_helper,
+ $this->template_base,
+ $this->notice,
+ $this->profile_manager,
+ $this->upgrade_routines_manager
+ );
+
+ $this->cli_manager = new CliManager();
+
+ $this->cli = new Common\Cli\Cli(
+ $this->form_data,
+ $this->util,
+ $this->cli_manager,
+ $this->table,
+ $this->error_log,
+ $this->initiate_migration,
+ $this->finalize_migration,
+ $this->http_helper,
+ $this->migration_manager,
+ $this->migration_state_manager
+ );
+ $this->flush = new Flush($this->http_helper, $this->util, $this->remote_post, $this->http);
+
+ // Transfers classes
+
+ $this->transfers_util = new Util(
+ $this->filesystem,
+ $this->http,
+ $this->error_log,
+ $this->http_helper,
+ $this->remote_post,
+ $this->settings,
+ $this->migration_state_manager,
+ $this->util
+ );
+
+ $this->queue_manager = new Manager(
+ $this->properties,
+ $this->state_data_container,
+ $this->migration_state_manager,
+ $this->form_data
+ );
+
+ $this->transfers_manager = new TransferManager(
+ $this->queue_manager,
+ $this->transfers_util,
+ $this->http,
+ $this->full_site_export
+ );
+
+ $this->recursive_scanner = new RecursiveScanner($this->filesystem, $this->transfers_util);
+
+
+ $this->transfers_file_processor = new FileProcessor(
+ $this->filesystem,
+ $this->http,
+ $this->recursive_scanner
+ );
+
+ $this->transfers_queue_helper = new QueueHelper(
+ $this->filesystem,
+ $this->http,
+ $this->http_helper,
+ $this->transfers_util,
+ $this->queue_manager,
+ $this->util
+ );
+
+ $this->transfers_plugin_helper = new PluginHelper(
+ $this->filesystem,
+ $this->properties,
+ $this->http,
+ $this->http_helper,
+ $this->settings,
+ $this->migration_state_manager,
+ $this->scrambler,
+ $this->transfers_file_processor,
+ $this->transfers_util,
+ $this->queue_manager,
+ $this->queue_manager,
+ $this->state_data_container
+ );
+
+ $this->addon = new Addon(
+ $this->error_log,
+ $this->settings,
+ $this->properties
+ );
+
+ /* Start MF Section */
+ $this->media_files_addon = new MediaFilesAddon(
+ $this->addon,
+ $this->properties,
+ $this->util,
+ $this->transfers_util,
+ $this->filesystem
+ );
+
+ $this->media_files_addon_local = new MediaFilesLocal(
+ $this->form_data,
+ $this->http,
+ $this->util,
+ $this->http_helper,
+ $this->WPMDBRestAPIServer,
+ $this->transfers_manager,
+ $this->transfers_util,
+ $this->transfers_file_processor,
+ $this->transfers_queue_helper,
+ $this->queue_manager,
+ $this->transfers_plugin_helper,
+ $this->profile_manager
+ );
+
+ $this->media_files_manager = new Common\MF\Manager();
+ /* End MF Section */
+
+ /* Start TPF Section */
+ $this->tp_addon_finalize = new ThemePluginFilesFinalize(
+ $this->form_data,
+ $this->filesystem,
+ $this->transfers_util,
+ $this->error_log,
+ $this->http,
+ $this->state_data_container,
+ $this->queue_manager,
+ $this->migration_state_manager,
+ $this->transfers_plugin_helper
+ );
+
+ $this->tp_addon = new ThemePluginFilesAddon(
+ $this->addon,
+ $this->properties,
+ $this->filesystem,
+ $this->profile_manager,
+ $this->util,
+ $this->transfers_util,
+ $this->tp_addon_finalize,
+ $this->transfers_plugin_helper
+ );
+
+ $this->tp_addon_transfer_check = new TransferCheck(
+ $this->form_data,
+ $this->http,
+ $this->error_log
+ );
+
+ $this->tp_addon_local = new ThemePluginFilesLocal(
+ $this->transfers_util,
+ $this->util,
+ $this->transfers_file_processor,
+ $this->queue_manager,
+ $this->transfers_manager,
+ $this->migration_state_manager,
+ $this->http,
+ $this->filesystem,
+ $this->tp_addon_transfer_check,
+ $this->WPMDBRestAPIServer,
+ $this->http_helper,
+ $this->transfers_queue_helper
+ );
+
+ $this->theme_plugin_manager = new Common\TPF\Manager();
+ /* End TPF Section */
+
+ $this->addons_facade = new AddonsFacade([
+ $this->media_files_manager,
+ $this->theme_plugin_manager
+ ]);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/Addon.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/Addon.php
new file mode 100644
index 000000000..5c3fb4047
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/Addon.php
@@ -0,0 +1,210 @@
+props = $properties;
+ $this->log = $log;
+ $this->settings = $settings;
+
+ $this->setAddons();
+ }
+
+ public function getAddons()
+ {
+ return $this->addons;
+ }
+
+ /**
+ * Set versions of Addons required for this version of WP Migrate DB Pro
+ */
+ public function setAddons()
+ {
+ $this->addons = array(
+ 'wp-migrate-db-pro-media-files/wp-migrate-db-pro-media-files.php' => array(
+ 'name' => 'Media Files',
+ 'required_version' => '1.4.18',
+ ),
+ 'wp-migrate-db-pro-cli/wp-migrate-db-pro-cli.php' => array(
+ 'name' => 'CLI',
+ 'required_version' => '1.3.6',
+ ),
+ 'wp-migrate-db-pro-multisite-tools/wp-migrate-db-pro-multisite-tools.php' => array(
+ 'name' => 'Multisite Tools',
+ 'required_version' => '1.2.7',
+ ),
+ 'wp-migrate-db-pro-theme-plugin-files/wp-migrate-db-pro-theme-plugin-files.php' => array(
+ 'name' => 'Theme & Plugin Files',
+ 'required_version' => '1.0.6',
+ ),
+ );
+ }
+
+ public function register()
+ {
+ $this->setAddons();
+
+ // allow developers to change the temporary prefix applied to the tables
+ $this->props->temp_prefix = apply_filters('wpmdb_temporary_prefix', $this->props->temp_prefix);
+ }
+
+ public function is_addon_outdated($addon_basename)
+ {
+ $addon_slug = current(explode('/', $addon_basename));
+
+ // If pre-1.1.2 version of Media Files addon, then it is outdated
+ if (!isset($GLOBALS['wpmdb_meta'][$addon_slug]['version'])) {
+ return true;
+ }
+
+ $installed_version = $GLOBALS['wpmdb_meta'][$addon_slug]['version'];
+ $required_version = $this->addons[$addon_basename]['required_version'];
+
+ return version_compare($installed_version, $required_version, '<');
+ }
+
+ public function get_plugin_name($plugin = false)
+ {
+ if (!is_admin()) {
+ return false;
+ }
+
+ $plugin_basename = (false !== $plugin ? $plugin : $this->props->plugin_basename);
+
+ $plugins = get_plugins();
+
+ if (!isset($plugins[$plugin_basename]['Name'])) {
+ return false;
+ }
+
+ return $plugins[$plugin_basename]['Name'];
+ }
+
+ public function get_latest_version($slug)
+ {
+ if ( ! Util::isPro()) {
+ return false;
+ }
+
+ $data = $this->get_upgrade_data();
+
+ if (!isset($data[$slug])) {
+ return false;
+ }
+
+ $latest_version = empty ($data[$slug]['version']) ? false : $data[$slug]['version'];
+
+ if (!isset($data[$slug]['beta_version'])) {
+ // No beta version available
+ return $latest_version;
+ }
+
+ if (version_compare($data[$slug]['version'], $data[$slug]['beta_version'], '>')) {
+ // Stable version greater than the beta
+ return $latest_version;
+ }
+
+ if (\DeliciousBrains\WPMDB\Pro\Beta\BetaManager::is_rolling_back_plugins()) {
+ // We are in the process of rolling back to stable versions
+ return $latest_version;
+ }
+
+ //Reload the settings to get fresh beta optin value
+ $this->settings->load_settings();
+
+ if (!\DeliciousBrains\WPMDB\Pro\Beta\BetaManager::has_beta_optin($this->settings->get_settings())) {
+ // Not opted in to beta updates
+ // The required version isn't a beta version
+ return $latest_version;
+ }
+
+ return $data[$slug]['beta_version'];
+ }
+
+ public function get_upgrade_data()
+ {
+ $api = WPMDBDI::getInstance()->get('api');
+ $info = get_site_transient('wpmdb_upgrade_data');
+
+ if (isset($info['version'])) {
+ delete_site_transient( Helpers::get_licence_response_transient_key() );
+ delete_site_transient('wpmdb_upgrade_data');
+ $info = false;
+ }
+
+ if ($info) {
+ return $info;
+ }
+
+ $data = $api->dbrains_api_request('upgrade_data');
+
+ $data = json_decode($data, true);
+
+ /*
+ We need to set the transient even when there's an error,
+ otherwise we'll end up making API requests over and over again
+ and slowing things down big time.
+ */
+ $default_upgrade_data = array('wp-migrate-db-pro' => array('version' => $GLOBALS['wpmdb_meta'][$this->props->core_slug]['version']));
+
+ if (!$data) {
+ set_site_transient('wpmdb_upgrade_data', $default_upgrade_data, $this->props->transient_retry_timeout);
+ $this->log->log_error('Error trying to decode JSON upgrade data.');
+
+ return false;
+ }
+
+ if (isset($data['errors'])) {
+ set_site_transient('wpmdb_upgrade_data', $default_upgrade_data, $this->props->transient_retry_timeout);
+ $this->log->log_error('Error trying to get upgrade data.', $data['errors']);
+
+ return false;
+ }
+
+ set_site_transient('wpmdb_upgrade_data', $data, $this->props->transient_timeout);
+
+ return $data;
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/AddonAbstract.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/AddonAbstract.php
new file mode 100644
index 000000000..df1aed5ba
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/AddonAbstract.php
@@ -0,0 +1,136 @@
+addon = $addon;
+ $this->properties = $properties;
+ $this->dynamic_properties = DynamicProperties::getInstance();
+ $this->dynamic_properties->is_addon = true;
+ }
+
+ function meets_version_requirements($version_required)
+ {
+ $wpmdb_pro_version = $GLOBALS['wpmdb_meta']['wp-migrate-db-pro']['version'];
+ $result = version_compare($wpmdb_pro_version, $version_required, '>=');
+ $this->version_required = $version_required;
+
+
+ if ($result) {
+ // If pre-1.1.2 version of Media Files addon,
+ // then it's not supported by this version of core
+ if (empty($this->properties->plugin_version)) {
+ $result = false;
+ } else { // Check that this version of core supports the addon version
+ $plugin_basename = sprintf('%1$s/%1$s.php', $this->plugin_slug);
+ $this->plugin_basename = $plugin_basename;
+ $required_addon_version = $this->addon->getAddons()[$plugin_basename]['required_version'];
+ $result = version_compare($this->properties->plugin_version, $required_addon_version, '>=');
+ }
+ }
+
+ if (false == $result) {
+ $this->hook_version_requirement_actions();
+
+ }
+
+ return $result;
+ }
+
+ function hook_version_requirement_actions()
+ {
+ add_filter('wpmdb_notification_strings', array($this, 'version_requirement_actions'));
+ }
+
+ function version_requirement_actions($notifications)
+ {
+ $addon_requirement_check = get_site_option('wpmdb_addon_requirement_check', array());
+
+ // we only want to delete the transients once, here we keep track of which versions we've checked
+ if (!isset($addon_requirement_check[$this->properties->plugin_slug]) || $addon_requirement_check[$this->properties->plugin_slug] != $GLOBALS['wpmdb_meta'][$this->properties->plugin_slug]['version']) {
+ delete_site_transient('wpmdb_upgrade_data');
+ delete_site_transient('update_plugins');
+ $addon_requirement_check[$this->properties->plugin_slug] = $GLOBALS['wpmdb_meta'][$this->properties->plugin_slug]['version'];
+ update_site_option('wpmdb_addon_requirement_check', $addon_requirement_check);
+ }
+
+ $notice_id = $this->plugin_basename . '-notice';
+
+ $notifications[$notice_id] = [
+ 'message' => $this->version_requirement_warning(),
+ 'link' => false,
+ 'id' => $notice_id,
+ ];
+
+ return $notifications;
+ }
+
+ function version_requirement_warning()
+ {
+ $str = 'Update Required — ';
+
+ $addon_name = $this->addon_name;
+ $required = $this->version_required;
+ $installed = $GLOBALS['wpmdb_meta']['wp-migrate-db-pro']['version'];
+ $wpmdb_basename = sprintf('%s/%s.php', $GLOBALS['wpmdb_meta']['wp-migrate-db-pro']['folder'], 'wp-migrate-db');
+ $update = wp_nonce_url(network_admin_url('update.php?action=upgrade-plugin&plugin=' . urlencode($wpmdb_basename)), 'upgrade-plugin_' . $wpmdb_basename);
+ $str .= sprintf(__('The version of %1$s you have installed requires version %2$s of WP Migrate. You currently have %3$s installed. Update Now', 'wp-migrate-db'), $addon_name, $required, $installed, $update);
+
+ return $str;
+ }
+
+
+ /**
+ * @param bool $is_licensed
+ **/
+ public function set_licensed($is_licensed)
+ {
+ $this->licensed = $is_licensed;
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/AddonManagerInterface.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/AddonManagerInterface.php
new file mode 100644
index 000000000..219c27757
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Addon/AddonManagerInterface.php
@@ -0,0 +1,8 @@
+addons = $addons;
+ }
+
+ public function register() {
+ if (false === self::$initialized) {
+ add_action('activate_plugin', [$this, 'prevent_legacy_addon_activation']);
+ add_action('admin_notices', [$this, 'legacy_addon_notice']);
+ add_action('plugins_loaded', [$this, 'initialize_addons'], PHP_INT_MAX);
+
+ if (false === get_site_transient('wpmdb_disabled_legacy_addons')) {
+ add_action('plugins_loaded', [$this, 'disable_legacy_addons'], PHP_INT_MAX);
+ set_site_transient('wpmdb_disabled_legacy_addons', true);
+ }
+
+ self::$initialized = true;
+ }
+ }
+
+
+ /**
+ * Initializes registered addons
+ *
+ * @return void
+ */
+ public function initialize_addons()
+ {
+ foreach ($this->addons as $addon) {
+ $addon->register(false);
+ }
+ }
+
+
+ /**
+ * Deactivates legacy addons on upgrade
+ *
+ * @return void
+ */
+ public static function disable_legacy_addons()
+ {
+ Util::disable_legacy_addons();
+ }
+
+ /**
+ * Prevents legacy addons from being activated
+ *
+ * @return void
+ */
+ public function prevent_legacy_addon_activation($plugin)
+ {
+ if (in_array($plugin, self::LEGACY_ADDONS)) {
+ $redirect = self_admin_url('plugins.php?legacyaddon=1');
+ wp_redirect($redirect);
+ exit;
+ }
+ }
+
+ /**
+ * Notice when trying to activate addon
+ *
+ * @return void
+ */
+ public function legacy_addon_notice()
+ {
+ if (isset($_GET['legacyaddon'])) {
+ $message = __('Legacy addons cannot be activated alongside WP Migrate version 2.3.0 or above. These features have been moved to WP Migrate.', 'wp-migrate-db');
+ echo '';
+ }
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/BackupExport.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/BackupExport.php
new file mode 100644
index 000000000..a96c557cc
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/BackupExport.php
@@ -0,0 +1,167 @@
+props = $properties;
+ $this->settings = $settings->get_settings();
+ $this->filesystem = $filesystem;
+ $this->table_helper = $table_helper;
+ $this->http = $http;
+ $this->form_data = $form_data;
+ $this->table = $table;
+ $this->migration_state_manager = $migration_state_manager;
+ }
+
+ public function register()
+ {
+ add_filter('wpmdb_backup_header_included_tables', array($this, 'backup_header_included_tables'));
+ }
+
+ public function delete_export_file($filename, $is_backup)
+ {
+ $dump_file = $this->table_helper->format_dump_name($filename);
+
+ if (true === $is_backup) {
+ $dump_file = preg_replace('/.gz$/', '', $dump_file);
+ }
+
+ $dump_file = $this->filesystem->get_upload_info('path') . DIRECTORY_SEPARATOR . $dump_file;
+
+ if (empty($dump_file) || false === $this->filesystem->file_exists($dump_file)) {
+ return $this->http->end_ajax(new \WP_Error('wp-migrate-db-export-not-found', __('MySQL export file not found.', 'wp-migrate-db')));
+ }
+
+ if (false === $this->filesystem->unlink($dump_file)) {
+ return $this->http->end_ajax(new \WP_Error('wp-migrate-db-export-not-found', __('Could not delete the MySQL export file.', 'wp-migrate-db')));
+ }
+
+ return null;
+ }
+
+ /**
+ * Determine which tables to backup (if required).
+ *
+ * @param $profile
+ * @param $prefixed_tables
+ * @param $all_tables
+ *
+ * @return mixed|void
+ */
+ public function get_tables_to_backup($profile, $prefixed_tables, $all_tables)
+ {
+ $tables_to_backup = array();
+
+ switch ($profile['backup_option']) {
+ case 'backup_only_with_prefix':
+ $tables_to_backup = $prefixed_tables;
+ break;
+ case 'backup_selected':
+ $selected_tables = isset($profile['select_backup']) && !empty($profile['select_backup']) ? $profile['select_backup'] : $profile['select_tables'];
+
+ /**
+ * When tables to migrate is tables with prefix, select_tables
+ * might be empty. Intersecting it with remote/local tables
+ * throws notice/warning and won't backup the file either.
+ */
+ if ('migrate_only_with_prefix' === $profile['table_migrate_option'] || empty($selected_tables)) {
+ $tables_to_backup = $prefixed_tables;
+ } else {
+ $selected_tables = isset($profile['select_backup']) && !empty($profile['select_backup']) ? $profile['select_backup'] : $profile['select_tables'];
+ $tables_to_backup = array_intersect($selected_tables, $all_tables);
+ }
+ break;
+ case 'backup_manual_select':
+ $tables_to_backup = array_intersect($profile['select_backup'], $all_tables);
+ break;
+ }
+
+ return apply_filters('wpmdb_tables_to_backup', $tables_to_backup, $profile);
+ }
+
+ /**
+ * Updates the database backup header with the tables that were backed up.
+ *
+ * @param $included_tables
+ *
+ * @return mixed|void
+ */
+ public function backup_header_included_tables($included_tables)
+ {
+ $state_data = $this->migration_state_manager->set_post_data();
+ $form_data = $this->form_data->getFormData();
+
+ if ('backup' === $state_data['stage']) {
+ $included_tables = $this->get_tables_to_backup($form_data, $this->table->get_tables('prefix'), $this->table->get_tables());
+ }
+
+ return $included_tables;
+ }
+
+ public function setup_backups()
+ {
+ $dump_filename = wp_basename($this->table->get_sql_dump_info('backup', 'path'));
+ $dump_filename = substr($dump_filename, 0, -4);
+
+ return [
+ $dump_filename,
+ $this->table->get_sql_dump_info('backup', 'url'),
+ ];
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/Cli.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/Cli.php
new file mode 100644
index 000000000..624903857
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/Cli.php
@@ -0,0 +1,1008 @@
+form_data = $form_data;
+ $this->util = $util;
+ $this->cli_manager = $cli_manager;
+ $this->table = $table;
+ $this->error_log = $error_log;
+ $this->initiate_migration = $initiate_migration;
+ $this->finalize_migration = $finalize_migration;
+ $this->http_helper = $http_helper;
+ $this->migration_manager = $migration_manager;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->dynamic_properties = DynamicProperties::getInstance();
+ $this->profile_importer = new ProfileImporter($this->util, $this->table);
+ }
+
+ public function register()
+ {
+ add_filter('wpmdb_cli_finalize_migration_response', array($this, 'finalize_ajax'), 10, 2);
+ add_filter('wpmdb_cli_tables_to_migrate', array($this, 'filter_non_database_migration_tables'), 99, 2);
+ }
+
+ /**
+ * Checks profile data before CLI migration.
+ *
+ * @param int|array $profile Profile key or array.
+ *
+ * @return mixed|WP_Error
+ */
+ public function pre_cli_migration_check($profile)
+ {
+ $profile = apply_filters('wpmdb_cli_profile_before_migration', $profile);
+
+ if (is_wp_error($profile)) {
+ return $profile;
+ }
+
+ if (is_array($profile)) {
+ Persistence::cleanupStateOptions();
+ $profile = $this->form_data->parse_and_save_migration_form_data(json_encode($profile));
+ }
+
+ $this->profile = $profile;
+
+ if (!isset($this->profile['current_migration']['stages'])) {
+ $this->profile['current_migration']['stages'] = array('tables');
+ }
+
+ $this->profile['current_migration']['migration_id'] = Util::uuidv4();
+
+ return true;
+ }
+
+ /**
+ * Performs CLI migration given a profile data.
+ *
+ * @param int|array $profile Profile key or array.
+ * @param array $assoc_args
+ *
+ * @return bool|WP_Error Returns true if succeed or WP_Error if failed.
+ */
+ public function cli_migration($profile, $assoc_args = array())
+ {
+ $pre_check = $this->pre_cli_migration_check($profile);
+ if (is_wp_error($pre_check)) {
+ return $pre_check;
+ }
+
+ // At this point, $profile has been checked a retrieved into $this->profile, so should not be used in this function any further.
+ if (empty($this->profile)) {
+ return $this->cli_error(__('Profile not found or unable to be generated from params.', 'wp-migrate-db-cli'));
+ }
+ unset($profile);
+
+ $this->util->set_time_limit();
+ $this->cli_manager->set_cli_migration();
+
+ if ('savefile' === $this->profile['action']) {
+ $this->post_data['intent'] = 'savefile';
+ if (!empty($this->profile['export_dest'])) {
+ $this->post_data['export_dest'] = $this->profile['export_dest'];
+ } else {
+ $this->post_data['export_dest'] = 'ORIGIN';
+ }
+ }
+
+ if ('find_replace' === $this->profile['action']) {
+ $this->post_data['intent'] = 'find_replace';
+ }
+
+ if ('import' === $this->profile['action']) {
+ $this->post_data['intent'] = 'import';
+
+ if (!isset($this->profile['import_file'])) {
+ if (isset($assoc_args['import-file'])) {
+ $this->profile['import_file'] = $assoc_args['import-file'];
+ } else {
+ return $this->cli_error(__('Missing path to import file. Use --import-file=/path/to/import.sql.gz', 'wp-migrate-db'));
+ }
+ }
+ }
+
+ if (
+ isset($this->profile['current_migration'], $this->profile['current_migration']['intent'])
+ && 'backup_local' === $this->profile['current_migration']['intent']
+ ) {
+ $this->post_data['intent'] = 'savefile';
+ }
+
+ // Ensure local site_details available.
+ $this->post_data['site_details']['local'] = $this->util->site_details();
+
+ $this->profile = apply_filters('wpmdb_cli_filter_before_cli_initiate_migration', $this->profile, $this->post_data);
+
+ if (is_wp_error($this->profile)) {
+ \WP_CLI::error($this->profile->get_error_message());
+ }
+
+ // Check for tables specified in migration profile that do not exist in the source database
+ if (!empty($this->profile['select_tables']) && 'import' !== $this->profile['action']) {
+ $source_tables = apply_filters('wpmdb_cli_filter_source_tables', $this->table->get_tables(), $this->profile);
+
+ if (!empty($source_tables)) {
+ // Return error if selected tables do not exist in source database
+ $nonexistent_tables = array();
+ foreach ($this->profile['select_tables'] as $table) {
+ if (!in_array($table, $source_tables)) {
+ $nonexistent_tables[] = $table;
+ }
+ }
+
+ if (!empty($nonexistent_tables)) {
+ $local_or_remote = ('pull' === $this->profile['action']) ? 'remote' : 'local';
+
+ return $this->cli_error(sprintf(__('The following table(s) do not exist in the %1$s database: %2$s', 'wp-migrate-db-cli'), $local_or_remote, implode(', ', $nonexistent_tables)));
+ }
+ }
+ }
+
+ if (!empty($this->dynamic_properties->post_data)) {
+ $this->post_data = $this->dynamic_properties->post_data;
+ }
+
+ if (is_wp_error($this->profile)) {
+ return $this->profile;
+ }
+
+ $this->profile = apply_filters('wpmdb_cli_filter_before_migration', $this->profile, $this->post_data);
+ do_action('wpmdb_cli_before_migration', $this->post_data, $this->profile);
+ $this->migration = $this->cli_initiate_migration();
+
+ if (is_wp_error($this->migration)) {
+ return $this->migration;
+ }
+
+ if ('import' === $this->profile['action']) {
+ if ($this->profile['create_backup']) {
+ $tables_to_process = $this->migrate_tables();
+ } else {
+ $tables_to_process = $this->get_tables_to_migrate();
+ }
+ } else {
+ $tables_to_process = $this->migrate_tables();
+ }
+
+ if (is_wp_error($tables_to_process)) {
+ return $tables_to_process;
+ }
+
+ $this->post_data['tables'] = implode(',', $tables_to_process);
+
+ do_action('wpmdb_cli_during_cli_migration', $this->post_data, $this->profile);
+
+ $finalize = $this->finalize_migration();
+
+ if (is_wp_error($finalize) || in_array($this->profile['action'], ['savefile', 'backup_local'])) {
+ return $finalize;
+ }
+
+ return true;
+ }
+
+ /**
+ * Verify CLI response from endpoint.
+ *
+ * @param string $response Response from endpoint.
+ * @param string $function_name Name of called function.
+ *
+ * @return WP_Error|string
+ */
+ function verify_cli_response($response, $function_name)
+ {
+ if (is_wp_error($response)) {
+ return $response;
+ }
+
+ $response = trim($response);
+ if (false === $response) {
+ return $this->cli_error($this->error_log->getError());
+ }
+
+ if (false === Util::is_json($response)) {
+ return $this->cli_error(sprintf(__('We were expecting a JSON response, instead we received: %2$s (function name: %1$s)', 'wp-migrate-db-cli'), $function_name, $response));
+ }
+
+ $response = json_decode($response, true);
+ if (isset($response['wpmdb_error'])) {
+ return $this->cli_error($response['body']);
+ }
+
+ // Display warnings and non fatal error messages as CLI warnings without aborting.
+ if (isset($response['wpmdb_warning']) || isset($response['wpmdb_non_fatal_error'])) {
+ $body = (isset($response['cli_body'])) ? $response['cli_body'] : $response['body'];
+ $messages = maybe_unserialize($body);
+ foreach ((array) $messages as $message) {
+ if ($message) {
+ \WP_CLI::warning(self::cleanup_message($message));
+ }
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * Return instance of WP_Error.
+ *
+ * @param string $message Error message.
+ *
+ * @return \WP_Error.
+ */
+ function cli_error($message)
+ {
+ return new \WP_Error('wpmdb_cli_error', self::cleanup_message($message));
+ }
+
+ /**
+ * Cleanup message, replacing
with \n and removing HTML.
+ *
+ * @param string $message Error message.
+ *
+ * @return string $message.
+ */
+ static function cleanup_message($message)
+ {
+ $message = html_entity_decode($message, ENT_QUOTES);
+ $message = preg_replace('#
#', "\n", $message);
+ $message = trim(strip_tags($message));
+
+ return $message;
+ }
+
+ /**
+ * Initiates migration and verifies result
+ *
+ * @return array|WP_Error
+ */
+ function cli_initiate_migration()
+ {
+ do_action('wpmdb_cli_before_initiate_migration', $this->profile);
+
+ \WP_CLI::log(__('Initiating migration...', 'wp-migrate-db-cli'));
+
+ $migration_args = $this->post_data;
+ $migration_args['form_data'] = json_encode($this->profile);
+ $migration_args['stage'] = 'migrate';
+ $migration_args['site_details']['local'] = $this->util->site_details();
+
+ if ('find_replace' === $this->profile['action']) {
+ $migration_args['stage'] = 'find_replace';
+ }
+
+ $this->post_data = apply_filters('wpmdb_cli_initiate_migration_args', $migration_args, $this->profile);
+
+ $this->post_data['site_details'] = json_encode($this->post_data['site_details']);
+
+ $response = $this->initiate_migration($this->post_data);
+
+ $initiate_migration_response = $this->verify_cli_response($response, 'initiate_migration()');
+ if (!is_wp_error($initiate_migration_response)) {
+ $initiate_migration_response = apply_filters('wpmdb_cli_initiate_migration_response', $initiate_migration_response);
+ }
+
+ return $initiate_migration_response;
+ }
+
+ /**
+ * Determine which tables to migrate
+ *
+ * @return array|WP_Error
+ */
+ function get_tables_to_migrate()
+ {
+ $tables_to_migrate = $this->table->get_tables('prefix');
+
+ // @TODO Hack to get profile and post_data info available in other areas of the codebase...
+ $this->dynamic_properties->profile = $this->profile;
+ $this->dynamic_properties->post_data = $this->post_data;
+
+ return apply_filters('wpmdb_cli_tables_to_migrate', $tables_to_migrate, $this->profile, $this->migration);
+ }
+
+ /**
+ * Returns a WP-CLI progress bar instance
+ *
+ * @param array $tables
+ * @param int $stage
+ *
+ * @return cli\progress\Bar|WP_CLI\NoOp
+ */
+ function get_progress_bar($tables, $stage)
+ {
+
+ if($this->is_non_database_migration($this->profile)) {
+ return null;
+ }
+
+ $progress_label = __('Exporting tables', 'wp-migrate-db-cli');
+
+ if ('find_replace' === $this->profile['action']) {
+ $progress_label = __('Running find & replace', 'wp-migrate-db-cli');
+
+ if (1 === $stage) {
+ $progress_label = __('Performing backup', 'wp-migrate-db-cli');
+ }
+ }
+
+ $progress_label = apply_filters('wpmdb_cli_progress_label', $progress_label, $stage, $tables);
+
+ $progress_label = str_pad($progress_label, 20, ' ');
+
+ $count = $this->get_total_rows_from_table_list($tables, $stage);
+
+ return \WP_CLI\Utils\make_progress_bar($progress_label, $count);
+ }
+
+ /**
+ * Returns total rows from list of tables
+ *
+ * @param array $tables
+ * @param int $stage
+ *
+ * @return Int
+ */
+ function get_total_rows_from_table_list($tables, $stage)
+ {
+ static $cached_results = array();
+
+ if (isset($cached_results[$stage])) {
+ return $cached_results[$stage];
+ }
+
+ $table_rows = $this->get_row_counts_from_table_list($tables, $stage);
+ $cached_results[$stage] = array_sum(array_intersect_key($table_rows, array_flip($tables)));
+
+ return $cached_results[$stage];
+ }
+
+ /**
+ * Returns row counts from list of tables
+ *
+ * @param array $tables
+ * @param int $stage
+ *
+ * @return mixed
+ */
+ function get_row_counts_from_table_list($tables, $stage)
+ {
+ static $cached_results = array();
+
+ if (isset($cached_results[$stage])) {
+ return $cached_results[$stage];
+ }
+
+ $local_table_rows = $this->table->get_table_row_count();
+ $cached_results[$stage] = apply_filters('wpmdb_cli_get_row_counts_from_table_list', $local_table_rows, $stage);
+
+ return $cached_results[$stage];
+ }
+
+ /**
+ * @return array|mixed|string|void|WP_Error
+ */
+ function migrate_tables()
+ {
+ if($this->is_non_database_migration($this->profile)) {
+ return [];
+ }
+ $tables_to_migrate = $this->get_tables_to_migrate();
+ $this->dynamic_properties->post_data = $this->post_data;
+
+ $tables = $tables_to_migrate;
+ $stage_iterator = 2;
+
+ $filtered_vars = apply_filters('wpmdb_cli_filter_before_migrate_tables', array(
+ 'tables' => $tables,
+ 'stage_iterator' => $stage_iterator,
+ ));
+ if (!is_array($filtered_vars)) {
+ return $filtered_vars;
+ } else {
+ extract($filtered_vars, EXTR_OVERWRITE);
+ }
+
+ if (empty($tables) && !$this->is_non_database_migration($this->profile)) {
+ return $this->cli_error(__('No tables selected for migration.', 'wp-migrate-db'));
+ }
+
+ $table_rows = $this->get_row_counts_from_table_list($tables, $stage_iterator);
+
+ do_action('wpmdb_cli_before_migrate_tables', $this->profile, $this->migration);
+
+ $notify = $this->get_progress_bar($tables, $stage_iterator);
+ $args = $this->post_data;
+
+ do {
+ $migration_progress = 0;
+
+ foreach ($tables as $key => $table) {
+ $current_row = -1;
+ $primary_keys = '';
+ $table_progress = 0;
+ $table_progress_last = 0;
+
+ $args['table'] = $table;
+ $args['last_table'] = ($key == count($tables) - 1) ? '1' : '0';
+
+ do {
+ // reset the current chunk
+ $this->table->empty_current_chunk();
+
+ $args['current_row'] = $current_row;
+ $args['primary_keys'] = $primary_keys;
+ $args = apply_filters('wpmdb_cli_migrate_table_args', $args, $this->profile, $this->migration);
+
+ $response = $this->migrate_table($args);
+
+ $migrate_table_response = $this->verify_cli_response($response, 'migrate_table()');
+
+ if (is_wp_error($migrate_table_response)) {
+ return $migrate_table_response;
+ }
+
+ $migrate_table_response = apply_filters('wpmdb_cli_migrate_table_response', $migrate_table_response, $_POST, $this->profile, $this->migration);
+
+ $current_row = $migrate_table_response['current_row'];
+ $primary_keys = $migrate_table_response['primary_keys'];
+
+ $last_migration_progress = $migration_progress;
+
+ if (-1 == $current_row) {
+ $migration_progress -= $table_progress;
+ $migration_progress += $table_rows[$table];
+ } else {
+ if (0 === $table_progress_last) {
+ $table_progress_last = $current_row;
+ $table_progress = $table_progress_last;
+ $migration_progress += $table_progress_last;
+ } else {
+ $iteration_progress = $current_row - $table_progress_last;
+ $table_progress_last = $current_row;
+ $table_progress += $iteration_progress;
+ $migration_progress += $iteration_progress;
+ }
+ }
+
+ $increment = $migration_progress - $last_migration_progress;
+
+ if (null !== $notify) {
+ $notify->tick($increment);
+ }
+ } while (-1 != $current_row);
+ }
+
+ if (null !== $notify) {
+ $notify->finish();
+ }
+
+ ++$stage_iterator;
+ $args['stage'] = 'migrate';
+
+ if ('find_replace' === $args['intent']) {
+ $args['stage'] = 'find_replace';
+ }
+
+ if ('import' === $args['intent']) {
+ break;
+ }
+
+ $tables = $tables_to_migrate;
+ $table_rows = $this->get_row_counts_from_table_list($tables, $stage_iterator);
+
+ if ($stage_iterator < 3) {
+ $notify = $this->get_progress_bar($tables, $stage_iterator);
+ }
+ } while ($stage_iterator < 3);
+
+ $this->post_data = $args;
+
+ return $tables;
+ }
+
+ /**
+ * Finalize migration
+ *
+ * @return bool|WP_Error
+ */
+ function finalize_migration()
+ {
+ do_action('wpmdb_cli_before_finalize_migration', $this->profile, $this->migration);
+
+ if (!$this->is_non_database_migration($this->profile)) {
+ \WP_CLI::log(__('Cleaning up...', 'wp-migrate-db-cli'));
+ }
+
+ $finalize = apply_filters('wpmdb_cli_finalize_migration', true, $this->profile, $this->migration);
+ if (is_wp_error($finalize)) {
+ return $finalize;
+ }
+
+ $this->post_data = apply_filters('wpmdb_cli_finalize_migration_args', $this->post_data, $this->profile, $this->migration);
+
+ $this->dynamic_properties->post_data = $this->post_data;
+
+ if ('savefile' === $this->post_data['intent']) {
+ return $this->finalize_export();
+ }
+
+ $response = apply_filters('wpmdb_cli_finalize_migration_response', null, $this->post_data);
+ $response = $this->verify_cli_response($response, 'finalize_migration()');
+
+ if (is_wp_error($response)) {
+ return $response;
+ }
+
+ do_action('wpmdb_cli_after_finalize_migration', $this->profile, $this->migration);
+
+ return true;
+ }
+
+ /**
+ * Stub for ajax_initiate_migration()
+ *
+ * @param array|bool $args
+ *
+ * @return string
+ */
+ function initiate_migration($args = false)
+ {
+ $_POST = $args;
+ $response = $this->initiate_migration->ajax_initiate_migration();
+
+ return $response;
+ }
+
+ /**
+ * stub for ajax_migrate_table()
+ *
+ * @param array|bool $args
+ *
+ * @return string
+ */
+ function migrate_table($args = false)
+ {
+ $_POST = $args;
+ $response = $this->migration_manager->ajax_migrate_table();
+
+ return $response;
+ }
+
+ /**
+ * Stub for ajax_finalize_migration()
+ * hooks on: wpmdb_cli_finalize_migration_response
+ *
+ * @param string $response
+ *
+ * @return string
+ */
+ function finalize_ajax($response, $post_data)
+ {
+ if (is_wp_error($response)) {
+ return $response;
+ }
+ // don't send redundant POST variables
+ $args = $this->http_helper->filter_post_elements($post_data, array('action', 'migration_state_id', 'prefix', 'tables', 'profileID', 'profileType'));
+ $_POST = $args;
+
+ $response = $this->finalize_migration->ajax_finalize_migration();
+
+ return $this->verify_cli_response($response, 'finalize_ajax()');
+ }
+
+ /**
+ * Finalize Export by moving file to specified destination
+ *
+ * @return string|error
+ */
+ function finalize_export()
+ {
+ $state_data = $this->migration_state_manager->set_post_data();
+
+ $temp_file = $state_data['dump_path'];
+ if (!isset($state_data['export_dest']) || 'ORIGIN' === $state_data['export_dest']) {
+ $response = $temp_file;
+ } else {
+ $dest_file = $state_data['export_dest'];
+ if (file_exists($temp_file) && rename($temp_file, $dest_file)) {
+ $response = $dest_file;
+ } else {
+ $response = $this->cli_error(__('Unable to move exported file.', 'wp-migrate-db'));
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * Returns array of CLI options that are unknown to plugin and addons.
+ *
+ * @param array $assoc_args
+ *
+ * @return array
+ */
+ public function get_unknown_args($assoc_args = array())
+ {
+ $unknown_args = array();
+
+ if (empty($assoc_args)) {
+ return $unknown_args;
+ }
+
+ $known_args = array(
+ 'action',
+ 'export_dest',
+ 'find',
+ 'replace',
+ 'regex-find',
+ 'regex-replace',
+ 'case-sensitive-find',
+ 'case-sensitive-replace',
+ 'exclude-spam',
+ 'gzip-file',
+ 'exclude-post-revisions',
+ 'skip-replace-guids',
+ 'include-transients',
+ 'exclude-database'
+ );
+
+ $known_args = apply_filters('wpmdb_cli_filter_get_extra_args', $known_args);
+ return array_diff(array_keys($assoc_args), $known_args);
+ }
+
+ /**
+ * Get profile data from CLI args.
+ *
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @return array|WP_Error
+ */
+ public function get_profile_data_from_args($args, $assoc_args)
+ {
+ $name = null;
+ $export_dest = null;
+ $create_backup = '0';
+ $cli_profile = true;
+
+ //load correct cli class
+ if (function_exists('wp_migrate_db_pro_cli_addon') && function_exists('wp_migrate_db_pro')) {
+ $this->wpmdb_cli = wp_migrate_db_pro_cli_addon();
+ } elseif (function_exists('wpmdb_pro_cli')) {
+ $this->wpmdb_cli = wpmdb_pro_cli();
+ } else {
+ $this->wpmdb_cli = wpmdb_cli();
+ }
+
+ $unknown_args = $this->get_unknown_args($assoc_args);
+
+ if (!empty($unknown_args)) {
+ $message = __('Parameter errors: ', 'wp-migrate-db-cli');
+ foreach ($unknown_args as $unknown_arg) {
+ $message .= "\n " . sprintf(__('unknown %s parameter', 'wp-migrate-db-cli'), '--' . $unknown_arg);
+ }
+
+
+ return $this->wpmdb_cli->cli_error($message);
+ }
+
+ foreach ($assoc_args as $key => $value) {
+ if (empty($value)) {
+ \WP_CLI::warning(__('--' . $key . ' parameter needs a value.', 'wp-migrate-db-cli'));
+ }
+ }
+
+ if (empty($assoc_args['action'])) {
+ return $this->wpmdb_cli->cli_error(__('Missing action parameter', 'wp-migrate-db-cli'));
+ }
+
+ if ('savefile' === $assoc_args['action'] && !empty($assoc_args['export_dest'])) {
+ $export_dest = $assoc_args['export_dest'];
+ }
+
+ $action = $assoc_args['action'];
+
+ // --find= and --replace= and --regex-find= and --regex-replace=
+ $replace_old = array();
+ $replace_new = array();
+ $regex = array();
+ $case_sensitive = array();
+
+ if(!empty($assoc_args['regex-find'])) {
+ $regex_search = $assoc_args['regex-find'];
+
+ if(!Util::is_regex_pattern_valid($regex_search)){
+ return $this->wpmdb_cli->cli_error(__('Please make sure Regular Expression find & replace pattern is valid', 'wp-migrate-db-cli'));
+ }
+
+ if (('find_replace' === $assoc_args['action']) && empty($assoc_args['regex-replace'])) {
+ return $this->wpmdb_cli->cli_error(__('Missing Regex find and replace values.', 'wp-migrate-db-cli'));
+ }
+
+ $replace_old[] = $regex_search;
+ $regex[count($replace_old)] = true;
+ }
+
+ if (!empty($assoc_args['regex-replace'])) {
+ $regex_replace = $assoc_args['regex-replace'];
+ if (('find_replace' === $assoc_args['action']) && empty($assoc_args['regex-find'])) {
+ return $this->wpmdb_cli->cli_error(__('Missing Regex find and replace values.', 'wp-migrate-db-cli'));
+ }
+ $replace_new[] = $regex_replace;
+ }
+
+ if (!empty($assoc_args['case-sensitive-find'])) {
+ $case_sensitive_search = $this->extract_argument('case-sensitive-find', $assoc_args);
+ if (('find_replace' === $assoc_args['action']) && empty($assoc_args['case-sensitive-replace'])) {
+ return $this->wpmdb_cli->cli_error(__('Missing case sensitive find and replace values.', 'wp-migrate-db-cli'));
+ }
+
+ $replace_old_count = count($replace_old);
+ $i = $replace_old_count === 0 ? 1 : $replace_old_count+1;
+ $replace_old = array_merge($replace_old, $case_sensitive_search);
+
+ foreach ($case_sensitive_search as $value) {
+ $case_sensitive[$i] = true;
+ $i++;
+ }
+ }
+
+ if (!empty($assoc_args['case-sensitive-replace'])) {
+ $case_sensitive_replace = $this->extract_argument('case-sensitive-replace', $assoc_args);
+ if (('find_replace' === $assoc_args['action']) && empty($assoc_args['case-sensitive-find'])) {
+ return $this->wpmdb_cli->cli_error(__('Missing case sensitive find and replace values.', 'wp-migrate-db-cli'));
+ }
+ $replace_new = array_merge($replace_new, $case_sensitive_replace);
+ }
+
+ if (!empty($assoc_args['find'])) {
+ $replace_old = array_merge($replace_old, str_getcsv($assoc_args['find']));
+ } else if (('find_replace' === $assoc_args['action']) && empty($regex_replace) && empty($regex_search) && empty($case_sensitive_search) && empty($case_sensitive_replace)) {
+ if (empty($assoc_args['replace'])) {
+
+ return $this->wpmdb_cli->cli_error(__('Missing find and replace values.', 'wp-migrate-db-cli'));
+ }
+
+ return $this->wpmdb_cli->cli_error(__('Find value is required.', 'wp-migrate-db-cli'));
+ }
+
+ if (!empty($assoc_args['replace'])) {
+ $replace_new = array_merge($replace_new, str_getcsv($assoc_args['replace']));
+ } else {
+ if ('find_replace' === $assoc_args['action'] && empty($regex_replace) && empty($regex_search) && empty($case_sensitive_search) && empty($case_sensitive_replace)) {
+ return $this->wpmdb_cli->cli_error(__('Replace value is required.', 'wp-migrate-db-cli'));
+ }
+ }
+
+ if (count($replace_old) !== count($replace_new)) {
+ return $this->wpmdb_cli->cli_error(sprintf(__('%1$s and %2$s must contain the same number of values', 'wp-migrate-db-cli'), '--find', '--replace'));
+ }
+
+ // --exclude-spam
+ $exclude_spam = (int)isset($assoc_args['exclude-spam']);
+
+ // --gzip-file
+ $gzip_file = (int)isset($assoc_args['gzip-file']);
+
+ $select_post_types = $this->table->get_post_types();
+ $exclude_post_types = '0';
+
+ // --exclude-post-revisions
+ if (!empty($assoc_args['exclude-post-revisions'])) {
+ $select_post_types = ['revision']; // This gets flipped around in ProfileImporter::profileFormat().
+ $exclude_post_types = '1';
+ }
+
+ // --skip-replace-guids
+ $replace_guids = 1;
+ if (isset($assoc_args['skip-replace-guids'])) {
+ $replace_guids = 0;
+ }
+
+ $select_tables = array();
+ $table_migrate_option = 'migrate_only_with_prefix';
+
+ // --include-transients.
+ $exclude_transients = intval(!isset($assoc_args['include-transients']));
+
+ //cleanup filename for exports
+ if (!empty($export_dest)) {
+ if ($gzip_file) {
+ if ('gz' !== pathinfo($export_dest, PATHINFO_EXTENSION)) {
+ if ('sql' === pathinfo($export_dest, PATHINFO_EXTENSION)) {
+ $export_dest .= '.gz';
+ } else {
+ $export_dest .= '.sql.gz';
+ }
+ }
+ } elseif ('sql' !== pathinfo($export_dest, PATHINFO_EXTENSION)) {
+ $export_dest = preg_replace('/(\.sql)?(\.gz)?$/i', '', $export_dest) . '.sql';
+ }
+
+ // ensure export destination is writable
+ if (!@touch($export_dest)) {
+ return $this->wpmdb_cli->cli_error(sprintf(__('Cannot write to file "%1$s". Please ensure that the specified directory exists and is writable.', 'wp-migrate-db-cli'), $export_dest));
+ }
+ }
+
+ $databaseEnabled = true;
+ if ( ! empty($assoc_args['exclude-database'])) {
+ $databaseEnabled = false;
+ }
+
+ $profile = compact(
+ 'action',
+ 'replace_old',
+ 'table_migrate_option',
+ 'replace_new',
+ 'select_tables',
+ 'exclude_post_types',
+ 'select_post_types',
+ 'replace_guids',
+ 'exclude_spam',
+ 'gzip_file',
+ 'exclude_transients',
+ 'export_dest',
+ 'create_backup',
+ 'name',
+ 'cli_profile',
+ 'regex',
+ 'case_sensitive',
+ 'databaseEnabled'
+ );
+
+ $home = preg_replace('/^https?:/', '', home_url());
+ $path = esc_html(addslashes(Util::get_absolute_root_file_path()));
+
+ $old_profile = apply_filters('wpmdb_cli_filter_get_profile_data_from_args', $profile, $args, $assoc_args);
+
+ if (is_wp_error($old_profile)) {
+ return $old_profile;
+ }
+
+ $new_profile = $this->profile_importer->profileFormat($old_profile, $home, $path);
+ return array_merge($old_profile, $new_profile);
+ }
+
+ private function extract_argument($argument, $assoc_args) {
+ if(!empty($assoc_args[$argument])) {
+ return str_getcsv($assoc_args[$argument]);
+ }
+ return null;
+ }
+
+
+ /**
+ * Checks if a database migration is turned off for the current migration profile.
+ *
+ * @param array $profile
+ *
+ * @return bool
+ */
+ public function is_non_database_migration($profile)
+ {
+ return $profile['current_migration']['databaseEnabled'] === false && in_array($profile['action'], ['push', 'pull']);
+ }
+
+
+ /**
+ * If the current migration is a non database migration, it filters the provided tables and returns an empty array.
+ * hooks on: wpmdb_cli_tables_to_migrate.
+ *
+ * @param string[] $tables
+ * @param array $profile
+ *
+ * @return array
+ */
+ public function filter_non_database_migration_tables($tables, $profile)
+ {
+ if ($this->is_non_database_migration($profile)) {
+ return [];
+ }
+
+ return $tables;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/CliManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/CliManager.php
new file mode 100644
index 000000000..84b448079
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/CliManager.php
@@ -0,0 +1,24 @@
+dynamic_properties = DynamicProperties::getInstance();
+ }
+
+ function set_cli_migration()
+ {
+ DynamicProperties::getInstance()->doing_cli_migration = true;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/Command.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/Command.php
new file mode 100644
index 000000000..1c6c2636c
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Cli/Command.php
@@ -0,0 +1,226 @@
+
+ * : A file path to export to. Filename will be modified to end in .sql or
+ * .sql.gz if necessary.
+ *
+ * [--find=]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * Table names should be quoted as needed, i.e. when using a comma in the
+ * find/replace string.
+ *
+ * The --replace= argument should be used in conjunction to specify
+ * the replace values for the strings found using this argument. The number
+ * of strings specified in this argument should match the number passed into
+ * --replace= argument.
+ *
+ * [--replace=]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * Should be used in conjunction with the --find= argument, see it's
+ * documentation for further explanation of the find & replace functionality.
+ *
+ * [--case-sensitive-find]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * [--case-sensitive-replace]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * [--exclude-post-revisions]
+ * : Exclude post revisions from export.
+ *
+ * [--skip-replace-guids]
+ * : Do not perform a find & replace on the guid column in the wp_posts table.
+ *
+ * [--exclude-spam]
+ * : Exclude spam comments.
+ *
+ * [--gzip-file]
+ * : GZip compress export file.
+ *
+ * [--include-transients]
+ * : Include transients (temporary cached data).
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb export ./migratedb.sql \
+ * --find=http://dev.bradt.ca,/Users/bradt/home/bradt.ca
+ * --replace=http://bradt.ca,/home/bradt.ca
+ *
+ * @param array $args
+ * @param array $assoc_args
+ */
+ public function export($args, $assoc_args)
+ {
+
+ $assoc_args['action'] = 'savefile';
+ $assoc_args['export_dest'] = Util::sanitize_file_path($args[0]);
+
+ if (empty($assoc_args['export_dest'])) {
+ \WP_CLI::error(Cli::cleanup_message(__('You must provide a destination filename.', 'wp-migrate-db-cli')));
+ }
+
+ $profile = $this->_get_profile_data_from_args($args, $assoc_args);
+
+ if (is_wp_error($profile)) {
+ \WP_CLI::error($profile);
+ }
+
+ $this->_perform_cli_migration($profile);
+ }
+
+ /**
+ * Run a find/replace on the database.
+ *
+ * ## OPTIONS
+ *
+ * [--find=]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * Table names should be quoted as needed, i.e. when using a comma in the
+ * find/replace string.
+ *
+ * The --replace= argument should be used in conjunction to specify
+ * the replace values for the strings found using this argument. The number
+ * of strings specified in this argument should match the number passed into
+ * --replace= argument.
+ *
+ * [--replace=]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * Should be used in conjunction with the --find= argument, see it's
+ * documentation for further explanation of the find & replace functionality.
+ *
+ * [--case-sensitive-find]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * [--case-sensitive-replace]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * [--exclude-post-revisions]
+ * : Exclude post revisions from the find & replace.
+ *
+ * [--skip-replace-guids]
+ * : Do not perform a find & replace on the guid column in the wp_posts table.
+ *
+ * [--exclude-spam]
+ * : Exclude spam comments.
+ *
+ * [--include-transients]
+ * : Include transients (temporary cached data).
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb find-replace
+ * --find=http://dev.bradt.ca,/Users/bradt/home/bradt.ca
+ * --replace=http://bradt.ca,/home/bradt.ca
+ *
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @subcommand find-replace
+ */
+ public function find_replace($args, $assoc_args)
+ {
+
+ $assoc_args['action'] = 'find_replace';
+
+ $profile = $this->_get_profile_data_from_args($args, $assoc_args);
+
+ if (is_wp_error($profile)) {
+ \WP_CLI::error($profile);
+ }
+
+ $this->_perform_cli_migration($profile);
+ }
+
+ /**
+ * Get profile data from CLI args.
+ *
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @return array|WP_Error
+ */
+ protected function _get_profile_data_from_args($args, $assoc_args)
+ {
+ // Load the correct CLI class
+ if (function_exists('wpmdb_pro_cli')) {
+ if (function_exists('wp_migrate_db_pro_cli_addon')) {
+ $wpmdb_cli = wp_migrate_db_pro_cli_addon();
+ } else {
+ $wpmdb_cli = wpmdb_pro_cli();
+ }
+ } else {
+ $wpmdb_cli = wpmdb_cli();
+ }
+
+ return $wpmdb_cli->get_profile_data_from_args($args, $assoc_args);
+ }
+
+ /**
+ * Perform CLI migration.
+ *
+ * @param mixed $profile Profile key or array
+ *
+ * @return void
+ */
+ protected function _perform_cli_migration($profile)
+ {
+ $wpmdb_cli = null;
+
+ //load correct cli class
+ if (function_exists('wpmdb_pro_cli')) {
+ $wpmdb_cli = wpmdb_pro_cli();
+ } else {
+ $wpmdb_cli = wpmdb_cli();
+ }
+
+ if (empty($wpmdb_cli)) {
+ \WP_CLI::error(__('WP Migrate CLI class not available.', 'wp-migrate-db-cli'));
+
+ return;
+ }
+
+ $result = $wpmdb_cli->cli_migration($profile);
+
+ if (!is_wp_error($result)) {
+ $success_msg = sprintf(__('Export saved to: %s', 'wp-migrate-db-cli'), $result);
+
+ if ('find_replace' === $profile['current_migration']['intent']) {
+ $success_msg = __('Find & Replace complete', 'wp-migrate-db-cli');
+ }
+
+ \WP_CLI::success($success_msg);
+ } elseif (is_wp_error($result)) {
+ \WP_CLI::error(Cli::cleanup_message($result->get_error_message()));
+ }
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Compatibility/Compatibility.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Compatibility/Compatibility.php
new file mode 100644
index 000000000..8a987a8c3
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Compatibility/Compatibility.php
@@ -0,0 +1,357 @@
+muplugin_class_dir = plugin_dir_path( __FILE__ );
+ $this->muplugin_dir = ( defined( 'WPMU_PLUGIN_DIR' ) && defined( 'WPMU_PLUGIN_URL' ) ) ? WPMU_PLUGIN_DIR : trailingslashit( WP_CONTENT_DIR ) . 'mu-plugins';
+
+
+ }
+
+ /**
+ * Registers action and filter hooks
+ *
+ * @return void
+ **/
+ public function register()
+ {
+ add_action( 'admin_init', array( $this, 'wpmdbc_tgmpa_compatibility' ), 1 );
+ add_filter( 'option_active_plugins', array( $this, 'wpmdbc_include_plugins' ) );
+ add_filter( 'site_option_active_sitewide_plugins', array( $this, 'wpmdbc_include_site_plugins' ) );
+ add_filter( 'stylesheet_directory', array( $this, 'wpmdbc_disable_theme' ) );
+ add_filter( 'template_directory', array( $this, 'wpmdbc_disable_theme' ) );
+ add_action( 'muplugins_loaded', array( $this, 'wpmdbc_set_default_whitelist' ), 5 );
+ add_action( 'muplugins_loaded', array( $this, 'wpmdbc_plugins_loaded' ), 10 );
+ add_action( 'after_setup_theme', array( $this, 'wpmdbc_after_theme_setup' ) );
+ }
+
+ /**
+ * During the `wpmdb_flush` and `wpmdb_remote_flush` actions, start output buffer in case theme spits out errors
+ */
+ public function wpmdbc_plugins_loaded() {
+ if ( $this->wpmdbc_is_wpmdb_flush_call() ) {
+ ob_start();
+ }
+ }
+
+ /**
+ * During the `wpmdb_flush` and `wpmdb_remote_flush` actions, if buffer isn't empty, log content and flush buffer.
+ */
+ public function wpmdbc_after_theme_setup() {
+ if ( $this->wpmdbc_is_wpmdb_flush_call() ) {
+ if ( ob_get_length() ) {
+ error_log( ob_get_clean() );
+ }
+ }
+ }
+
+ /**
+ *
+ * Disables the theme during MDB AJAX requests
+ *
+ * Called from the `stylesheet_directory` hook
+ *
+ * @param $stylesheet_dir
+ *
+ * @return string
+ */
+ public function wpmdbc_disable_theme( $stylesheet_dir ) {
+ $force_enable_theme = apply_filters( 'wpmdb_compatibility_enable_theme', false );
+
+ if ( $this->wpmdbc_is_compatibility_mode_request() && ! $force_enable_theme ) {
+ $theme_dir = realpath( dirname( __FILE__ ) . '/../Compatibility' );
+ $stylesheet = 'temp-theme';
+ $theme_root = "$theme_dir/$stylesheet";
+
+ return $theme_root;
+ }
+
+ return $stylesheet_dir;
+ }
+
+ public function wpmdbc_set_default_whitelist() {
+
+ // Allow users to filter whitelisted plugins
+ $filtered_plugins = apply_filters( 'wpmdb_compatibility_plugin_whitelist', array() );
+
+ // List of default plugins that should be whitelisted. Can be partial names or slugs
+ $wpmdb_plugins = array(
+ 'wpmdb', // Some tweaks plugins start with this string
+ 'wp-migrate-db',
+ );
+
+ $plugins = array_merge( $filtered_plugins, $wpmdb_plugins );
+ $this->default_whitelisted_plugins = $plugins;
+ }
+
+ /**
+ * Remove TGM Plugin Activation 'force_activation' admin_init action hook if present.
+ *
+ * This is to stop excluded plugins being deactivated after a migration, when a theme uses TGMPA to require a
+ * plugin to be always active. Also applies to the WDS-Required-Plugins by removing `activate_if_not` action
+ */
+ public function wpmdbc_tgmpa_compatibility() {
+ $remove_function = false;
+
+ // run on wpmdb page
+ if ( isset( $_GET['page'] ) && 'wp-migrate-db-pro' == $_GET['page'] ) {
+ $remove_function = true;
+ }
+ // run on wpmdb ajax requests
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_POST['action'] ) && false !== strpos( $_POST['action'], 'wpmdb' ) ) {
+ $remove_function = true;
+ }
+
+ if ( $remove_function ) {
+ global $wp_filter;
+ $admin_init_functions = $wp_filter['admin_init'];
+ foreach ( $admin_init_functions as $priority => $functions ) {
+ foreach ( $functions as $key => $function ) {
+ // searching for function this way as can't rely on the calling class being named TGM_Plugin_Activation
+ if ( false !== strpos( $key, 'force_activation' ) || false !== strpos( $key, 'activate_if_not' ) ) {
+
+ if ( is_array( $wp_filter['admin_init'] ) ) {
+ // for core versions prior to WP 4.7
+ unset( $wp_filter['admin_init'][ $priority ][ $key ] );
+ } else {
+ unset( $wp_filter['admin_init']->callbacks[ $priority ][ $key ] );
+ }
+
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * remove blog-active plugins
+ *
+ * @param array $plugins numerically keyed array of plugin names
+ *
+ * @return array
+ */
+ public function wpmdbc_include_plugins( $plugins ) {
+ if ( ! is_array( $plugins ) || empty( $plugins ) ) {
+ return $plugins;
+ }
+
+ if ( ! $this->wpmdbc_is_compatibility_mode_request() ) {
+ return $plugins;
+ }
+
+ $whitelist_plugins = $this->wpmdbc_get_whitelist_plugins();
+ $default_whitelist = $this->default_whitelisted_plugins;
+
+ foreach ( $plugins as $key => $plugin ) {
+ if ( true === $this->wpmdbc_plugin_in_default_whitelist( $plugin, $default_whitelist ) || isset( $whitelist_plugins[ $plugin ] ) ) {
+ continue;
+ }
+
+ unset( $plugins[ $key ] );
+ }
+
+ return $plugins;
+ }
+
+ /**
+ * remove network-active plugins
+ *
+ * @param array $plugins array of plugins keyed by name (name=>timestamp pairs)
+ *
+ * @return array
+ */
+ public function wpmdbc_include_site_plugins( $plugins ) {
+ if ( ! is_array( $plugins ) || empty( $plugins ) ) {
+ return $plugins;
+ }
+
+ if ( ! $this->wpmdbc_is_compatibility_mode_request() ) {
+ return $plugins;
+ }
+
+ $whitelist_plugins = $this->wpmdbc_get_whitelist_plugins();
+
+ if ( ! $this->default_whitelisted_plugins ) {
+ $this->wpmdbc_set_default_whitelist();
+ }
+
+ $default_whitelist = $this->default_whitelisted_plugins;
+
+ foreach ( array_keys( $plugins ) as $plugin ) {
+ if ( true === $this->wpmdbc_plugin_in_default_whitelist( $plugin, $default_whitelist ) || isset( $whitelist_plugins[ $plugin ] ) ) {
+ continue;
+ }
+ unset( $plugins[ $plugin ] );
+ }
+
+ return $plugins;
+ }
+ /**
+ *
+ * Checks if the current request is a WPMDB request
+ *
+ * @return bool
+ */
+ public function is_wpmdb_ajax_call() {
+ if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) && ( isset( $_POST['action'] ) && false !== strpos( $_POST['action'], 'wpmdb' ) ) ) {
+ return true;
+ }
+
+ return false;
+ }
+ /**
+ * @return bool
+ */
+ public function wpmdbc_is_wpmdb_ajax_call() {
+ return $this->is_wpmdb_ajax_call();
+ }
+
+ /**
+ * Checks if the current request is a WPMDB REST API migration request.
+ *
+ * Uses `$_SERVER` global since we're attempting to grab the current
+ * route _before_ `rest_api_init` is fired by WordPress core.
+ *
+ * @return bool
+ */
+ public function wpmdbc_is_wpmdb_rest_request() {
+ $api_base = 'mdb-api/v1/';
+ $request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
+
+ if (false === strpos($request_uri, $api_base)) {
+ return false;
+ }
+
+ $current_endpoint = explode($api_base, $request_uri);
+ $current_endpoint = end($current_endpoint);
+
+ $migration_endpoints = apply_filters(
+ 'wpmdb_compatibility_mode_api_endpoints',
+ [
+ 'initiate-migration',
+ 'verify-connection',
+ 'finalize-migration',
+ 'cancel-migration',
+ 'mf-initiate-file-migration',
+ 'mf-get-queue-items',
+ 'mf-transfer-files',
+ 'tpf-initiate-file-migration',
+ 'tpf-get-queue-items',
+ 'tpf-transfer-files',
+ 'prepare-upload',
+ 'upload-file',
+ 'import-file',
+ ]
+ );
+
+ // Checks that the current API call is a MDB migration request.
+ if (in_array($current_endpoint, $migration_endpoints)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @return bool
+ */
+ public function wpmdbc_is_wpmdb_flush_call() {
+ if ( $this->wpmdbc_is_wpmdb_ajax_call() && in_array( $_POST['action'], array(
+ 'wpmdb_flush',
+ 'wpmdb_remote_flush',
+ ) ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Should the current request be processed by Compatibility Mode?
+ *
+ * @return bool
+ */
+ public function wpmdbc_is_compatibility_mode_request() {
+ if ($this->wpmdbc_is_wpmdb_rest_request()) {
+ return true;
+ }
+
+ // Requests that shouldn't be handled by compatibility mode.
+ if ( ! $this->wpmdbc_is_wpmdb_ajax_call() || in_array( $_POST['action'], array(
+ 'wpmdb_get_log',
+ 'wpmdb_maybe_collect_data',
+ 'wpmdb_flush',
+ 'wpmdb_remote_flush',
+ 'wpmdb_get_themes',
+ 'wpmdb_get_plugins',
+ 'wpmdb_verify_connection_to_remote_site'
+ ) ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns an array of plugin slugs to be blacklisted.
+ *
+ * @return array
+ */
+ public function wpmdbc_get_whitelist_plugins() {
+ $whitelist_plugins = array();
+
+ $wpmdb_settings = get_site_option( 'wpmdb_settings' );
+
+ if ( ! empty( $wpmdb_settings['whitelist_plugins'] ) ) {
+ $whitelist_plugins = array_flip( $wpmdb_settings['whitelist_plugins'] );
+ }
+
+ return $whitelist_plugins;
+ }
+
+ /**
+ *
+ * Checks if $plugin is in the $whitelisted_plugins property array
+ *
+ * @param $plugin
+ * @param $whitelisted_plugins
+ *
+ * @return bool
+ */
+ public function wpmdbc_plugin_in_default_whitelist( $plugin, $whitelisted_plugins ) {
+
+ if ( ! is_array( $whitelisted_plugins ) ) {
+ return false;
+ }
+
+ if ( in_array( $plugin, $whitelisted_plugins ) ) {
+ return true;
+ }
+
+ // strpos() check to see if the item slug is in the current $plugin name
+ foreach ( $whitelisted_plugins as $item ) {
+ if ( false !== strpos( $plugin, $item ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Compatibility/CompatibilityManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Compatibility/CompatibilityManager.php
new file mode 100644
index 000000000..3774e9a22
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Compatibility/CompatibilityManager.php
@@ -0,0 +1,261 @@
+filesystem = $filesystem;
+ $this->settings = $settings->get_settings();
+ $this->props = $properties;
+ self::$static_props = $this->props;
+ $this->template = $template;
+ $this->notices = $notice;
+ $this->http = $http;
+ $this->migration_state = $migration_state;
+ $this->util = $util;
+
+ //Version of the compatibility plugin, to force an update of the MU plugin, increment this value
+ $this->compatibility_plugin_version = '1.3';
+
+ $this->mu_plugin_dir = $this->props->mu_plugin_dir;
+ $this->mu_plugin_source = $this->props->mu_plugin_source;
+ $this->mu_plugin_dest = $this->props->mu_plugin_dest;
+ $this->http_helper = $http_helper;
+ }
+
+ public function register() {
+ // Checks the compatibility mode MU plugin version and updates if it's out of date.
+ add_action( 'admin_init', array( $this, 'muplugin_version_check' ), 1 );
+
+ // Fired in the register_deactivation_hook() call in both the pro and non-pro plugins.
+ add_action( 'wp_migrate_db_remove_compatibility_plugin', array( $this, 'remove_muplugin_on_deactivation' ) );
+ }
+
+ public function addNotices(){
+ add_filter('wpmdb_notification_strings', array($this, 'template_muplugin_update_fail'));
+ }
+
+ /**
+ * Triggered with the `admin_init` hook on the WP Migrate DB Pro dashboard page
+ *
+ * The 'compatibility_plugin_version' option key signifies that the latest compatibility plugin has been installed. If it's not present, copy the plugin, enabling it by default.
+ *
+ * Otherwise check the 'compatibility_plugin_version' option to see if the MU plugin needs updating.
+ *
+ * @return bool|string
+ */
+ public function muplugin_version_check() {
+ if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'wp-migrate-db-pro', 'wp-migrate-db' ) ) ) {
+ if ( true === $this->is_muplugin_update_required() ) {
+ return $this->copy_muplugin();
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if the compatibility mu-plugin requires an update based on the 'compatibility_plugin_version' setting in
+ * the database
+ *
+ * @param bool $wpmdb_settings
+ *
+ * @return bool
+ */
+ public function is_muplugin_update_required( $wpmdb_settings = false ) {
+ $update_required = false;
+
+ if ( false === $wpmdb_settings ) {
+ $wpmdb_settings = $this->settings;
+ }
+
+ if ( ! isset( $wpmdb_settings['compatibility_plugin_version'] ) ) {
+ $update_required = true;
+ } else if ( version_compare( $this->compatibility_plugin_version, $wpmdb_settings['compatibility_plugin_version'], '>' ) && $this->util->is_muplugin_installed() ) {
+ $update_required = true;
+ }
+
+ return $update_required;
+ }
+
+ /**
+ * Preemptively shows a warning warning on WPMDB pages if the mu-plugins folder isn't writable
+ */
+ function template_muplugin_update_fail($notifications) {
+ if ( $this->is_muplugin_update_required() && false === $this->util->is_muplugin_writable() ) {
+ $notice_id = 'muplugin_failed_update_' . $this->compatibility_plugin_version;
+ $notice_links = $this->notices->check_notice( $notice_id, 'SHOW_ONCE' );
+
+ if ( is_array( $notice_links ) ) {
+ $notifications[$notice_id] = [
+ 'message' => $this->template->template_to_string('muplugin-failed-update-warning', 'common', $notice_links),
+ 'link' => $notice_links,
+ 'id' => $notice_id,
+ ];
+ }
+ }
+ return $notifications;
+ }
+
+ /**
+ *
+ * Copies the compatibility plugin as well as updates the version number in the database
+ *
+ * @return bool|string
+ */
+ public function copy_muplugin() {
+ $wpmdb_settings = $this->settings;
+
+ if ( ! $this->filesystem->mkdir( $this->mu_plugin_dir ) || ! $this->filesystem->copy( $this->mu_plugin_source, $this->mu_plugin_dest ) ) {
+ return sprintf( __( 'The compatibility plugin could not be activated because your mu-plugin directory is currently not writable. Please update the permissions of the mu-plugins folder: %s', 'wp-migrate-db' ), $this->mu_plugin_dir );
+ }
+
+ //Rename muplugin in header
+ if ( ! $this->props->is_pro ) {
+ $mu_contents = file_get_contents( $this->mu_plugin_dest );
+ $mu_contents = str_replace( 'Plugin Name: WP Migrate Compatibility', 'Plugin Name: WP Migrate DB Compatibility', $mu_contents );
+ file_put_contents( $this->mu_plugin_dest, $mu_contents );
+ }
+
+ if ( $this->is_muplugin_update_required() ) {
+ // Update version number in the database
+ $wpmdb_settings['compatibility_plugin_version'] = $this->compatibility_plugin_version;
+
+ // Remove blacklist_plugins key as it's no longer used.
+ if ( isset( $wpmdb_settings['blacklist_plugins'] ) ) {
+ unset( $wpmdb_settings['blacklist_plugins'] );
+ }
+
+ update_site_option( 'wpmdb_settings', $wpmdb_settings );
+ }
+
+ return true;
+ }
+
+ /**
+ *
+ * Removes the compatibility plugin
+ *
+ * @return bool|string
+ */
+ public function remove_muplugin() {
+ if ( $this->filesystem->file_exists( $this->mu_plugin_dest ) && ! $this->filesystem->unlink( $this->mu_plugin_dest ) ) {
+ return sprintf( __( 'The compatibility plugin could not be deactivated because your mu-plugin directory is currently not writable. Please update the permissions of the mu-plugins folder: %s', 'wp-migrate-db' ), $this->mu_plugin_dir );
+ }
+
+ return true;
+ }
+
+ /**
+ *
+ * Fired on the `wp_migrate_db_remove_compatibility_plugin` action. Removes the compatibility plugin on deactivation
+ *
+ * @return bool|string
+ */
+ public function remove_muplugin_on_deactivation() {
+ $plugin_removed = $this->remove_muplugin();
+
+ if ( true === $plugin_removed ) {
+ $wpmdb_settings = $this->settings;
+ unset( $wpmdb_settings['compatibility_plugin_version'] );
+
+ update_site_option( 'wpmdb_settings', $wpmdb_settings );
+
+ return true;
+ }
+
+ return $plugin_removed;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Db/MDBWPDB.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Db/MDBWPDB.php
new file mode 100644
index 000000000..1ecb13fd0
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Db/MDBWPDB.php
@@ -0,0 +1,62 @@
+dbuser, $wpdb->dbpassword, $wpdb->dbname, $wpdb->dbhost );
+
+ // TODO: Determine if it's better to extend $wpdb or just rep some of its methods
+ }
+
+ /**
+ * Find the first table name referenced in a query.
+ *
+ * @param string $query The query to search.
+ *
+ * @return string|false $table The table name found, or false if a table couldn't be found.
+ */
+ public function get_table_from_query( $query ) {
+ return parent::get_table_from_query( $query );
+ }
+
+ /**
+ * Strips any invalid characters from the query and caches the stripped query for later use
+ *
+ * @param string $query Query to convert.
+ *
+ * @return string|WP_Error The converted query, or a WP_Error object if the conversion fails.
+ */
+ public function strip_invalid_text_from_query( $query ) {
+ $query = apply_filters( 'wpmdb_before_strip_invalid_text_from_query', $query );
+ $fallback = false;
+ if ( method_exists( $this, 'strip_invalid_text_from_query' ) ) {
+ $query = parent::strip_invalid_text_from_query( $query );
+ $this->flush();
+ } else {
+ $fallback = true;
+ }
+ $this->last_stripped_query = apply_filters( 'wpmdb_after_strip_invalid_text_from_query', $query, $fallback );
+
+ return $this->last_stripped_query;
+ }
+
+ /**
+ * Determine if a query has invalid text.
+ *
+ * @param $query query to check
+ *
+ * @return bool
+ */
+ public function query_has_invalid_text( $query ) {
+ return ( $query !== $this->strip_invalid_text_from_query( $query ) );
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffEntity.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffEntity.php
new file mode 100644
index 000000000..2298424b7
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffEntity.php
@@ -0,0 +1,126 @@
+original_expression = $original_expression;
+ $this->replace_expression = $replace_expression;
+ $this->column = $column;
+ $this->row = $row;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getOriginalExpression()
+ {
+ return $this->original_expression;
+ }
+
+
+ /**
+ * @param mixed $original_expression
+ */
+ public function setOriginalExpression($original_expression)
+ {
+ $this->original_expression = $original_expression;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getReplaceExpression()
+ {
+ return $this->replace_expression;
+ }
+
+
+ /**
+ * @param mixed $replace_expression
+ */
+ public function setReplaceExpression($replace_expression)
+ {
+ $this->replace_expression = $replace_expression;
+ }
+
+
+ /**
+ * @return int
+ */
+ public function getRow()
+ {
+ return $this->row;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getColumn()
+ {
+ return $this->column;
+ }
+
+
+ /**
+ * Json serializes the class data
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return [
+ 'original' => $this->original_expression,
+ 'replace' => $this->replace_expression,
+ 'row' => $this->row,
+ 'column' => $this->column
+ ];
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffGroup.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffGroup.php
new file mode 100644
index 000000000..c017f03a2
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffGroup.php
@@ -0,0 +1,75 @@
+persistence = $persistence;
+ }
+
+
+ /**
+ * @param $table
+ */
+ public function setTable($table)
+ {
+ $this->table = $table;
+ }
+
+
+ /**
+ * @param DiffEntity $entity
+ */
+ public function addEntity(DiffEntity $entity)
+ {
+ $this->entities[] = $entity;
+ }
+
+
+ /**
+ * @return DiffEntity[]
+ */
+ public function getEntities()
+ {
+ return $this->entities;
+ }
+
+
+ /**
+ * @return mixed
+ */
+ public function getTable()
+ {
+ return $this->table;
+ }
+
+
+ /**
+ * post-find/replace procedures.
+ */
+ public function finalize() {
+ $this->persistence->add($this);
+ $this->persistence->store();
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffInterpreter.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffInterpreter.php
new file mode 100644
index 000000000..04ed3b66a
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/DiffInterpreter.php
@@ -0,0 +1,61 @@
+group = $group;
+ }
+
+
+ /**
+ * Computes string difference and adds the entity to the group if diff exists.
+ *
+ * @param DiffEntity $entity
+ */
+ public function compute(DiffEntity $entity)
+ {
+ if (0 !== strcmp($entity->getOriginalExpression(), $entity->getReplaceExpression())) {
+ $this->group->addEntity($entity);
+ }
+ }
+
+
+ /**
+ * post-find/replace procedures.
+ */
+ public function finalize() {
+ $this->group->finalize();
+ }
+
+
+ /**
+ * Returns array of diff entities with unmatching strings.
+ *
+ * @return DiffEntity[]
+ */
+ public function results() {
+ return $this->group->getEntities();
+ }
+
+ /**
+ * @return DiffGroup
+ */
+ public function getGroup()
+ {
+ return $this->group;
+ }
+}
+
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/MemoryPersistence.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/MemoryPersistence.php
new file mode 100644
index 000000000..be5f6a390
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/MemoryPersistence.php
@@ -0,0 +1,41 @@
+store[] = $group;
+ }
+
+
+ /**
+ * @param array $options
+ *
+ * @return DiffGroup[]
+ */
+ public function retrieve($options = [])
+ {
+ return $this->store;
+ }
+
+ public function store()
+ {
+ // TODO: Implement store() method.
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/PersistenceInterface.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/PersistenceInterface.php
new file mode 100644
index 000000000..0c9d9be4c
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/DryRun/PersistenceInterface.php
@@ -0,0 +1,25 @@
+props = $properties;
+ $this->settings = $settings->get_settings();
+ $this->filesystem = $filesystem;
+ $this->util = $util;
+ }
+
+ public function get_error_log()
+ {
+ return $this->error_log;
+ }
+
+ /**
+ * Loads the error log into the error log class property.
+ */
+ function load_error_log()
+ {
+ $this->error_log = get_site_option('wpmdb_error_log');
+
+ /*
+ * The error log was previously stored and retrieved using get_option and update_option respectively.
+ * Here we update the subsite option to a network wide option if applicable.
+ */
+ if (false === $this->error_log && is_multisite() && is_network_admin()) {
+ $this->error_log = get_option('wpmdb_error_log');
+ if (false !== $this->error_log) {
+ update_site_option('wpmdb_error_log', $this->error_log);
+ delete_option('wpmdb_error_log');
+ }
+ }
+
+ return $this->error_log;
+ }
+
+ function log_error($wpmdb_error, $additional_error_var = false)
+ {
+ $state_data = WPMDBDI::getInstance()->get(MigrationStateManager::class)->set_post_data();
+
+ $error_header = "********************************************\n****** Log date: " . date('Y/m/d H:i:s') . " ******\n********************************************\n\n";
+ $error = $error_header;
+ if ($this->get_migration_id($state_data)) {
+ $error .= 'Migration ID: ' . $this->get_migration_id($state_data) . "\n";
+ }
+ if (isset($state_data['intent'])) {
+ $error .= 'Intent: ' . $state_data['intent'] . "\n";
+ }
+ if (isset($state_data['stages'])) {
+ $error .= 'Stages: ' . implode(' | ', json_decode($state_data['stages'], true) ). "\n";
+ }
+ if (isset($state_data['action'])) {
+ $error .= 'Action: ' . $state_data['action'] . "\n";
+ }
+ if (isset($state_data['local']) && isset($state_data['local']['site_url'])) {
+ $error .= 'Local: ' . $state_data['site_details']['local']['site_url'] . "\n";
+ }
+ if (isset($state_data['remote']) && isset($state_data['remote']['site_url'])) {
+ $error .= 'Remote: ' . $state_data['site_details']['remote']['site_url'] . "\n";
+ }
+ if (isset($GLOBALS['wpmdb_meta']) && isset($GLOBALS['wpmdb_meta']['wp-migrate-db-pro'])) {
+ $error .= 'WP Migrate Version: ' .$GLOBALS['wpmdb_meta']['wp-migrate-db-pro']['version'] . "\n\n";
+ }
+ $error .= 'Error: ' . $wpmdb_error . "\n\n";
+
+
+
+ if (!empty($this->props->attempting_to_connect_to)) {
+ $ip = $this->get_remote_ip($this->props->attempting_to_connect_to);
+ $error .= 'Attempted to connect to: ' . $this->props->attempting_to_connect_to . ' (' . ($ip ? $ip : 'ip lookup failed') . ')' . "\n\n";
+ }
+
+ if ($additional_error_var !== false) {
+ // don't print the whole response object to the log
+ if (is_array($additional_error_var) && isset($additional_error_var['http_response'])) {
+ if (isset($additional_error_var['http_response']) && ($additional_error_var['http_response'] instanceof \WP_HTTP_Requests_Response)) {
+ $response = $additional_error_var['http_response']->get_response_object();
+ $additional_error_var['url'] = $response->url;
+ }
+ unset($additional_error_var['http_response']);
+ }
+
+ $error .= print_r($additional_error_var, true) . "\n\n";
+ }
+
+ $this->load_error_log();
+
+ // Error log length in bytes (default 1Mb)
+ $max_log_length = apply_filters('wpmdb_max_error_log_length', 1000000);
+ $max_individual_log_length = apply_filters('wpmdb_max_individual_error_log_length', $max_log_length / 2.2);
+
+ // If error is longer than max individual log length, trim and add notice of doing so
+ if (strlen($error) > $max_individual_log_length) {
+ $length_trimmed = strlen($error) - $max_individual_log_length;
+ $error = substr($error, 0, $max_individual_log_length);
+ $error .= "\n[$length_trimmed bytes were truncated from this error]\n\n";
+ }
+
+ // Trim existing log to accommodate new error if needed
+ $existing_log_max_length = $max_log_length - strlen($error);
+ if (strlen($this->error_log) > $existing_log_max_length) {
+ $this->error_log = substr($this->error_log, -($existing_log_max_length));
+
+ // Crop at first log header
+ $first_header_pos = strpos($this->error_log, substr($error_header, 0, strpos($error_header, ' ')));
+ if ($first_header_pos) {
+ $this->error_log = substr($this->error_log, $first_header_pos);
+ }
+ }
+
+ if (isset($this->error_log)) {
+ $this->error_log .= $error;
+ } else {
+ $this->error_log = $error;
+ }
+
+ update_site_option('wpmdb_error_log', $this->error_log);
+ }
+
+ /**
+ * Get Migration ID
+ *
+ * @param array $state_data Description
+ * @return mixed string|bool
+ **/
+ public function get_migration_id($state_data)
+ {
+ if (!isset($state_data['form_data'])) {
+ return false;
+ }
+ $form_data = json_decode($state_data['form_data']);
+ if ($form_data
+ && property_exists($form_data, 'current_migration')
+ && property_exists($form_data->current_migration, 'migration_id')
+ ) {
+ return $form_data->current_migration->migration_id;
+ }
+ return false;
+ }
+
+ function get_remote_ip($url)
+ {
+ $parsed_url = Util::parse_url($url);
+ if (!isset($parsed_url['host'])) {
+ return false;
+ }
+ // '.' appended to host name to avoid issues with nslookup caching - see documentation of gethostbyname for more info
+ $host = $parsed_url['host'] . '.';
+
+ $ip = gethostbyname($host);
+
+ return ($ip === $host) ? false : $ip;
+ }
+
+ /**
+ * Check for wpmdb-download-log and related nonce
+ * if found begin diagnostic logging
+ *
+ * @return void
+ */
+ function http_prepare_download_log()
+ {
+ if (isset($_GET['wpmdb-download-log']) && wp_verify_nonce($_GET['nonce'], 'wpmdb-download-log')) {
+ ob_start();
+ $this->output_diagnostic_info();
+ $this->output_log_file();
+ $log = ob_get_clean();
+ $url = Util::parse_url(home_url());
+ $host = sanitize_file_name($url['host']);
+ $filename = sprintf('%s-diagnostic-log-%s.txt', $host, date('YmdHis'));
+ header('Content-Description: File Transfer');
+ header('Content-Type: application/octet-stream');
+ header('Content-Length: ' . strlen($log));
+ header('Content-Disposition: attachment; filename=' . $filename);
+ echo $log;
+ exit;
+ }
+ }
+
+ /**
+ * Outputs useful diagnostic info text at the Diagnostic Info & Error Log
+ * section under the Help tab so the information can be viewed or
+ * downloaded and shared for debugging.
+ *
+ *
+ * @return void
+ */
+ function output_diagnostic_info()
+ {
+ $diagnostic_info = $this->get_diagnostic_info();
+
+ foreach ($diagnostic_info as $section => $arr) {
+ $key_lengths = array_map('strlen', array_keys($arr));
+ $max_key_length = max($key_lengths);
+ foreach ($arr as $key => $val) {
+ if (0 === $key) {
+ echo $val . "\r\n";
+ continue;
+ }
+ if (is_array($val)) {
+ foreach ($val as $subsection => $subval) {
+ echo " - ";
+ if (!preg_match('/^\d+$/', $subsection)) {
+ echo "$subsection: ";
+ }
+ echo "$subval\r\n";
+ }
+ continue;
+ }
+ if (!preg_match('/^\d+$/', $key)) {
+ $pad_chr = '.';
+ if ($max_key_length - strlen($key) < 3) {
+ $pad_chr = ' ';
+ }
+ echo str_pad("$key: ", $max_key_length + 2, $pad_chr, STR_PAD_RIGHT);
+ }
+ echo " $val\r\n";
+ }
+ echo "\r\n";
+ }
+
+ return;
+ }
+
+ /**
+ * Gets diagnostic information about current site
+ *
+ * @return array
+ */
+ function get_diagnostic_info()
+ {
+ global $wpdb;
+ $diagnostic_info = array(); // group display sections into arrays
+
+ $diagnostic_info['basic-info'] = array(
+ 'site_url()' => site_url(),
+ 'home_url()' => Util::home_url(),
+ 'site_path()' => esc_html(addslashes(Util::get_absolute_root_file_path())),
+ );
+
+ $diagnostic_info['db-info'] = array(
+ 'Database Name' => $wpdb->dbname,
+ 'Table Prefix' => $wpdb->base_prefix,
+ );
+
+ $diagnostic_info['wp-version'] = array(
+ 'WordPress Version' => get_bloginfo('version'),
+ );
+
+ if (is_multisite()) {
+ $diagnostic_info['multisite-info'] = array(
+ 'Multisite' => defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL ? 'Sub-domain' : 'Sub-directory',
+ 'Domain Current Site' => defined('DOMAIN_CURRENT_SITE') ? DOMAIN_CURRENT_SITE : 'Not Defined',
+ 'Path Current Site' => defined('PATH_CURRENT_SITE') ? PATH_CURRENT_SITE : 'Not Defined',
+ 'Site ID Current Site' => defined('SITE_ID_CURRENT_SITE') ? SITE_ID_CURRENT_SITE : 'Not Defined',
+ 'Blog ID Current Site' => defined('BLOG_ID_CURRENT_SITE') ? BLOG_ID_CURRENT_SITE : 'Not Defined',
+ );
+ }
+
+ $mdb_plugins = array();
+ foreach (array_reverse($GLOBALS['wpmdb_meta']) as $wpmdb_plugin => $wpmdb_plugin_info) {
+ if (strlen($wpmdb_plugin) > strlen('wp-migrate-db-pro')) {
+ $wpmdb_plugin = str_replace('wp-migrate-db-pro-', '', $wpmdb_plugin);
+ }
+ $wpmdb_plugin = ucwords(str_replace(array('wp', 'db', 'cli', '-'), array(
+ 'WP',
+ 'DB',
+ 'CLI',
+ ' ',
+ ), $wpmdb_plugin));
+ $mdb_plugins[$wpmdb_plugin] = $wpmdb_plugin_info['version'];
+ }
+ $diagnostic_info['mdb-plugins'] = $mdb_plugins;
+
+ $diagnostic_info['server-info'] = array(
+ 'Web Server' => ! empty( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '',
+ 'PHP' => ( function_exists( 'phpversion' ) ) ? phpversion() : '',
+ 'WP Memory Limit' => WP_MEMORY_LIMIT,
+ 'PHP Time Limit' => ( function_exists( 'ini_get' ) ) ? ini_get( 'max_execution_time' ) : '',
+ 'Blocked External HTTP Requests' => ( ! defined( 'WP_HTTP_BLOCK_EXTERNAL' ) || ! WP_HTTP_BLOCK_EXTERNAL ) ? 'None' : ( WP_ACCESSIBLE_HOSTS ? 'Partially (Accessible Hosts: ' . WP_ACCESSIBLE_HOSTS . ')' : 'All' ),
+ 'fsockopen' => ( function_exists( 'fsockopen' ) ) ? 'Enabled' : 'Disabled',
+ 'OpenSSL' => ( $this->util->open_ssl_enabled() ) ? OPENSSL_VERSION_TEXT : 'Disabled',
+ 'cURL' => ( function_exists( 'curl_init' ) ) ? 'Enabled' : 'Disabled',
+ 'Enable SSL verification setting' => ( 1 == $this->settings['verify_ssl'] ) ? 'Yes' : 'No',
+ // phpcs:disable
+ 'Opcache Enabled' => ( function_exists( 'ini_get' ) && ini_get( 'opcache.enable' ) ) ? 'Enabled' : 'Disabled',
+ // phpcs:enable
+ );
+
+ $diagnostic_info['db-server-info'] = array(
+ 'MySQL' => mysqli_get_server_info($wpdb->dbh),
+ 'ext/mysqli' => empty($wpdb->use_mysqli) ? 'no' : 'yes',
+ 'WP Locale' => get_locale(),
+ 'DB Charset' => DB_CHARSET,
+ 'WPMDB_STRIP_INVALID_TEXT' => (defined('WPMDB_STRIP_INVALID_TEXT') && WPMDB_STRIP_INVALID_TEXT) ? 'Yes' : 'No',
+ );
+
+ $diagnostic_info['debug-settings'] = array(
+ 'Debug Mode' => (defined('WP_DEBUG') && WP_DEBUG) ? 'Yes' : 'No',
+ 'Debug Log' => (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) ? 'Yes' : 'No',
+ 'Debug Display' => (defined('WP_DEBUG_DISPLAY') && WP_DEBUG_DISPLAY) ? 'Yes' : 'No',
+ 'Script Debug' => (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? 'Yes' : 'No',
+ 'PHP Error Log' => (function_exists('ini_get')) ? ini_get('error_log') : '',
+ );
+
+ $server_limits = array(
+ 'WP Max Upload Size' => size_format(wp_max_upload_size()),
+ 'PHP Post Max Size' => size_format($this->util->get_post_max_size()),
+ );
+
+ if (function_exists('ini_get')) {
+ if ($suhosin_limit = ini_get('suhosin.post.max_value_length')) {
+ $server_limits['Suhosin Post Max Value Length'] = is_numeric($suhosin_limit) ? size_format($suhosin_limit) : $suhosin_limit;
+ }
+ if ($suhosin_limit = ini_get('suhosin.request.max_value_length')) {
+ $server_limits['Suhosin Request Max Value Length'] = is_numeric($suhosin_limit) ? size_format($suhosin_limit) : $suhosin_limit;
+ }
+ }
+ $diagnostic_info['server-limits'] = $server_limits;
+
+ $diagnostic_info['mdb-settings'] = array(
+ 'WPMDB Bottleneck' => size_format($this->util->get_bottleneck()),
+ 'Compatibility Mode' => (isset($GLOBALS['wpmdb_compatibility']['active'])) ? 'Yes' : 'No',
+ 'Delay Between Requests' => ($this->settings['delay_between_requests'] > 0) ? $this->settings['delay_between_requests'] . 's' : 0,
+ );
+
+ $constants = array(
+ 'WP_HOME' => (defined('WP_HOME') && WP_HOME) ? WP_HOME : 'Not defined',
+ 'WP_SITEURL' => (defined('WP_SITEURL') && WP_SITEURL) ? WP_SITEURL : 'Not defined',
+ 'WP_CONTENT_URL' => (defined('WP_CONTENT_URL') && WP_CONTENT_URL) ? WP_CONTENT_URL : 'Not defined',
+ 'WP_CONTENT_DIR' => (defined('WP_CONTENT_DIR') && WP_CONTENT_DIR) ? WP_CONTENT_DIR : 'Not defined',
+ 'WP_PLUGIN_DIR' => (defined('WP_PLUGIN_DIR') && WP_PLUGIN_DIR) ? WP_PLUGIN_DIR : 'Not defined',
+ 'WP_PLUGIN_URL' => (defined('WP_PLUGIN_URL') && WP_PLUGIN_URL) ? WP_PLUGIN_URL : 'Not defined',
+ );
+
+ if (is_multisite()) {
+ $constants['UPLOADS'] = (defined('UPLOADS') && UPLOADS) ? UPLOADS : 'Not defined';
+ $constants['UPLOADBLOGSDIR'] = (defined('UPLOADBLOGSDIR') && UPLOADBLOGSDIR) ? UPLOADBLOGSDIR : 'Not defined';
+ }
+
+ $diagnostic_info['constants'] = $constants;
+
+ $diagnostic_info = array_merge($diagnostic_info, apply_filters('wpmdb_diagnostic_info', array(), $diagnostic_info));
+
+ $theme_info = wp_get_theme();
+ $theme_info_log = array(
+ 'Active Theme Name' => $theme_info->Name,
+ 'Active Theme Folder' => $theme_info->get_stylesheet_directory(),
+ );
+ if ($theme_info->get('Template')) {
+ $theme_info_log['Parent Theme Folder'] = $theme_info->get('Template');
+ }
+ if (!$this->filesystem->file_exists($theme_info->get_stylesheet_directory())) {
+ $theme_info_log['WARNING'] = 'Active Theme Folder Not Found';
+ }
+ $diagnostic_info['theme-info'] = $theme_info_log;
+
+ $active_plugins_log = array('Active Plugins');
+
+ $active_plugins_log[1] = array();
+ if (isset($GLOBALS['wpmdb_compatibility']['active'])) {
+ $whitelist = array_flip((array)$this->settings['whitelist_plugins']);
+ } else {
+ $whitelist = array();
+ }
+ $active_plugins = (array)get_option('active_plugins', array());
+ if (is_multisite()) {
+ $network_active_plugins = wp_get_active_network_plugins();
+ $active_plugins = array_map(array($this->util, 'remove_wp_plugin_dir'), $network_active_plugins);
+ }
+ foreach ($active_plugins as $plugin) {
+ if (!file_exists(WP_PLUGIN_DIR . '/' . $plugin)) {
+ continue;
+ }
+ $active_plugins_log[1][] = $this->util->get_plugin_details(WP_PLUGIN_DIR . '/' . $plugin, isset($whitelist[$plugin]) ? '*' : '');
+ }
+
+ $diagnostic_info['active-plugins'] = $active_plugins_log;
+
+ $mu_plugins = wp_get_mu_plugins();
+ if ($mu_plugins) {
+ $mu_plugins_log = array('Must-Use Plugins');
+ $mu_plugins_log[1] = array();
+ foreach ($mu_plugins as $mu_plugin) {
+ $mu_plugins_log[1][] = $this->util->get_plugin_details($mu_plugin);
+ }
+ $diagnostic_info['mu-plugins'] = $mu_plugins_log;
+ }
+
+ return $diagnostic_info;
+ }
+
+ function output_log_file()
+ {
+ $error_log = $this->load_error_log();
+ if (!empty($error_log)) {
+ echo $error_log;
+ }
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * @param mixed $error
+ */
+ public function setError($error)
+ {
+ $this->error = $error;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Error/HandleRemotePostError.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Error/HandleRemotePostError.php
new file mode 100644
index 000000000..a527df199
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Error/HandleRemotePostError.php
@@ -0,0 +1,50 @@
+get(Http::class);
+
+ // WP_Error is thrown manually by remote_post() to tell us something went wrong
+ if (is_wp_error($response)) {
+ {
+ return $http->end_ajax(
+ $response
+ );
+ }
+ }
+
+ $decoded_response = json_decode($response, true);
+
+ if (false === $response || !$decoded_response['success']) {
+ $http->end_ajax(
+ new \WP_Error(
+ $key,
+ $decoded_response['data']
+ )
+ );
+ }
+
+ if (isset($decoded_response['data'])) {
+ return $decoded_response['data'];
+ }
+
+// if($decoded_response['success'] === false){
+// return $http->end_ajax(
+//
+// )
+// }
+
+ return $response;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Error/Logger.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Error/Logger.php
new file mode 100644
index 000000000..591d874a7
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Error/Logger.php
@@ -0,0 +1,143 @@
+ $args['type'],
+ 'location' => $args['location'],
+ 'target' => isset($args['target']) ? $args['target'] : $target
+ ];
+
+ if (isset($state_data['site_url'], $state_data['url'])) {
+ $stats['sites'] = [
+ 'local' => $state_data['site_url'],
+ 'remote' => $state_data['url']
+ ];
+ }
+
+ if (isset($state_data['migration_state_id'])) {
+ $stats['migration_id'] = $state_data['migration_state_id'];
+ }
+
+ if (isset($state_data['remote_state_id'])) {
+ $stats['migration_id'] = $state_data['remote_state_id'];
+ }
+
+ error_log($log_message . json_encode($stats));
+ }
+
+ /**
+ * Hooked to 'wpmdb_initiate_migration'
+ *
+ * @param array $state_data
+ **/
+ public function initiate($state_data)
+ {
+ $args = [
+ 'type' => 'initiate',
+ 'location' => 'local'
+ ];
+ $this->logMessage($args, $state_data);
+ }
+
+ /**
+ * Hooked to 'wpmdb_respond_remote_initiate'
+ *
+ * @param array $state_data
+ **/
+ public function remoteInitiate($state_data)
+ {
+ $args = [
+ 'type' => 'initiate',
+ 'location' => 'remote'
+ ];
+ $this->logMessage($args, $state_data);
+ }
+
+ /**
+ * Hooked to 'wpmdb_remote_finalize'
+ *
+ * @param array $state_data
+ **/
+ public function remoteFinalize($state_data)
+ {
+ $args = [
+ 'type' => 'complete',
+ 'location' => 'remote',
+ 'target' => true
+ ];
+ $this->logMessage($args, $state_data);
+ }
+
+ /**
+ * Log on migration complete
+ * Hooked to 'wpmdb_after_finalize_migration'
+ **/
+ public function complete()
+ {
+ $args = [
+ 'type' => 'complete',
+ 'location' => 'local'
+ ];
+ $this->logMessage($args);
+ }
+
+ /**
+ * Cancellation log
+ * Hooked to 'wpmdb_cancellation'
+ **/
+ public function cancellation()
+ {
+ $args = [
+ 'type' => 'cancel',
+ 'location' => 'local'
+ ];
+ $this->logMessage($args);
+ }
+
+ /**
+ * Remote cancellation log
+ * Hooked to 'wpmdb_respond_to_push_cancellation'
+ **/
+ public function remoteCancellation()
+ {
+ $args = [
+ 'type' => 'cancel',
+ 'location' => 'remote'
+ ];
+ $this->logMessage($args);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Exceptions/EmptyPropertyException.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Exceptions/EmptyPropertyException.php
new file mode 100644
index 000000000..6aea76b6c
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Exceptions/EmptyPropertyException.php
@@ -0,0 +1,4 @@
+chmod_dir = FS_CHMOD_DIR;
+ } else {
+ $this->chmod_dir = (fileperms(ABSPATH) & 0777 | 0755);
+ }
+
+ if (defined('FS_CHMOD_FILE')) {
+ $this->chmod_file = FS_CHMOD_FILE;
+ } else {
+ $this->chmod_file = (fileperms(ABSPATH . 'index.php') & 0777 | 0644);
+ }
+
+ $this->container = WPMDBDI::getInstance();
+ }
+
+ public function register()
+ {
+ add_action('tools_page_wp-migrate-db-pro', [$this, 'check_for_wp_filesystem']); // Single sites
+ add_action('tools_page_wp-migrate-db', [$this, 'check_for_wp_filesystem']);
+ add_action('settings_page_wp-migrate-db-pro', [$this, 'check_for_wp_filesystem']); // Multisites
+ add_action('settings_page_wp-migrate-db', [$this, 'check_for_wp_filesystem']);
+
+ if (Util::is_wpmdb_ajax_call()) {
+ add_action('admin_init', [$this, 'check_for_wp_filesystem']);
+ }
+ }
+
+ public function check_for_wp_filesystem()
+ {
+ if (function_exists('request_filesystem_credentials')) {
+ if ((defined('WPMDB_WP_FILESYSTEM') && WPMDB_WP_FILESYSTEM) || !defined('WPMDB_WP_FILESYSTEM')) {
+ $this->maybe_init_wp_filesystem();
+ }
+ }
+ }
+
+ /**
+ * Getter for the instantiated WP_Filesystem
+ *
+ * @return WP_Filesystem|false
+ *
+ * This should be used carefully since $wp_filesystem won't always have a value.
+ */
+ public function get_wp_filesystem()
+ {
+ if ($this->use_filesystem) {
+ return $this->wp_filesystem;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Is WP_Filesystem being used?
+ *
+ * @return bool
+ */
+ public function using_wp_filesystem()
+ {
+ return $this->use_filesystem;
+ }
+
+ /**
+ * Attempts to use the correct path for the FS method being used
+ *
+ * @param string $abs_path
+ *
+ * @return string
+ */
+ public function get_sanitized_path($abs_path)
+ {
+ if ($this->using_wp_filesystem()) {
+ return str_replace(ABSPATH, $this->wp_filesystem->abspath(), $abs_path);
+ }
+
+ return $abs_path;
+ }
+
+ /**
+ * Attempt to initiate WP_Filesystem
+ *
+ * If this fails, $use_filesystem is set to false and all methods in this class should use native php fallbacks
+ * Thwarts `request_filesystem_credentials()` attempt to display a form for obtaining creds from users
+ *
+ * TODO: provide notice and input in wp-admin for users when this fails
+ */
+ public function maybe_init_wp_filesystem()
+ {
+ ob_start();
+ $this->credentials = \request_filesystem_credentials('', '', false, false, null);
+ $ob_contents = ob_get_contents();
+ ob_end_clean();
+
+ if (wp_filesystem($this->credentials)) {
+ global $wp_filesystem;
+ $this->wp_filesystem = $wp_filesystem;
+ $this->use_filesystem = true;
+ }
+ }
+
+ /**
+ * Create file if not exists then set mtime and atime on file
+ *
+ * @param string $abs_path
+ * @param int $time
+ * @param int $atime
+ *
+ * @return bool
+ */
+ public function touch($abs_path, $time = 0, $atime = 0)
+ {
+ if (0 == $time) {
+ $time = time();
+ }
+ if (0 == $atime) {
+ $atime = time();
+ }
+
+ // @TODO revisit usage of error supression opearator
+ $return = @touch( $abs_path, $time, $atime );
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->touch($abs_path, $time, $atime);
+ }
+
+ return $return;
+ }
+
+ /**
+ * file_put_contents with chmod
+ *
+ * @param string $abs_path
+ * @param string $contents
+ *
+ * @return bool
+ */
+ public function put_contents( $abs_path, $contents ) {
+ // @TODO revisit usage of error supression opearator
+ $return = @file_put_contents( $abs_path, $contents );
+ $this->chmod( $abs_path );
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->put_contents($abs_path, $contents, $this->chmod_file);
+ }
+
+ return (bool)$return;
+ }
+
+ /**
+ * Does the specified file or dir exist
+ *
+ * @param string $abs_path
+ *
+ * @return bool
+ */
+ public function file_exists($abs_path)
+ {
+ $return = file_exists($abs_path);
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->exists($abs_path);
+ }
+
+ return (bool)$return;
+ }
+
+ /**
+ * Get a file's size
+ *
+ * @param string $abs_path
+ *
+ * @return int
+ */
+ public function filesize($abs_path)
+ {
+ $return = filesize($abs_path);
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->size($abs_path);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Get the contents of a file as a string
+ *
+ * @param string $abs_path
+ *
+ * @return string
+ */
+ public function get_contents( $abs_path ) {
+ // @TODO revisit usage of error supression opearator
+ $return = @file_get_contents( $abs_path );
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->get_contents($abs_path);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Delete a file
+ *
+ * @param string $abs_path
+ *
+ * @return bool
+ */
+ public function unlink( $abs_path ) {
+ // @TODO revisit usage of error supression opearator
+ $return = @unlink( $abs_path );
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->delete($abs_path, false, false);
+ }
+
+ return $return;
+ }
+
+ /**
+ * chmod a file
+ *
+ * @param string $abs_path
+ * @param int $perms
+ *
+ * @return bool
+ *
+ * Leave $perms blank to use $this->chmod_file/DIR or pass value like 0777
+ */
+ public function chmod($abs_path, $perms = null)
+ {
+ if (is_null($perms)) {
+ $perms = $this->is_file($abs_path) ? $this->chmod_file : $this->chmod_dir;
+ }
+
+ $return = chmod( $abs_path, $perms );
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->chmod($abs_path, $perms, false);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Is the specified path a directory?
+ *
+ * @param string $abs_path
+ *
+ * @return bool
+ */
+ public function is_dir($abs_path)
+ {
+ $return = is_dir($abs_path);
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->is_dir($abs_path);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Is the specified path a file?
+ *
+ * @param string|null $abs_path
+ *
+ * @return bool
+ */
+ public function is_file($abs_path)
+ {
+ // if the path is not valid, return false
+ if (null === $abs_path) {
+ return false;
+ }
+
+ $return = is_file($abs_path);
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->is_file($abs_path);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Is the specified path readable
+ *
+ * @param string $abs_path
+ *
+ * @return bool
+ */
+ public function is_readable($abs_path)
+ {
+ $return = is_readable($abs_path);
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->is_readable($abs_path);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Is the specified path writable
+ *
+ * @param string $abs_path
+ *
+ * @return bool
+ */
+ public function is_writable($abs_path)
+ {
+ $return = is_writable($abs_path);
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+ $return = $this->wp_filesystem->is_writable($abs_path);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Recursive mkdir
+ *
+ * @param string $abs_path
+ * @param int $perms
+ *
+ * @return bool
+ */
+ public function mkdir($abs_path, $perms = null)
+ {
+ if (is_null($perms)) {
+ $perms = $this->chmod_dir;
+ }
+
+ if ( $this->is_dir( $abs_path ) ) {
+ $this->chmod( $abs_path, $perms );
+
+ return true;
+ }
+
+ $mkdirp = wp_mkdir_p( $abs_path );
+
+ if ( $mkdirp ) {
+ $this->chmod( $abs_path, $perms );
+
+ return true;
+ }
+
+ $return = mkdir( $abs_path, $perms, true );
+
+ //WP_Filesystem fallback
+ if ( ! $return && $this->use_filesystem ) {
+ $abs_path = $this->get_sanitized_path( $abs_path );
+
+ if ($this->is_dir($abs_path)) {
+ return true;
+ }
+
+ $return = $this->wp_filesystem_mkdir( $abs_path, $perms );
+ }
+
+ return $return;
+ }
+
+ /**
+ * WP_Filesystem doesn't offer a recursive mkdir(), so this is that
+ *
+ * @param string $abs_path
+ * @param int|null $perms
+ *
+ * @return string
+ */
+ public function wp_filesystem_mkdir( $abs_path, $perms )
+ {
+ $abs_path = str_replace( '//', '/', $abs_path );
+ $abs_path = rtrim( $abs_path, '/' );
+
+ if ( empty( $abs_path ) ) {
+ $abs_path = '/';
+ }
+
+ $dirs = explode( '/', ltrim( $abs_path, '/' ) );
+ $current_dir = '';
+
+ foreach ( $dirs as $dir ) {
+ $current_dir .= '/' . $dir;
+ if ( !$this->is_dir( $current_dir ) ) {
+ $this->wp_filesystem->mkdir( $current_dir, $perms );
+ }
+ }
+
+ return $this->is_dir( $abs_path );
+ }
+
+ /**
+ * Delete a directory
+ *
+ * @param string $abs_path
+ * @param bool $recursive
+ *
+ * @return bool
+ */
+ public function rmdir($abs_path, $recursive = false)
+ {
+ if (!$this->is_dir($abs_path)) {
+ return false;
+ }
+
+ // taken from WP_Filesystem_Direct
+ if (!$recursive) {
+ $return = @rmdir($abs_path);
+ } else {
+ // At this point it's a folder, and we're in recursive mode
+ $abs_path = trailingslashit($abs_path);
+ $filelist = $this->scandir($abs_path);
+
+ $return = true;
+ if (is_array($filelist)) {
+ foreach ($filelist as $filename => $fileinfo) {
+ if ('d' === $fileinfo['type']) {
+ $return = $this->rmdir($abs_path . $filename, $recursive);
+ } else {
+ $return = $this->unlink($abs_path . $filename);
+ }
+ }
+ }
+
+ if (file_exists($abs_path) && !@rmdir($abs_path)) {
+ $return = false;
+ }
+ }
+
+ if (!$return && $this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+
+ return $this->wp_filesystem->rmdir($abs_path, $recursive);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Get a list of files/folders under specified directory
+ *
+ * @param string $abs_path
+ * @param string $stage
+ * @param int $offset
+ * @param int $limit
+ * @param int $scan_count
+ *
+ * @return array|bool|\WP_error
+ */
+ public function scandir($abs_path, $stage = '', $offset = 0, $limit = -1, &$scan_count = 0)
+ {
+ $symlink = is_link($abs_path);
+ $dirlist = @scandir($abs_path, SCANDIR_SORT_DESCENDING);
+
+ if (false === $dirlist || empty($dirlist)) {
+ if ($this->use_filesystem) {
+ $abs_path = $this->get_sanitized_path($abs_path);
+
+ return $this->wp_filesystem->dirlist($abs_path, true, false);
+ }
+
+ return false;
+ }
+
+ if (-1 !== $limit) {
+ $dirlist = array_slice($dirlist, $offset, $limit, true);
+ $scan_count = count($dirlist);
+ }
+
+ $return = array();
+
+ // normalize return to look somewhat like the return value for WP_Filesystem::dirlist
+ foreach ($dirlist as $entry) {
+ if ('.' === $entry || '..' === $entry) {
+ continue;
+ }
+
+ $return[$entry] = $this->get_file_info($entry, $abs_path, $symlink, $stage);
+ }
+
+ return $return;
+ }
+
+ /**
+ * @param string $entry
+ * @param string $abs_path
+ * @param bool $symlink
+ * @param string $stage
+ *
+ * @return array
+ */
+ public function get_file_info($entry, $abs_path, $symlink = false, $stage = '')
+ {
+ $abs_path = $this->slash_one_direction($abs_path);
+ $full_path = trailingslashit($abs_path) . $entry;
+ $real_path = realpath($full_path); // Might be different due to symlinks.
+
+ $upload_info = wp_get_upload_dir();
+ $uploads_basedir = $upload_info['basedir'];
+ $uploads_folder = wp_basename($uploads_basedir);
+ $is_uploads_in_content = strpos($uploads_basedir, WP_CONTENT_DIR);
+ $content_path = false !== $is_uploads_in_content ? WP_CONTENT_DIR : dirname($uploads_basedir);
+ $return = array();
+ $return['name'] = $entry;
+ $return['relative_path'] = str_replace($abs_path, '', $full_path);
+ $return['wp_content_path'] = str_replace($this->slash_one_direction($content_path) . DIRECTORY_SEPARATOR, '', $full_path);
+ $return['relative_root_path'] = str_replace($this->slash_one_direction(ABSPATH), '', $full_path);
+ $return['absolute_path'] = $full_path;
+ $return['type'] = $this->is_dir($abs_path . DIRECTORY_SEPARATOR . $entry) ? 'd' : 'f';
+ $return['size'] = $this->filesize($abs_path . DIRECTORY_SEPARATOR . $entry);
+ $return['filemtime'] = filemtime($abs_path . DIRECTORY_SEPARATOR . $entry);
+
+ if ($symlink) {
+ $return['subpath'] = DIRECTORY_SEPARATOR . basename(dirname($real_path)) . DIRECTORY_SEPARATOR . $entry;
+ } else {
+ $return['subpath'] = str_replace(Util::get_stage_base_dir($stage), '', $full_path);
+ }
+
+ $exploded = explode(DIRECTORY_SEPARATOR, $return['subpath']);
+ $return['folder_name'] = isset($exploded[1]) ? $exploded[1] : $return['relative_path'];
+
+ return $return;
+ }
+
+ /**
+ * List all files in a directory recursively
+ *
+ * @param $abs_path
+ * @param string $stage
+ *
+ * @return array|bool
+ */
+ public function scandir_recursive($abs_path, $stage = '')
+ {
+ $dirlist = $this->scandir($abs_path, $stage);
+
+ if (is_wp_error($dirlist)) {
+ return $dirlist;
+ }
+
+ foreach ($dirlist as $key => $entry) {
+ if ('d' === $entry['type']) {
+ $current_dir = trailingslashit($entry['name']);
+ $current_path = trailingslashit($abs_path) . $current_dir;
+ $contents = $this->scandir_recursive($current_path, $stage);
+ unset($dirlist[$key]);
+ foreach ($contents as $filename => $value) {
+ $contents[$current_dir . $filename] = $value;
+ unset($contents[$filename]);
+ }
+ $dirlist += $contents;
+ }
+ }
+
+ return $dirlist;
+ }
+
+ /**
+ * Light wrapper for move_uploaded_file with chmod
+ *
+ * @param string $file
+ * @param string $destination
+ * @param int $perms
+ *
+ * @return bool
+ *
+ * TODO: look into replicating more functionality from wp_handle_upload()
+ */
+ public function move_uploaded_file( $file, $destination, $perms = null ) {
+ $return = move_uploaded_file( $file, $destination );
+
+ if ($return) {
+ $this->chmod($destination, $perms);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Copy a file
+ *
+ * @param string $source_abs_path
+ * @param string $destination_abs_path
+ * @param bool $overwrite
+ * @param mixed $perms
+ *
+ * @return bool
+ *
+ * Taken from WP_Filesystem_Direct
+ */
+ public function copy($source_abs_path, $destination_abs_path, $overwrite = true, $perms = false)
+ {
+ // error if source file doesn't exist
+ if (!$this->file_exists($source_abs_path)) {
+ return false;
+ }
+
+ if (!$overwrite && $this->file_exists($destination_abs_path)) {
+ return false;
+ }
+
+ // @TODO revisit usage of error supression opearator
+ $return = @copy( $source_abs_path, $destination_abs_path );
+ if ( $perms && $return ) {
+ $this->chmod( $destination_abs_path, $perms );
+ }
+
+ if (!$return && $this->use_filesystem) {
+ $source_abs_path = $this->get_sanitized_path($source_abs_path);
+ $destination_abs_path = $this->get_sanitized_path($destination_abs_path);
+ $return = $this->wp_filesystem->copy($source_abs_path, $destination_abs_path, $overwrite, $perms);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Move a file
+ *
+ * @param string $source_abs_path
+ * @param string $destination_abs_path
+ * @param bool $overwrite
+ *
+ * @return bool
+ */
+ public function move($source_abs_path, $destination_abs_path, $overwrite = true)
+ {
+ // error if source file doesn't exist
+ if (!$this->file_exists($source_abs_path)) {
+ return false;
+ }
+
+ // Try using rename first. if that fails (for example, source is read only) try copy.
+ // Taken in part from WP_Filesystem_Direct
+ if (!$overwrite && $this->file_exists($destination_abs_path)) {
+ return false;
+ } elseif (rename($source_abs_path, $destination_abs_path)) {
+ return true;
+ } else {
+ if ($this->copy($source_abs_path, $destination_abs_path, $overwrite) && $this->file_exists($destination_abs_path)) {
+ $this->unlink($source_abs_path);
+
+ return true;
+ } else {
+ $return = false;
+ }
+ }
+
+ //@TODO clean up temp location if using the rcopy() method
+
+ if (!$return && $this->use_filesystem) {
+ $source_abs_path = $this->get_sanitized_path($source_abs_path);
+ $destination_abs_path = $this->get_sanitized_path($destination_abs_path);
+
+ $return = $this->wp_filesystem->move($source_abs_path, $destination_abs_path, $overwrite);
+ }
+
+ return $return;
+ }
+
+ /**
+ *
+ * Recursively copy files, alternative to rename()
+ *
+ * @param $source
+ * @param $dest
+ *
+ * @return bool
+ */
+ public function rcopy($source, $dest)
+ {
+ // @TODO should probably throw on Maintenance Mode if using this as it takes much longer to complete vs. rename()
+
+ $this->rmdir($dest, true);
+ $this->mkdir($dest, 0755);
+
+ $return = true;
+
+ $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
+
+ foreach ($iterator as $item) {
+ if ($item->isDir()) {
+ if (!$this->mkdir($dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName())) {
+ $return = false;
+ }
+ } else {
+ if (!$this->copy($item, $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName())) {
+ $return = false;
+ }
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Converts file paths that include mixed slashes to use the correct type of slash for the current operating system.
+ *
+ * @param $path string
+ *
+ * @return string
+ */
+ public function slash_one_direction($path)
+ {
+ return str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
+ }
+
+ function download_file()
+ {
+ $is_full_site_export = !empty($_GET['fullSiteExport']);
+ if ($is_full_site_export) {
+ do_action('wpmdb_migration_complete');
+ }
+
+ $util = $this->container->get(Util::class);
+ $table_helper = $this->container->get(TableHelper::class);
+
+ // don't need to check for user permissions as our 'add_management_page' already takes care of this
+ $util->set_time_limit();
+
+ $raw_dump_name = htmlspecialchars($_GET['download'], ENT_QUOTES | ENT_HTML5);
+ $dump_name = $table_helper->format_dump_name($raw_dump_name);
+ $diskfile = $this->get_upload_info('path') . DIRECTORY_SEPARATOR . $dump_name;
+ if ($is_full_site_export) {
+ $diskfile = $this->get_upload_info('path') . DIRECTORY_SEPARATOR . $raw_dump_name . '.zip';
+ }
+
+ $filename = basename($diskfile);
+ $last_dash = strrpos($filename, '-');
+ $salt = substr($filename, $last_dash, 6);
+ $filename_no_salt = str_replace($salt, '', $filename);
+
+ if (file_exists($diskfile)) {
+ $filesize = $this->filesize($diskfile);
+ if (!headers_sent()) {
+
+ header('Content-Description: File Transfer');
+ header('Content-Type: application/octet-stream');
+ header('Content-Length: ' . $filesize);
+ header('Content-Disposition: attachment; filename=' . $filename_no_salt);
+ header('Connection: Keep-Alive');
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+
+ while(@ob_end_clean()){
+ // Clear all output buffer at any level
+ }
+
+ readfile($diskfile);
+
+ Persistence::cleanupStateOptions();
+
+ if ( ! isset($_GET['backup'])) { // Don't delete file if file was created during a local backup
+ $this->unlink($diskfile);
+ }
+
+ exit;
+ } else {
+ $last_error = error_get_last();
+ $msg = isset($last_error['message']) ? 'Error: ' . $last_error['message'] . '
' : '';
+ wp_die(sprintf(__('Output prevented download.
%s', 'wp-migrate-db'), $msg));
+ }
+ } else {
+ wp_die(__('Could not find the file to download:', 'wp-migrate-db') . '
' . esc_html($diskfile));
+ }
+ }
+
+ public function get_wp_upload_dir(){
+ $upload_info = wp_upload_dir();
+
+ return $upload_info['basedir'];
+ }
+ /**
+ * Determines, sets up, and returns folder information for storing files.
+ *
+ * By default, the folder created will be `wp-migrate-db` and will be stored
+ * inside of the `uploads` folder in WordPress' current `WP_CONTENT_DIR`,
+ * usually `wp-content/uploads`
+ *
+ * To change the folder name of `wp-migrate-db` to something else, you can use
+ * the `wpmdb_upload_dir_name` filter to change it. e.g.:
+ *
+ * function upload_dir_name() {
+ * return 'database-dumps';
+ * }
+ *
+ * add_filter( 'wpmdb_upload_dir_name', 'upload_dir_name' );
+ *
+ * If `WP_CONTENT_DIR` was set to `wp-content` in this example,
+ * this would change the folder to `wp-content/uploads/database-dumps`.
+ *
+ * To change the entire path, for example to store these files outside of
+ * WordPress' `WP_CONTENT_DIR`, use the `wpmdb_upload_info` filter to do so. e.g.:
+ *
+ * function upload_info() {
+ * // The returned data needs to be in a very specific format, see below for example
+ * return array(
+ * 'path' => '/path/to/custom/uploads/directory', // note missing end trailing slash
+ * 'url' => 'http://yourwebsite.com/custom/uploads/directory' // note missing end trailing slash
+ * );
+ * }
+ *
+ * add_filter( 'wpmdb_upload_info', 'upload_info' );
+ *
+ * This would store files in `/path/to/custom/uploads/directory` with a
+ * URL to access files via `http://yourwebsite.com/custom/uploads/directory`
+ *
+ * @link https://github.com/deliciousbrains/wp-migrate-db-pro-tweaks
+ *
+ * @param string $type Either `path` or `url`.
+ *
+ * @return string The Path or the URL to the folder being used.
+ */
+ function get_upload_info($type = 'path')
+ {
+ // @TODO - Don't grab Properties class here
+ $props = WPMDBDI::getInstance()->get(Properties::class);
+ $upload_info = apply_filters('wpmdb_upload_info', array());
+
+ // No need to create the directory structure since it should already exist.
+ if (!empty($upload_info)) {
+ return $upload_info[$type];
+ }
+
+ $upload_dir = wp_upload_dir();
+
+ $upload_info['path'] = $upload_dir['basedir'];
+ $upload_info['url'] = $upload_dir['baseurl'];
+
+ $upload_dir_name = apply_filters('wpmdb_upload_dir_name', 'wp-migrate-db');
+
+ if (!file_exists($upload_dir['basedir'] . DIRECTORY_SEPARATOR . $upload_dir_name)) {
+ $url = wp_nonce_url($props->plugin_base, 'wp-migrate-db-pro-nonce');
+
+ // Create the directory.
+
+ // TODO: Do not silence errors, use wp_mkdir_p?
+ if (false === @mkdir($upload_dir['basedir'] . DIRECTORY_SEPARATOR . $upload_dir_name, 0755)) {
+ return $upload_info[$type];
+ }
+
+ // Protect from directory listings by making sure an index file exists.
+ $filename = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $upload_dir_name . DIRECTORY_SEPARATOR . 'index.php';
+ // TODO: Do not silence errors, use WP_Filesystem API?
+ if (false === @file_put_contents($filename, "")) {
+ return $upload_info[$type];
+ }
+ }
+
+ // Protect from directory listings by ensuring this folder does not allow Indexes if using Apache.
+ $htaccess = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $upload_dir_name . DIRECTORY_SEPARATOR . '.htaccess';
+ if (!file_exists($htaccess)) {
+ // TODO: Do not silence errors, use WP_Filesystem API?
+ if (false === @file_put_contents($htaccess, "Options -Indexes\r\nDeny from all")) {
+ return $upload_info[$type];
+ }
+ }
+
+ $upload_info['path'] .= DIRECTORY_SEPARATOR . $upload_dir_name;
+ $upload_info['url'] .= '/' . $upload_dir_name;
+
+ return $upload_info[$type];
+ }
+
+
+ function open($filename = '', $mode = 'a', $is_full_site_export = false)
+ {
+ $form_data_class = $this->container->get(FormData::class);
+ $form_data = $form_data_class->getFormData();
+
+ $util = $this->container->get(Util::class);
+
+ if ('' == $filename) {
+ return false;
+ }
+
+ if ($util->gzip() && $form_data['gzip_file'] && !$is_full_site_export) {
+ $fp = gzopen($filename, $mode);
+ } else {
+ $fp = fopen($filename, $mode);
+ }
+
+ return $fp;
+ }
+
+ function close($fp, $is_full_site_export = false)
+ {
+ $form_data_class = $this->container->get(FormData::class);
+ $form_data = $form_data_class->getFormData();
+ $util = $this->container->get(Util::class);
+
+ if ($util->gzip() && $form_data['gzip_file'] && !$is_full_site_export) {
+ gzclose($fp);
+ } else {
+ fclose($fp);
+ }
+ }
+
+ public function format_backup_name($file_name)
+ {
+ $new_name = preg_replace('/-\w{5}.sql/', '.sql', $file_name);
+
+ return $new_name;
+ }
+
+ public function get_backups()
+ {
+ $backup_dir = $this->get_upload_info('path') . DIRECTORY_SEPARATOR;
+ $files = $this->scandir($backup_dir);
+ $output = [];
+
+ if (!is_array($files) || empty($files)) {
+ return false;
+ }
+
+ usort($files, function ($a, $b) {
+ return $a['filemtime'] < $b['filemtime'] ? 1 : 0;
+ });
+
+ foreach ($files as $file) {
+ if (!preg_match('/(.*)-(backup|migrate)-\d{14}-\w{5}(.sql|.sql.gz)$/', $file['name'])) {
+ continue;
+ }
+
+ $file_name_formatted = $this->format_backup_name($file['name']);
+
+ // Respects WordPress core options 'timezone_string' or 'gmt_offset'
+ $modified = get_date_from_gmt(date('Y-m-d H:i:s', $file['filemtime']), 'M d, Y g:i a');
+
+ $backup_info = [
+ 'path' => $file['absolute_path'],
+ 'modified' => $modified,
+ 'download_url' => WP_CONTENT_URL . DIRECTORY_SEPARATOR . $file['wp_content_path'],
+ 'name' => $file_name_formatted,
+ 'raw_name' => $file['name'],
+ ];
+
+ $output[] = $backup_info;
+ }
+
+ if (empty($output)) {
+ return false;
+ }
+
+ return $output;
+ }
+
+ /**
+ * @return array|bool|mixed|void
+ */
+ protected function get_active_plugins()
+ {
+ $active_plugins = get_option('active_plugins');
+
+ if (is_multisite()) {
+ // get active plugins for the network
+ $network_plugins = get_site_option('active_sitewide_plugins');
+ if ($network_plugins) {
+ $network_plugins = array_keys($network_plugins);
+ $active_plugins = array_merge($active_plugins, $network_plugins);
+ }
+ $sites = get_sites();
+ if (!empty($sites)) {
+ foreach($sites as $site) {
+ $site_plugins = get_blog_option($site->blog_id, 'active_plugins');
+ if (is_array($site_plugins)) {
+ $active_plugins = array_merge($active_plugins, $site_plugins);
+ }
+ }
+ }
+ }
+
+ return $active_plugins;
+ }
+
+ /**
+ * @return array
+ */
+ public function get_plugin_paths()
+ {
+ $plugin_root = $this->slash_one_direction(WP_PLUGIN_DIR);
+
+ $plugins_dir = @opendir($plugin_root);
+ $plugin_files = array();
+
+ if ($plugins_dir) {
+ while (false !== ($file = readdir($plugins_dir))) {
+ if ('.' === $file[0]) {
+ continue;
+ }
+
+
+ if (is_dir($plugin_root . DIRECTORY_SEPARATOR . $file)) {
+ $plugin_files[$file] = $plugin_root . DIRECTORY_SEPARATOR . $file;
+ } else {
+ if ('.php' === substr($file, -4)) {
+ $plugin_files[$file] = $plugin_root . DIRECTORY_SEPARATOR . $file;
+ }
+ }
+ }
+ closedir($plugins_dir);
+ }
+
+ return $plugin_files;
+ }
+
+ public function get_local_plugins($exclude_mdb = true)
+ {
+ $plugins = get_plugins();
+ $plugin_paths = $this->get_plugin_paths();
+
+ // @TODO get MU plugins in the list as well
+ $active_plugins = $this->get_active_plugins();
+
+ $plugin_list = array();
+
+ foreach ($plugins as $key => $plugin) {
+ if ($exclude_mdb && 0 === strpos($key, 'wp-migrate-db')) {
+ continue;
+ }
+ $base_folder = preg_replace('/\/(.*)\.php/i', '', $key);
+
+
+ $plugin_path = array_key_exists($base_folder, $plugin_paths) ? $plugin_paths[$base_folder] : false;
+ $plugin_list[$key] = array(
+ array(
+ 'name' => $plugin['Name'],
+ 'active' => in_array($key, $active_plugins),
+ 'path' => $plugin_path,
+ 'version' => $plugin['Version'],
+ ),
+ );
+ }
+
+ return $plugin_list;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Filesystem/RecursiveScanner.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Filesystem/RecursiveScanner.php
new file mode 100644
index 000000000..350ec1441
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Filesystem/RecursiveScanner.php
@@ -0,0 +1,514 @@
+filesystem = $filesystem;
+ $this->transfer_utils = $transfer_utils;
+
+ $this->register();
+ }
+
+ /**
+ * Registers required action hooks
+ */
+ public function register()
+ {
+ add_action('wpmdb_migration_complete', [$this, 'finalize_migration']);
+ add_action('wpmdb_cancellation', [$this, 'finalize_migration']);
+ }
+
+ /**
+ * Initializes manifest entry for a specific path.
+ *
+ * @param string $abs_path
+ */
+ public function initialize($abs_path)
+ {
+ $this->load_manifest();
+
+ if (null === $this->get_root($abs_path)) {
+ $this->build_manifest_tree($abs_path);
+ }
+ }
+
+ /**
+ * Recursively scans a directory contents while minding the scan bottleneck.
+ *
+ * @param string $abs_path
+ * @param string $stage
+ *
+ * @return array|bool|\WP_error
+ */
+ public function scan($abs_path, $stage = '')
+ {
+ $offset = 0;
+
+ $root = $abs_path;
+ $manifest_item = $this->get_root($abs_path);
+ $dir_name = '';
+ //If there's a manifest item for the current path, we attempt to find a resume position.
+ if (!empty($manifest_item)) {
+ $resume_position = $this->get_resume_position($abs_path);
+ //If there's a valid resume position we change the path and offset to that position.
+ if (null !== $resume_position) {
+ $abs_path = (string)key($resume_position);
+ $offset = $resume_position[$abs_path]['offset'];
+ if (!$this->is_root_item($abs_path)) {
+ $dir_name = $resume_position[$abs_path]['dir_name'];
+ }
+ } else if ($this->is_scan_complete($abs_path)) {
+ //If the scan is complete for that path just return.
+ return [];
+ } else {
+ //Otherwise keep scanning the root directory and update the offset.
+ $offset = $this->get_root($abs_path)['offset'];
+ }
+ }
+
+ $scan_count = 0;
+
+ $dirlist = $this->filesystem->scandir($abs_path, $stage, $offset, $this->get_bottleneck(), $scan_count);
+
+ if (is_wp_error($dirlist)) {
+ return $dirlist;
+ }
+
+ foreach ($dirlist as $filename => $value) {
+ if ($value['type'] !== 'd') {
+ $dirlist[$dir_name . $filename] = $value;
+ if(!empty($dir_name)) {
+ unset($dirlist[$filename]);
+ }
+ } else {
+ //Unset directories.
+ unset($dirlist[$filename]);
+ }
+ }
+
+ $this->increment_scan_count($scan_count);
+
+ //If the bottleneck isn't reached, mark the current path scan as complete.
+ //And call the scan method again recursively to pickup the next resume position.
+ if (!$this->reached_bottleneck()) {
+ $this->update_manifest_item($abs_path, $root, 0, true);
+ if (!$this->is_scan_complete($root)) {
+ $dirlist += $this->scan($root, $stage);
+ }
+ } else {
+ //Scan isn't complete, just update the offset.
+ $this->update_manifest_item($abs_path, $root, $scan_count - 1);
+ }
+
+ $this->save_manifest();
+
+ return $dirlist;
+ }
+
+ /**
+ * Returns scan completion status for a specific root entry.
+ *
+ * @param $root
+ * @return bool
+ */
+ public function is_scan_complete($root)
+ {
+ if ($this->should_exclude($root)) {
+ return true;
+ }
+
+ if ($this->is_root_item($root)) {
+ if (false === $this->manifest[$root]['completed']) {
+ return false;
+ }
+
+ foreach ($this->manifest[$root]['children'] as $child) {
+ if (false === $child['completed']) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Recursively builds a manifest tree for a specific path.
+ *
+ * @param $abs_path
+ * @param string|null $root
+ * @param string|null $dir_name
+ */
+ public function build_manifest_tree($abs_path, &$root = null, $dir_name = null)
+ {
+ $completed = $this->should_exclude($abs_path);
+ $dirlist = @scandir($abs_path, SCANDIR_SORT_DESCENDING);
+
+ if (null === $root) {
+ $this->add_root_item($abs_path, 0, $completed);
+ $root = $abs_path;
+ } else {
+ $this->add_child_item($root, $abs_path, $dir_name, 0, $completed);
+ }
+
+ foreach ($dirlist as $entry) {
+ if ('.' === $entry || '..' === $entry) {
+ continue;
+ }
+
+ $path = $abs_path . DIRECTORY_SEPARATOR . $entry;
+
+ if (is_dir($path)) {
+ $this->build_manifest_tree($path, $root, $dir_name . trailingslashit($entry));
+ }
+ }
+
+ $this->save_manifest();
+ }
+
+ /**
+ * Runs finalization actions.
+ */
+ public function finalize_migration()
+ {
+ $this->remove_scandir_manifest();
+ }
+
+ /**
+ * Returns true if recursive scanning is enabled.
+ *
+ * @return mixed|null
+ */
+ public function is_enabled()
+ {
+ return apply_filters('wpmdb_bottleneck_dir_scan', false);
+ }
+
+ /**
+ * Unsets the manifest file entry from a dir list array.
+ *
+ * @param $directories
+ * @return mixed
+ */
+ public function unset_manifest_file($directories)
+ {
+ $manifest_index = array_search($this->get_scandir_manifest_filename(), $directories, true);
+ if (false !== $manifest_index) {
+ unset($directories[$manifest_index]);
+ }
+
+ return $directories;
+ }
+
+ /**
+ * Returns the bottleneck status.
+ *
+ * @return bool
+ */
+ public function reached_bottleneck()
+ {
+ $bottleneck = apply_filters('wpmdb_recursive_scan_bottleneck', self::BOTTLENECK);
+ return $this->scan_count >= $bottleneck && $this->is_enabled();
+ }
+
+ /**
+ * @param string[] $excludes
+ */
+ public function set_excludes($excludes = []) {
+ $this->excludes = $excludes;
+ }
+
+ /**
+ * Sets the migration intent.
+ *
+ * @param string $intent
+ */
+ public function set_intent($intent) {
+ $this->intent = $intent;
+ }
+
+ /**
+ * Checks whether a manifest file exists for the current migration.
+ *
+ * @return bool
+ */
+ private function scan_manifest_exists()
+ {
+ return $this->filesystem->is_file($this->get_scandir_manifest_filename());
+ }
+
+ /**
+ * Adds a root item to the manifest.
+ *
+ * @param string $abs_path
+ * @param int $offset
+ * @param bool $completed
+ * @param array $children
+ */
+ private function add_root_item($abs_path, $offset = 0, $completed = false, $children = [])
+ {
+ if (!array_key_exists($abs_path, $this->manifest)) {
+ $this->manifest[$abs_path] = ['offset' => $offset, 'completed' => $completed, 'children' => $children];
+ }
+ }
+
+ /**
+ * Adds a child item to a root manifest item.
+ *
+ * @param string $root
+ * @param string $abs_path
+ * @param string $dir_name
+ * @param int $offset
+ * @param bool $completed
+ */
+ private function add_child_item($root, $abs_path, $dir_name = '', $offset = 0, $completed = false)
+ {
+ if (array_key_exists($root, $this->manifest) && !array_key_exists($abs_path, $this->manifest[$root])) {
+ $this->manifest[$root]['children'][$abs_path] = ['offset' => $offset, 'completed' => $completed, 'dir_name' => $dir_name];
+ }
+ }
+
+ /**
+ * Updates a manifest entry, the entry could be a root or a child. For child entries, a root must be provided.
+ *
+ * @param string $abs_path
+ * @param null|string $root
+ * @param int $offset
+ * @param bool $completed
+ */
+ private function update_manifest_item($abs_path, $root = null, $offset = 0, $completed = false)
+ {
+ if (null === $root || $this->is_root_item($abs_path)) {
+ $this->update_root_item($abs_path, $offset, $completed);
+ } else {
+ $this->update_child_item($root, $abs_path, $offset, $completed);
+ }
+ }
+
+ /**
+ * Updates a manifest child item.
+ *
+ * @param string $root
+ * @param string $abs_path
+ * @param int $offset
+ * @param bool $completed
+ */
+ private function update_child_item($root, $abs_path, $offset = 0, $completed = false)
+ {
+ if ($this->is_root_item($root) && array_key_exists($abs_path, $this->manifest[$root]['children'])) {
+ $this->manifest[$root]['children'][$abs_path]['offset'] += $offset;
+ $this->manifest[$root]['children'][$abs_path]['completed'] = $completed;
+ }
+ }
+
+ /**
+ * Updates a manifest root item.
+ *
+ * @param string $abs_path
+ * @param int $offset
+ * @param bool $completed
+ */
+ private function update_root_item($abs_path, $offset = 0, $completed = false)
+ {
+ if ($this->is_root_item($abs_path)) {
+ $this->manifest[$abs_path]['completed'] = $completed;
+ $this->manifest[$abs_path]['offset'] += $offset;
+ }
+ }
+
+ /**
+ * Checks if a given path is a root item in the manifest.
+ *
+ * @param $abs_path
+ * @return bool
+ */
+ private function is_root_item($abs_path)
+ {
+ return array_key_exists($abs_path, $this->manifest);
+ }
+
+ /**
+ * Retrieves the root manifest item of a given path.
+ *
+ * @param string $abs_path
+ * @return mixed|null
+ */
+ private function get_root($abs_path)
+ {
+ if ($this->is_root_item($abs_path)) {
+ return $this->manifest[$abs_path];
+ }
+ return null;
+ }
+
+ /**
+ * Returns the scan resume position from the manifest.
+ * The position is the first folder that's not completely scanned.
+ *
+ * @param string $abs_path
+ * @return array|null
+ */
+ private function get_resume_position($abs_path)
+ {
+ if (!$this->is_root_item($abs_path)) {
+ return null;
+ }
+
+ $root = $this->get_root($abs_path);
+ if(!$root['completed']) {
+ return null;
+ }
+
+ $items = array_filter($this->manifest[$abs_path]['children'], static function ($item) {
+ return false === $item['completed'];
+ });
+
+ if (!empty($items)) {
+ $keys = array_keys($items);
+ return [$keys[0] => current($items)];
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieves the saved manifest data.
+ *
+ * @return mixed|false
+ */
+ private function get_scandir_manifest()
+ {
+ $file_data = $this->filesystem->get_contents($this->get_scandir_manifest_filename());
+ return json_decode($file_data, true);
+ }
+
+ /**
+ * Saves the current manifest.
+ */
+ private function save_manifest()
+ {
+ $manifest_filename = $this->get_scandir_manifest_filename();
+ $result = $this->filesystem->put_contents($manifest_filename, json_encode($this->manifest));
+
+ if ( ! $result) {
+ $this->transfer_utils->catch_general_error('Could not create scandir manifest.');
+ }
+ }
+
+ /**
+ * Returns the string name of the manifest file based on the current migration id.
+ *
+ * @return string|null
+ */
+ private function get_scandir_manifest_filename()
+ {
+ $remote_state = $this->intent === 'pull' ? Persistence::getRemoteStateData() : Persistence::getStateData();
+
+ if (!isset($remote_state['form_data'])) {
+ return null;
+ }
+
+ $form_data = json_decode($remote_state['form_data'], false);
+ if (is_object($form_data) && property_exists($form_data, 'current_migration')) {
+ return Util::get_wp_uploads_dir() . DIRECTORY_SEPARATOR . '.' . $form_data->current_migration->migration_id . '-wpmdb-scandir-manifest';
+ }
+
+ return null;
+ }
+
+ /**
+ * Unlinks the manifest file.
+ */
+ private function remove_scandir_manifest()
+ {
+ $filename = $this->get_scandir_manifest_filename();
+ if ($this->filesystem->is_file($filename)) {
+ $this->filesystem->unlink($filename);
+ }
+ }
+
+ /**
+ * Loads the manifest file into the manifest property.
+ */
+ private function load_manifest()
+ {
+ if ($this->scan_manifest_exists()) {
+ $this->manifest = $this->get_scandir_manifest();
+ }
+ }
+
+ /**
+ * Increments the scan items count.
+ *
+ * @param int $count
+ */
+ private function increment_scan_count($count)
+ {
+ $this->scan_count += $count;
+ }
+
+ /**
+ * Returns the bottleneck value.
+ *
+ * @return int
+ */
+ private function get_bottleneck()
+ {
+ $bottleneck = apply_filters('wpmdb_recursive_scan_bottleneck', self::BOTTLENECK);
+ return $this->is_enabled() ? $bottleneck - $this->scan_count : -1;
+ }
+
+ /**
+ * Tests exclusion of a specific path.
+ *
+ * @param string $path
+ * @return bool
+ */
+ private function should_exclude($path) {
+ $excludes = Excludes::shouldExcludeFile($path, $this->excludes);
+
+ return !empty($excludes['exclude']);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/FormData/FormData.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/FormData/FormData.php
new file mode 100644
index 000000000..3509a917a
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/FormData/FormData.php
@@ -0,0 +1,277 @@
+util = $util;
+ $this->migration_state_manager = $migration_state_manager;
+
+ $this->accepted_fields = array(
+ 'action',
+ 'save_computer',
+ 'gzip_file',
+ 'connection_info',
+ 'replace_old',
+ 'replace_new',
+ 'table_migrate_option',
+ 'select_tables',
+ 'replace_guids',
+ 'exclude_spam',
+ 'save_migration_profile',
+ 'save_migration_profile_option',
+ 'create_new_profile',
+ 'create_backup',
+ 'remove_backup',
+ 'keep_active_plugins',
+ 'select_post_types',
+ 'backup_option',
+ 'select_backup',
+ 'exclude_transients',
+ 'exclude_post_types',
+ 'exclude_post_revisions',
+ 'compatibility_older_mysql',
+ 'export_dest',
+ 'import_find_replace',
+ 'current_migration',
+ 'search_replace',
+ 'regex',
+ 'case_sensitive',
+ );
+ }
+
+ public function get_accepted_fields()
+ {
+ return $this->accepted_fields;
+ }
+
+ public function set_accepted_fields()
+ {
+ }
+
+ public function form_data_compat($data)
+ {
+ $current_migration = $data['current_migration'];
+ $advanced_options = $current_migration['advanced_options_selected'];
+
+ $return = [
+ 'action' => $current_migration['intent'],
+ 'select_tables' => isset($current_migration['tables_selected']) ? $current_migration['tables_selected'] : [],
+ 'table_migrate_option' => isset($current_migration['tables_option']) ? $current_migration['tables_option'] : '',
+ 'create_backup' => isset($current_migration['backup_option']) && $current_migration['backup_option'] !== 'none' ? 1 : 0,
+ 'backup_option' => isset($current_migration['backup_option']) ? $current_migration['backup_option'] : '',
+ 'select_backup' => isset($current_migration['backup_tables_selected']) ? $current_migration['backup_tables_selected'] : [],
+ 'select_post_types' => isset($current_migration['post_types_selected']) ? $current_migration['post_types_selected'] : [],
+ 'exclude_post_revisions' => in_array('exclude_post_revisions', $advanced_options) ? '1' : '0',
+ 'replace_guids' => in_array('replace_guids', $advanced_options) ? '1' : '0',
+ 'compatibility_older_mysql' => in_array('compatibility_older_mysql', $advanced_options) ? '1' : '0',
+ 'exclude_transients' => in_array('exclude_transients', $advanced_options) ? '1' : '0',
+ 'exclude_spam' => in_array('exclude_spam', $advanced_options) ? '1' : '0',
+ 'keep_active_plugins' => in_array('keep_active_plugins', $advanced_options) ? '1' : '0',
+ 'gzip_file' => in_array('gzip_file', $advanced_options) ? '1' : '0',
+ 'exclude_post_types' => '0',
+ ];
+
+ if (in_array($current_migration['intent'], array('push', 'pull'))) {
+ $return['connection_info'] = isset($data['connection_info'], $data['connection_info']['connection_state']) ? $data['connection_info']['connection_state']['value'] : '';
+ }
+
+ if ($return['table_migrate_option'] === 'selected') {
+ $return['table_migrate_option'] = 'migrate_select';
+ }
+
+ if ($current_migration['post_types_option'] !== 'all') {
+ $return['exclude_post_types'] = 1;
+ }
+
+ if ($return['exclude_post_revisions'] === '1' && $current_migration['post_types_option'] === 'all' ) {
+ $table = WPMDBDI::getInstance()->get(Table::class);
+ $return['select_post_types'] = array_diff($table->get_post_types(), ['revision']);
+ $return['exclude_post_types'] = 1;
+ }
+
+ //make sure revisions are included when user has selected post types to migrate but did not exclude revisions
+ if ($return['exclude_post_revisions'] === '0' && $return['exclude_post_types'] === 1 && ! in_array('revision',
+ $return['select_post_types'], true)) {
+ $return['select_post_types'][] = 'revision';
+ }
+ return $return;
+ }
+
+ /**
+ * Sets up the form data for the migration.
+ */
+ public function setup_form_data()
+ {
+ $this->util->set_time_limit();
+ $state_data = $this->migration_state_manager->set_post_data();
+
+ if (empty($this->form_data)) {
+ // ***+=== @TODO - revisit usage of parse_migration_form_data
+ $this->parse_and_save_migration_form_data($state_data['form_data']);
+ }
+ }
+
+ /**
+ * Returns validated and sanitized form data.
+ *
+ * @param array|string $data
+ *
+ * @return array|string
+ */
+
+ // @TODO - refactor usage
+ public function parse_and_save_migration_form_data($data)
+ {
+ $form_data = json_decode($data, true);
+
+ $this->accepted_fields = apply_filters('wpmdb_accepted_profile_fields', $this->accepted_fields);
+
+ $form_data = array_intersect_key($form_data, array_flip($this->accepted_fields));
+
+ $compat_form_data = $this->form_data_compat($form_data);
+
+ if (!empty($compat_form_data) && is_array($compat_form_data)) {
+ $form_data = array_merge($form_data, $compat_form_data);
+ }
+
+ $existing_form_data = Persistence::getMigrationOptions();
+
+ if (!empty($existing_form_data)) {
+ $form_data = array_merge($existing_form_data, $form_data);
+ }
+
+ // @TODO maybe sanitize JSON?
+ Persistence::saveMigrationOptions($form_data);
+
+ $this->form_data = $form_data;
+
+ return $form_data;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFormData()
+ {
+ if (!empty($this->form_data)) {
+ return $this->form_data;
+ }
+
+ $saved_form_data = Persistence::getMigrationOptions();
+
+ if (empty($saved_form_data)) {
+ return null;
+ }
+
+ return $saved_form_data;
+ }
+
+ public function getAdvancedOptions()
+ {
+ $form_data = $this->getFormData();
+
+ if (isset($form_data['current_migration']['advanced_options_selected'])) {
+ return $form_data['current_migration']['advanced_options_selected'];
+ }
+
+ return false;
+ }
+
+ public function getAdvancedOption($key)
+ {
+ $form_data = $this->getFormData();
+
+ if (!isset($form_data['current_migration']['advanced_options_selected'])) {
+ return false;
+ }
+
+ $advanced_options = $form_data['current_migration']['advanced_options_selected'];
+
+ if (isset($advanced_options[$key])) {
+ return $advanced_options[$key];
+ }
+
+ return false;
+ }
+
+ public function getCurrentMigrationData()
+ {
+ $form_data = $this->getFormData();
+
+ if (isset($form_data['current_migration'])) {
+ return $form_data['current_migration'];
+ }
+
+ return false;
+ }
+
+ public function getMigrationStages()
+ {
+ $current_migration = $this->getCurrentMigrationData();
+
+ if (!$current_migration) {
+ return;
+ }
+
+ if (isset($current_migration['stages'])) {
+ return $current_migration['stages'];
+ }
+
+ return false;
+ }
+
+
+ /**
+ * @param mixed $form_data
+ */
+ public function setFormData($form_data)
+ {
+ $this->form_data = $form_data;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/FullSite/FullSiteExport.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/FullSite/FullSiteExport.php
new file mode 100644
index 000000000..7298c7c4b
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/FullSite/FullSiteExport.php
@@ -0,0 +1,280 @@
+file_filters = $file_filters;
+ }
+
+ /**
+ * Create export file and add empty wp-content dir structure
+ *
+ * @param string $file_name
+ * @param array $state_data
+ * @return mixed bool|WP_Error
+ * @throws WP_Error
+ **/
+ public function create_export_zip($file_name, $state_data)
+ {
+ $zip = new ZipArchive();
+ if ($zip->open($file_name, ZipArchive::CREATE) !== TRUE) {
+ return new \WP_Error('wp-migrate-db-export-not-created', __('Could not create ZIP Archive', 'wp-migrate-db'));
+ }
+ $stages = json_decode($state_data['stages']);
+ $zip->addEmptyDir($this->determine_path('other_files', $stages));
+ $zip->addEmptyDir($this->determine_path('theme_files', $stages));
+ $zip->addEmptyDir($this->determine_path('plugin_files', $stages));
+ $zip->addEmptyDir($this->determine_path('media_files', $stages));
+ $zip->addFromString(self::MANIFEST, $this->get_manifest_json());
+ $zip->close();
+ return true;
+ }
+
+ /**
+ * Adds batch of files to ZIP
+ *
+ * @param array $batch
+ * @param array $state_data
+ * @return mixed array|WP_Error
+ * @throws WP_Error
+ **/
+ public function add_batch_to_zip($batch, $state_data)
+ {
+ $zip = new ZipArchive();
+ $zip_filename = $state_data['export_path'];
+ $stage = $state_data['stage'];
+ $stages = json_decode($state_data['stages']);
+ $zip->open($zip_filename);
+
+ $count = 0;
+ $total_size = 0;
+ $path = $this->determine_path($stage, $stages);
+
+
+ foreach ($batch as $key => $file) {
+ if (file_exists($file['absolute_path'])) {
+ //Apply filters to file
+ $file = $this->apply_file_filters($file, $state_data);
+ $relative_path = $stage === 'core' ? $file['relative_root_path'] : $file['relative_path'];
+ $relative_path = apply_filters('wpmdb_export_relative_path', $relative_path, $state_data);
+ $add_file = $zip->addFile($file['absolute_path'], $path . DIRECTORY_SEPARATOR . $relative_path);
+ if ( ! $add_file) {
+ return new \WP_Error('wp-migrate-db-could-not-add-file-to-archive',
+ sprintf(__('Could not add %s to ZIP Archive', 'wp-migrate-db'), $file['name']));
+ }
+ $count++;
+ $total_size += $file['size'];
+ }
+ }
+
+ $zip->close();
+ return [
+ 'count' => $count,
+ 'total_size' => $total_size
+ ];
+ }
+
+ /**
+ * Determines the file path in ZIP
+ *
+ * Returns path as defined by WP constants in wp_config.php
+ * if Core files included in export
+ *
+ * @param string $stage
+ * @param array $stages
+ * @return string
+ **/
+ protected function determine_path($stage, $stages)
+ {
+ $honor_const = !empty(array_intersect(['core_files', 'core'], $stages));
+ $honor_const = apply_filters('wpmdb_export_honor_constant', $honor_const);
+ $default_paths = [
+ 'media_files' => 'wp-content/uploads',
+ 'theme_files' => 'wp-content/themes',
+ 'themes' => 'wp-content/themes',
+ 'plugin_files' => 'wp-content/plugins',
+ 'plugins' => 'wp-content/plugins',
+ 'mu_plugin_files' => 'wp-content/mu-plugins',
+ 'muplugins' => 'wp-content/mu-plugins',
+ 'other_files' => 'wp-content',
+ 'others' => 'wp-content',
+ 'core_files' => '',
+ 'core' => '',
+ ];
+
+ $path = $honor_const ? $this->get_relative_dir($stage) : $default_paths[$stage];
+
+ return self::FILES_ROOT . DIRECTORY_SEPARATOR . $path;
+ }
+
+ /**
+ * Get directory relative to ABSPATH
+ *
+ * @param string $stage
+ * @return string
+ **/
+ protected function get_relative_dir($stage)
+ {
+ return str_replace(ABSPATH, '', Util::get_stage_base_dir($stage));
+ }
+
+ /**
+ * Move SQL file into ZIP archive
+ *
+ * @param string $dump_filename
+ * @param string $zip_filename
+ * @return bool
+ **/
+ public function move_into_zip($dump_filename, $zip_filename)
+ {
+ $zip = new ZipArchive();
+ $zip->open($zip_filename);
+ $add_file = $zip->addFile($dump_filename, 'database.sql');
+ if ($add_file) {
+ $zip->close();
+ unlink($dump_filename);
+ return true;
+ }
+ return false;
+
+ }
+
+ /**
+ * Deletes ZIP archive
+ *
+ * @param string $zip_filename
+ * @return bool|WPError
+ **/
+ public function delete_export_zip($zip_filename)
+ {
+ if (false === file_exists($zip_filename)){
+ return new \WP_Error('wp-migrate-db-could-not-find-archive-file', sprintf(__(' ZIP Archive %s does not exist', 'wp-migrate-db'), $zip_filename));
+
+ }
+ $removed = unlink($zip_filename);
+ if (false === $removed) {
+ return new \WP_Error('wp-migrate-db-could-not-delete-archive-file', sprintf(__(' ZIP Archive %s could not be deleted', 'wp-migrate-db'), $zip_filename));
+ }
+ return true;
+ }
+
+ /**
+ * Creates JSON string to insert into export manifest
+ *
+ * @return string JSON
+ **/
+ protected function get_manifest_json()
+ {
+ $export_data = [
+ 'name' => get_bloginfo('name'),
+ 'domain' => site_url(),
+ 'path' => esc_html(Util::get_absolute_root_file_path()),
+ 'wpVersion' => get_bloginfo('version'),
+ 'services' => $this->get_services(),
+ 'wpMigrate' => Util::getPluginMeta()
+ ];
+
+ return json_encode($export_data, JSON_UNESCAPED_SLASHES);
+ }
+
+ /**
+ * Get services array in Local format
+ *
+ * @return array
+ **/
+ protected function get_services()
+ {
+ global $wpdb;
+ $services = [
+ 'php' => [
+ 'name' => 'php',
+ 'version' => function_exists( 'phpversion' ) ? phpversion() : '',
+ ]
+ ];
+
+ return array_merge($services, $this->get_db(mysqli_get_server_info($wpdb->dbh)), $this->get_server($_SERVER['SERVER_SOFTWARE']));
+ }
+
+ /**
+ * Get db software array in Local format
+ *
+ * @param string $data_base
+ * @return array
+ **/
+ protected function get_db($data_base)
+ {
+ $db_name = stripos($data_base, 'mariadb') === false ? 'mysql' : 'mariadb';
+ return [
+ $db_name => [
+ 'name' => $db_name,
+ 'version' => $data_base
+ ]
+ ];
+ }
+
+ /**
+ * Get server information
+ *
+ * Convert to Local friendly format
+ *
+ * @param string $server
+ * @return array
+ **/
+ protected function get_server($server)
+ {
+ if (empty($server)) {
+ return [];
+ }
+ $server_divided = explode('/', $server);
+ $type = strtolower($server_divided[0]);
+ $server_info = ['name' => $type];
+ if (count($server_divided) > 1) {
+ $after_type = explode(' ', $server_divided[1]);
+ $version = reset($after_type);
+ $server_info['version'] = $version;
+ }
+
+ return [$type => $server_info];
+ }
+
+ /**
+ * Iterates through file filters and apply them to the supplied file.
+ *
+ * @param array $file
+ * @param array $state_data
+ *
+ * @return array
+ */
+ private function apply_file_filters($file, $state_data) {
+ foreach ($this->file_filters as $filter) {
+ if ($filter->can_filter($file, $state_data)) {
+ $file = $filter->filter($file);
+ }
+ }
+
+ return $file;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Helpers.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Helpers.php
new file mode 100644
index 000000000..69e3bb098
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Helpers.php
@@ -0,0 +1,98 @@
+get(FormData::class)->getFormData();
+ }
+
+ /**
+ * Get transient key used for storing licence response.
+ *
+ * @param bool|int $initial_user_id Get transient key for a specific user.
+ * @param bool $user_fallback Whether to fallback to finding current or first users with licence key.
+ *
+ * @return string
+ */
+ public static function get_licence_response_transient_key($initial_user_id = false, $user_fallback = true)
+ {
+ $key = 'wpmdb_licence_response';
+
+ if ($initial_user_id) {
+ $user = get_user_by('id', $initial_user_id);
+ if ($user) {
+ return "{$key}_{$initial_user_id}";
+ }
+ }
+
+ if ($user_fallback) {
+ $user_id = self::get_current_or_first_user_id_with_licence_key();
+ if ($user_id) {
+ return "{$key}_{$user_id}";
+ }
+ }
+
+ return $key;
+ }
+
+ /**
+ * Get current user ID.
+ * If not logged in, looks for first user with a licence key.
+ *
+ * @return false|int
+ */
+ public static function get_current_or_first_user_id_with_licence_key()
+ {
+ if ((defined('DOING_AJAX') && DOING_AJAX) || (defined('REST_REQUEST') && REST_REQUEST) || (defined('WP_CLI') && WP_CLI) || is_admin()) {
+
+ if (is_user_logged_in()) {
+ return get_current_user_id();
+ }
+
+ $user_query = new \WP_User_Query([
+ 'number' => 1,
+ 'meta_key' => self::USER_LICENCE_META_KEY,
+ 'meta_value' => '',
+ 'meta_compare' => '!=',
+ ]);
+
+ $users = $user_query->get_results();
+
+ if ( ! empty($users) && ! is_wp_error($users)) {
+ return $users[0]->ID;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get user defined licence key.
+ *
+ * @param bool $user_id User ID to which retrieve licence for.
+ * @return false|string
+ */
+ public static function get_user_licence_key($user_id = false)
+ {
+ if (! $user_id) {
+ $user_id = get_current_user_id();
+ }
+ $licence = get_user_meta($user_id, self::USER_LICENCE_META_KEY, true);
+
+ if (! $licence || empty($licence)) {
+ return false;
+ }
+
+ return $licence;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Helper.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Helper.php
new file mode 100644
index 000000000..d949f4155
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Helper.php
@@ -0,0 +1,113 @@
+settings = $settings->get_settings();
+
+ self::$http = $http;
+ }
+
+ function filter_post_elements($post_array, $accepted_elements)
+ {
+ $accepted_elements[] = 'sig';
+
+ return array_intersect_key($post_array, array_flip($accepted_elements));
+ }
+
+ function sanitize_signature_data($value)
+ {
+ if (is_bool($value)) {
+ $value = $value ? 'true' : 'false';
+ }
+
+ return $value;
+ }
+
+ /**
+ * Generate a signature string for the supplied data given a key.
+ *
+ * @param array $data
+ * @param string $key
+ *
+ * @return string
+ */
+ function create_signature($data, $key)
+ {
+ if (isset($data['sig'])) {
+ unset($data['sig']);
+ }
+ $data = array_map(array($this, 'sanitize_signature_data'), $data);
+ ksort($data);
+ $flat_data = implode('', $data);
+
+ return base64_encode(hash_hmac('sha1', $flat_data, $key, true));
+ }
+
+ function verify_signature($data, $key)
+ {
+ if (empty($data['sig'])) {
+ return false;
+ }
+
+ if (isset($data['nonce'])) {
+ unset($data['nonce']);
+ }
+
+ $temp = $data;
+ $computed_signature = $this->create_signature($temp, $key);
+
+ return $computed_signature === $data['sig'];
+ }
+
+ function get_sensible_pull_limit()
+ {
+ return apply_filters('wpmdb_sensible_pull_limit', min(26214400, $this->settings['max_request']));
+ }
+
+ public function convert_json_body_to_post()
+ {
+ if (DynamicProperties::getInstance()->doing_cli_migration) {
+ return $_POST;
+ }
+
+ $_POST = $_REQUEST = json_decode(file_get_contents('php://input'), true);
+
+ $cap = (is_multisite()) ? 'manage_network_options' : 'export';
+ $cap = apply_filters('wpmdb_ajax_cap', $cap);
+
+ if (!current_user_can($cap)) {
+ self::$http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-convert-json-post-error',
+ __('Invalid Request. Did you pass the correct nonce?', 'wp-migrate-db')
+ )
+ );
+ }
+
+ return $_POST;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Http.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Http.php
new file mode 100644
index 000000000..1121d5637
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Http.php
@@ -0,0 +1,186 @@
+props = $properties;
+ $this->util = $util;
+ $this->filesystem = $filesystem;
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->scrambler = $scrambler;
+ $this->error_log = $error_log;
+ }
+
+ /**
+ * Ends the current AJAX request.
+ *
+ * @param mixed $return Value to be returned as response.
+ * @param mixed $extraInfo Additional info to be logged.
+ * @param bool $raw If $return should just be echoed out.
+ *
+ * @return mixed
+ * @throws \WP_CLI\ExitException
+ */
+ public function end_ajax($return = false, $extraInfo = false, $raw = false)
+ {
+ if (is_wp_error($return)) {
+ $error_msg = $return->get_error_message();
+ $this->error_log->log_error($error_msg, $extraInfo);
+
+ if ($this->dynamic_props->doing_cli_migration) {
+ return \WP_CLI::error(Cli::cleanup_message($error_msg));
+ }
+
+ return wp_send_json_error($error_msg);
+ }
+
+ // Handle legacy `wpmdb_error` format
+ $json = Util::validate_json($return);
+
+ if ($json) {
+ if (isset($json['wpmdb_error']) && $json['wpmdb_error'] == '1') {
+ if (isset($json['body'])) {
+ return wp_send_json_error($json['body']);
+ }
+ if (isset($json['msg'])) {
+ return wp_send_json_error($json['msg']);
+ }
+
+ return wp_send_json_error(sprintf(__('An error occurred - JSON response: %s', 'wp-migrate-db'), $json));
+ }
+ }
+
+ $return = apply_filters('wpmdb_before_response', $return);
+
+ if ($this->dynamic_props->doing_cli_migration) {
+ // This function should signal the end of the PHP process, but for CLI it carries on so we need to reset our own usage
+ // of the wpmdb_before_response filter before another respond_to_* function adds it again.
+ remove_filter('wpmdb_before_response', array($this->scrambler, 'scramble'));
+
+ if (!$json && $this->dynamic_props->doing_cli_migration) {
+ $return = json_encode($return);
+ }
+
+ return false === $return ? null : $return;
+ }
+
+ $output = false === $return ? '' : $return;
+
+ if ($raw) {
+ echo $output;
+
+ if (defined('DOING_WPMDB_TESTS')) {
+ return $output;
+ }
+
+ die();
+ }
+
+ return wp_send_json_success($output);
+ }
+
+ public function check_ajax_referer($action)
+ {
+ if (defined('DOING_WPMDB_TESTS') || $this->dynamic_props->doing_cli_migration) {
+ return;
+ }
+
+ $result = Util::check_ajax_referer($action, 'nonce', false);
+
+ if (false === $result) {
+ return $this->end_ajax(new \WP_Error('wpmdb_invalid_nonce', sprintf(__('Invalid nonce for: %s', 'wp-migrate-db'), $action)));
+ }
+
+ $cap = (is_multisite()) ? 'manage_network_options' : 'export';
+ $cap = apply_filters('wpmdb_ajax_cap', $cap);
+
+ if (!current_user_can($cap)) {
+ $return = array('wpmdb_error' => 1, 'body' => sprintf(__('Access denied for: %s', 'wp-migrate-db'), $action));
+ $this->end_ajax(json_encode($return));
+ }
+ }
+
+ function array_to_multipart($data)
+ {
+ if (!$data || !is_array($data)) {
+ return $data;
+ }
+
+ $result = '';
+
+ foreach ($data as $key => $value) {
+ $result .= '--' . $this->props->multipart_boundary . "\r\n" . sprintf('Content-Disposition: form-data; name="%s"', $key);
+
+ if ('chunk' == $key) {
+ if ($data['chunk_gzipped']) {
+ $result .= "; filename=\"chunk.txt.gz\"\r\nContent-Type: application/x-gzip";
+ } else {
+ $result .= "; filename=\"chunk.txt\"\r\nContent-Type: text/plain;";
+ }
+ } else {
+ $result .= "\r\nContent-Type: text/plain; charset=" . get_option('blog_charset');
+ }
+
+ $result .= "\r\n\r\n" . $value . "\r\n";
+ }
+
+ $result .= '--' . $this->props->multipart_boundary . "--\r\n";
+
+ return $result;
+ }
+
+
+ /**
+ * Check for download
+ * if found prepare file for download
+ *
+ * @return void
+ */
+ function http_verify_download()
+ {
+ if (!empty($_GET['download'])) {
+ $this->filesystem->download_file();
+ }
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/RemotePost.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/RemotePost.php
new file mode 100644
index 000000000..10843dd28
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/RemotePost.php
@@ -0,0 +1,490 @@
+util = $util;
+ $this->filesystem = $filesystem;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->settings = $settings->get_settings();
+ $this->error_log = $error_log;
+ $this->scrambler = $scrambler;
+ }
+
+ /**
+ * Post data to a remote site with WP Migrate DB Pro and check the response.
+ *
+ * @param string $url The URL to post to.
+ * @param array $data The associative array of data to be posted to the remote.
+ * @param string $scope A string to be used in error messages defining the function that initiated the remote post.
+ * @param array $args An optional array of args to alter the timeout, blocking and sslverify options.
+ * @param bool $expecting_serial Verify that the response is a serialized string (defaults to false).
+ *
+ * @return bool|string
+ */
+ public function post($url, $data, $scope, $args = [], $expecting_serial = false)
+ {
+ $this->util->set_time_limit();
+ $state_data = Persistence::getStateData();
+ $this->state_data = $state_data;
+
+ if (function_exists('fsockopen') && 0 === strpos($url, 'https://') && 'ajax_verify_connection_to_remote_site' === $scope) {
+ $url_parts = Util::parse_url($url);
+ $host = $url_parts['host'];
+ if ($pf = @fsockopen($host, 443, $err, $err_string, 1)) {
+ // worked
+ fclose($pf);
+ } else {
+ // failed
+ $url = substr_replace($url, 'http', 0, 5);
+ }
+ }
+
+ $sslverify = (1 === $this->settings['verify_ssl'] ? true : false);
+
+ $default_remote_post_timeout = apply_filters('wpmdb_default_remote_post_timeout', 60 * 20);
+
+ $args = wp_parse_args(
+ $args,
+ [
+ 'timeout' => $default_remote_post_timeout,
+ 'blocking' => true,
+ 'sslverify' => $sslverify,
+ ]
+ );
+
+ $args['method'] = 'POST';
+ $remote_cookie = Persistence::getRemoteWPECookie();
+ if (false !== $remote_cookie) {
+ $cookies = [];
+ $cookie_args = [
+ 'name' => 'wpe-auth',
+ 'value' => $remote_cookie,
+ ];
+ $cookie = new \WP_Http_Cookie($cookie_args);
+ $cookies[] = $cookie;
+ $args['cookies'] = $cookies;
+ }
+ if (!isset($args['body'])) {
+ $args['body'] = $this->array_to_multipart($data);
+ }
+
+ $args['headers']['Content-Type'] = 'multipart/form-data; boundary=' . $this->props->multipart_boundary;
+ $args['headers']['Referer'] = $this->util->referer_from_url($url);
+
+ $this->dynamic_props->attempting_to_connect_to = $url;
+ do_action('wpmdb_before_remote_post');
+
+ $response = wp_remote_post($url, $args);
+
+ if (!is_wp_error($response)) {
+ // Every response should be scrambled, but other processes may have been applied too so we use a filter.
+ add_filter('wpmdb_after_response', array($this->scrambler, 'unscramble'));
+ $response['body'] = apply_filters('wpmdb_after_response', trim($response['body'], "\xef\xbb\xbf"));
+ remove_filter('wpmdb_after_response', array($this->scrambler, 'unscramble'));
+ }
+
+ $response_status = $this->handle_remote_post_responses($response, $url, $scope, $expecting_serial, $state_data);
+
+ if (true !== $response_status) {
+ return $this->handle_response_code($response_status, $response, $url, $data, $scope, $args, $expecting_serial);
+ }
+
+ return trim($response['body']);
+ }
+
+
+ public function handle_remote_post_responses($response, $url, $scope, $expecting_serial, $state_data = array())
+ {
+ if (is_wp_error($response)) {
+ return $this->handle_remote_post_error($response, $scope, $url);
+ }
+
+ if (
+ 200 > (int)$response['response']['code']
+ || 399 < (int)$response['response']['code']
+ ) {
+ return $this->handle_http_error_codes($response, $url, $scope);
+ }
+
+ if (empty($response['body'])) {
+ return $this->handle_empty_response_body($response, $url, $scope);
+ }
+
+ if (Util::is_json($response['body'])) {
+ $decoded_body = json_decode($response['body'], true);
+ } else {
+ $decoded_body =
+ [
+ 'success' => 1,
+ 'body' => $response['body'],
+ ];
+ }
+
+ if (!$decoded_body) {
+ return self::RESPONSE_REMOTE_ERROR;
+ }
+
+ //Handle connecting to an older version of the plugin
+ if (isset($decoded_body['error_id'], $decoded_body['message'])) {
+ if ($decoded_body['error_id'] === 'version_mismatch') {
+ $this->remote_error = str_replace('%%plugins_url%%', network_admin_url('plugins.php'), $decoded_body['message']);
+
+ return self::RESPONSE_VERSION_MISMATCH;
+ }
+
+ $this->remote_error = $decoded_body['message'];
+
+ return self::RESPONSE_REMOTE_ERROR;
+ }
+
+ // Pull migrations return straight up SQL
+ // ajax_verify_connection_to_remote_site returns array of all the remote's data
+ if (
+ !isset($decoded_body['success']) || !$decoded_body['success']
+ ) {
+ if ($scope === 'ajax_verify_connection_to_remote_site') {
+ if (!isset($decoded_body['tables'])) { // Successful response returns a bunch of data, including a 'tables' key
+ $this->remote_error = $decoded_body['data'];
+
+ return self::RESPONSE_REMOTE_ERROR;
+ }
+ } else { // Pull migrations return straight up SQL
+
+ $this->remote_error = $decoded_body['data'];
+
+ return self::RESPONSE_REMOTE_ERROR;
+ }
+ }
+
+ return true;
+ }
+
+ public function handle_response_code($code, $response, $url, $data, $scope, $args, $expecting_serial)
+ {
+ if (is_wp_error($response) && 'RESPONSE_RETRY_HTTPS' !== $code) {
+ return $response;
+ }
+
+ if (!is_wp_error($response)) {
+ $response_code = $response['response']['code'];
+ $message = $response['response']['message'];
+ }
+
+ switch ($code) {
+ case self::RESPONSE_RETRY_HTTPS:
+ return new \WP_Error(
+ 'wpmdb-remote-post-retry-https',
+ 'Error connecting over HTTPS #197'
+ );
+ case self::RESPONSE_BLOCKED_EXTERNAL:
+ $url_parts = Util::parse_url($url);
+ $host = $url_parts['host'];
+
+ return new \WP_Error(
+ 'wpmdb-remote-post-http-blocked-external',
+ sprintf(
+ __('We\'ve detected that WP_HTTP_BLOCK_EXTERNAL
is enabled and the host %1$s has not been added to WP_ACCESSIBLE_HOSTS
. Please disable WP_HTTP_BLOCK_EXTERNAL
or add %1$s to WP_ACCESSIBLE_HOSTS
to continue. More information. (#147 - scope: %3$s)', 'wp-migrate-db'),
+ esc_attr($host),
+ 'https://deliciousbrains.com/wp-migrate-db-pro/doc/wp_http_block_external/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin',
+ $scope
+ )
+ );
+
+ break;
+ case self::RESPONSE_UNEXPECTED_ERROR:
+ return new \WP_Error(
+ 'wpmdb-remote-post-unexpected_error',
+ sprintf(__('The connection failed, an unexpected error occurred, please contact support. (#121 - scope: %s)', 'wp-migrate-db'), $scope)
+ );
+ case self::RESPONSE_TIMED_OUT:
+ return new \WP_Error(
+ self::RESPONSE_TIMED_OUT,
+ sprintf(__('The connection to the remote server has timed out, no changes have been committed. (#134 - scope: %s)', 'wp-migrate-db'), $scope)
+ );
+ break;
+ case self::RESPONSE_NO_RESOLVE_HOST:
+ return $this->handle_unresolvable_host($url);
+ case self::RESPONSE_443_ERROR:
+ return new \WP_Error(
+ self::RESPONSE_443_ERROR,
+ sprintf(__('Couldn\'t connect over HTTPS. You might want to try regular HTTP instead. (#121 - scope: %s)', 'wp-migrate-db'), $scope)
+ );
+ case self::RESPONSE_SSL_ERROR:
+ return new \WP_Error(
+ self::RESPONSE_SSL_ERROR,
+ sprintf(
+ __('HTTPS Connection Error: (#121 - scope: %s) This typically means that the version of OpenSSL that your local site is using to connect to the remote is incompatible or, more likely, being rejected by the remote server because it\'s insecure. See our documentation for possible solutions.', 'wp-migrate-db'),
+ $scope,
+ 'https://deliciousbrains.com/wp-migrate-db-pro/doc/ssl-errors/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin'
+ )
+ );
+ case self::RESPONSE_401_ERROR:
+ return new \WP_Error(
+ self::RESPONSE_401_ERROR,
+ __('The remote site is protected with Basic Authentication. Please enter the username and password above to continue. (401 Unauthorized)', 'wp-migrate-db')
+ );
+ case self::RESPONSE_500_ERROR:
+ return new \WP_Error(
+ self::RESPONSE_500_ERROR,
+ sprintf(__('Unable to connect to the remote server, the remote server responded with: %s %s (scope: %s)', 'wp-migrate-db'), $response_code, $message, $scope)
+ );
+ case self::RESPONSE_STATUS_ERROR:
+ return new \WP_Error(
+ self::RESPONSE_STATUS_ERROR,
+ sprintf(__('Unable to connect to the remote server, please check the connection details - %1$s %2$s (#129 - scope: %3$s)', 'wp-migrate-db'), $response_code, $message, $scope)
+ );
+ case self::RESPONSE_MDB_INACTIVE:
+ return new \WP_Error(
+ self::RESPONSE_MDB_INACTIVE,
+ sprintf(__('WP Migrate does not seem to be installed or active on the remote site. (#131 - scope: %s)', 'wp-migrate-db'), $scope)
+ );
+ case self::RESPONSE_EMPTY_RESPONSE:
+ return new \WP_Error(
+ self::RESPONSE_EMPTY_RESPONSE,
+ sprintf(__('A response was expected from the remote, instead we got nothing. (#146 - scope: %1$s) Please review %2$s for possible solutions.', 'wp-migrate-db'), $scope, sprintf('%s', 'https://deliciousbrains.com/wp-migrate-db-pro/doc/a-response-was-expected-from-the-remote/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin', __('our documentation', 'wp-migrate-db')))
+ );
+ case self::RESPONSE_REMOTE_ERROR:
+ return new \WP_Error(
+ self::RESPONSE_REMOTE_ERROR,
+ $this->remote_error
+ );
+ case self::RESPONSE_VERSION_MISMATCH:
+ return new \WP_Error(
+ self::RESPONSE_VERSION_MISMATCH,
+ $this->remote_error
+ );
+ default:
+ ;
+ }
+
+ return false;
+ }
+
+ public function maybe_retry($url, $scope)
+ {
+ return 0 === strpos($url, 'https://') && 'ajax_verify_connection_to_remote_site' === $scope;
+ }
+
+ /**
+ * Handle HTTP errors from wp_remote_post()
+ *
+ * @param $response
+ * @param $scope
+ * @param $url
+ *
+ * @return bool|string
+ */
+ public function handle_remote_post_error($response, $scope, $url)
+ {
+ if (!is_wp_error($response)) {
+ return false;
+ }
+
+ if ($this->maybe_retry($url, $scope)) {
+ return self::RESPONSE_RETRY_HTTPS;
+ }
+
+ if ($this->handle_block_external($url)) {
+ return self::RESPONSE_BLOCKED_EXTERNAL;
+ }
+
+ if (!isset($response->errors['http_request_failed'][0])) {
+ return self::RESPONSE_UNEXPECTED_ERROR;
+ }
+
+ $resp = $response->errors['http_request_failed'][0];
+
+ switch ($resp) {
+ case strstr($resp, 'timed out'):
+ return self::RESPONSE_TIMED_OUT;
+ case strstr($resp, 'Could not resolve host'):
+ case strstr($resp, 'Couldn\'t resolve host'):
+ case strstr($resp, 'couldn\'t connect to host'):
+ return self::RESPONSE_NO_RESOLVE_HOST;
+ case strstr($resp, 'port 443: Connection refused'):
+ return self::RESPONSE_443_ERROR;
+ case strstr($resp, 'SSL'):
+ return self::RESPONSE_SSL_ERROR;
+ }
+
+ return false;
+ }
+
+ protected function handle_http_error_codes($response, $url, $scope)
+ {
+ switch ((int)$response['response']['code']) {
+ case 401:
+ return self::RESPONSE_401_ERROR;
+ //Explicitly do no retry http URL if remote returns 500 error
+ case 500:
+ return self::RESPONSE_500_ERROR;
+ case 0 === strpos($url, 'https://') && 'ajax_verify_connection_to_remote_site' === $scope:
+ return self::RESPONSE_RETRY_HTTPS;
+ }
+
+ return self::RESPONSE_STATUS_ERROR;
+ }
+
+ protected function handle_empty_response_body($response, $url, $scope)
+ {
+ if ('ajax_verify_connection_to_remote_site' === $scope && '0' === $response['body']) {
+ if (0 === strpos($url, 'https://')) {
+ return self::RESPONSE_RETRY_HTTPS;
+ }
+
+ return self::RESPONSE_MDB_INACTIVE;
+ }
+
+ return self::RESPONSE_EMPTY_RESPONSE;
+ }
+
+ /**
+ * Verify a remote response is valid
+ *
+ * @param mixed $response Response
+ *
+ * @return mixed Response if valid, error otherwise
+ */
+ public function verify_remote_post_response($response)
+ {
+ if (false === $response) {
+ $return = array('wpmdb_error' => 1, 'body' => $this->error_log->getError());
+ $error_msg = 'Failed attempting to verify remote post response (#114mf)';
+ $this->error_log->log_error($error_msg, $this->error_log->getError());
+ $result = $this->end_ajax(json_encode($return));
+
+ return $result;
+ }
+
+ if (is_wp_error($response)) {
+ return $this->end_ajax($response);
+ }
+
+ if ( ! Util::is_json($response)) {
+ $return = array('wpmdb_error' => 1, 'body' => $response);
+ $error_msg = 'Failed as the response is not serialized string (#115mf)';
+ $this->error_log->log_error($error_msg, $response);
+ $result = $this->end_ajax(json_encode($return));
+
+ return $result;
+ }
+
+ $response = json_decode($response, true);
+
+ if (isset($response['wpmdb_error'])) {
+ $this->error_log->log_error($response['wpmdb_error'], $response);
+ $result = $this->end_ajax(json_encode($response));
+
+ return $result;
+ }
+
+ return $response;
+ }
+
+ protected function handle_block_external($url)
+ {
+ if (\defined('WP_HTTP_BLOCK_EXTERNAL') && WP_HTTP_BLOCK_EXTERNAL) {
+ $url_parts = Util::parse_url($url);
+ $host = $url_parts['host'];
+ if (!\defined('WP_ACCESSIBLE_HOSTS') || (\defined('WP_ACCESSIBLE_HOSTS') && !\in_array($host, explode(',', WP_ACCESSIBLE_HOSTS)))) {
+ return true;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param $url
+ *
+ * @return \WP_Error
+ */
+ protected function handle_unresolvable_host($url)
+ {
+ $error = sprintf(__('We could not find: %s. Are you sure this is the correct URL?', 'wp-migrate-db'), $url);
+
+ $url_bits = Util::parse_url($url);
+
+ if (strstr($url, 'dev.') || strstr($url, '.dev') || !strstr($url_bits['host'], '.')) {
+ $error .= '
';
+ if ('pull' === $this->state_data['intent']) {
+ $error .= __('It appears that you might be trying to pull from a local environment. This will not work if this website happens to be located on a remote server, it would be impossible for this server to contact your local environment.', 'wp-migrate-db');
+ } else {
+ $error .= __('It appears that you might be trying to push to a local environment. This will not work if this website happens to be located on a remote server, it would be impossible for this server to contact your local environment.', 'wp-migrate-db');
+ }
+ }
+
+ return new \WP_Error(
+ 'wpmdb-remote-post-could-not-resolve-host',
+ $error
+ );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Scramble.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Scramble.php
new file mode 100644
index 000000000..153f36e84
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/Scramble.php
@@ -0,0 +1,70 @@
+ $val) {
+ $input[$key] = $this->unscramble($val, true);
+ }
+ } else {
+ $input = $this->unscramble($input, true);
+ }
+
+ // Re-encode just once when finished doing JSON.
+ if ( ! $doing_json) {
+ $input = json_encode($input);
+ }
+ } elseif (0 === strpos($input, 'WPMDB-SCRAMBLED')) {
+ // If the string begins with WPMDB-SCRAMBED we can unscramble.
+ // As the scrambled string could be multiple segments of scrambling (from stow) we remove indicators in one go.
+ $input = str_replace(array('WPMDB-SCRAMBLED', '%#047%', '%#092%'), array('', '/', '\\'), $input);
+ $input = str_rot13($input);
+ } elseif (false !== strpos($input, 'WPMDB-SCRAMBLED')) {
+ // Starts with non-scrambled data (error), but with scrambled string following.
+ $pos = strpos($input, 'WPMDB-SCRAMBLED');
+ $input = substr($input, 0, $pos) . $this->unscramble(substr($input, $pos), $doing_json);
+ }
+ }
+
+ return $input;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/WPMDBRestAPIServer.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/WPMDBRestAPIServer.php
new file mode 100644
index 000000000..2baab547d
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Http/WPMDBRestAPIServer.php
@@ -0,0 +1,40 @@
+props = $props;
+ }
+
+ public function permission_callback() {
+ $cap = is_multisite() ? 'manage_network_options' : 'export';
+ $cap = apply_filters( 'wpmdb_ajax_cap', $cap );
+
+ // Restrict endpoint to only users who have the edit_posts capability.
+ if ( ! current_user_can( $cap ) ) {
+ return new \WP_Error( 'rest_forbidden', esc_html__( 'Only authenticated users can access endpoint.', 'wp-migrate-db' ), [ 'status' => 401 ] );
+ }
+
+ return true;
+ }
+
+ public function registerRestRoute( $end_point, $args ) {
+ if ( ! isset( $args['permissions_callback'] ) ) {
+ $args['permission_callback'] = [ $this, 'permission_callback' ];
+ }
+
+ register_rest_route( $this->props->rest_api_base, $end_point, $args );
+ }
+}
+
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/MF/Manager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/MF/Manager.php
new file mode 100644
index 000000000..72c6e1354
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/MF/Manager.php
@@ -0,0 +1,34 @@
+get(MediaFilesAddon::class);
+ $media_files->register();
+ $media_files->set_licensed($licensed);
+
+ $container->get(MediaFilesLocal::class)->register();
+
+ add_filter('wpmdb_addon_registered_mf', '__return_true');
+
+ return $media_files;
+ }
+
+ public function get_license_response_key()
+ {
+ return 'wp-migrate-db-pro-media-files';
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/MF/MediaFilesAddon.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/MF/MediaFilesAddon.php
new file mode 100644
index 000000000..900e1793d
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/MF/MediaFilesAddon.php
@@ -0,0 +1,225 @@
+plugin_slug = $properties->plugin_slug;
+ $this->plugin_version = $properties->plugin_version;
+ $this->plugin_path = $properties->plugin_folder_name;
+
+ // @TODO see if this works
+ $this->util = $util;
+ $this->transfers_util = $transfers_util;
+ $this->filesystem = $filesystem;
+ }
+
+ public function register()
+ {
+
+ // Register Queue manager actions
+ WPMDBDI::getInstance()->get( Manager::class )->register();
+
+ add_action( 'wpmdb_load_assets', array($this, 'load_assets') );
+
+ add_filter( 'wpmdb_diagnostic_info', array($this, 'diagnostic_info') );
+ add_filter( 'wpmdb_establish_remote_connection_data', array($this, 'establish_remote_connection_data') );
+ add_filter( 'wpmdb_data', array($this, 'js_variables') );
+
+ add_action( 'wpmdb_migration_complete', array($this, 'cleanup_transfer_migration') );
+ add_filter( 'wpmdb_site_details', array($this, 'filter_site_details') );
+ }
+
+ /**
+ *
+ * Strings are used on the CLI
+ * Get translated strings for javascript and other functions
+ *
+ * @return array Array of translations
+ *
+ */
+ function get_strings()
+ {
+ $strings = array(
+ 'migrate_media_files_pull' => __( 'Downloading files', 'wp-migrate-db' ),
+ 'migrate_media_files_push' => __( 'Uploading files', 'wp-migrate-db' )
+ );
+
+ if ( is_null( $this->media_strings ) ) {
+ $this->media_strings = $strings;
+ }
+
+ return $this->media_strings;
+ }
+
+ /**
+ * Retrieve a specific translated string
+ *
+ * @param string $key Array key
+ *
+ * @return string Translation
+ */
+ function get_string( $key )
+ {
+ $strings = $this->get_strings();
+
+ return ( isset( $strings[$key] ) ) ? $strings[$key] : '';
+ }
+
+ /**
+ * Load media related assets in core plugin
+ */
+ function load_assets()
+ {
+
+ $version = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? time() : $this->plugin_version;
+ $public_url = plugins_url($this->plugin_path . "/frontend/public/");
+ $src = $public_url . 'noop.js' ;
+
+ wp_enqueue_script( 'wp-migrate-db-pro-media-files-script', $src, array(
+ 'jquery',
+ 'wp-migrate-db-pro-script-v2',
+ ), $version, true );
+
+ wp_localize_script( 'wp-migrate-db-pro-media-files-script', 'wpmdbmf_strings', $this->get_strings() );
+ wp_localize_script( 'wp-migrate-db-pro-media-files-script', 'wpmdbmf', [
+ 'enabled' => true,
+ ] );
+
+ if ($this->util->isMDBPage()) {
+ wp_enqueue_style('wp-migrate-db-pro-media-files-styles', $public_url . 'noop.css', array('wp-components'),
+ $version);
+ }
+ }
+
+ /**
+ * Check the remote site has the media addon setup
+ *
+ * @param array $data Connection data
+ *
+ * @return array Updated connection data
+ */
+ function establish_remote_connection_data( $data )
+ {
+ $data['media_files_available'] = '1';
+ $data['media_files_version'] = $this->plugin_version;
+ if ( function_exists( 'ini_get' ) ) {
+ $max_file_uploads = ini_get( 'max_file_uploads' );
+ }
+ $max_file_uploads = ( empty( $max_file_uploads ) ) ? 20 : $max_file_uploads;
+ $data['media_files_max_file_uploads'] = apply_filters( 'wpmdbmf_max_file_uploads', $max_file_uploads );
+
+ return $data;
+ }
+
+ /**
+ * Add media related javascript variables to the page
+ *
+ * @param array $data
+ *
+ * @return array
+ */
+ function js_variables( $data )
+ {
+ $data['media_files_version'] = $this->plugin_version;
+ $data['mf_is_licensed'] = $this->licensed ? '1' : '0';
+
+ return $data;
+ }
+
+ /**
+ * Adds extra information to the core plugin's diagnostic info
+ */
+ function diagnostic_info( $diagnostic_info )
+ {
+ $diagnostic_info['media-files'] = array(
+ "Media Uploads",
+ 'Transfer Bottleneck' => size_format( $this->transfers_util->get_transfer_bottleneck() ),
+ 'Upload Folder Permissions' => decoct( fileperms( $this->filesystem->get_wp_upload_dir() ) & 0777 ),
+ );
+
+ return $diagnostic_info;
+ }
+
+ public function cleanup_transfer_migration()
+ {
+ $uploads = \DeliciousBrains\WPMDB\Common\Transfers\Files\Util::get_wp_uploads_dir();
+
+ $this->transfers_util->remove_manifests( $uploads );
+ }
+
+ /**
+ * @param $site_details
+ *
+ * @return mixed
+ */
+ public function filter_site_details( $site_details )
+ {
+ if ( isset( $site_details['plugins'] ) ) {
+ return $site_details;
+ }
+
+ if ( array_key_exists( 'max_request', $site_details ) && array_key_exists( 'transfer_bottleneck', $site_details ) ) {
+ return $site_details;
+ }
+
+ $site_details['content_dir'] = $this->filesystem->slash_one_direction( WP_CONTENT_DIR );
+ $site_details['transfer_bottleneck'] = $this->transfers_util->get_transfer_bottleneck();
+ $site_details['max_request_size'] = $this->util->get_bottleneck();
+ $site_details['php_os'] = PHP_OS;
+
+ return $site_details;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/MF/MediaFilesLocal.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/MF/MediaFilesLocal.php
new file mode 100644
index 000000000..6a3d9cbd7
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/MF/MediaFilesLocal.php
@@ -0,0 +1,351 @@
+http = $http;
+ $this->util = $util;
+ $this->http_helper = $http_helper;
+ $this->rest_API_server = $rest_API_server;
+ $this->transfer_manager = $transfer_manager;
+ $this->transfer_util = $transfer_util;
+ $this->file_processor = $file_processor;
+ $this->queue_helper = $queue_helper;
+ $this->queue_manager = $queue_manager;
+ $this->plugin_helper = $plugin_helper;
+ $this->form_data = $form_data;
+ $this->profile_manager = $profile_manager;
+ }
+
+ public function register()
+ {
+ add_action('wpmdb_migration_complete', array($this, 'mf_migration_complete'));
+ add_action('wpmdb_respond_to_push_cancellation', array($this, 'remove_remote_tmp_files'));
+ add_action('wpmdb_cancellation', array($this, 'mf_migration_stopped_actions'));
+ add_action('wpmdb_finalize_migration', array($this, 'mf_update_profile'));
+ add_action(
+ 'wpmdb_finalize_key_rules',
+ function ($key_rules) {
+ $key_rules['profileID'] = 'int';
+ $key_rules['profileType'] = 'string';
+
+ return $key_rules;
+ }
+ );
+
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute(
+ '/mf-initiate-file-migration',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_initiate_media_file_migration'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/mf-get-queue-items',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_mf_get_queue_items'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/mf-transfer-files',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_mf_transfer_files'],
+ ]
+ );
+ }
+
+ /**
+ *
+ * @TODO Break this up into smaller, testable functions
+ * @return bool|null
+ */
+ public function ajax_initiate_media_file_migration()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $this->util->set_time_limit();
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'excludes' => 'json',
+ 'migration_state_id' => 'key',
+ 'folder' => 'string',
+ 'stage' => 'string',
+ 'date' => 'string',
+ 'timezone' => 'string',
+ 'is_cli_migration' => 'int'
+ );
+
+ $state_data = Persistence::setPostData($key_rules, __METHOD__);
+
+ $excludes = isset($state_data['excludes']) ? trim($state_data['excludes'], "\" \t\n\r\0\x0B") : [];
+
+ if (!is_array($excludes)) {
+ $excludes = preg_split('/\r\n|\r|\n/', stripcslashes($excludes));//stripcslashes() makes the $excludes string double quoted so we can use preg_split()
+ }
+
+ $excludes[] = 'wp-migrate-db';
+ $excludes = apply_filters('wpmdb_mf_excludes', $excludes, $state_data);
+
+ //Cleanup partial chunk files.
+ $this->transfer_util->cleanup_temp_chunks(WP_CONTENT_DIR . DIRECTORY_SEPARATOR, 'tmpchunk');
+
+ //Bottleneck files scanning
+ if (empty($state_data['is_cli_migration'])) {
+ Files_Util::enable_scandir_bottleneck();
+ }
+
+ //State data populated
+ $folder = $state_data['folder'];
+ $date = isset($state_data['date']) ? $state_data['date'] : null;
+ $timezone = !empty($state_data['timezone']) ? $state_data['timezone'] : 'UTC';
+
+ if (empty($folder)) {
+ return $this->transfer_util->ajax_error(__('Invalid folder path supplied.', 'wp-migrate-db'));
+ }
+
+ if ('pull' === $state_data['intent']) {
+ // Set up local meta data
+ $folder = apply_filters('wpmdb_mf_remote_uploads_source_folder', $folder, $state_data);
+ $file_list = $this->transfer_util->get_remote_files([$folder], 'wpmdbmf_respond_to_get_remote_media', $excludes, $date, $timezone);
+ } else {
+ // Push = get local files
+ $abs_path = Files_Util::get_wp_uploads_dir();
+ $abs_path = apply_filters('wpmdb_mf_local_uploads_folder', $abs_path, $state_data);
+ $file_list = $this->file_processor->get_local_files([$abs_path], $abs_path, $excludes, $state_data['stage'], $date, $timezone,'push');
+ }
+
+ if (is_wp_error($file_list)) {
+ return $file_list;
+ }
+
+ $queue_status = $this->queue_helper->populate_queue($file_list, $state_data['intent'], $state_data['stage'], $state_data['migration_state_id']);
+ set_site_transient('wpmdb_queue_status', $queue_status);
+
+ if (isset($file_list['meta']['scan_completed'])) {
+ if (true === $file_list['meta']['scan_completed']) {
+ return $this->http->end_ajax(['queue_status' => $queue_status]);
+ }
+ return $this->http->end_ajax(
+ [
+ 'recursive_queue' => true,
+ 'items_count' => $queue_status['total']
+ ]);
+ }
+
+ return $this->http->end_ajax(['queue_status' => $queue_status]);
+ }
+
+ /**
+ * Get queue items in batches to populate the UI
+ *
+ * @return mixed|null
+ */
+ public function ajax_mf_get_queue_items()
+ {
+ return $this->queue_helper->get_queue_items();
+ }
+
+ /**
+ * @return null
+ */
+ public function ajax_mf_transfer_files()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $this->util->set_time_limit();
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'stage' => 'string',
+ 'offset' => 'numeric',
+ 'folder' => 'string',
+ 'migration_state_id' => 'key',
+ 'payloadSize' => 'numeric',
+ 'stabilizePayloadSize' => 'bool',
+ 'stepDownSize' => 'bool',
+ 'nonce' => 'key',
+ 'retries' => 'numeric',
+ 'forceHighPerformanceTransfers' => 'bool',
+ );
+
+ $state_data = Persistence::setPostData($key_rules, __METHOD__);
+
+ $count = apply_filters('wpmdbmf_file_batch_size', 1000);
+ $data = $this->queue_manager->list_jobs($count);
+
+ if (is_wp_error($data)) {
+ return $this->http->end_ajax($data);
+ }
+
+ $processed = $this->transfer_util->process_file_data($data);
+
+ if (empty($data)) {
+ do_action('wpmdbmf_file_transfer_complete');
+
+ // Clear out queue in case there is a next step
+ $this->queue_manager->truncate_queue();
+
+ return $this->http->end_ajax(['status' => 'complete']);
+ }
+
+ $remote_url = $state_data['intent'] === 'savefile' ? null : $state_data['url'];
+ $processed = $this->transfer_manager->manage_file_transfer($remote_url, $processed, $state_data);
+
+ $result = [
+ 'status' => $processed,
+ ];
+
+ if (isset($processed['error'], $processed['message']) && true === $processed['error']) {
+ $result = new \WP_Error(400, $processed['message']);
+ }
+
+ //Client should check error status for files and if a 500 is encountered kill the migration stage
+ return $this->http->end_ajax($result);
+ }
+
+ public function mf_migration_complete()
+ {
+ $this->mf_migration_stopped_actions();
+ }
+
+ public function mf_migration_stopped_actions()
+ {
+ $stages = $this->form_data->getMigrationStages();
+
+ if (is_array($stages) && in_array('media_files', $stages, true)) {
+ $this->plugin_helper->cleanup_transfer_migration('media_files');
+ }
+ }
+
+ public function remove_remote_tmp_files()
+ {
+ $stages = $this->form_data->getMigrationStages();
+
+ if (in_array('media_files', $stages)) {
+ $this->plugin_helper->remove_tmp_files('media_files', 'remote');
+ }
+ }
+
+ public function mf_update_profile($state_data)
+ {
+ if (!isset($state_data['profileID'], $state_data['profileType'])) {
+ return;
+ }
+
+ $profileID = $state_data['profileID'];
+ $option = $state_data['profileType'];
+
+ if (empty($option)) {
+ return;
+ }
+
+ $profile = $this->profile_manager->get_profile_by_id($option, $profileID);
+ $profile_data = json_decode($profile["value"]);
+
+ if (!property_exists($profile_data, 'media_files')) {
+ return;
+ }
+
+ $datetime = new \DateTime();
+ $newdate = $datetime->format(\DateTime::ATOM);
+
+ $profile_data->media_files->last_migration = $newdate;
+
+ $profile_type = $option === 'unsaved' ? 'wpmdb_recent_migrations' : 'wpmdb_saved_profiles';
+ $saved_profiles = get_site_option($profile_type);
+
+ if (isset($saved_profiles[$profileID])) {
+ $saved_profiles[$profileID]["value"] = json_encode($profile_data);
+ update_site_option($profile_type, $saved_profiles);
+ }
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/FinalizeMigration.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/FinalizeMigration.php
new file mode 100644
index 000000000..c18d9374e
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/FinalizeMigration.php
@@ -0,0 +1,283 @@
+migration_state_manager = $migration_state_manager;
+ $this->table = $table;
+ $this->http = $http;
+ $this->props = $properties;
+ $this->table_helper = $table_helper;
+ $this->http_helper = $http_helper;
+ $this->util = $util;
+ $this->remote_post = $remote_post;
+ $this->form_data = $form_data;
+ $this->migration_helper = $migration_helper;
+ }
+
+ /**
+ * After table migration, delete old tables and rename new tables removing the temporarily prefix.
+ *
+ * @return mixed
+ */
+ function ajax_finalize_migration()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'prefix' => 'string',
+ 'tables' => 'string',
+ 'nonce' => 'key',
+ );
+
+ $key_rules = apply_filters('wpmdb_finalize_key_rules', $key_rules);
+
+ //@todo - revisit
+ $migration_state = get_site_option('wpmdb_migration_state');
+
+ if (!isset($migration_state['site_details'])) {
+ return $this->http->end_ajax(new \WP_Error('wpmdb_finalize_failed', __('Unable to finalize the migration, migration state empty.')));
+ }
+
+ $state_data = Persistence::setPostData($key_rules, __METHOD__);
+ $state_data['site_details'] = $migration_state['site_details'];
+
+ if ('savefile' === $state_data['intent']) {
+ return true;
+ }
+
+ global $wpdb;
+
+ do_action('wpmdb_finalize_migration', $state_data); // Fires on local site
+
+ if ('push' === $state_data['intent']) {
+ do_action('wpmdb_migration_complete', 'push', $state_data['url']);
+ $data = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'url',
+ 'form_data',
+ 'site_details',
+ 'tables',
+ 'temp_prefix',
+ )
+ );
+ $data['form_data'] = base64_encode($data['form_data']);
+ $data['site_details'] = base64_encode(json_encode($data['site_details']));
+
+ $data['action'] = 'wpmdb_remote_finalize_migration';
+ $data['intent'] = 'pull';
+ $data['prefix'] = $wpdb->base_prefix;
+ $data['type'] = 'push';
+ $data['location'] = Util::home_url();
+ $data['sig'] = $this->http_helper->create_signature($data, $state_data['key']);
+ $data['stage'] = $state_data['stage'];
+ $ajax_url = $this->util->ajax_url();
+ $response = $this->remote_post->post($ajax_url, $data, __FUNCTION__);
+ $return = HandleRemotePostError::handle('wpmdb-remote-finalize-failed', $response);
+ } else {
+ $return = $this->finalize_migration($state_data);
+ }
+ do_action('wpmdb_after_finalize_migration');
+ $result = $this->http->end_ajax($return);
+
+ return $result;
+ }
+
+ /**
+ * Internal function for finalizing a migration.
+ *
+ * @return bool|null
+ */
+ function finalize_migration($state_data = false)
+ {
+ $state_data = !$state_data ? Persistence::getStateData() : $state_data;
+ if ( in_array($state_data['intent'], ['push', 'pull'])) {
+ $intent_type = isset($state_data['type']) ? $state_data['type'] : $state_data['intent'];
+ $state_data['destination_prefix'] = ('push' === $intent_type) ? $state_data['site_details']['remote']['prefix'] : $state_data['site_details']['local']['prefix'];
+ $state_data['source_prefix'] = ('push' === $intent_type) ? $state_data['site_details']['local']['prefix'] : $state_data['site_details']['remote']['prefix'];
+ }
+
+ $temp_prefix = isset($state_data['temp_prefix']) ? $state_data['temp_prefix'] : $this->props->temp_prefix;
+ $temp_tables = array();
+ $type = $state_data['intent'];
+ $alter_table_name = $this->table->get_alter_table_name();
+ $before_finalize_response = apply_filters('wpmdb_before_finalize_migration', true, $this);
+
+ if (is_wp_error($before_finalize_response)) {
+ return $this->http->end_ajax($before_finalize_response);
+ }
+
+ if (isset($state_data['type']) && 'push' === $state_data['type']) {
+ $type = 'push';
+ }
+
+ $tables = $this->get_tables($state_data);
+
+ if ('find_replace' === $state_data['intent'] || 'import' === $state_data['intent']) {
+ $location = Util::home_url();
+ } else {
+ $location = (isset($state_data['location'])) ? $state_data['location'] : $state_data['url'];
+ }
+
+ if ('import' === $state_data['intent']) {
+ $temp_tables = $this->table->get_tables('temp');
+ $tables = array();
+
+ foreach ($temp_tables as $key => $temp_table) {
+ if ($alter_table_name === $temp_table) {
+ unset($temp_tables[$key]);
+ continue;
+ }
+
+ $tables[] = substr($temp_table, strlen($temp_prefix));
+ }
+ } else {
+ foreach ($tables as $table) {
+ $temp_tables[] = $temp_prefix . apply_filters(
+ 'wpmdb_finalize_target_table_name',
+ $table,
+ $state_data
+ );
+ }
+ }
+
+ $sql = "SET FOREIGN_KEY_CHECKS=0;\n";
+
+ $sql .= $this->table->get_preserved_options_queries($state_data, $temp_tables, $type);
+
+ foreach ($temp_tables as $table) {
+ $sql .= 'DROP TABLE IF EXISTS ' . $this->table_helper->backquote(substr($table, strlen($temp_prefix))) . ';';
+ $sql .= "\n";
+ $sql .= 'RENAME TABLE ' . $this->table_helper->backquote($table) . ' TO ' . $this->table_helper->backquote(substr($table, strlen($temp_prefix))) . ';';
+ $sql .= "\n";
+ }
+
+ $sql .= $this->table->get_alter_queries($state_data);
+ $sql .= 'DROP TABLE IF EXISTS ' . $this->table_helper->backquote($alter_table_name) . ";\n";
+
+ $process_chunk_result = $this->table->process_chunk($sql);
+ if (true !== $process_chunk_result) {
+ $result = $this->http->end_ajax($process_chunk_result);
+
+ return $result;
+ }
+
+ if (!isset($state_data['location']) && !in_array($state_data['intent'], array('find_replace', 'import'))) {
+ $data = array();
+ $data['action'] = 'wpmdb_fire_migration_complete';
+ $data['url'] = Util::home_url();
+ $data['sig'] = $this->http_helper->create_signature($data, $state_data['key']);
+ $ajax_url = $this->util->ajax_url();
+ $response = $this->remote_post->post($ajax_url, $data, __FUNCTION__);
+
+ $this->util->display_errors();
+ $decoded_response = json_decode($response, true);
+
+ if (!isset($decoded_response['success']) || !$decoded_response['success']) {
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-remote-finalize-failed',
+ $response
+ )
+ );
+ }
+ }
+
+ do_action('wpmdb_migration_complete', $type, $location);
+
+ return true;
+ }
+
+ /**
+ * Convert string of table names to array, changes prefix if needed.
+ *
+ * @param array $state_data
+ *
+ * @return array of tables
+ *
+ **/
+ private function get_tables($state_data)
+ {
+ if ($state_data['tables'] === '') {
+ return [];
+ }
+ $source_tables = is_string($state_data['tables']) ? explode(',', $state_data['tables']) : $state_data['tables'];
+ $source_prefix = $state_data['source_prefix'];
+ $destination_prefix = $state_data['destination_prefix'];
+ if ($source_prefix === $destination_prefix || (isset($state_data['mst_select_subsite']) && '1' === $state_data['mst_select_subsite'])) {
+ return $source_tables;
+ }
+ return Util::change_tables_prefix($source_tables, $source_prefix, $destination_prefix);
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/Flush.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/Flush.php
new file mode 100644
index 000000000..53835f403
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/Flush.php
@@ -0,0 +1,108 @@
+http_helper = $helper;
+ $this->util = $util;
+ $this->remote_post = $remote_post;
+ $this->http = $http;
+ }
+
+ public function register()
+ {
+ add_action('wp_ajax_nopriv_wpmdb_flush', array($this, 'ajax_nopriv_flush',));
+ add_action('wp_ajax_wpmdb_flush', array($this, 'ajax_flush'));
+ }
+
+ /**
+ * Handles the request to flush caches and cleanup migration when pushing or not migrating user tables.
+ *
+ * @return bool|null
+ */
+ function ajax_flush()
+ {
+ $this->http->check_ajax_referer('flush');
+
+ return $this->ajax_nopriv_flush();
+ }
+
+ /**
+ * Handles the request to flush caches and cleanup migration when pulling with user tables being migrated.
+ *
+ * @return bool|null
+ */
+ function ajax_nopriv_flush()
+ {
+ $state_data = Persistence::getStateData();
+
+ if ('push' === $state_data['intent']) {
+ $data = array();
+ $data['action'] = 'wpmdb_remote_flush';
+ $data['sig'] = $this->http_helper->create_signature($data, $state_data['key']);
+ $ajax_url = $this->util->ajax_url();
+ $response = $this->remote_post->post($ajax_url, $data, __FUNCTION__);
+ $return = HandleRemotePostError::handle('wpmdb-remote-flush-failed', $response);
+ } else {
+ $return = $this->flush();
+ }
+
+ Persistence::cleanupStateOptions();
+
+ $result = $this->http->end_ajax($return);
+
+ return $result;
+ }
+
+ /**
+ * Flushes the cache and rewrite rules.
+ *
+ * @return bool
+ */
+ function flush()
+ {
+ // flush rewrite rules to prevent 404s and other oddities
+ wp_cache_flush();
+ global $wp_rewrite;
+ $endpoints = $wp_rewrite->endpoints;
+ $wp_rewrite->init();
+ $wp_rewrite->endpoints = $endpoints;
+ flush_rewrite_rules(true); // true = hard refresh, recreates the .htaccess file
+
+ return true;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/InitiateMigration.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/InitiateMigration.php
new file mode 100644
index 000000000..58fb9d65d
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/InitiateMigration.php
@@ -0,0 +1,360 @@
+migration_state_manager = $migration_state_manager;
+ $this->table = $table;
+ $this->http = $http;
+ $this->props = $properties;
+ $this->http_helper = $http_helper;
+ $this->util = $util;
+ $this->remote_post = $remote_post;
+ $this->form_data = $form_data;
+ $this->migration_options = $form_data->getFormData();
+ $this->filesystem = $filesystem;
+ $this->error_log = $error_log;
+ $this->migration_state = $migration_state;
+ $this->migration_helper = $migration_helper;
+ $this->backup_export = $backup_export;
+ $this->full_site_export = $full_site_export;
+ }
+
+ /**
+ * Occurs right before the first table is migrated / backed up during the migration process.
+ *
+ * @return string
+ *
+ * Does a quick check to make sure the verification string is valid and also opens / creates files for writing to (if required).
+ */
+ public function ajax_initiate_migration()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ global $wpdb;
+
+ $key_rules = apply_filters(
+ 'wpmdb_initiate_key_rules',
+ array(
+ 'action' => 'key',
+ 'intent' => 'key',
+ 'url' => 'url',
+ 'key' => 'string',
+ 'form_data' => 'json',
+ 'stage' => 'key',
+ 'stages' => 'json',
+ 'nonce' => 'key',
+ 'temp_prefix' => 'string',
+ 'site_details' => 'json_array',
+ 'export_dest' => 'string',
+ 'import_info' => 'array',
+ ),
+ __FUNCTION__
+ );
+
+ Persistence::cleanupStateOptions();
+
+ // Save initial migration state as an option
+ // Save migration options (form_data)
+
+ $state_data = Persistence::setPostData($key_rules, __METHOD__);
+
+ // This only needs to be called once per request cycle
+ // @see FormData::getFormData();
+ $this->migration_options = $this->form_data->parse_and_save_migration_form_data($state_data['form_data']);
+
+ update_site_option('wpmdb_usage', array('action' => $state_data['intent'], 'time' => time()));
+
+ // A little bit of house keeping.
+ MigrationState::cleanup();
+
+ if (in_array($state_data['intent'], array('find_replace', 'savefile', 'import'))) {
+ $return = $this->initiateLocalMigration($state_data);
+ } else { // does one last check that our verification string is valid
+ $return = $this->initiatePushOrPull($state_data);
+ }
+
+ if (is_wp_error($return)) {
+ return $this->http->end_ajax($return);
+ }
+
+ $return['dump_filename'] = (empty($return['dump_filename'])) ? '' : $return['dump_filename'];
+ $return['dump_url'] = (empty($return['dump_url'])) ? '' : $return['dump_url'];
+
+ // A successful call to wpmdb_remote_initiate_migration for a Push migration will have set db_version.
+ // Otherwise ensure it is set with own db_version so that we always return one.
+ $return['db_version'] = (empty($return['db_version'])) ? $wpdb->db_version() : $return['db_version'];
+
+ // A successful call to wpmdb_remote_initiate_migration for a Push migration will have set site_url.
+ // Otherwise ensure it is set with own site_url so that we always return one.
+ $return['site_url'] = (empty($return['site_url'])) ? site_url() : $return['site_url'];
+
+ $return['find_replace_pairs'] = Replace::parse_find_replace_pairs($state_data['intent'], $return['site_url']);
+ $return['source_prefix'] = ('pull' === $state_data['intent']) ? $state_data['site_details']['remote']['prefix'] : $state_data['site_details']['local']['prefix'];
+ $return['destination_prefix'] = ('push' === $state_data['intent']) ? $state_data['site_details']['remote']['prefix'] : $state_data['site_details']['local']['prefix'];
+ // Store current migration state.
+ $state = array_merge($state_data, $return);
+ Persistence::saveStateData($state);
+
+ do_action('wpmdb_initiate_migration', $state_data);
+
+ return $this->http->end_ajax($state);
+ }
+
+ /**
+ * @param array $state_data
+ *
+ * @return array
+ */
+ protected function initiatePushOrPull(
+ array $state_data
+ ) {
+ $return = [];
+ $data = [
+ 'action' => 'wpmdb_remote_initiate_migration',
+ 'intent' => $state_data['intent'],
+ 'form_data' => base64_encode( $state_data['form_data'] ),
+ 'site_details' => base64_encode( json_encode( $this->migration_helper->getMergedSiteDetails($state_data) ) ),
+ ];
+
+ $data['sig'] = $this->http_helper->create_signature($data, $state_data['key']);
+ $data['site_details'] = addslashes($data['site_details']);
+ $data = apply_filters( 'wpmdb_initiate_push_pull_post', $data, $state_data );
+
+ $ajax_url = $this->util->ajax_url();
+ $response = $this->remote_post->post($ajax_url, $data, __FUNCTION__);
+
+ // WP_Error is thrown manually by remote_post() to tell us something went wrong
+ if (is_wp_error($response)) {
+ return $this->http->end_ajax(
+ $response
+ );
+ }
+
+ $decoded_response = json_decode($response, true);
+
+ if (false === $response || !$decoded_response['success']) {
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-initiate-migration-failed',
+ $decoded_response['data']
+ )
+ );
+ }
+
+ if ('pull' === $state_data['intent']) {
+ // sets up our table to store 'ALTER' queries
+ $create_alter_table_query = $this->table->get_create_alter_table_query();
+ $process_chunk_result = $this->table->process_chunk($create_alter_table_query);
+ if (true !== $process_chunk_result) {
+ return $this->http->end_ajax($process_chunk_result);
+ }
+
+ if ('none' !== $this->migration_options['current_migration']['backup_option']) {
+ list($dump_filename, $dump_url) = $this->backup_export->setup_backups();
+ $return['dump_filename'] = $dump_filename;
+ $return['dump_url'] = $dump_url;
+ }
+ } else {
+ // Remote DB version only matters for pushes, otherwise, we just use local db version.
+ if (isset($decoded_response['data'], $decoded_response['data']['db_version'])) {
+ $return['db_version'] = $decoded_response['data']['db_version'];
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ *
+ * Local migrations: (savefile (export), find-replace, import, backup
+ *
+ * @param $state_data
+ *
+ * @return array
+ */
+ protected function initiateLocalMigration($state_data)
+ {
+ $return = array(
+ 'code' => 200,
+ 'message' => 'OK',
+ 'body' => json_encode(array('error' => 0)),
+ );
+
+ if ('import' === $state_data['intent']) {
+ $return['import_path'] = $this->table->get_sql_dump_info('import', 'path');
+ $return['import_filename'] = wp_basename($return['import_path'], '.sql');
+
+ if (Util::gzip() && isset($state_data['import_info']['import_gzipped']) && 'true' === $state_data['import_info']['import_gzipped']) {
+ $return['import_path'] .= '.gz';
+ }
+
+ $this->table->delete_temporary_tables($this->props->temp_prefix);
+ }
+
+ // Backups and exports require special handling
+ if (in_array($state_data['stage'], array('backup', 'migrate'))) {
+ $is_full_site_export = isset($state_data['stages']) ? json_decode($state_data['stages']) !== ['tables']: false;
+ $migration_type = $is_full_site_export ? 'export' : $state_data['stage'];
+ $return['full_site_export'] = $is_full_site_export;
+ $return['dump_path'] = $this->table->get_sql_dump_info($migration_type, 'path');
+ $return['dump_filename'] = wp_basename($return['dump_path']);
+ $return['dump_url'] = $this->table->get_sql_dump_info($migration_type, 'url');
+ $dump_filename_no_extension = substr($return['dump_filename'], 0, -4);
+
+ if ($is_full_site_export) {
+ $return['export_path'] = substr($return['dump_path'], 0, -4) . '.zip';
+ $return['export_url'] = substr($return['dump_url'], 0 , -4) . '.zip';
+ $return['export_dir_name'] = $dump_filename_no_extension;
+ //might need a export directory here.
+ }
+ // sets up our table to store 'ALTER' queries
+ $create_alter_table_query = $this->table->get_create_alter_table_query();
+ $process_chunk_result = $this->table->process_chunk($create_alter_table_query);
+
+ if (true !== $process_chunk_result) {
+ return $this->http->end_ajax($process_chunk_result);
+ }
+
+ // 'savefile' === 'export'
+ if ('savefile' === $state_data['intent']) {
+ if (
+ isset($this->migration_options['gzip_file'])
+ && $this->migration_options['gzip_file'] === '1'
+ && Util::gzip()
+ && !$is_full_site_export
+ ) {
+ $return['dump_path'] .= '.gz';
+ $return['dump_filename'] .= '.gz';
+ $return['dump_url'] .= '.gz';
+ }
+
+ $upload_path = $this->filesystem->get_upload_info('path');
+
+ if (false === $this->filesystem->is_writable($upload_path)) {
+ $error = sprintf(__('Export Failed — We can\'t save your export to the following folder:
%s
Please adjust the permissions on this folder. See our documentation for more information »
', 'wp-migrate-db'), $upload_path, 'https://deliciousbrains.com/wp-migrate-db-pro/doc/uploads-folder-permissions/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin');
+
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-export-failed',
+ $error
+ )
+ );
+ }
+ if ($is_full_site_export) {
+ $create_zip = $this->full_site_export->create_export_zip($upload_path . DIRECTORY_SEPARATOR . $return['export_dir_name'] . '.zip' , $state_data);
+
+ if (is_wp_error($create_zip)) {
+ return $this->http->end_ajax($create_zip);
+ }
+ }
+
+ $destination = !$is_full_site_export ? $return['dump_filename'] : $return['export_dir_name'] . DIRECTORY_SEPARATOR . $return['dump_filename'];
+ $fp = $this->filesystem->open($upload_path . DIRECTORY_SEPARATOR . $return['dump_filename'], 'a', $is_full_site_export);
+ $this->table->db_backup_header($fp);
+ $this->filesystem->close($fp, $is_full_site_export);
+ }
+ $return['dump_filename'] = $dump_filename_no_extension;
+ }
+
+ return $return;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/MigrationHelper.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/MigrationHelper.php
new file mode 100644
index 000000000..e25d7f5be
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/MigrationHelper.php
@@ -0,0 +1,143 @@
+multisite = $multisite;
+ $this->util = $util;
+ $this->tables = $tables;
+ $this->filesystem = $filesystem;
+ $this->props = $props;
+ $this->settings = $settings->get_settings();
+ $this->assets = $assets;
+ }
+
+ /**
+ * Merge local and remote site details
+ * @param Array $state_dat
+ * @return array
+ **/
+ public function getMergedSiteDetails($state_data)
+ {
+ $local = $this->util->site_details($state_data);
+ $remote_info = get_site_option('wpmdb_remote_response');
+ $remote = ! empty($remote_info) ? $remote_info['site_details'] : '';
+
+ return [
+ 'local' => $local,
+ 'remote' => $remote,
+ ];
+ }
+
+ public function siteDetails()
+ {
+ $site_details = $this->util->site_details();
+ $url = esc_html(addslashes(Util::home_url()));
+
+ return [
+ 'connection_info' => array(site_url('', 'https'), $this->settings['key']),
+ 'this_url' => $url,
+ 'this_path' => esc_html(addslashes(Util::get_absolute_root_file_path())),
+ 'this_domain' => esc_html($this->multisite->get_domain_current_site()),
+ 'this_tables' => $this->tables->get_tables(),
+ 'this_prefixed_tables' => $this->tables->get_tables('prefix'),
+ 'this_table_sizes' => $this->tables->get_table_sizes(),
+ 'this_table_sizes_hr' => array_map(array($this->tables, 'format_table_sizes'),
+ $this->tables->get_table_sizes()),
+ 'this_table_rows' => $this->tables->get_table_row_count(),
+ 'this_upload_url' => esc_html(addslashes(trailingslashit($this->filesystem->get_upload_info('url')))),
+ 'this_upload_dir_long' => esc_html(addslashes(trailingslashit($this->filesystem->get_upload_info('path')))),
+ 'this_wp_upload_dir' => $this->filesystem->get_wp_upload_dir(),
+ 'this_uploads_dir' => $site_details['uploads_dir'], // TODO: Remove backwards compatibility.
+ 'this_plugin_url' => trailingslashit(plugins_url($this->props->plugin_folder_name)),
+ 'this_website_name' => sanitize_title_with_dashes(DB_NAME),
+ 'this_download_url' => network_admin_url($this->props->plugin_base . '&download='),
+ 'this_prefix' => $site_details['prefix'], // TODO: Remove backwards compatibility.
+ 'this_temp_prefix' => $this->props->temp_prefix,
+ 'this_plugin_base' => esc_html($this->props->plugin_base),
+ 'this_post_types' => $this->tables->get_post_types(),
+ 'url' => $url,
+ 'is_multisite' => $site_details['is_multisite'], // TODO: Remove backwards compatibility.
+ 'openssl_available' => esc_html($this->util->open_ssl_enabled() ? 'true' : 'false'),
+ 'max_request' => esc_html($this->settings['max_request']),
+ 'delay_between_requests' => esc_html($this->settings['delay_between_requests']),
+ 'prog_tables_hidden' => ( bool )$this->settings['prog_tables_hidden'],
+ 'pause_before_finalize' => ( bool )$this->settings['pause_before_finalize'],
+ 'bottleneck' => esc_html($this->util->get_bottleneck('max')),
+ // TODO: Use WP_Filesystem API.
+ 'write_permissions' => esc_html(is_writable($this->filesystem->get_upload_info('path')) ? 'true' : 'false'),
+ 'themes_permissions' => is_writeable(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'themes') ? 'true' : 'false',
+ 'plugins_permissions' => is_writeable(WP_PLUGIN_DIR) ? 'true' : 'false',
+ 'muplugins_permissions' => is_writeable(WPMU_PLUGIN_DIR) ? 'true' : 'false',
+ 'others_permissions' => is_writeable(WP_CONTENT_DIR) ? 'true' : 'false',
+ 'firewall_plugins' => $site_details['firewall_plugins'],
+ 'profile' => isset($_GET['wpmdb-profile']) ? $_GET['wpmdb-profile'] : '-1',
+ 'is_pro' => esc_html($this->props->is_pro ? 'true' : 'false'),
+ 'lower_case_table_names' => esc_html($this->tables->get_lower_case_table_names_setting()),
+ 'subsites' => $site_details['subsites'], // TODO: Remove backwards compatibility.
+ 'site_details' => $site_details,
+ 'alter_table_name' => $this->tables->get_alter_table_name(),
+ 'allow_tracking' => $this->settings['allow_tracking'],
+ 'MDB_API_BASE' => $this->util->rest_url(),
+ 'diagnostic_log_download_url' => network_admin_url($this->props->plugin_base . '&nonce=' . Util::create_nonce('wpmdb-download-log') . '&wpmdb-download-log=1'),
+ 'migration_profiles' => $this->assets->get_saved_migration_profiles(),
+ 'recent_migrations' => $this->assets->get_recent_migrations(get_site_option('wpmdb_recent_migrations')),
+ 'mst_available' => Util::isPro() && Util::is_addon_registered('mst'),
+ 'tpf_available' => Util::is_addon_registered('tpf'),
+ 'mf_available' => Util::is_addon_registered('mf'),
+ 'mst_required_message_push' => $this->multisite->mst_required_message('push'),
+ 'mst_required_message_pull' => $this->multisite->mst_required_message('pull'),
+ 'time_format' => get_option('time_format'),
+ 'theoreticalTransferBottleneck' => apply_filters('wpmdb_theoretical_transfer_bottleneck', 0)
+ ];
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/MigrationManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/MigrationManager.php
new file mode 100644
index 000000000..1da49f9b8
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Migration/MigrationManager.php
@@ -0,0 +1,566 @@
+migration_state_manager = $migration_state_manager;
+ $this->table = $table;
+ $this->http = $http;
+ $this->props = $properties;
+ $this->table_helper = $table_helper;
+ $this->http_helper = $http_helper;
+ $this->util = $util;
+ $this->remote_post = $remote_post;
+ $this->filesystem = $filesystem;
+ $this->error_log = $error_log;
+ $this->migration_state = $migration_state;
+ $this->backup_export = $backup_export;
+ $this->multisite = $multisite;
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->form_data = $form_data;
+ $this->form_data_arr = $form_data->getFormData();
+ $this->initiate_migration = $initiate_migration;
+ $this->finalize_migration = $finalize_migration;
+ $this->rest_API_server = $rest_API_server;
+ $this->migration_helper = $migration_helper;
+ $this->full_site_export = $full_site_export;
+ }
+
+ public function register()
+ {
+ // REST endpoints
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ add_action('wp_ajax_wpmdb_migrate_table', array($this, 'ajax_migrate_table'));
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute('/initiate-migration', [
+ 'methods' => 'POST',
+ 'callback' => [$this->initiate_migration, 'ajax_initiate_migration'],
+ ]);
+
+ $this->rest_API_server->registerRestRoute('/finalize-migration', [
+ 'methods' => 'POST',
+ 'callback' => [$this->finalize_migration, 'ajax_finalize_migration'],
+ ]);
+
+ $this->rest_API_server->registerRestRoute('/cancel-migration', [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_cancel_migration'],
+ ]);
+
+ $this->rest_API_server->registerRestRoute('/error-migration', [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'error_migration'],
+ ]);
+ }
+
+ /**
+ * Called for each database table to be migrated.
+ *
+ * @return string
+ */
+ function ajax_migrate_table()
+ {
+ $this->http->check_ajax_referer('migrate-table');
+ // This *might* be set to a file pointer below
+ // @TODO using a global file pointer is extremely error prone and not a great idea
+ $fp = null;
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'migration_state_id' => 'key',
+ 'table' => 'string',
+ 'stage' => 'key',
+ 'current_row' => 'numeric',
+ 'form_data' => 'json',
+ 'last_table' => 'positive_int',
+ 'primary_keys' => 'json',
+ 'gzip' => 'int',
+ 'nonce' => 'key',
+ 'bottleneck' => 'positive_int',
+ 'prefix' => 'string',
+ 'path_current_site' => 'string',
+ 'domain_current_site' => 'text',
+ 'import_info' => 'array',
+ );
+
+ if (!Util::is_json($_POST['form_data'])) {
+ $_POST['form_data'] = stripslashes($_POST['form_data']);
+ }
+ if (!Util::is_json($_POST['primary_keys'])) {
+ $_POST['primary_keys'] = stripslashes($_POST['primary_keys']);
+ }
+
+ $state_data = Persistence::setPostData($key_rules, __METHOD__);
+
+ if (is_wp_error($state_data)) {
+ return wp_send_json_error($state_data->get_error_message());
+ }
+
+ global $wpdb;
+ // ***+=== @TODO - revisit usage of parse_migration_form_data
+ $this->migration_options = $this->form_data->parse_and_save_migration_form_data($state_data['form_data']);
+
+ if ('import' === $state_data['intent'] && !$this->table->table_exists($state_data['table'])) {
+ return $this->http->end_ajax(json_encode(array('current_row' => -1)));
+ }
+
+ // checks if we're performing a backup, if so, continue with the backup and exit immediately after
+ if ($state_data['stage'] === 'backup' && $state_data['intent'] !== 'savefile') {
+ // if performing a push we need to backup the REMOTE machine's DB
+ if ($state_data['intent'] === 'push') {
+ $return = $this->handle_remote_backup($state_data);
+ } else {
+ $return = $this->handle_table_backup();
+ }
+
+ if (Util::is_json($return)) {
+ $return = json_decode($return, true);
+ }
+ return $this->http->end_ajax($return);
+ }
+
+ // Pull and push need to be handled differently for obvious reasons,
+ // and trigger different code depending on the migration intent (push or pull).
+ if (in_array($state_data['intent'], array('push', 'savefile', 'find_replace', 'import'))) {
+ $this->dynamic_props->maximum_chunk_size = $this->util->get_bottleneck();
+
+ if (isset($state_data['bottleneck'])) {
+ $this->dynamic_props->maximum_chunk_size = (int)$state_data['bottleneck'];
+ }
+ $is_full_site_export = isset($state_data['full_site_export']) ? $state_data['full_site_export'] : false;
+ if ('savefile' === $state_data['intent']) {
+ $sql_dump_file_name = $this->filesystem->get_upload_info('path') . DIRECTORY_SEPARATOR;
+ $sql_dump_file_name .= $this->table_helper->format_dump_name($state_data['dump_filename']);
+ $fp = $this->filesystem->open($sql_dump_file_name, 'a', $is_full_site_export);
+ }
+
+ if (!empty($state_data['db_version'])) {
+ $this->dynamic_props->target_db_version = $state_data['db_version'];
+ if ('push' == $state_data['intent']) {
+ // $this->dynamic_props->target_db_version has been set to remote database's version.
+ add_filter('wpmdb_create_table_query', array($this->table_helper, 'mysql_compat_filter'), 10, 5);
+ } elseif ('savefile' == $state_data['intent'] && !empty($this->form_data_arr['compatibility_older_mysql'])) {
+ // compatibility_older_mysql is currently a checkbox meaning pre-5.5 compatibility (we play safe and target 5.1),
+ // this may change in the future to be a dropdown or radiobox returning the version to be compatible with.
+ $this->dynamic_props->target_db_version = '5.1';
+ add_filter('wpmdb_create_table_query', array($this->table_helper, 'mysql_compat_filter'), 10, 5);
+ }
+ }
+
+ if (!empty($state_data['find_replace_pairs'])) {
+ $this->dynamic_props->find_replace_pairs = $state_data['find_replace_pairs'];
+ }
+
+ ob_start();
+ $result = $this->table->process_table($state_data['table'], $fp, $state_data);
+
+ if (\is_resource($fp) && $state_data['intent'] === 'savefile') {
+ $this->filesystem->close($fp, $is_full_site_export);
+ }
+
+ return $this->http->end_ajax($result);
+ } else { // PULLS
+ $data = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'remote_state_id',
+ 'intent',
+ 'url',
+ 'table',
+ 'form_data',
+ 'stage',
+ 'bottleneck',
+ 'current_row',
+ 'last_table',
+ 'gzip',
+ 'primary_keys',
+ 'site_url',
+ 'find_replace_pairs',
+ 'source_prefix',
+ 'destination_prefix',
+ )
+ );
+
+ $data['action'] = 'wpmdb_process_pull_request';
+ $data['pull_limit'] = $this->http_helper->get_sensible_pull_limit();
+ $data['db_version'] = $wpdb->db_version();
+
+ if (is_multisite()) {
+ $data['path_current_site'] = $this->util->get_path_current_site();
+ $data['domain_current_site'] = $this->multisite->get_domain_current_site();
+ }
+
+ $data['prefix'] = $wpdb->base_prefix;
+
+ if (isset($data['sig'])) {
+ unset($data['sig']);
+ }
+
+ $sig_data = $data;
+ unset($sig_data['find_replace_pairs'], $sig_data['form_data'], $sig_data['source_prefix'], $sig_data['destination_prefix']);
+ $data['find_replace_pairs'] = base64_encode(json_encode($data['find_replace_pairs']));
+ $data['form_data'] = base64_encode($data['form_data']);
+ $data['primary_keys'] = base64_encode($data['primary_keys']);
+ $data['source_prefix'] = base64_encode($data['source_prefix']);
+ $data['destination_prefix'] = base64_encode($data['destination_prefix']);
+
+ $data['sig'] = $this->http_helper->create_signature($sig_data, $state_data['key']);
+
+ // Don't add to computed signature
+ $data['site_details'] = base64_encode(json_encode($state_data['site_details']));
+ $ajax_url = $this->util->ajax_url();
+ $response = $this->remote_post->post($ajax_url, $data, __FUNCTION__);
+
+ ob_start();
+ $this->util->display_errors();
+ $maybe_errors = trim(ob_get_clean());
+
+ // WP_Error is thrown manually by remote_post() to tell us something went wrong
+ if (is_wp_error($response)) {
+ return $this->http->end_ajax(
+ $response
+ );
+ }
+
+ // returned data is just a big string like this query;query;query;33
+ // need to split this up into a chunk and row_tracker
+ // only strip the last new line if it exists
+ $row_information = false !== strpos($response, "\n") ? trim(substr(strrchr($response, "\n"), 1)) : trim($response);
+ $row_information = explode('##MDB_SEPARATOR##', $row_information);
+ $chunk = substr($response, 0, strrpos($response, ";\n") + 1);
+
+ if (!empty($chunk)) {
+ $process_chunk_result = $this->table->process_chunk($chunk);
+ if (true !== $process_chunk_result) {
+ return $this->http->end_ajax($process_chunk_result);
+ }
+ }
+
+ $result = array(
+ 'current_row' => $row_information[0],
+ 'primary_keys' => $row_information[1],
+ );
+
+ $result = $this->http->end_ajax($result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Appends an export of a table to a backup file as per params defined in $this->state_data.
+ *
+ * @return mixed|null
+ */
+ function handle_table_backup($key = 'wpmdb_migration_state')
+ {
+ $state_data = Persistence::getStateData($key);
+
+ if (empty($state_data['dumpfile_created'])) {
+ $state_data['dumpfile_created'] = true;
+
+ Persistence::saveStateData($state_data, $key);
+ }
+
+ $this->dynamic_props->maximum_chunk_size = $this->util->get_bottleneck();
+ $sql_dump_file_name = $this->filesystem->get_upload_info('path') . DIRECTORY_SEPARATOR;
+ $sql_dump_file_name .= $this->table_helper->format_dump_name($state_data['dump_filename']);
+ $file_created = file_exists($sql_dump_file_name);
+ $fp = $this->filesystem->open($sql_dump_file_name);
+
+ if ($file_created == false) {
+ $this->table->db_backup_header($fp);
+ }
+
+ $result = $this->table->process_table($state_data['table'], $fp, $state_data);
+
+ if (isset($fp) && \is_resource($fp)) {
+ $this->filesystem->close($fp);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Called to cleanup on error
+ */
+ function error_migration()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $error_message = '';
+ if (isset($_POST['error_message'])) {
+ $error_message = sanitize_text_field($_POST['error_message']);
+ }
+ do_action('wpmdb_error_migration', $error_message);
+ $this->ajax_cancel_migration();
+ }
+
+ /**
+ * Called to cancel an in-progress migration.
+ */
+ function ajax_cancel_migration()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'action' => 'key',
+ );
+
+ $state_data = Persistence::setPostData($key_rules, __METHOD__);
+
+ if (is_wp_error($state_data)) {
+ return wp_send_json_error($state_data->get_error_message());
+ }
+
+ $this->migration_options = $this->form_data->parse_and_save_migration_form_data($state_data['form_data']);
+
+ switch ($state_data['intent']) {
+ case 'savefile':
+ if($state_data['full_site_export'] !== true || $state_data['stage'] === 'migrate') {
+ $this->backup_export->delete_export_file($state_data['dump_filename'], false);
+ }
+ if($state_data['full_site_export'] === true) {
+ $zip_removed = $this->full_site_export->delete_export_zip($state_data['export_path']);
+ return $this->http->end_ajax($zip_removed);
+ }
+ break;
+ case 'push':
+ $data = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'remote_state_id',
+ 'intent',
+ 'url',
+ 'form_data',
+ 'temp_prefix',
+ 'stage',
+ )
+ );
+
+ $data['form_data'] = base64_encode($state_data['form_data']);
+ $data['action'] = 'wpmdb_process_push_migration_cancellation';
+ $data['sig'] = $this->http_helper->create_signature($data, $state_data['key']);
+ $ajax_url = $this->util->ajax_url();
+
+ $response = $this->remote_post->post($ajax_url, $data, __FUNCTION__);
+ $filtered_response = HandleRemotePostError::handle('wpmdb_remote_cancellation_failed', $response);
+ do_action('wpmdb_cancellation');
+
+ return $this->http->end_ajax($filtered_response);
+ case 'pull':
+ if ($state_data['stage'] == 'backup') {
+ if (!empty($state_data['dumpfile_created'])) {
+ $this->backup_export->delete_export_file($state_data['dump_filename'], true);
+ }
+ } else {
+ $this->table->delete_temporary_tables($state_data['temp_prefix']);
+ }
+ break;
+ case 'find_replace':
+ if ('backup' === $state_data['stage'] && !empty($state_data['dumpfile_created'])) {
+ $this->backup_export->delete_export_file($state_data['dump_filename'], true);
+ } else {
+ $this->table->delete_temporary_tables($this->props->temp_prefix);
+ }
+ break;
+ case 'import':
+ if ('backup' === $state_data['stage'] && !empty($state_data['dumpfile_created'])) {
+ $this->backup_export->delete_export_file($state_data['dump_filename'], true);
+ } else {
+ // Import might have been deleted already
+ if ($this->filesystem->file_exists($state_data['import_path'])) {
+ $sanitized_import_filename = sanitize_file_name($state_data['import_filename']);
+ if ($state_data['import_info']['import_gzipped']) {
+ $is_backup = $this->filesystem->file_exists(substr($state_data['import_path'], 0, -3)) ? true : false;
+ $this->backup_export->delete_export_file($sanitized_import_filename, $is_backup);
+ } else {
+ $this->backup_export->delete_export_file($sanitized_import_filename, true);
+ }
+ }
+ $this->table->delete_temporary_tables($this->props->temp_prefix);
+ }
+ break;
+ default:
+ break;
+ }
+
+ do_action('wpmdb_cancellation');
+
+ $this->http->end_ajax('success');
+ }
+
+ protected function handle_remote_backup($state_data)
+ {
+ $data = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'action',
+ 'url',
+ 'table',
+ 'form_data',
+ 'stage',
+ 'current_row',
+ 'last_table',
+ 'gzip',
+ 'primary_keys',
+ 'path_current_site',
+ 'domain_current_site',
+ )
+ );
+
+ $data['action'] = 'wpmdb_backup_remote_table';
+ $data['intent'] = 'pull';
+
+ $data['form_data'] = base64_encode($data['form_data']);
+
+ $data['sig'] = $this->http_helper->create_signature($data, $state_data['key']);
+
+ $data['primary_keys'] = base64_encode($data['primary_keys']);
+ $ajax_url = $this->util->ajax_url();
+ $response = $this->remote_post->post($ajax_url, $data, __FUNCTION__);
+
+ return HandleRemotePostError::handle('wpmdb-handle-remote-backup-failed', $response);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationPersistence/Persistence.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationPersistence/Persistence.php
new file mode 100644
index 000000000..c6082e02f
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationPersistence/Persistence.php
@@ -0,0 +1,246 @@
+get(Http::class);
+ $util = WPMDBDI::getInstance()->get(Util::class);
+ $util->set_time_limit();
+
+ $state_data = false;
+ $post_data = !$post_data ? $_POST : $post_data;
+
+ if ($sanitize) {
+ $state_data = self::sanitizeFields($fields, $context, $post_data);
+
+ if (is_wp_error($state_data)) {
+ return $http->end_ajax($state_data);
+ }
+ } elseif ($post_data) {
+ $state_data = $post_data;
+ }
+
+ if (!$state_data) {
+ return false;
+ }
+
+ if (is_wp_error($state_data)) {
+ return $http->end_ajax($state_data);
+ }
+
+ $existing_data = get_site_option($key);
+ if (!empty($existing_data) && is_array($existing_data)) {
+ $state_data = array_merge($existing_data, $state_data);
+ }
+
+ //Make sure $state_data['site_details']['remote'] is set
+ // @TODO refactor
+ if (
+ (!isset($state_data['site_details']['remote']) || empty($state_data['site_details']['remote']))
+ && isset($state_data['site_details']['local'])
+ ) {
+ $migration_helper = WPMDBDI::getInstance()->get(MigrationHelper::class);
+ $state_data['site_details']['remote'] = $migration_helper->siteDetails($state_data)['site_details'];;
+ }
+
+
+ if ($save) {
+ update_site_option($key, $state_data);
+ }
+
+ return $state_data;
+ }
+
+ /**
+ * @param $fields
+ * @param $context
+ * @param string $key
+ * @param bool $post_data
+ * @param bool $sanitize
+ *
+ * @return bool|mixed|\WP_Error
+ */
+ public static function setRemotePostData(
+ $fields,
+ $context,
+ $key = 'wpmdb_remote_migration_state',
+ $post_data = false,
+ $sanitize = true,
+ $save = true
+ ) {
+ return self::setPostData($fields, $context, $key, $post_data, $sanitize, $save);
+ }
+
+ /**
+ * Remove options from the site_meta/wp_options table
+ *
+ * @param string $key
+ */
+ public static function cleanupStateOptions($key = 'wpmdb_migration_state')
+ {
+ delete_site_option($key);
+ delete_site_option('wpmdb_migration_options');
+ delete_site_option('wpmdb_remote_response');
+ }
+
+ /**
+ * @param $fields
+ * @param $context
+ * @param $post_data
+ *
+ * @return mixed
+ */
+ public static function sanitizeFields($fields, $context, $post_data)
+ {
+ try {
+ $state_data = Sanitize::sanitize_data($post_data, $fields, $context);
+ } catch (SanitizationFailureException $exception) {
+ return new \WP_Error('sanitization_error', $exception->getMessage());
+ }
+
+ return $state_data;
+ }
+
+ public static function getStateDataByIntent($intent)
+ {
+ if ('pull' === $intent) {
+ return self::getRemoteStateData();
+ }
+
+ return self::getStateData();
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/MigrationState.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/MigrationState.php
new file mode 100644
index 000000000..de82083b0
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/MigrationState.php
@@ -0,0 +1,308 @@
+_option( $id ), false, false );
+
+ if ( false !== $value ) {
+ $this->_id = $id;
+ $this->_value = $value;
+ }
+ }
+ }
+
+ /**
+ * Returns the unique id of the instance.
+ *
+ * @return string
+ */
+ function id() {
+ if ( empty( $this->_id ) ) {
+ $this->_id = uniqid( 'wpmdb', true );
+ }
+
+ return $this->_id;
+ }
+
+ /**
+ * Returns the site option string used to save the migration state.
+ *
+ * @param string $id
+ *
+ * @return string
+ */
+ private function _option( $id = null ) {
+ if ( empty( $id ) ) {
+ $id = $this->id();
+ }
+
+ $return = self::OPTION_PREFIX . $id;
+
+ return $return;
+ }
+
+ /**
+ * Returns the site option string used to save the migration state timeout.
+ *
+ * @param string $id
+ *
+ * @return string
+ */
+ private function _timeout_option( $id = null ) {
+ if ( empty( $id ) ) {
+ $id = $this->id();
+ }
+
+ return self::TIMEOUT_PREFIX . $id;
+ }
+
+ /**
+ * Set the migration state.
+ *
+ * @param $value
+ *
+ * @return bool
+ */
+ function set( $value, $id = null ) {
+ if ( $this->_update_timeout( $id ) && update_site_option( $this->_option( $id ), $value ) ) {
+ return true;
+ }
+
+ // If nothing changed it's still OK.
+ if ( $this->get() === $value ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Updates the companion timeout setting to the current migration state option.
+ *
+ * @return bool
+ */
+ private function _update_timeout( $id = null ) {
+ $value = time() + self::EXPIRATION;
+
+ if ( update_site_option( $this->_timeout_option( $id ), $value ) ) {
+ return true;
+ }
+
+ // If nothing changed it's still OK.
+ if ( get_site_option( $this->_timeout_option(), false, false ) == $value ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the current saved migration state.
+ *
+ * @return mixed
+ */
+ function get( $id = null ) {
+ return get_site_option( $this->_option( $id ), false, false );
+ }
+
+ /**
+ * Deletes the site options for migration state and its companion timeout record.
+ *
+ * @param $id
+ *
+ * @return bool
+ */
+ private static function _delete_id( $id ) {
+ if ( false === get_site_option( self::OPTION_PREFIX . $id, false, false ) || delete_site_option( self::OPTION_PREFIX . $id ) ) {
+ delete_site_option( self::TIMEOUT_PREFIX . $id );
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Delete the current migration state.
+ *
+ * @return bool
+ */
+ function delete() {
+ return $this->_delete_id( $this->id() );
+ }
+
+ /**
+ * Get all migration state ids that have timed out.
+ *
+ * @param int $timeout Optional UNIX timestamp for timeout, default of 0 uses current timestamp.
+ *
+ * @return array
+ */
+ private static function _timed_out_ids( $timeout = 0 ) {
+ global $wpdb;
+
+ $ids = array();
+
+ if ( empty( $timeout ) ) {
+ $timeout = time();
+ }
+
+ if ( is_multisite() ) {
+ $timeout_keys = $wpdb->get_col(
+ $wpdb->prepare(
+ "SELECT meta_key FROM {$wpdb->sitemeta} WHERE site_id = %d AND meta_key like %s AND meta_value < %d",
+ $wpdb->siteid,
+ addcslashes( self::TIMEOUT_PREFIX, '_' ) . '%',
+ $timeout
+ )
+ );
+ } else {
+ $timeout_keys = $wpdb->get_col(
+ $wpdb->prepare(
+ "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s and option_value < %d",
+ addcslashes( self::TIMEOUT_PREFIX, '_' ) . '%',
+ $timeout
+ )
+ );
+ }
+
+ if ( ! empty( $timeout_keys ) ) {
+ $id_start = strlen( self::TIMEOUT_PREFIX );
+
+ foreach ( $timeout_keys as $timeout_key ) {
+ $ids[] = substr( $timeout_key, $id_start );
+ }
+ }
+
+ return $ids;
+ }
+
+ /**
+ * Get all migration state ids that have no time out companion.
+ *
+ * @return array
+ */
+ private static function _orphaned_ids() {
+ global $wpdb;
+
+ $ids = array();
+
+ if ( is_multisite() ) {
+ $keys = $wpdb->get_col(
+ $wpdb->prepare( "
+ SELECT meta_key
+ FROM {$wpdb->sitemeta}
+ WHERE site_id = %d
+ AND meta_key LIKE %s
+ AND meta_key NOT LIKE %s
+ AND meta_key NOT IN (
+ SELECT CONCAT(%s, SUBSTR(meta_key, %d))
+ FROM {$wpdb->sitemeta}
+ WHERE site_id = %d
+ AND meta_key LIKE %s
+ )
+ ",
+ $wpdb->siteid,
+ addcslashes( self::OPTION_PREFIX, '_' ) . '%',
+ addcslashes( self::TIMEOUT_PREFIX, '_' ) . '%',
+ self::OPTION_PREFIX,
+ strlen( self::TIMEOUT_PREFIX ) + 1,
+ $wpdb->siteid,
+ addcslashes( self::TIMEOUT_PREFIX, '_' ) . '%'
+ )
+ );
+ } else {
+ $keys = $wpdb->get_col(
+ $wpdb->prepare( "
+ SELECT option_name
+ FROM $wpdb->options
+ WHERE option_name LIKE %s
+ AND option_name NOT LIKE %s
+ AND option_name NOT IN (
+ SELECT CONCAT(%s, SUBSTR(option_name, %d))
+ FROM $wpdb->options
+ WHERE option_name LIKE %s
+ )
+ ",
+ addcslashes( self::OPTION_PREFIX, '_' ) . '%',
+ addcslashes( self::TIMEOUT_PREFIX, '_' ) . '%',
+ self::OPTION_PREFIX,
+ strlen( self::TIMEOUT_PREFIX ) + 1,
+ addcslashes( self::TIMEOUT_PREFIX, '_' ) . '%'
+ )
+ );
+ }
+
+ if ( ! empty( $keys ) ) {
+ $id_start = strlen( self::OPTION_PREFIX );
+
+ foreach ( $keys as $key ) {
+ $ids[] = substr( $key, $id_start );
+ }
+ }
+
+ return $ids;
+ }
+
+ /**
+ * returns count of all migration state records that have timed out.
+ *
+ * @param int $timeout Optional UNIX timestamp for timeout, default of 0 uses current timestamp.
+ *
+ * @return int
+ */
+ static function cleanup_count( $timeout = 0 ) {
+ return count( self::_timed_out_ids( $timeout ) ) + count( self::_orphaned_ids() );
+ }
+
+ /**
+ * Remove all migration state records that have timed out or are orphaned from their timeout companion.
+ *
+ * @param int $timeout Optional UNIX timestamp for timeout, default of 0 uses current timestamp.
+ *
+ * @return int Count of successfully cleaned up options.
+ */
+ static function cleanup( $timeout = 0 ) {
+ $count = 0;
+
+ delete_site_option( 'wpmdb_migration_id' );
+
+ $timed_out_ids = self::_timed_out_ids( $timeout );
+
+ if ( ! empty( $timed_out_ids ) ) {
+ foreach ( $timed_out_ids as $id ) {
+ if ( self::_delete_id( $id ) ) {
+ $count ++;
+ }
+ }
+ }
+
+ $orphaned_ids = self::_orphaned_ids();
+
+ if ( ! empty( $orphaned_ids ) ) {
+ foreach ( $orphaned_ids as $id ) {
+ if ( self::_delete_id( $id ) ) {
+ $count ++;
+ }
+ }
+ }
+
+ return $count;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/MigrationStateManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/MigrationStateManager.php
new file mode 100644
index 000000000..c63691d12
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/MigrationStateManager.php
@@ -0,0 +1,212 @@
+error_log = $error_log;
+ $this->props = $properties;
+ $this->util = $util;
+ $this->state_container = $state_data_container;
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->state_data = $this->state_container->state_data;
+ $this->migration_state_class = $migration_state;
+ $this->http = $http;
+ }
+
+ public function get_state_data()
+ {
+ return $this->state_data;
+ }
+
+ /**
+ * Save the migration state, and replace the current item to be returned if there is an error.
+ *
+ * @param $state mixed
+ * @param $default mixed The default value to return on success, optional defaults to null.
+ *
+ * @return mixed
+ */
+ public function save_migration_state($state, $default = null, $migration_id = null)
+ {
+ if (! $this->migration_state->set($state, $migration_id)) {
+ $error_msg = __('Failed to save migration state. Please contact support.', 'wp-migrate-db');
+ $default = array('wpmdb_error' => 1, 'body' => $error_msg);
+ $this->error_log->log_error($error_msg);
+ }
+
+ return $default;
+ }
+
+ public function get_state()
+ {
+ $migration_id = get_site_transient('wpmdb_migration_id');
+
+ if (empty($migration_id)) {
+ return false;
+ }
+
+ return $this->migration_state->get($migration_id);
+ }
+
+ /**
+ * Restore previous migration state and merge in new information or initialize new migration state.
+ *
+ * @param null $id
+ *
+ * @return array|bool|mixed|null
+ */
+ public function get_migration_state($id = null)
+ {
+ $return = true;
+
+ if (! empty($id)) {
+ $this->migration_state = new MigrationState($id);
+ $state = $this->migration_state->get();
+ if (empty($state) || $this->migration_state->id() !== $id) {
+ $error_msg = __('Failed to retrieve migration state. Please contact support.', 'wp-migrate-db');
+ $return = array('wpmdb_error' => 1, 'body' => $error_msg);
+ $this->error_log->log_error($error_msg);
+ $return = $this->http->end_ajax(json_encode($return));
+ } else {
+ $this->state_data = array_merge($state, $this->state_data);
+
+ $return = $this->save_migration_state($this->state_data, $return);
+
+ if (! empty($return['wpmdb_error'])) {
+ $return = $this->http->end_ajax(json_encode($return));
+ }
+ }
+ } else {
+ $this->migration_state = new MigrationState();
+ }
+
+ return $return;
+ }
+
+ /**
+ * Sets $this->state_data from $_POST, potentially un-slashed and un-sanitized.
+ *
+ * @param array $key_rules An optional associative array of expected keys and their sanitization rule(s).
+ * @param string $state_key The key in $_POST that contains the migration state id (defaults to 'migration_state_id').
+ * @param string $context The method that is specifying the sanitization rules. Defaults to calling method.
+ *
+ * @return array|mixed|string
+ * @throws \DI\DependencyException
+ * @throws \DI\NotFoundException
+ */
+
+ public function set_post_data($key_rules=[], $state_key = 'migration_state_id', $context = '')
+ {
+ if(empty($key_rules)){
+ return Persistence::getStateData();
+ }
+
+ if (defined('DOING_WPMDB_TESTS') || $this->dynamic_props->doing_cli_migration) {
+ // @TODO this is a major hack and should be refactored
+ $this->state_data = $_POST;
+ } elseif (empty($this->state_data) || null === $this->state_data) {
+ $this->state_data = Util::safe_wp_unslash($_POST);
+ } else {
+ return $this->state_data;
+ }
+
+ // From this point on we're handling data originating from $_POST, so original $key_rules apply.
+ global $wpmdb_key_rules;
+
+ if ( empty( $key_rules ) && ! empty( $wpmdb_key_rules ) ) {
+ $key_rules = $wpmdb_key_rules;
+ }
+
+ // Sanitize the new state data.
+ if ( !empty( $key_rules ) ) {
+ $wpmdb_key_rules = $key_rules;
+ $context = empty( $context ) ? $this->util->get_caller_function() : trim( $context );
+ $sanitized = Sanitize::sanitize_data( $this->state_data, $key_rules, $context );
+
+ if ( is_wp_error( $sanitized ) ) {
+ $this->error_log->log_error( $sanitized->get_error_message() );
+ error_log($sanitized->get_error_message());
+ return $this->http->end_ajax( json_encode( array('wpmdb_error' => 1, 'body' => $sanitized->get_error_message()) ) );
+ }
+
+ $this->state_data = $sanitized;
+
+ if (false === $this->state_data) {
+ exit;
+ }
+ }
+
+ $migration_state_id = null;
+ if (! empty($this->state_data[$state_key])) {
+ $migration_state_id = $this->state_data[$state_key];
+ }
+
+ // Always pass migration_state_id or $state_key with every AJAX request
+ if (true !== $this->get_migration_state($migration_state_id)) {
+ exit;
+ }
+
+ WPMDBDI::getInstance()->get(StateDataContainer::class)->setData($this->state_data);
+
+ return $this->state_data;
+ }
+}
+
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/StateDataContainer.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/StateDataContainer.php
new file mode 100644
index 000000000..f3d14e2ab
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/MigrationState/StateDataContainer.php
@@ -0,0 +1,33 @@
+state_data` usage throughout codebase
+ *
+ * Generally, state_data is set in MigrationStateManager::set_post_data();
+ *
+ * @TODO In future, refactor usage of state_data globally
+ *
+ * @package DeliciousBrains\WPMDB\Common\MigrationState
+ */
+class StateDataContainer {
+ use Singleton;
+
+ public $state_data = [];
+ public $migration_state_manager;
+
+ public function __construct( ) { }
+
+ public function setData( $data ) {
+ $this->state_data = $data;
+ }
+
+ public function getData() {
+ return $this->state_data;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Multisite/Multisite.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Multisite/Multisite.php
new file mode 100644
index 000000000..1b232b904
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Multisite/Multisite.php
@@ -0,0 +1,235 @@
+props = $properties;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->util = $util;
+ }
+
+ /**
+ * Get the remote site's base domain for subdomain multisite search/replace.
+ *
+ * @return string|bool The remote site's domain or false on error.
+ */
+ function get_domain_replace()
+ {
+ $state_data = $this->migration_state_manager->set_post_data();
+
+ if ( !isset( $this->domain_replace ) ) {
+ $this->domain_replace = false;
+
+ if ( is_multisite() && !empty( $this->dynamic_props->find_replace_pairs ) ) {
+ $grep = preg_grep( sprintf( '/^(\/\/|http:\/\/|https:\/\/|)%s/', $this->get_domain_current_site() ), $this->dynamic_props->find_replace_pairs['replace_old'] );
+ if ( $grep ) {
+ $domain_find_keys = array_keys( $grep );
+ $url = Util::parse_url( $this->dynamic_props->find_replace_pairs['replace_new'][$domain_find_keys[0]] );
+ if ( isset( $url['host'] ) ) {
+ $this->domain_replace = $url['host'] . ( isset( $url['port'] ) ? ':' . $url['port'] : '' );
+ } elseif ( !empty( $state_data['domain_current_site'] ) ) {
+ $this->domain_replace = $state_data['domain_current_site'];
+ }
+ }
+ }
+ }
+
+ return $this->domain_replace;
+ }
+
+ /**
+ * Get the domain for the current site.
+ *
+ * @return string
+ */
+ function get_domain_current_site()
+ {
+ if ( !is_multisite() ) {
+ return '';
+ }
+
+ $current_site = get_current_site();
+
+ return $current_site->domain;
+ }
+
+ /**
+ * Checks given subsite id or url to see if it exists and returns its blog id.
+ *
+ * @param int|string $subsite Blog ID or URL
+ * @param array $subsites_list Optional array of blog_id => simple urls to use, defaults to result of subsites_list().
+ *
+ * @return bool|string
+ */
+ public function get_subsite_id( $subsite, $subsites_list = array() )
+ {
+ if ( !is_numeric( $subsite ) ) {
+ $subsite = $this->util->simple_site_url( $subsite );
+ }
+
+ if ( empty( $subsites_list ) ) {
+ $subsites_list = $this->util->subsites_list();
+ }
+
+ foreach ( $subsites_list as $blog_id => $subsite_path ) {
+ if ( is_numeric( $subsite ) ) {
+ if ( $blog_id == $subsite ) {
+ return $blog_id;
+ }
+ } elseif ( $subsite == $subsite_path ) {
+ return $blog_id;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks given array of subsite ids or urls to see if they exist and returns array of blog ids.
+ *
+ * @param array $subsites
+ * @param array $subsites_list Optional array of blog_id => simple urls to use, defaults to result of subsites_list().
+ *
+ * @return array
+ *
+ * Returned array element values will be false if the given value does not correspond to a subsite.
+ */
+ public function get_subsite_ids( $subsites, $subsites_list = array() )
+ {
+ if ( empty( $subsites ) ) {
+ return array();
+ }
+
+ if ( !is_array( $subsites ) ) {
+ $subsites = array( $subsites );
+ }
+
+ foreach ( $subsites as $index => $subsite ) {
+ $subsites[$index] = $this->get_subsite_id( $subsite, $subsites_list );
+ }
+
+ return $subsites;
+ }
+
+ public function mst_required_message($intent)
+ {
+ $local = __('single site', 'wp-migrate-db');
+ $remote = __('multisite', 'wp-migrate-db');
+ $local_is_multisite = false;
+ $action = '';
+ $msg = '';
+ $plugin_file = 'wp-migrate-db-pro-multisite-tools/wp-migrate-db-pro-multisite-tools.php';
+ $plugin_ids = array_keys(get_plugins());
+ $mst_addon = sprintf('%s',
+ 'https://deliciousbrains.com/wp-migrate-db-pro/doc/multisite-tools-addon/',
+ __('Multisite Tools upgrade', 'wp-migrate-db')
+ );
+ $import_msg = sprintf(__('It looks like the file you are trying to import is from a multisite install and this install is a single site. To run this type of import you\'ll need to use the %s to export a subsite as a single site. Learn more »',
+ 'wp-migrate-db'),
+ $mst_addon,
+ 'https://deliciousbrains.com/wp-migrate-db-pro/doc/multisite-tools-addon/#export-subsite'
+ );
+
+ $replace_single = sprintf('%s', __('Learn more about replacing a single site with a multisite network', 'wp-migrate-db'));
+ $replace_multisite = sprintf('%s', __('Learn more about replacing a multisite network with a single site', 'wp-migrate-db'));
+
+ if ( is_multisite() ) {
+ $local_is_multisite = true;
+ $local = __('multisite', 'wp-migrate-db');
+ $remote = __('single site', 'wp-migrate-db');
+ $import_msg = sprintf(__('It looks like the file you are trying to import is from a single site install and this install is a multisite. This type of migration isn\'t currently supported. Learn more »',
+ 'wp-migrate-db'),
+ 'https://deliciousbrains.com/wp-migrate-db-pro/doc/multisite-tools-addon/'
+ );
+ }
+
+ if ( in_array( $plugin_file, $plugin_ids ) ) {
+ if ( !is_plugin_active( $plugin_file ) ) {
+ $url = wp_nonce_url( network_admin_url( 'plugins.php?action=activate&plugin=wp-migrate-db-pro-multisite-tools/wp-migrate-db-pro-multisite-tools.php' ), 'activate-plugin_wp-migrate-db-pro-multisite-tools/wp-migrate-db-pro-multisite-tools.php' );
+ $action = sprintf( '[%s]', $url, __( 'Activate', 'wp-migrate-db' ) );
+ } else {
+ $msg = sprintf( __( 'It looks like the remote site is a %s install and this install is a %s. To run this type of migration you\'ll need the %s activated on the remote site.', 'wp-migrate-db' ),
+ $remote,
+ $local,
+ $mst_addon
+ );
+ }
+ } else {
+ $license_response = get_site_transient( Helpers::get_licence_response_transient_key() );
+
+ if ( $license_response ) {
+ $license_response = json_decode( $license_response );
+
+ if ( isset( $license_response->addons_available ) && '0' !== $license_response->addons_available ) {
+ $url = wp_nonce_url( network_admin_url( 'update.php?action=install-plugin&plugin=wp-migrate-db-pro-multisite-tools' ), 'install-plugin_wp-migrate-db-pro-multisite-tools' );
+ $action = sprintf( '[%s]', $url, __( 'Install', 'wp-migrate-db' ) );
+ } else {
+ $url = 'https://deliciousbrains.com/my-account/?utm_campaign=support%2Bdocs&utm_source=MDB%2BPaid&utm_medium=insideplugin';
+ $action = sprintf( '[%s]', $url, __( 'Upgrade your license', 'wp-migrate-db' ) );
+ }
+ }
+ }
+
+ if ($intent === 'push' && $local_is_multisite) {
+ $doc_link = $replace_single;
+ } elseif ($intent === 'push' && ! $local_is_multisite) {
+ $doc_link = $replace_multisite;
+ } elseif ($intent === 'pull' && $local_is_multisite) {
+ $doc_link = $replace_multisite;
+ } else {
+ $doc_link = $replace_single;
+ }
+
+ if ('' === $msg) {
+ $msg = sprintf(__('It looks like the remote site is a %s install and this install is a %s. %s',
+ 'wp-migrate-db'),
+ $remote,
+ $local,
+ $doc_link
+ );
+ }
+
+ $msg = '' . $msg . '';
+
+ return $msg;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/Assets.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/Assets.php
new file mode 100644
index 000000000..5d23e1d86
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/Assets.php
@@ -0,0 +1,179 @@
+http = $http;
+ $this->filesystem = $filesystem;
+ $this->props = $properties;
+ $this->error_log = $error_log;
+ $this->settings = $settings;
+ $this->util = $util;
+ }
+
+ public function register(){
+ add_action('admin_enqueue_scripts', [$this, 'wpmdb_enqueue_assets']);
+ }
+ /**
+ * Checks and sets up plugin assets.
+ * Filter actions, enqueue scripts, define configuration, and constants.
+ *
+ * @return void
+ */
+ function load_assets()
+ {
+ $this->http->http_verify_download();
+
+ $log = $this->error_log;
+ $log->http_prepare_download_log();
+
+ // add our custom CSS classes to
+ add_filter('admin_body_class', array($this, 'admin_body_class'));
+
+ $plugins_url = trailingslashit(plugins_url($this->props->plugin_folder_name));
+ $version = \defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? time() : $this->props->plugin_version;
+
+ do_action('wpmdb_load_assets');
+ $src = $plugins_url . "frontend/mdb-2.0.js";
+ wp_enqueue_script('wp-migrate-db-pro-script-v2', $src, [], $version, true);
+
+ wp_localize_script('wp-migrate-db-pro-script-v2', 'wpmdb_settings', apply_filters('wpmdb_settings_js', $this->settings->get_settings_for_frontend()));
+ wp_localize_script('wp-migrate-db-pro-script-v2',
+ 'wpmdb_strings',
+ apply_filters('wpmdb_js_strings', array(
+ 'connection_info_missing' => __('The connection information appears to be missing, please enter it to continue.', 'wp-migrate-db'),
+ 'connection_info_url_invalid' => __('The URL on the first line appears to be invalid, please check it and try again.', 'wp-migrate-db'),
+ 'connection_info_key_invalid' => __('The secret key on the second line appears to be invalid. It should be a 40 character string that consists of letters, numbers and special characters only.', 'wp-migrate-db'),
+ 'connection_info_local_url' => __("It appears you've entered the URL for this website, you need to provide the URL of the remote website instead.", 'wp-migrate-db'),
+ 'connection_info_local_key' => __('Looks like your remote secret key is the same as the secret key for this site. To fix this, go to the Settings tab and click "Reset Secret Key"', 'wp-migrate-db'),
+ 'migration_cancelled' => _x('Migration cancelled', 'The migration has been cancelled', 'wp-migrate-db'),
+ 'migration_cancelled_success' => __('The migration has been stopped and all temporary files and data have been cleaned up.', 'wp-migrate-db'),
+ 'welcome_title' => __('Welcome to WP Migrate! 🎉', 'wp-migrate-db'),
+ 'welcome_text' => __('Hey, this is the first time activating your license, nice! Your migrations are about to get awesome! If you haven’t already, you should check out our Quick Start Guide and Videos. If you run into any trouble at all, use the Help tab above to submit a support request.', 'wp-migrate-db'),
+ 'invalid_sql_file' => __('The selected file does not have a recognized file type. Please upload a valid SQL file to continue.', 'wp-migrate-db'),
+ 'please_select_sql_file' => __('Please select an SQL export file above to continue.', 'wp-migrate-db'),
+ 'importing_file_to_db' => __('Importing data from %s', 'wp-migrate-db'),
+ 'plugins_url' => network_admin_url('plugins.php'),
+ 'permalinks_link' => network_admin_url('options-permalink.php'),
+ ))
+ );
+ }
+
+ function localize_notification_strings() {
+ wp_localize_script('wp-migrate-db-pro-script-v2', 'wpmdb_notifications', apply_filters('wpmdb_notification_strings', []));
+ }
+
+ function admin_body_class($classes)
+ {
+ if (!$classes) {
+ $classes = array();
+ } else {
+ $classes = explode(' ', $classes);
+ }
+
+ $version_class = 'wpmdb-not-pro';
+ if (true == $this->props->is_pro) {
+ $version_class = 'wpmdb-pro';
+ }
+
+ $classes[] = $version_class;
+
+ // Recommended way to target WP 3.8+
+ // http://make.wordpress.org/ui/2013/11/19/targeting-the-new-dashboard-design-in-a-post-mp6-world/
+ if (version_compare($GLOBALS['wp_version'], '3.8-alpha', '>')) {
+ if (!in_array('mp6', $classes)) {
+ $classes[] = 'mp6';
+ }
+ }
+
+ return implode(' ', $classes);
+ }
+
+ public function get_saved_migration_profiles()
+ {
+ $raw_data = get_site_option('wpmdb_saved_profiles');
+
+ if (empty($raw_data)) {
+ return [];
+ }
+
+ return array_map(function ($key, $item) {
+ return [
+ 'id' => $key,
+ 'guid' => $item['guid'],
+ 'name' => $item['name'],
+ ];
+ }, array_keys($raw_data), $raw_data);
+ }
+
+ public function get_recent_migrations($raw_data)
+ {
+ if (empty($raw_data)) {
+ return [];
+ }
+
+ $migrations = array_map(function ($key, $item) {
+ return [
+ 'id' => $key,
+ 'name' => $item['name'],
+ 'date' => $item['date'],
+ 'guid' => $item['guid'],
+ ];
+ }, array_keys($raw_data), $raw_data);
+
+ usort( $migrations, function ( $a, $b ) {
+ return $a < $b ? 1 : 0;
+ } );
+
+ return $migrations;
+ }
+
+ public function wpmdb_enqueue_assets()
+ {
+ $is_dev = isset($_ENV['MDB_IS_DEV']) ? (bool) $_ENV['MDB_IS_DEV'] : false;
+
+ // Need to know if isDev so we can load free assets from pro folder
+ $is_pro = defined("WPMDB_PRO") && WPMDB_PRO || $is_dev ? '-pro' : '';
+
+ // @TODO update when Multisite enabled and free version ready
+ if ($this->util->isMDBPage() && Util::is_wp_compatible()) {
+ $assets_path = apply_filters('wpmdb_frontend_assets_path', WP_PLUGIN_DIR . '/wp-migrate-db' . $is_pro . '/frontend');
+ \ReactWPScripts\enqueue_assets(
+ $assets_path,
+ [
+ 'base_url' => plugins_url('wp-migrate-db' . $is_pro . '/frontend'),
+ 'key' => 'mdb',
+ ]
+ );
+
+ do_action('wpmdb_load_frontend');
+ }
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/Menu.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/Menu.php
new file mode 100644
index 000000000..d44ad030f
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/Menu.php
@@ -0,0 +1,151 @@
+properties = $properties;
+ $this->plugin_manager_base = $plugin_manager_base;
+ $this->assets = $assets;
+ $this->compatibility_manager = $compatibility_manager;
+ $this->util = $util;
+ }
+
+ public function register()
+ {
+ $container = WPMDBDI::getInstance();
+ if (defined('WPMDB_PRO') && WPMDB_PRO) {
+ $this->template = $container->get(Template::class);
+ } else {
+ $this->template = $container->get(\DeliciousBrains\WPMDB\Free\UI\Template::class);
+ }
+
+ add_action('admin_head', [$this, 'admin_head']);
+
+ if (is_multisite()) {
+ add_action('network_admin_menu', array($this, 'network_admin_menu'));
+ add_action('admin_menu', array($this, 'network_tools_admin_menu'));
+ } else {
+ add_action('admin_menu', array($this, 'admin_menu'));
+ }
+
+
+ }
+
+ public function admin_head()
+ {
+ if ($this->util->isMDBPage()) {
+ ?>
+
+ properties->core_slug,
+ array($this->template, $template));
+
+ // Bail out early since WP isn't compatible.
+ if (!Util::is_wp_compatible()) {
+ return;
+ }
+
+ add_action('admin_head-' . $hook_suffix, array($this->plugin_manager_base, 'admin_head_connection_info'));
+ add_action('load-' . $hook_suffix, array($this->assets, 'load_assets'));
+ $this->compatibility_manager->addNotices();
+ }
+
+ /**
+ * Add a tools menu item to sites on a Multisite network
+ *
+ */
+ function network_tools_admin_menu()
+ {
+ add_management_page(
+ $this->plugin_manager_base->get_plugin_title(),
+ $this->plugin_manager_base->get_plugin_title(),
+ 'manage_network_options',
+ $this->properties->core_slug,
+ array(
+ $this->template,
+ 'subsite_tools_options_page',
+ )
+ );
+ }
+
+ function admin_menu()
+ {
+ $template = Util::is_wp_compatible() ? 'options_page' : 'options_page_outdated_wp';
+ $title = __('WP Migrate', 'wp-migrate-db');
+ $hook_suffix = add_management_page($title,
+ $title,
+ 'export',
+ $this->properties->core_slug,
+ array($this->template, $template));
+
+ // Bail out early since WP isn't compatible.
+ if (!Util::is_wp_compatible()) {
+ return;
+ }
+
+ add_action('admin_head-' . $hook_suffix, array($this->plugin_manager_base, 'admin_head_connection_info'));
+ add_action('admin_head-' . $hook_suffix, array($this->assets, 'localize_notification_strings'));
+ add_action('load-' . $hook_suffix, array($this->assets, 'load_assets'));
+ $this->compatibility_manager->addNotices();
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/PluginManagerBase.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/PluginManagerBase.php
new file mode 100644
index 000000000..dd69213ab
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Plugin/PluginManagerBase.php
@@ -0,0 +1,544 @@
+props = $properties;
+ $this->settings = $settings->get_settings();
+ $this->assets = $assets;
+ $this->util = $util;
+ $this->tables = $table;
+ $this->http = $http;
+ $this->filesystem = $filesystem;
+ $this->multisite = $multisite;
+ $this->migration_helper = $migration_helper;
+ $this->rest_API_server = $rest_API_server;
+ $this->http_helper = $http_helper;
+ $this->template = $template;
+ $this->notice = $notice;
+ $this->profile_manager = $profile_manager;
+ $this->upgrade_routines_manager = $upgrade_routines_manager;
+ }
+
+ /**
+ * Register a bunch of action and hooks for the plugin initialization
+ */
+ public function register()
+ {
+ // display a notice when either WP Migrate DB or WP Migrate DB Pro is automatically deactivated
+ add_action('pre_current_active_plugins', array($this, 'plugin_deactivated_notice'));
+ // check if WP Engine is filtering the buffer and prevent it
+ add_action('plugins_loaded', array($this, 'maybe_disable_wp_engine_filtering'));
+
+ //Remove 'Expect' header which some setups have issues with
+ add_filter('http_request_args', array($this->util, 'preempt_expect_header'), 10, 2);
+
+ add_action('admin_init', array($this, 'maybe_schema_update'));
+
+ // REST endpoints
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+
+ if (!is_writable($this->filesystem->get_upload_info('path'))) {
+ add_filter('wpmdb_notification_strings', array($this, 'template_uploads_not_writable'));
+ }
+ }
+
+ public function template_uploads_not_writable($templates)
+ {
+ $reminder_notice_name = 'wpmdb_uploads_not_writable';
+ $reminder_links = $this->notice->check_notice($reminder_notice_name, true, (DAY_IN_SECONDS * 5));
+
+ // reminder notice
+ if ($reminder_links) {
+ $templates[$reminder_notice_name] = [
+ 'message' => sprintf(__('Uploads directory not writable — the following directory is not writable: %s
. Update the file permissions for this folder to enable backups and export migrations. More information.
'), $this->filesystem->get_upload_info('path')),
+ 'link' => $reminder_links,
+ 'id' => $reminder_notice_name,
+ ];
+ }
+
+ return $templates;
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute('/process-notice-link', [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_process_notice_link'],
+ ]);
+ }
+
+
+ /**
+ * Performs a schema update if required.
+ *
+ */
+ public function maybe_schema_update()
+ {
+ if ((defined('DOING_AJAX') && DOING_AJAX) || (defined('DOING_CRON') && DOING_CRON)) {
+ return;
+ }
+
+ $schema_version = get_site_option('wpmdb_schema_version');
+ $update_schema = false;
+
+ /*
+ * Upgrade this option to a network wide option if the site has been upgraded
+ * from a regular WordPress installation to a multisite installation.
+ */
+ if (false === $schema_version && is_multisite() && is_network_admin()) {
+ $schema_version = get_option('wpmdb_schema_version');
+ if (false !== $schema_version) {
+ update_site_option('wpmdb_schema_version', $schema_version);
+ delete_option('wpmdb_schema_version');
+ }
+ }
+
+ do_action('wpmdb_before_schema_update', $schema_version);
+
+ if (false === $schema_version) {
+ $schema_version = 0;
+ }
+
+ if ($schema_version < 3.2) {
+ $error_log = get_option('wpmdb_error_log');
+ // skip multisite installations as we can't use add_site_option because it doesn't include an 'autoload' argument
+ if (false !== $error_log && false === is_multisite()) {
+ delete_option('wpmdb_error_log');
+ add_option('wpmdb_error_log', $error_log, '', 'no');
+ }
+
+ if (isset($this->settings['delay_between_requests'])) {
+ $delay_between_requests = (int) $this->settings['delay_between_requests'];
+
+ if ($delay_between_requests >= 1000) {
+ $this->settings['delay_between_requests'] = $delay_between_requests / 1000;
+ update_site_option('wpmdb_settings', $this->settings);
+ }
+ }
+
+ $update_schema = true;
+ $schema_version = 3.2;
+ }
+
+ if ($schema_version < 3.6) {
+ $this->update_profiles();
+
+ $update_schema = true;
+ $schema_version = 3.6;
+ }
+
+ if (true === $update_schema) {
+ update_site_option('wpmdb_schema_version', $schema_version);
+ }
+
+ //Since 2.6.0, this is the way to manage upgrade routines.
+ $this->upgrade_routines_manager->perform_upgrade_routines();
+
+ do_action('wpmdb_after_schema_update', $schema_version);
+ }
+
+ function plugin_deactivated_notice()
+ {
+ if (false !== ($deactivated_notice_id = get_transient('wp_migrate_db_deactivated_notice_id'))) {
+ if ('1' === $deactivated_notice_id) {
+ $message = __("WP Migrate Lite and WP Migrate cannot both be active. We've automatically deactivated WP Migrate Lite.", 'wp-migrate-db');
+ } else {
+ $message = __("WP Migrate Lite and WP Migrate cannot both be active. We've automatically deactivated WP Migrate.", 'wp-migrate-db');
+ } ?>
+
+ Util::create_nonce('migrate-table'),
+ 'flush' => Util::create_nonce('flush'),
+ 'track_usage' => Util::create_nonce('track-usage'),
+ 'rest_nonce' => Util::create_nonce('wp_rest'),
+ ));
+
+ $site_details_extended = $this->migration_helper->siteDetails();
+ $site_details_extended['nonces'] = $nonces;
+
+ $data = $site_details_extended;
+
+ if (Util::isPro()) {
+ $data = apply_filters('wpmdb_data', $site_details_extended);
+ }
+
+ wp_localize_script('wp-migrate-db-pro-script-v2', 'wpmdb_data', $data);
+ }
+
+ /**
+ * When the "Use SSL for WP-admin and WP-login" option is checked in the
+ * WP Engine settings, the WP Engine must-use plugin buffers the output and
+ * does a find & replace for URLs. When we return PHP serialized data, it
+ * replaces http:// with https:// and corrupts the serialization.
+ * So here, we disable this filtering for our requests.
+ */
+ public function maybe_disable_wp_engine_filtering()
+ {
+ // Detect if the must-use WP Engine plugin is running
+ if (!defined('WPE_PLUGIN_BASE')) {
+ return;
+ }
+
+ // Make sure this is a WP Migrate DB Ajax request
+ if (!Util::is_ajax()) {
+ return;
+ }
+
+ // Turn off WP Engine's output filtering
+ if (!defined('WPE_NO_HTML_FILTER')) {
+ define('WPE_NO_HTML_FILTER', true);
+ }
+ }
+
+ public function plugins_dir()
+ {
+ $path = untrailingslashit($this->props->plugin_dir_path);
+
+ return substr($path, 0, strrpos($path, DIRECTORY_SEPARATOR)) . DIRECTORY_SEPARATOR;
+ }
+
+ /**
+ * Handler for ajax request to process a link click in a notice, e.g. licence deactivated ... re-check.
+ *
+ * @return bool|null
+ */
+ public function ajax_process_notice_link()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'notice' => 'string',
+ 'type' => 'key',
+ 'reminder' => 'int',
+ );
+
+ $_POST = Sanitize::sanitize_data($_POST, $key_rules, __METHOD__);
+
+ if (false === $_POST) {
+ $this->http->end_ajax(new \WP_Error(
+ 'wpmdb-process-link-ajax-failed',
+ __('AJAX request failed', 'wp-migrate-db')
+ ));
+ }
+
+ global $current_user;
+ $key = 'wpmdb_' . $_POST['type'] . '_' . $_POST['notice'];
+ $value = true;
+
+ if ('reminder' === $_POST['type'] && isset($_POST['reminder'])) {
+ $value = time() + (is_numeric($_POST['reminder']) ? $_POST['reminder'] : 604800);
+ }
+
+ update_user_meta($current_user->ID, $key, $value);
+
+ return $this->http->end_ajax('notice saved');
+ }
+
+ public function get_plugin_title()
+ {
+ return __('WP Migrate', 'wp-migrate-db');
+ }
+
+
+ /**
+ * Runs on schema update events, responsible for updating media file profiles.
+ */
+ private function update_profiles()
+ {
+ foreach (['wpmdb_saved_profiles', 'wpmdb_recent_migrations'] as $profile_type) {
+ $profiles = $profile_type === 'wpmdb_saved_profiles' ? $this->assets->get_saved_migration_profiles()
+ : $this->assets->get_recent_migrations(get_site_option($profile_type));
+ foreach ($profiles as $profile) {
+
+ $loaded_profile = $this->profile_manager->get_profile_by_id($profile_type === 'wpmdb_recent_migrations' ? 'unsaved' : $profile_type, $profile['id']);
+
+ if (is_wp_error($loaded_profile)) {
+ continue;
+ }
+
+ $profile_data = json_decode($loaded_profile['value']);
+
+ //Enable database migration by default for pre 2.3 profiles
+ if (!property_exists($profile_data->current_migration, 'databaseEnabled')) {
+ $profile_data->current_migration->databaseEnabled = true;
+ }
+
+ if (property_exists($profile_data, 'media_files') && !property_exists($profile_data->media_files, 'last_migration')) {
+ $profile_data->media_files->last_migration = property_exists($profile_data->media_files, 'date') ? $profile_data->media_files->date : null;
+ }
+
+ if (property_exists($profile_data, 'theme_plugin_files')) {
+ if (!property_exists($profile_data->theme_plugin_files, 'themes_option')) {
+ $profile_data->theme_plugin_files->themes_option = $profile_data->theme_plugin_files->themes_selected ? 'selected' : 'all';
+ }
+ if (!property_exists($profile_data->theme_plugin_files, 'plugins_option')) {
+ $profile_data->theme_plugin_files->plugins_option = $profile_data->theme_plugin_files->plugins_selected ? 'selected' : 'all';
+ }
+ if (!property_exists($profile_data->theme_plugin_files, 'themes_excluded')) {
+ $profile_data->theme_plugin_files->themes_excluded = [];
+ }
+ if (!property_exists($profile_data->theme_plugin_files, 'plugins_excluded')) {
+ $profile_data->theme_plugin_files->plugins_excluded = [];
+ }
+ if (!property_exists($profile_data->theme_plugin_files, 'plugins_excludes')) {
+ $profile_data->theme_plugin_files->plugins_excludes = property_exists($profile_data->theme_plugin_files, 'excludes')
+ ? $profile_data->theme_plugin_files->excludes
+ : '';
+ }
+ if (!property_exists($profile_data->theme_plugin_files, 'themes_excludes')) {
+ $profile_data->theme_plugin_files->themes_excludes = property_exists($profile_data->theme_plugin_files, 'excludes')
+ ? $profile_data->theme_plugin_files->excludes
+ : '';
+ }
+
+ //updates for others and muplugins added 2.3.4
+ if (!property_exists($profile_data->theme_plugin_files, 'other_files')) {
+ $profile_data->theme_plugin_files->other_files = ['enabled' => false];
+ $profile_data->theme_plugin_files->others_option = 'selected';
+ $profile_data->theme_plugin_files->others_selected = [];
+ $profile_data->theme_plugin_files->others_excludes = '';
+ }
+ if (!property_exists($profile_data->theme_plugin_files, 'muplugin_files')) {
+ $profile_data->theme_plugin_files->muplugin_files = ['enabled' => false];
+ $profile_data->theme_plugin_files->muplugins_option = 'selected';
+ $profile_data->theme_plugin_files->muplugins_selected = [];
+ $profile_data->theme_plugin_files->muplugins_excludes = '';
+ }
+
+ if (!property_exists($profile_data->theme_plugin_files, 'muplugin_files')) {
+ }
+ if (!property_exists($profile_data->theme_plugin_files, 'muplugins_option')) {
+ }
+ if (!property_exists($profile_data->theme_plugin_files, 'muplugins_selected')) {
+ }
+ if (!property_exists($profile_data->theme_plugin_files, 'muplugins_excludes')) {
+ }
+ }
+ //gonna need to update the profiles
+
+ $saved_profiles = get_site_option($profile_type);
+ $saved_profiles[$profile['id']]['value'] = json_encode($profile_data);
+ update_site_option($profile_type, $saved_profiles);
+ }
+ }
+ }
+
+ /**
+ * Filter admin footer text for Migrate pages
+ *
+ * @param string $text
+ * @param string $product_link
+ * @param string $wpe_link
+ * @return type
+ * @handles admin_footer_text
+ **/
+ public function generate_admin_footer_text($text, $product_link, $wpe_link)
+ {
+ if (empty($product_link) || empty($wpe_link)) {
+ return $text;
+ }
+ return sprintf(
+ /* translators: %1$s is a link to WP Migrate's website, and %2$s is a link to WP Engine's website. */
+ __('%1$s is developed and maintained by %2$s.', 'wp-migrate-db'),
+ $product_link,
+ $wpe_link
+ );
+ }
+
+ /**
+ * Generate Delicious Brains site URL with correct UTM tags.
+ *
+ * @param string $path
+ * @param array $args
+ * @param string $hash
+ *
+ * @return string
+ */
+ public static function delicious_brains_url( $path, $args = array(), $hash = '' ) {
+ $args = wp_parse_args( $args, array(
+ 'utm_medium' => 'insideplugin'
+ ) );
+ $args = array_map( 'urlencode', $args );
+ $url = trailingslashit( self::DBRAINS_URL ) . ltrim( $path, '/' );
+ $url = add_query_arg( $args, $url );
+ if ( $hash ) {
+ $url .= '#' . $hash;
+ }
+ return $url;
+ }
+
+ /**
+ * Generate WP Engine site URL with correct UTM tags.
+ *
+ * @param string $path
+ * @param array $args
+ * @param string $hash
+ *
+ * @return string
+ */
+ public static function wpe_url($path = '', $args = array(), $hash = '')
+ {
+ $args = wp_parse_args($args, [
+ 'utm_medium' => 'referral',
+ 'utm_campaign' => 'bx_prod_referral'
+ ]);
+ $args = array_map('urlencode', $args);
+ $url = trailingslashit(self::WPE_URL) . ltrim($path, '/');
+ $url = add_query_arg($args, $url);
+
+ if ($hash) {
+ $url .= '#' . $hash;
+ }
+
+ return $url;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Profile/ProfileImporter.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Profile/ProfileImporter.php
new file mode 100644
index 000000000..1ee8851a5
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Profile/ProfileImporter.php
@@ -0,0 +1,502 @@
+util = $util;
+ $this->table = $table;
+ }
+
+ /**
+ * Starts the profile import
+ *
+ * @param $schema_version
+ */
+ public function setProfileDefaults($schema_version)
+ {
+ if ($schema_version >= 3.1) {
+ return;
+ }
+
+ $new_opts = [
+ 'wpmdb_saved_profiles',
+ 'wpmdb_recent_migrations',
+ 'wpmdb_migration_options',
+ 'wpmdb_migration_state',
+ 'wpmdb_remote_response',
+ 'wpmdb_remote_migration_state',
+ ];
+
+ foreach ($new_opts as $opt) {
+ $saved_opt = get_site_option($opt);
+ if (empty($saved_opt)) {
+ update_site_option($opt, '');
+ }
+ }
+
+ $new_saved_profiles = get_site_option('wpmdb_saved_profiles'); //New profiles
+ $wpmdb_settings = get_site_option('wpmdb_settings');
+ $home = preg_replace('/^https?:/', '', Util::home_url());
+ $path = esc_html(addslashes(Util::get_absolute_root_file_path()));
+
+ $new_saved_profiles = $this->importOldProfiles($new_saved_profiles, $wpmdb_settings, $home, $path);
+
+ if (!empty($new_saved_profiles)) {
+ update_site_option('wpmdb_saved_profiles', $new_saved_profiles);
+ }
+
+ flush_rewrite_rules();
+ }
+
+
+ /**
+ *
+ * @param array $new_saved_profiles
+ * @param array $wpmdb_settings
+ * @param string $home
+ * @param string $path
+ *
+ * @return array|string|void
+ */
+ public function importOldProfiles($new_saved_profiles, $wpmdb_settings, $home, $path)
+ {
+ $old_profiles = isset($wpmdb_settings['profiles']) ? $wpmdb_settings['profiles'] : false;
+
+ if (empty($old_profiles)) {
+ return;
+ }
+
+ foreach ($old_profiles as $old_key => $profile) {
+ $profile = $this->profileFormat($profile, $home, $path);
+
+ if (!$profile) {
+ return false;
+ }
+
+ if (empty($new_saved_profiles)) {
+ $new_saved_profiles = [];
+ // Set index to start at 1
+ array_unshift($new_saved_profiles, "");
+ unset($new_saved_profiles[0]);
+ }
+
+ $new_saved_profiles[] = [
+ 'name' => $profile['current_migration']['profile_name'],
+ 'value' => json_encode($profile),
+ 'guid' => Util::uuidv4(),
+ 'date' => time(),
+ 'imported' => true,
+ 'old_id' => $old_key,
+ ];
+ }
+
+ // Unset the old profiles so we don't redo all this next schema upgrade.
+ unset($wpmdb_settings['profiles']);
+ update_site_option('wpmdb_settings', $wpmdb_settings);
+
+ return $new_saved_profiles;
+ }
+
+ /**
+ * @param array $profile
+ * @param array $current_migration_details
+ *
+ * @return array|bool
+ */
+ public function appendExtraProfileOpts($profile, $current_migration_details)
+ {
+ if (empty($profile)) {
+ return false;
+ }
+
+ $allowedExtraOpts = [
+ 'mst_select_subsite',
+ 'mst_selected_subsite',
+ 'mst_destination_subsite',
+ 'new_prefix',
+ 'media_files',
+ 'migrate_themes',
+ 'select_themes',
+ 'migrate_plugins',
+ 'select_plugins',
+ 'file_ignores',
+ 'mf_select_subsites',
+ 'mf_selected_subsites',
+ ];
+
+ foreach ($profile as $profile_key => $value) {
+ if (in_array($profile_key, $allowedExtraOpts)) {
+ $current_migration_details[$profile_key] = $value;
+ }
+ }
+
+ return $current_migration_details;
+ }
+
+ /**
+ * @param array $profile
+ * @param string $home
+ * @param string $path
+ *
+ * @return array
+ */
+ public function profileFormat($profile, $home, $path)
+ {
+ if (empty($profile)) {
+ return false;
+ }
+
+ $profileObj = (object)$profile;
+
+ if (!is_object($profileObj)) {
+ return false;
+ }
+
+ $intent = isset($profileObj->action) ? $profileObj->action : '';
+
+ list($connection_key, $url, $connection_info) = $this->computeConnectionInfo($profileObj);
+ $advanced_options = $this->computeAdvancedOptions($profileObj, $intent);
+
+ if (empty($this->valid_post_types)) {
+ $this->valid_post_types = $this->table->get_post_types();
+ }
+
+ $selected_post_types = !empty($profileObj->select_post_types) ? $profileObj->select_post_types : [];
+
+ /**
+ * In 1.9.x, post types were excluded, but in 2.0+ they are included.
+ *
+ * We don't have enough information for imports and pulls to fix this,
+ * so we leave the selected post types empty.
+ *
+ * For other migrations, we know what post types there could be so we fix that here.
+ */
+ $post_types = [];
+ if (!empty($selected_post_types) && !in_array($intent, ['import', 'pull'])) {
+ $post_types = array_values(array_diff($this->valid_post_types, $selected_post_types));
+ }
+
+ $custom_search_replace = $this->composeFindAndReplace($home, $path, $profileObj, $intent);
+ $current_migration_details = $this->composeCurrentMigrationDetails($profile, $intent, $profileObj, $post_types, $advanced_options);
+ $current_migration_details['cli_exclude_post_types'] = !empty($selected_post_types) ? $selected_post_types : [];
+
+ $formattedProfile = [
+ 'current_migration' => $current_migration_details,
+ 'connection_info' =>
+ [
+ 'connection_state' =>
+ [
+ 'value' => $connection_info,
+ 'url' => $url,
+ 'key' => $connection_key,
+ ],
+ ],
+ 'search_replace' =>
+ [
+ 'custom_search_replace' => $custom_search_replace ? $custom_search_replace : [],
+ 'standard_search_visible' => in_array($intent, ['pull', 'push', 'import']) ? true : false,
+ 'standard_options_enabled' => in_array($intent, ['pull', 'push', 'import']) ? ['domain', 'path'] : [],
+ ],
+ ];
+
+ // Addons.
+ $formattedProfile['media_files'] = $this->computeMediaFilesDetails($profileObj);
+ $formattedProfile['theme_plugin_files'] = $this->computeThemePluginDetails($profileObj);
+ $formattedProfile['multisite_tools'] = $this->computeMultisiteToolsDetails($profileObj);
+
+ return $formattedProfile;
+ }
+
+ /**
+ * Checks for legacy MST options.
+ *
+ * @param $profile
+ *
+ * @return array
+ */
+ public function computeMultisiteToolsDetails($profile)
+ {
+ // We might already be using the new format.
+ if (isset($profile->multisite_tools)) {
+ return $profile->multisite_tools;
+ }
+
+ // Set up some defaults.
+ $output = [
+ 'enabled' => false,
+ 'selected_subsite' => 0,
+ ];
+
+ if (property_exists($profile, 'mst_select_subsite') && $profile->mst_select_subsite === true && isset($profile->mst_selected_subsite)) {
+ $output['enabled'] = true;
+ $output['selected_subsite'] = (int) $profile->mst_selected_subsite;
+ if (isset($profile->mst_destination_subsite)) {
+ $output['destination_subsite'] = (int) $profile->mst_destination_subsite;
+ }
+ if (isset($profile->new_prefix)) {
+ $output['new_prefix'] = $profile->new_prefix;
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Gets legacy Media Files into a format we can work with.
+ *
+ * @param object $profile
+ *
+ * @return array
+ */
+ protected function computeMediaFilesDetails($profile)
+ {
+ // We might already be using the new format.
+ if (isset($profile->media_files, $profile->media_files['enabled'])) {
+ return $profile->media_files;
+ }
+
+ // None of the old media files options match up with 2.0 options,
+ // so this is all we can assume here.
+ $output = [
+ 'enabled' => false,
+ ];
+
+ if (isset($profile->media_files) && true == $profile->media_files) {
+ $output['enabled'] = true;
+ }
+
+ return $output;
+ }
+
+ protected function computeThemePluginDetails($profile)
+ {
+ // We might already be using the new format.
+ if (isset($profile->theme_plugin_files)) {
+ return $profile->theme_plugin_files;
+ }
+
+ // Set defaults rather than merge in on frontend
+ $output = [
+ 'plugin_files' => [
+ 'enabled' => false,
+ ],
+ 'plugins_option' => '',
+ 'plugins_selected' => [],
+ 'plugins_excluded' => [],
+ 'theme_files' => [
+ 'enabled' => false,
+ ],
+ 'themes_option' => '',
+ 'themes_selected' => [],
+ 'themes_excluded' => [],
+ ];
+
+ if (isset($profile->migrate_themes) && $profile->migrate_themes === '1') {
+ if (isset($profile->select_themes) && !empty($profile->select_themes)) {
+ $output['theme_files']['enabled'] = true;
+ $output['themes_selected'] = $profile->select_themes;
+ }
+ }
+
+ if (isset($profile->migrate_plugins) && $profile->migrate_plugins === '1') {
+ if (isset($profile->select_plugins) && !empty($profile->select_plugins)) {
+ $output['plugin_files']['enabled'] = true;
+ $output['plugins_selected'] = $profile->select_plugins;
+ }
+ }
+ //check if migrate_plugins === '1' and 'plugins_selected'
+ if (isset($profile->file_ignores)) {
+ $output['excludes'] = $profile->file_ignores;
+ }
+
+ return $output;
+ }
+
+ /**
+ * @param object $profileObj
+ *
+ * @return array
+ */
+ protected function computeConnectionInfo($profileObj)
+ {
+ $connection_info = $url = $connection_key = '';
+
+ if (empty($profileObj->connection_info)) {
+ return array($connection_key, $url, $connection_info);
+ }
+
+ $connection_info = $profileObj->connection_info;
+ $parts = explode(PHP_EOL, $connection_info);
+ $url = !empty($parts[0]) ? preg_replace('/\\r$/', '', $parts[0]) : '';
+ $connection_key = !empty($parts[1]) ? $parts[1] : '';
+
+ return array($connection_key, $url, $connection_info);
+ }
+
+ /**
+ * @param object $profileObj
+ * @param string $intent
+ *
+ * @return array
+ */
+ public function computeAdvancedOptions($profileObj, $intent)
+ {
+ $advanced_options = [];
+
+ $possibleAdvOptions = [
+ 'replace_guids',
+ 'exclude_spam',
+ 'exclude_transients',
+ 'keep_active_plugins',
+ 'compatibility_older_mysql',
+ 'gzip_file',
+ ];
+
+ $allowed_advanced_options = [
+ 'push' => [
+ 'replace_guids',
+ 'exclude_spam',
+ 'exclude_transients',
+ 'keep_active_plugins',
+ ],
+ 'pull' => [
+ 'replace_guids',
+ 'exclude_spam',
+ 'exclude_transients',
+ 'keep_active_plugins',
+ ],
+ 'import' => [
+ 'keep_active_plugins',
+ ],
+ 'savefile' => [
+ 'replace_guids',
+ 'exclude_spam',
+ 'exclude_transients',
+ 'compatibility_older_mysql',
+ 'gzip_file',
+ ],
+ 'find_replace' => [
+ 'replace_guids',
+ 'exclude_spam',
+ 'exclude_transients',
+ ],
+ ];
+
+ foreach ($possibleAdvOptions as $option) {
+ if (isset($profileObj->$option) && (string) $profileObj->$option === '1') {
+ if (!in_array($option, $allowed_advanced_options[$intent], true)) {
+ continue;
+ }
+ $advanced_options[] = $option;
+ }
+ }
+
+ return $advanced_options;
+ }
+
+ /**
+ * @param string $home
+ * @param string $path
+ * @param object $profileObj
+ * @param string $intent
+ *
+ * @return array|bool
+ */
+ public function composeFindAndReplace($home, $path, $profileObj, $intent)
+ {
+ if (!is_object($profileObj)) {
+ return false;
+ }
+
+ $custom_search_replace = [];
+
+ if (
+ !isset($profileObj->replace_old) || !is_array($profileObj->replace_old) ||
+ !isset($profileObj->replace_new) || !is_array($profileObj->replace_new)
+ ) {
+ return false;
+ }
+
+ $replace_old = $profileObj->replace_old;
+ $replace_new = $profileObj->replace_new;
+
+ if (in_array($intent, ['pull', 'push', 'import'])) {
+ $values = $intent === 'push' ? $replace_old : $replace_new;
+
+ foreach ($values as $replace_key => $value) {
+ if (($value === $home || $value === $path) && !isset($profileObj->cli_profile)) {
+ // Remove replacements as they'd be handled by the "Standard Search and Replace" options
+ unset($replace_old[$replace_key], $replace_new[$replace_key]);
+ }
+ }
+ }
+
+ foreach ($replace_old as $key => $item) {
+ $custom_search_replace[] =
+ [
+ 'replace_old' => $replace_old[$key],
+ 'replace_new' => $replace_new[$key],
+ 'id' => Util::uuidv4(),
+ ];
+ }
+
+ return $custom_search_replace;
+ }
+
+ /**
+ * @param array $profile
+ * @param string $intent
+ * @param object $profileObj
+ * @param array $post_types
+ * @param array $advanced_options
+ *
+ * @return array
+ */
+ protected function composeCurrentMigrationDetails($profile, $intent, $profileObj, array $post_types, array $advanced_options)
+ {
+ $current_migration_details = [
+ 'connected' => false,
+ 'intent' => $intent,
+ 'tables_option' => !empty($profileObj->table_migrate_option) && $profileObj->table_migrate_option === 'migrate_select' ? 'selected' : 'all',
+ 'tables_selected' => !empty($profileObj->select_tables) ? $profileObj->select_tables : [],
+ 'backup_option' => $profileObj->create_backup != "0" ? $profileObj->backup_option : 'none',
+ 'backup_tables_selected' => !empty($profileObj->select_backup) ? $profileObj->select_backup : [],
+ 'post_types_option' => !empty($profileObj->exclude_post_types) && $profileObj->exclude_post_types === '1' ? 'selected' : 'all',
+ 'post_types_selected' => $post_types,
+ 'advanced_options_selected' => $advanced_options,
+ 'profile_name' => $profileObj->name,
+ 'migration_enabled' => in_array($intent, ['savefile', 'find_replace']) ? true : false,
+ 'databaseEnabled' => isset($profile['databaseEnabled']) ? $profile['databaseEnabled'] : true
+ ];
+
+ $current_migration_details = $this->appendExtraProfileOpts($profile, $current_migration_details);
+
+ return $current_migration_details;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Profile/ProfileManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Profile/ProfileManager.php
new file mode 100644
index 000000000..3c03099bf
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Profile/ProfileManager.php
@@ -0,0 +1,698 @@
+default_profile = [
+ 'action' => 'savefile',
+ 'save_computer' => '1',
+ 'gzip_file' => '1',
+ 'table_migrate_option' => 'migrate_only_with_prefix',
+ 'replace_guids' => '1',
+ 'default_profile' => true,
+ 'name' => '',
+ 'select_tables' => [],
+ 'select_post_types' => [],
+ 'backup_option' => 'backup_only_with_prefix',
+ 'exclude_transients' => '1',
+ 'compatibility_older_mysql' => '0',
+ 'import_find_replace' => '1',
+ ];
+
+ $this->checkbox_options = [
+ 'save_computer' => '0',
+ 'gzip_file' => '0',
+ 'replace_guids' => '0',
+ 'exclude_spam' => '0',
+ 'keep_active_plugins' => '0',
+ 'create_backup' => '0',
+ 'exclude_post_types' => '0',
+ 'exclude_transients' => '0',
+ 'compatibility_older_mysql' => '0',
+ 'import_find_replace' => '0',
+ ];
+ $this->http = $http;
+ $this->properties = $properties;
+ $this->settings = $settings->get_settings();
+ $this->state_manager = $state_manager;
+ $this->util = $util;
+ $this->error_log = $error_log;
+ $this->table = $table;
+ $this->form_data = $form_data;
+ $this->http_helper = $http_helper;
+ $this->assets = $assets;
+ $this->rest_API_server = $rest_API_server;
+ $this->profile_importer = $profile_importer;
+ }
+
+ public function register()
+ {
+ // internal AJAX handlers
+ add_action('wp_ajax_wpmdb_delete_migration_profile', array($this, 'ajax_delete_migration_profile'));
+ add_action('wp_ajax_wpmdb_save_profile', array($this, 'ajax_save_profile'));
+
+ // REST endpoints
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ add_action('wpmdb_before_schema_update', [$this->profile_importer, 'setProfileDefaults']);
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute(
+ '/save-profile',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'save_profile'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/unsaved-profile',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'save_recent_migration'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/remove-recent-migration',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'remove_recent_migration'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/remove-profile',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'remove_profile'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/rename-profile',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'rename_profile'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/overwrite-profile',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'overwrite_profile'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/load-profile',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'load_profile'],
+ ]
+ );
+ }
+
+ public function save_profile()
+ {
+ $state_data = $this->set_state_data(
+ [
+ 'name' => 'string',
+ 'id' => 'int',
+ 'value' => 'json',
+ 'guid' => 'string',
+ 'fromRecent' => 'bool',
+ ],
+ 'save'
+ );
+
+ if (is_wp_error($state_data)) {
+ $this->http->end_ajax($state_data);
+ }
+
+ $existing_profiles = get_site_option('wpmdb_saved_profiles');
+
+ $profiles = [];
+
+ array_unshift($profiles, '');
+ unset($profiles[0]);
+
+ if (!empty($existing_profiles)) {
+ $profiles = $existing_profiles;
+ }
+
+ // @TODO Check if Profile already exists with same name
+ $date = current_time('timestamp');
+
+ $value = isset($state_data['value']) ? $state_data['value'] : '';
+
+ // Handle moving profile from 'Recent Migration' to saved profile
+ if (isset($state_data['fromRecent']) && $state_data['fromRecent']) {
+ $recent_migrations = get_site_option('wpmdb_recent_migrations');
+ if (!empty($recent_migrations) && isset($state_data['id'])) {
+ if (isset($recent_migrations[$state_data['id']])) {
+ $value = $recent_migrations[$state_data['id']]['value'];
+ }
+ }
+ }
+
+ $new_profile = [
+ 'name' => $state_data['name'],
+ 'value' => $value,
+ 'guid' => $state_data['guid'],
+ 'date' => $date,
+ ];
+
+ $profiles[] = $new_profile;
+ update_site_option('wpmdb_saved_profiles', $profiles);
+
+ return wp_send_json_success(['id' => max(array_keys($profiles))]);
+ }
+
+ public function save_recent_migration()
+ {
+ $max_recent_profiles = apply_filters('wpmdb_recent_profiles_limit', 10);
+ $saved_profiles = null;
+ $saved_profile_data = null;
+ $save_recent = true;
+
+ $state_data = $this->set_state_data(
+ [
+ 'name' => 'string',
+ 'id' => 'int',
+ 'value' => 'json',
+ 'guid' => 'string',
+ 'fromRecent' => 'bool',
+ ],
+ 'save'
+ );
+
+ if (is_wp_error($state_data)) {
+ $this->http->end_ajax($state_data);
+ }
+
+ $existing_profiles = get_site_option('wpmdb_recent_migrations');
+
+ if ($state_data['id'] !== null) {
+ $saved_profiles = get_site_option('wpmdb_saved_profiles');
+
+ if ($saved_profiles) {
+ //get saved profile by id
+
+ if (isset($saved_profiles[$state_data['id']])) {
+ $saved_profile_data = $saved_profiles[$state_data['id']];
+ //diff
+ if ($saved_profile_data['value'] === $state_data['value']) {
+ $save_recent = false;
+ }
+ }
+ }
+ }
+
+ if (!$save_recent) {
+ return wp_send_json_success('not saved');
+ }
+
+ $profiles = [];
+
+ if (!empty($existing_profiles)) {
+ $profiles = $existing_profiles;
+ }
+
+ $date = current_time('timestamp');
+
+ // @TODO Check if Profile already exists with same names
+ $new_profile = [
+ 'name' => $state_data['name'],
+ 'value' => $state_data['value'],
+ 'date' => $date,
+ 'guid' => $state_data['guid'],
+ ];
+
+ // Check if we've already got $max_recent_profiles saved, if so, remove oldest (index 0)
+ if (count($profiles) === $max_recent_profiles) {
+ \array_splice($profiles, 0, 1);
+ }
+
+ $profiles[] = $new_profile;
+
+ update_site_option('wpmdb_recent_migrations', $profiles);
+
+ return wp_send_json_success(['date' => $date, 'id' => max(array_keys($profiles)), 'profiles' => $this->assets->get_recent_migrations($profiles)]);
+ }
+
+ public function remove_recent_migration()
+ {
+ $state_data = $this->set_state_data(
+ [
+ 'id' => 'int',
+ ],
+ 'remove'
+ );
+
+ if (is_wp_error($state_data)) {
+ return Util::throw_ajax_error($state_data->get_error_message());
+ }
+
+ $existing_profiles = get_site_option('wpmdb_recent_migrations');
+
+ if (empty($existing_profiles)) {
+ return wp_send_json_error(__('No recent migrations', 'wp-migrate-db'));
+ }
+
+ unset($existing_profiles[$state_data['id']]);
+
+ update_site_option('wpmdb_recent_migrations', $existing_profiles);
+
+ return wp_send_json_success($state_data['id'] . ' Removed 👍');
+ }
+
+ public function remove_profile()
+ {
+ $state_data = $this->set_state_data(
+ [
+ 'guid' => 'text',
+ ],
+ 'remove'
+ );
+
+ if (is_wp_error($state_data)) {
+ return Util::throw_ajax_error($state_data->get_error_message());
+ }
+
+ $saved_profiles = get_site_option('wpmdb_saved_profiles');
+
+ if (empty($saved_profiles) || !\is_array($saved_profiles)) {
+ return wp_send_json_error(__('Profile not found.', 'wp-migrate-db'));
+ }
+
+ $profile_key = 0;
+ foreach ($saved_profiles as $key => $profile) {
+ if ($profile['guid'] === $state_data['guid']) {
+ $profile_key = $key;
+ }
+ }
+
+ unset($saved_profiles[$profile_key]);
+ update_site_option('wpmdb_saved_profiles', $saved_profiles);
+
+ return wp_send_json_success(__('Profile removed', 'wp-migrate-db'));
+ }
+
+ public function rename_profile()
+ {
+ $state_data = $this->set_state_data(
+ [
+ 'guid' => 'text',
+ 'name' => 'text',
+ ],
+ 'rename'
+ );
+
+ if (is_wp_error($state_data)) {
+ return Util::throw_ajax_error($state_data->get_error_message());
+ }
+
+ $saved_profiles = get_site_option('wpmdb_saved_profiles');
+
+ if (empty($saved_profiles) || !\is_array($saved_profiles)) {
+ return wp_send_json_error(__('Profile not found.', 'wp-migrate-db'));
+ }
+
+ $profile_key = 0;
+ foreach ($saved_profiles as $key => $profile) {
+ if ($profile['guid'] === $state_data['guid']) {
+ $profile_key = $key;
+ }
+ }
+
+ $saved_profiles[$profile_key]['name'] = $state_data['name'];
+
+ update_site_option('wpmdb_saved_profiles', $saved_profiles);
+
+ return wp_send_json_success(__('Profile saved', 'wp-migrate-db'));
+ }
+
+ public function overwrite_profile()
+ {
+ $state_data = $this->set_state_data(
+ [
+ 'guid' => 'text',
+ 'contents' => 'json',
+ ],
+ 'overwrite'
+ );
+
+ if (is_wp_error($state_data)) {
+ return Util::throw_ajax_error($state_data->get_error_message());
+ }
+
+ $saved_profiles = get_site_option('wpmdb_saved_profiles');
+
+ if (empty($saved_profiles) || !\is_array($saved_profiles)) {
+ return wp_send_json_error(__('Profile not found.', 'wp-migrate-db'));
+ }
+
+ $profile_key = 0;
+ foreach ($saved_profiles as $key => $profile) {
+ if ($profile['guid'] === $state_data['guid']) {
+ $profile_key = $key;
+ }
+ }
+
+ $profile_data = apply_filters('wpmdb_overwrite_profile', $state_data['contents']);
+
+ $saved_profiles[$profile_key]['value'] = $profile_data;
+
+ // We should have formatted everything correctly by now.
+ if (isset($saved_profiles[$profile_key]['imported'])) {
+ unset($saved_profiles[$profile_key]['imported']);
+ }
+
+ update_site_option('wpmdb_saved_profiles', $saved_profiles);
+
+ return wp_send_json_success(__('Profile saved', 'wp-migrate-db'));
+ }
+
+ public function load_profile()
+ {
+ $state_data = $this->set_state_data(
+ [
+ 'id' => 'string',
+ 'unSaved' => 'bool',
+ ],
+ 'load'
+ );
+
+ if (is_wp_error($state_data)) {
+ return Util::throw_ajax_error($state_data->get_error_message());
+ }
+
+ $profile_id = $state_data['id'];
+ $unsaved = $state_data['unSaved'] ? 'unsaved' : 'saved';
+
+ $the_profile = $this->get_profile_by_id($unsaved, $profile_id);
+
+ if (is_wp_error($the_profile)) {
+ wp_send_json_error($the_profile->get_error_message());
+ }
+
+ $parsed_profile = json_decode($the_profile['value'], true);
+ $parsed_profile['profile_type'] = $state_data['unSaved'] ? 'unsaved' : 'saved';
+
+ $the_profile['value'] = json_encode($parsed_profile);
+
+ return wp_send_json_success(['id' => $profile_id, 'profile' => $the_profile]);
+ }
+
+ public function set_state_data($key_rules, $action)
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $context = $this->util->get_caller_function();
+
+ $state_data = Sanitize::sanitize_data($_POST, $key_rules, $context);
+
+ if (empty($state_data)) {
+ return new \WP_Error('profile-save-failed', sprintf(__('Failed to %s profile, state data is empty.', 'wp-migrate-db'), $action));
+ }
+
+ return $state_data;
+ }
+
+ /**
+ * Handler for deleting a migration profile.
+ *
+ * @return bool|null
+ */
+ function ajax_delete_migration_profile()
+ {
+ $this->http->check_ajax_referer('delete-migration-profile');
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'profile_id' => 'positive_int',
+ 'nonce' => 'key',
+ );
+
+ $state_data = $this->state_manager->set_post_data($key_rules);
+
+ $key = absint($state_data['profile_id']);
+ --$key;
+ $return = '';
+
+ if (isset($this->settings['profiles'][$key])) {
+ unset($this->settings['profiles'][$key]);
+ update_site_option('wpmdb_settings', $this->settings);
+ } else {
+ $return = '-1';
+ }
+
+ $result = $this->http->end_ajax($return);
+
+ return $result;
+ }
+
+ /**
+ * Handler for the ajax request to save a migration profile.
+ *
+ * @return bool|null
+ */
+ function ajax_save_profile()
+ {
+ $this->http->check_ajax_referer('save-profile');
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'profile' => 'string',
+ 'nonce' => 'key',
+ );
+ $state_data = $this->state_manager->set_post_data($key_rules);
+
+ // ***+=== @TODO - revisit usage of parse_migration_form_data
+ $profile = $this->form_data->parse_and_save_migration_form_data($state_data['profile']);
+ $profile = wp_parse_args($profile, $this->checkbox_options);
+
+ if (isset($profile['save_migration_profile_option']) && $profile['save_migration_profile_option'] == 'new') {
+ $profile['name'] = $profile['create_new_profile'];
+ $this->settings['profiles'][] = $profile;
+ } else {
+ $key = $profile['save_migration_profile_option'];
+ $name = $this->settings['profiles'][$key]['name'];
+ $this->settings['profiles'][$key] = $profile;
+ $this->settings['profiles'][$key]['name'] = $name;
+ }
+
+ update_site_option('wpmdb_settings', $this->settings);
+ end($this->settings['profiles']);
+ $key = key($this->settings['profiles']);
+ $result = $this->http->end_ajax($key);
+
+ return $result;
+ }
+
+ function maybe_update_profile($profile, $profile_id)
+ {
+ $profile_changed = false;
+
+ if (isset($profile['exclude_revisions'])) {
+ unset($profile['exclude_revisions']);
+ $profile['select_post_types'] = array('revision');
+ $profile_changed = true;
+ }
+
+ if (isset($profile['post_type_migrate_option']) && 'migrate_select_post_types' == $profile['post_type_migrate_option'] && 'pull' != $profile['action']) {
+ unset($profile['post_type_migrate_option']);
+ $profile['exclude_post_types'] = '1';
+ $all_post_types = $this->table->get_post_types();
+ $profile['select_post_types'] = array_diff($all_post_types, $profile['select_post_types']);
+ $profile_changed = true;
+ }
+
+ if ($profile_changed) {
+ $this->settings['profiles'][$profile_id] = $profile;
+ update_site_option('wpmdb_settings', $this->settings);
+ }
+
+ return $profile;
+ }
+
+ // Retrieves the specified profile, if -1, returns the default profile
+ function get_profile($profile_id)
+ {
+ --$profile_id;
+
+ if ($profile_id == '-1' || !isset($this->settings['profiles'][$profile_id])) {
+ return $this->default_profile;
+ }
+
+ return $this->settings['profiles'][$profile_id];
+ }
+
+ /**
+ * @param $migration_details
+ *
+ * @return array
+ */
+ protected function filter_selected_tables($migration_details, $key, $all_tables)
+ {
+ $tables = $migration_details[$key];
+ $filtered_tables = array_filter(
+ $tables,
+ function ($item) use (&$all_tables) {
+ if (in_array($item, $all_tables)) {
+ return true;
+ }
+ }
+ );
+
+ return $filtered_tables;
+ }
+
+ /**
+ * @param $option_key
+ * @param $profile_id
+ *
+ * @return bool|mixed|\WP_Error
+ */
+ public function get_profile_by_id($option_key, $profile_id)
+ {
+ $profile_type = $option_key === 'unsaved' ? 'wpmdb_recent_migrations' : 'wpmdb_saved_profiles';
+
+ $saved_profiles = get_site_option($profile_type);
+
+ if (empty($saved_profiles) || !\is_array($saved_profiles)) {
+ return new \WP_Error('wpmdb_profile_not_found', __('Profile not found.', 'wp-migrate-db'));
+ }
+
+ $profile_key = null;
+ foreach ($saved_profiles as $key => $profile) {
+ if ($key === (int)$profile_id) {
+ $profile_key = $key;
+ break;
+ }
+ }
+
+ if (!isset($saved_profiles[$profile_key])) {
+ return new \WP_Error('wpmdb_profile_not_found', __('Profile not found.', 'wp-migrate-db'));
+ }
+
+ $the_profile = $saved_profiles[$profile_key];
+
+ return $the_profile;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Properties/DynamicProperties.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Properties/DynamicProperties.php
new file mode 100644
index 000000000..7d5efbc82
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Properties/DynamicProperties.php
@@ -0,0 +1,20 @@
+transient_timeout = 60 * 60 * 12;
+ $this->transient_retry_timeout = 60 * 60 * 2;
+
+ $free_plugin_active = is_plugin_active('wp-migrate-db-pro/wp-migrate-db.php');
+ $pro_plugin_active = is_plugin_active('wp-migrate-db-pro/wp-migrate-db-pro.php');
+
+ if ($pro_plugin_active && !$free_plugin_active) {
+ $is_pro = true;
+ }
+
+ $this->invalid_content_verification_error = __('Invalid content verification signature, please verify the connection information on the remote site and try again.', 'wp-migrate-db') . sprintf(__(' Remote URL: %s ', 'Ex. Remote URL: http://wp.dev', 'wp-migrate-db'), Util::home_url());
+
+ $this->plugin_file_path = $is_pro ? realpath(dirname(__DIR__) . '/../../wp-migrate-db-pro.php') : realpath(dirname(__DIR__) . '/../../wp-migrate-db.php');
+
+ if ($is_pro) {
+ $this->unhook_templates = ['wordpress_org_support', 'progress_upgrade', 'sidebar'];
+ }
+
+ $this->plugin_dir_path = plugin_dir_path($this->plugin_file_path);
+ $this->plugin_folder_name = basename($this->plugin_dir_path);
+ $this->plugin_basename = plugin_basename($this->plugin_file_path);
+ $this->template_dir = $this->plugin_dir_path . 'template' . DIRECTORY_SEPARATOR;
+ $this->plugin_title = ucwords(str_ireplace('-', ' ', basename($this->plugin_file_path)));
+ $this->plugin_title = str_ireplace(array('db', 'wp', '.php'), array('DB', 'WP', ''), $this->plugin_title);
+
+ $this->mu_plugin_dir = (defined('WPMU_PLUGIN_DIR') && defined('WPMU_PLUGIN_URL')) ? WPMU_PLUGIN_DIR : trailingslashit(WP_CONTENT_DIR) . 'mu-plugins';
+ $this->mu_plugin_source = trailingslashit($this->plugin_dir_path) . 'compatibility/wp-migrate-db-pro-compatibility.php';
+ $this->mu_plugin_dest = trailingslashit($this->mu_plugin_dir) . 'wp-migrate-db-pro-compatibility.php';
+
+ // We need to set $this->plugin_slug here because it was set here
+ // in Media Files prior to version 1.1.2. If we remove it the customer
+ // cannot upgrade, view release notes, etc
+ // used almost exclusively as a identifier for plugin version checking (both core and addons)
+ $this->plugin_slug = basename($this->plugin_file_path, '.php');
+
+ // used to add admin menus and to identify the core version in the $GLOBALS['wpmdb_meta'] variable for delicious brains api calls, version checking etc
+ $this->core_slug = $is_pro ? 'wp-migrate-db-pro' : 'wp-migrate-db';
+ $this->is_pro = $is_pro;
+
+ if (is_multisite()) {
+ $this->plugin_base = 'settings.php?page=' . $this->core_slug;
+ } else {
+ $this->plugin_base = 'tools.php?page=' . $this->core_slug;
+ }
+
+ if (empty($this->core_slug)) {
+ $this->core_slug;
+ }
+
+ if (isset($GLOBALS['wpmdb_meta'][$this->core_slug])) {
+ $this->plugin_version = $GLOBALS['wpmdb_meta'][$this->core_slug]['version'];
+ }
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connection.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connection.php
new file mode 100644
index 000000000..a7b471006
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connection.php
@@ -0,0 +1,79 @@
+get('temp_prefix');
+ }
+
+ // We should be able to call parent to set database
+ // and allowed_job_classes, but unit test setup is broken
+ // and throws a wobbly in Mockery. Yay, mocks. 😞
+ // parent::__construct($wpdb, $allowed_job_classes);
+ $this->database = $wpdb;
+ $this->allowed_job_classes = $allowed_job_classes;
+
+ $this->jobs_table = $prefix . 'queue_jobs';
+ $this->failures_table = $prefix . 'queue_failures';
+ }
+
+ /**
+ * Get list of jobs in queue
+ *
+ * @param int $limit
+ * @param int $offset
+ * @param bool $raw if true, method will return serialized instead of instantiated objects
+ *
+ * @return array|WP_Error
+ */
+ public function list_jobs( $limit, $offset, $raw = false ) {
+ $offset = null === $offset ? 0 : $offset;
+ $limit = null === $limit ? 9999999 : $limit;
+ $raw_sql = "
+ SELECT * FROM {$this->jobs_table}
+ WHERE reserved_at IS NULL
+ AND available_at <= %s
+ LIMIT %d,%d
+ ";
+ $sql = $this->database->prepare( $raw_sql, $this->datetime(), $offset, $limit );
+ $results = $this->database->get_results( $sql );
+
+ if ( $raw ) {
+ return $results;
+ }
+
+ $jobs = [];
+ foreach ( $results as $raw_job ) {
+ $job = $this->vitalize_job($raw_job);
+
+ if ($job && is_a($job, Job::class)) {
+ $jobs[$raw_job->id] = $job;
+ } else {
+ return new WP_Error(
+ 'invalid-queue-job',
+ __('An invalid item was found in the queue of files to be transferred.', 'wp-migrate-db')
+ );
+ }
+ }
+
+ return $jobs;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connections/ConnectionInterface.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connections/ConnectionInterface.php
new file mode 100644
index 000000000..31ca2a444
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connections/ConnectionInterface.php
@@ -0,0 +1,65 @@
+database = $wpdb;
+ $this->allowed_job_classes = $allowed_job_classes;
+ $this->jobs_table = $this->database->prefix . 'queue_jobs';
+ $this->failures_table = $this->database->prefix . 'queue_failures';
+ }
+
+ /**
+ * Push a job onto the queue.
+ *
+ * @param Job $job
+ * @param int $delay
+ *
+ * @return bool|int
+ */
+ public function push( Job $job, $delay = 0 ) {
+ $result = $this->database->insert( $this->jobs_table, array(
+ 'job' => serialize( $job ),
+ 'available_at' => $this->datetime( $delay ),
+ 'created_at' => $this->datetime(),
+ ) );
+
+ if ( ! $result ) {
+ return false;
+ }
+
+ return $this->database->insert_id;
+ }
+
+ /**
+ * Retrieve a job from the queue.
+ *
+ * @return bool|Job
+ */
+ public function pop() {
+ $this->release_reserved();
+
+ $sql = $this->database->prepare( "
+ SELECT * FROM {$this->jobs_table}
+ WHERE reserved_at IS NULL
+ AND available_at <= %s
+ ORDER BY available_at
+ LIMIT 1
+ ", $this->datetime() );
+
+ $raw_job = $this->database->get_row( $sql );
+
+ if ( is_null( $raw_job ) ) {
+ return false;
+ }
+
+ $job = $this->vitalize_job( $raw_job );
+
+ if ($job && is_a($job, Job::class)) {
+ $this->reserve($job);
+ }
+
+ return $job;
+ }
+
+ /**
+ * Delete a job from the queue.
+ *
+ * @param Job $job
+ *
+ * @return bool
+ */
+ public function delete( $job ) {
+ if (is_a($job, Job::class)) {
+ $id = $job->id();
+ } elseif (is_object($job) && property_exists($job, 'id')) {
+ $raw_job = (object)$job;
+ $id = $raw_job->id;
+ } else {
+ return false;
+ }
+
+ $where = array(
+ 'id' => $id,
+ );
+
+ if ( $this->database->delete( $this->jobs_table, $where ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Release a job back onto the queue.
+ *
+ * @param Job $job
+ *
+ * @return bool
+ */
+ public function release( Job $job ) {
+ $data = array(
+ 'job' => serialize( $job ),
+ 'attempts' => $job->attempts(),
+ 'reserved_at' => null,
+ );
+ $where = array(
+ 'id' => $job->id(),
+ );
+
+ if ( $this->database->update( $this->jobs_table, $data, $where ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Push a job onto the failure queue.
+ *
+ * @param Job $job
+ * @param Exception $exception
+ *
+ * @return bool
+ */
+ public function failure( $job, Exception $exception ) {
+ $insert = $this->database->insert( $this->failures_table, array(
+ 'job' => serialize( $job ),
+ 'error' => $this->format_exception( $exception ),
+ 'failed_at' => $this->datetime(),
+ ) );
+
+ if ( $insert ) {
+ $this->delete( $job );
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get total jobs in the queue.
+ *
+ * @return int
+ */
+ public function jobs() {
+ $sql = "SELECT COUNT(*) FROM {$this->jobs_table}";
+
+ return (int) $this->database->get_var( $sql );
+ }
+
+ /**
+ * Get total jobs in the failures queue.
+ *
+ * @return int
+ */
+ public function failed_jobs() {
+ $sql = "SELECT COUNT(*) FROM {$this->failures_table}";
+
+ return (int) $this->database->get_var( $sql );
+ }
+
+ /**
+ * Reserve a job in the queue.
+ *
+ * @param Job $job
+ */
+ protected function reserve( $job ) {
+ $data = array(
+ 'reserved_at' => $this->datetime(),
+ );
+
+ $this->database->update( $this->jobs_table, $data, array(
+ 'id' => $job->id(),
+ ) );
+ }
+
+ /**
+ * Release reserved jobs back onto the queue.
+ */
+ protected function release_reserved() {
+ $expired = $this->datetime( -300 );
+
+ $sql = $this->database->prepare( "
+ UPDATE {$this->jobs_table}
+ SET attempts = attempts + 1, reserved_at = NULL
+ WHERE reserved_at <= %s", $expired );
+
+ $this->database->query( $sql );
+ }
+
+ /**
+ * Vitalize Job with latest data.
+ *
+ * @param mixed $raw_job
+ *
+ * @return Job|bool
+ */
+ protected function vitalize_job($raw_job)
+ {
+ $options = [];
+ if ( ! empty($this->allowed_job_classes)) {
+ $options['allowed_classes'] = $this->allowed_job_classes;
+ }
+
+ // Because we support PHP versions less than 7.0 we need to use the polyfill.
+ $job = Unserialize::unserialize($raw_job->job, $options);
+
+ if ( ! is_a($job, Job::class)) {
+ $this->failure($raw_job, new InvalidJobTypeException());
+
+ return false;
+ }
+
+ $job->set_id($raw_job->id);
+ $job->set_attempts($raw_job->attempts);
+ $job->set_reserved_at(empty($raw_job->reserved_at) ? null : new DateTime($raw_job->reserved_at));
+ $job->set_available_at(new DateTime($raw_job->available_at));
+ $job->set_created_at(new DateTime($raw_job->created_at));
+
+ return $job;
+ }
+
+ /**
+ * Get MySQL datetime.
+ *
+ * @param int $offset Seconds, can pass negative int.
+ *
+ * @return string
+ */
+ protected function datetime( $offset = 0 ) {
+ $timestamp = time() + $offset;
+
+ return gmdate( 'Y-m-d H:i:s', $timestamp );
+ }
+
+ /**
+ * Format an exception error string.
+ *
+ * @param Exception $exception
+ *
+ * @return string
+ */
+ protected function format_exception( Exception $exception ) {
+ $string = get_class( $exception );
+ $message = $exception->getMessage();
+ if ( ! empty( $message ) ) {
+ $string .= " : {$exception->getMessage()}";
+ }
+ $code = $exception->getCode();
+ if ( ! empty( $code ) ) {
+ $string .= " (#{$code})";
+ }
+
+ return $string;
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connections/RedisConnection.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connections/RedisConnection.php
new file mode 100644
index 000000000..a01a77dbc
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Connections/RedisConnection.php
@@ -0,0 +1,77 @@
+id = strtolower( str_replace( '\\', '_', $id ) );
+ $this->worker = $worker;
+ $this->interval = $interval;
+ }
+
+ /**
+ * Is the cron queue worker enabled?
+ *
+ * @return bool
+ */
+ protected function is_enabled() {
+ if ( defined( 'DISABLE_WP_QUEUE_CRON' ) && DISABLE_WP_QUEUE_CRON ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Init cron class.
+ *
+ * @return bool
+ */
+ public function init() {
+ if ( ! $this->is_enabled() ) {
+ return false;
+ }
+
+ add_filter( 'cron_schedules', array( $this, 'schedule_cron' ) );
+ add_action( $this->id, array( $this, 'cron_worker' ) );
+
+ if ( ! wp_next_scheduled( $this->id ) ) {
+ // Schedule health check
+ wp_schedule_event( time(), $this->id, $this->id );
+ }
+
+ return true;
+ }
+
+ /**
+ * Add interval to cron schedules.
+ *
+ * @param array $schedules
+ *
+ * @return array
+ */
+ public function schedule_cron( $schedules ) {
+ $schedules[ $this->id ] = array(
+ 'interval' => MINUTE_IN_SECONDS * $this->interval,
+ 'display' => sprintf( __( 'Every %d Minutes' ), $this->interval ),
+ );
+
+ return $schedules;
+ }
+
+ /**
+ * Process any jobs in the queue.
+ */
+ public function cron_worker() {
+ if ( $this->is_worker_locked() ) {
+ return;
+ }
+
+ $this->start_time = time();
+
+ $this->lock_worker();
+
+ while ( ! $this->time_exceeded() && ! $this->memory_exceeded() ) {
+ if ( ! $this->worker->process() ) {
+ break;
+ }
+ }
+
+ $this->unlock_worker();
+ }
+
+ /**
+ * Is the cron worker locked?
+ *
+ * @return bool
+ */
+ protected function is_worker_locked() {
+ return (bool) get_site_transient( $this->id );
+ }
+
+ /**
+ * Lock the cron worker.
+ */
+ protected function lock_worker() {
+ set_site_transient( $this->id, time(), 300 );
+ }
+
+ /**
+ * Unlock the cron worker.
+ */
+ protected function unlock_worker() {
+ delete_site_transient( $this->id );
+ }
+
+ /**
+ * Memory exceeded
+ *
+ * Ensures the worker process never exceeds 80%
+ * of the maximum allowed PHP memory.
+ *
+ * @return bool
+ */
+ protected function memory_exceeded() {
+ $memory_limit = $this->get_memory_limit() * 0.8; // 80% of max memory
+ $current_memory = memory_get_usage( true );
+ $return = false;
+
+ if ( $current_memory >= $memory_limit ) {
+ $return = true;
+ }
+
+ return apply_filters( 'wp_queue_cron_memory_exceeded', $return );
+ }
+
+ /**
+ * Get memory limit.
+ *
+ * @return int
+ */
+ protected function get_memory_limit() {
+ if ( function_exists( 'ini_get' ) ) {
+ $memory_limit = ini_get( 'memory_limit' );
+ } else {
+ $memory_limit = '256M';
+ }
+
+ if ( ! $memory_limit || - 1 == $memory_limit ) {
+ // Unlimited, set to 1GB
+ $memory_limit = '1000M';
+ }
+
+ return intval( $memory_limit ) * 1024 * 1024;
+ }
+
+ /**
+ * Time exceeded
+ *
+ * Ensures the worker never exceeds a sensible time limit (20s by default).
+ * A timeout limit of 30s is common on shared hosting.
+ *
+ * @return bool
+ */
+ protected function time_exceeded() {
+ $finish = $this->start_time + apply_filters( 'wp_queue_cron_time_limit', 20 ); // 20 seconds
+ $return = false;
+
+ if ( time() >= $finish ) {
+ $return = true;
+ }
+
+ return apply_filters( 'wp_queue_cron_time_exceeded', $return );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Exceptions/ConnectionNotFoundException.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Exceptions/ConnectionNotFoundException.php
new file mode 100644
index 000000000..6d848b979
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Exceptions/ConnectionNotFoundException.php
@@ -0,0 +1,9 @@
+id;
+ }
+
+ /**
+ * Set job ID.
+ *
+ * @param int $id
+ */
+ public function set_id( $id ) {
+ $this->id = $id;
+ }
+
+ /**
+ * Get job attempts;
+ *
+ * @return int
+ */
+ public function attempts() {
+ return $this->attempts;
+ }
+
+ /**
+ * Set job attempts.
+ *
+ * @param int $attempts
+ */
+ public function set_attempts( $attempts ) {
+ $this->attempts = $attempts;
+ }
+
+ /**
+ * Get reserved at date.
+ *
+ * @return DateTime
+ */
+ public function reserved_at() {
+ return $this->reserved_at;
+ }
+
+ /**
+ * Set reserved at date.
+ *
+ * @param null|DateTime $reserved_at
+ */
+ public function set_reserved_at( $reserved_at ) {
+ $this->reserved_at = $reserved_at;
+ }
+
+ /**
+ * Get available at date.
+ *
+ * @return DateTime
+ */
+ public function available_at() {
+ return $this->available_at;
+ }
+
+ /**
+ * Set available at date.
+ *
+ * @param DateTime $available_at
+ */
+ public function set_available_at( DateTime $available_at ) {
+ $this->available_at = $available_at;
+ }
+
+ /**
+ * Get created at date.
+ *
+ * @return DateTime
+ */
+ public function created_at() {
+ return $this->created_at;
+ }
+
+ /**
+ * Set created at date.
+ *
+ * @param DateTime $created_at
+ */
+ public function set_created_at( DateTime $created_at ) {
+ $this->created_at = $created_at;
+ }
+
+ /**
+ * Flag job as released.
+ */
+ public function release() {
+ $this->released = true;
+ $this->attempts += 1;
+ }
+
+ /**
+ * Should the job be released back onto the queue?
+ *
+ * @return bool
+ */
+ public function released() {
+ return $this->released;
+ }
+
+ /**
+ * Flag job as failed.
+ */
+ public function fail() {
+ $this->failed = true;
+ }
+
+ /**
+ * Has the job failed?
+ *
+ * @return bool
+ */
+ public function failed() {
+ return $this->failed;
+ }
+
+ /**
+ * Determine which properties should be serialized.
+ *
+ * @return array
+ */
+ public function __sleep() {
+ $object_props = get_object_vars( $this );
+ $excluded_props = array(
+ 'id',
+ 'attempts',
+ 'reserved_at',
+ 'available_at',
+ 'created_at',
+ 'released',
+ 'failed',
+ );
+
+ foreach ( $excluded_props as $prop ) {
+ unset( $object_props[ $prop ] );
+ }
+
+ return array_keys( $object_props );
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Jobs/WPMDB_Job.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Jobs/WPMDB_Job.php
new file mode 100644
index 000000000..dd1abc809
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Jobs/WPMDB_Job.php
@@ -0,0 +1,26 @@
+file = $file;
+ }
+
+ /**
+ * Handle job logic.
+ */
+ public function handle() {
+ return true;
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Manager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Manager.php
new file mode 100644
index 000000000..ad1838381
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Manager.php
@@ -0,0 +1,211 @@
+wpdb = $GLOBALS['wpdb'];
+ $this->properties = $properties;
+ $this->state_data_container = $state_data_container;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->form_data = $form_data;
+
+ $this->prefix = $this->properties->temp_prefix;
+ $this->jobs_table = $this->prefix . "queue_jobs";
+ $this->failures_table = $this->prefix . "queue_failures";
+
+ $this->connection = new Connection($GLOBALS['wpdb'], [Jobs\WPMDB_Job::class], $properties->temp_prefix);
+ $this->queue = new Queue($this->connection, $this->prefix);
+ $this->worker = new Worker($this->connection, 1);
+ }
+
+ public function register()
+ {
+ add_action('wpmdb_initiate_migration', [$this, 'ensure_tables_exist']);
+ }
+
+ function enqueue_file($file)
+ {
+ return $this->enqueue_job(new Jobs\WPMDB_Job($file));
+ }
+
+ function enqueue_job(Job $job)
+ {
+ return $this->queue->push($job);
+ }
+
+ function process()
+ {
+ return $this->worker->process();
+ }
+
+ function ensure_tables_exist($state_data)
+ {
+ // ***+=== @TODO - revisit usage of parse_migration_form_data
+ $form_data = $this->form_data->parse_and_save_migration_form_data($state_data['form_data']);
+
+ if (!\in_array($state_data['intent'], ['push', 'pull', 'savefile'])) {
+ return;
+ }
+ $stages = $form_data['current_migration']['stages'];
+
+ $allowed_migration_types = [
+ 'theme_files',
+ 'plugin_files',
+ 'muplugin_files',
+ 'other_files',
+ 'core_files',
+ 'media_files'
+ ];
+
+ if (
+ empty(array_intersect($stages, $allowed_migration_types))
+ ) {
+ return;
+ }
+
+ $this->create_tables(true);
+ }
+
+ function tables_exist()
+ {
+ return ($this->wpdb->get_var("SHOW TABLES LIKE '{$this->jobs_table}'") == $this->jobs_table && $this->wpdb->get_var("SHOW TABLES LIKE '{$this->failures_table}'") == $this->failures_table);
+ }
+
+ function create_tables($drop = false)
+ {
+ require_once ABSPATH . 'wp-admin/includes/upgrade.php';
+
+ $this->wpdb->hide_errors();
+ $charset_collate = $this->wpdb->get_charset_collate();
+
+ $sql = "CREATE TABLE IF NOT EXISTS {$this->jobs_table} (
+ id bigint(20) NOT NULL AUTO_INCREMENT,
+ job longtext NOT NULL,
+ attempts tinyint(3) NOT NULL DEFAULT 0,
+ reserved_at datetime DEFAULT NULL,
+ available_at datetime NOT NULL,
+ created_at datetime NOT NULL,
+ PRIMARY KEY (id)
+ ) $charset_collate;";
+
+ if ($drop) {
+ $this->wpdb->query("DROP TABLE IF EXISTS {$this->jobs_table}");
+ }
+
+ $this->wpdb->query($sql);
+
+ $sql = "CREATE TABLE IF NOT EXISTS {$this->failures_table} (
+ id bigint(20) NOT NULL AUTO_INCREMENT,
+ job longtext NOT NULL,
+ error text DEFAULT NULL,
+ failed_at datetime NOT NULL,
+ PRIMARY KEY (id)
+ ) $charset_collate;";
+
+ if ($drop) {
+ $this->wpdb->query("DROP TABLE IF EXISTS {$this->failures_table}");
+ }
+
+ $this->wpdb->query($sql);
+ }
+
+ function drop_tables()
+ {
+ $this->wpdb->hide_errors();
+
+ $sql = "DROP TABLE IF EXISTS {$this->jobs_table}";
+ $this->wpdb->query($sql);
+
+ $sql = "DROP TABLE IF EXISTS {$this->failures_table}";
+ $this->wpdb->query($sql);
+ }
+
+ /**
+ * Wrapper for DatabaseConnection::jobs()
+ *
+ * @return int
+ */
+
+ public function count_jobs()
+ {
+ return $this->connection->jobs();
+ }
+
+ /**
+ *
+ * @param $count
+ * @param int $offset
+ *
+ * @return array|null|object
+ *
+ */
+ public function delete_data_from_queue($count = 99999999)
+ {
+ $sql = "DELETE FROM {$this->jobs_table} LIMIT {$count}";
+
+ $results = $this->wpdb->query($sql);
+
+ return $results;
+ }
+
+ public function truncate_queue()
+ {
+ $sql = "TRUNCATE TABLE {$this->jobs_table}";
+
+ $results = $this->wpdb->query($sql);
+
+ return $results;
+ }
+
+ /**
+ * Get list of jobs in queue
+ *
+ * @param int $limit
+ * @param int $offset
+ * @param bool $raw if true, method will return serialized instead of instantiated objects
+ *
+ * @return array|WP_Error
+ */
+ public function list_jobs($limit = 9999999, $offset = 0, $raw = false)
+ {
+ return $this->connection->list_jobs($limit, $offset, $raw);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Queue.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Queue.php
new file mode 100644
index 000000000..bc1f2b4b0
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Queue.php
@@ -0,0 +1,67 @@
+connection = $connection;
+ }
+
+ /**
+ * Push a job onto the queue;
+ *
+ * @param Job $job
+ * @param int $delay
+ *
+ * @return bool|int
+ */
+ public function push( Job $job, $delay = 0 ) {
+ return $this->connection->push( $job, $delay );
+ }
+
+ /**
+ * Create a cron worker.
+ *
+ * @param int $attempts
+ * @param int $interval
+ *
+ * @return Cron
+ */
+ public function cron( $attempts = 3, $interval = 5 ) {
+ if ( is_null( $this->cron ) ) {
+ $this->cron = new Cron( get_class( $this->connection ), $this->worker( $attempts ), $interval );
+ $this->cron->init();
+ }
+
+ return $this->cron;
+ }
+
+ /**
+ * Create a new worker.
+ *
+ * @param int $attempts
+ *
+ * @return Worker
+ */
+ public function worker( $attempts ) {
+ return new Worker( $this->connection, $attempts );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/QueueHelper.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/QueueHelper.php
new file mode 100644
index 000000000..dbc9da8f5
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/QueueHelper.php
@@ -0,0 +1,229 @@
+filesystem = $filesystem;
+ $this->http = $http;
+ $this->http_helper = $http_helper;
+ $this->transfer_util = $transfer_util;
+ $this->queue_manager = $queue_manager;
+ $this->util = $util;
+ }
+
+ /**
+ * @throws \Exception
+ */
+ public function populate_queue($file_data, $intent, $stage, $migration_state_id, $full_site_export = false )
+ {
+ if (!$file_data) {
+ return $this->transfer_util->ajax_error(__('File list empty or incomplete. Please contact support.'));
+ }
+
+ if (is_wp_error($file_data)) {
+ return $file_data;
+ }
+
+ foreach ($file_data['files'] as $item) {
+ if (is_array($item)) {
+ $this->transfer_util->enqueue_files($item, $this->queue_manager);
+ }
+ }
+
+ $queue_status = [
+ 'total' => $file_data['meta']['count'],
+ 'size' => $file_data['meta']['size'],
+ 'manifest' => $file_data['meta']['manifest'],
+ ];
+
+ //Always store local manifest even for push intents, to keep track of recursive scanning items count.
+ $complete_status = $this->store_local_manifest($queue_status, $file_data, $stage, $migration_state_id, $full_site_export);
+
+ if ('push' === $intent) {
+ $response = $this->store_remote_manifest($queue_status, $stage);
+ if (true !== $response) {
+ return $response;
+ }
+ }
+
+ // Manifest can get quite large, so remove it once it's no longer needed
+ unset($queue_status['manifest'], $complete_status['manifest']);
+
+ return $complete_status;
+ }
+
+ /**
+ * Saves the remote manifest.
+ *
+ * @param array $queue_status
+ * @param string $stage
+ * @return bool|mixed
+ * @throws \Exception
+ */
+ private function store_remote_manifest($queue_status, $stage) {
+ $key = $stage === 'media_files' ? 'mf' : 'tp';
+ $response = $this->transfer_util->save_queue_status_to_remote($queue_status, "wpmdb{$key}_respond_to_save_queue_status");
+ $decoded_response = json_decode($response->body, true);
+
+ if ((isset($decoded_response['success']) && $decoded_response['success'] === false) || empty($decoded_response)) {
+ return $this->transfer_util->ajax_error($decoded_response['data']);
+ }
+
+ return true;
+ }
+
+ /**
+ * Saves the local manifest.
+ *
+ * @param array $queue_status
+ * @param array $file_data
+ * @param string $stage
+ * @param string $migration_state_id
+ * @return bool|mixed
+ */
+ private function store_local_manifest($queue_status, $file_data, $stage, $migration_state_id, $full_site_export = false) {
+ $queue_status = $this->concat_existing_queue_items($queue_status, $file_data, $stage, $migration_state_id, $full_site_export);
+
+ try {
+ $this->transfer_util->save_queue_status($queue_status, $stage, $migration_state_id, $full_site_export);
+ } catch (\Exception $e) {
+ return $this->transfer_util->ajax_error($e->getMessage());
+ }
+
+ return $queue_status;
+ }
+
+ /**
+ * Concat existing queue status if exists.
+ *
+ * @param array $queue_status
+ * @param array $file_data
+ * @param string $stage
+ * @param string $migration_state_id
+ * @return array
+ */
+ private function concat_existing_queue_items($queue_status, $file_data, $stage, $migration_state_id, $full_site_export = false) {
+ //attempt to load queue status
+ $stored_queue = $this->transfer_util->get_queue_status($stage, $migration_state_id, $full_site_export);
+ if (false !== $stored_queue) {
+ $queue_status = $stored_queue;
+ $queue_status['total'] += $file_data['meta']['count'];
+ $queue_status['size'] += $file_data['meta']['size'];
+ $queue_status['manifest'] = array_merge($file_data['meta']['manifest'], $queue_status['manifest']);
+ }
+
+ return $queue_status;
+ }
+
+ public function get_queue_items()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $this->util->set_time_limit();
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'stage' => 'string',
+ 'migration_state_id' => 'key',
+ 'nonce' => 'key',
+ );
+
+ $state_data = Persistence::setPostData($key_rules, __METHOD__);
+
+ if ($state_data['stage'] === 'media_files') {
+ $folder_key = $state_data['folder'];
+ } else {
+ $folder_key = $state_data['folders'];
+ }
+ $current_option = isset($state_data[$state_data['stage']. '_option'])
+ ? $state_data[$state_data['stage']. '_option']
+ : null;
+ if (empty($folder_key) && $current_option !== 'except') {
+ return $this->transfer_util->ajax_error(__('Error: empty folder list supplied.', 'wp-migrate-db'));
+ }
+
+ $queue_status = get_site_transient('wpmdb_queue_status');
+ $count = apply_filters('wpmdb_tranfers_queue_batch_size', 1000);
+ $offset = isset($queue_status['offset']) ? $queue_status['offset'] : 0;
+
+ $q_data = $this->queue_manager->list_jobs($count, $offset);
+
+ if (is_wp_error($q_data)) {
+ return $this->http->end_ajax($q_data);
+ }
+
+ if (empty($q_data)) {
+ delete_site_transient('wpmdb_queue_status');
+
+ return $this->http->end_ajax(['status' => 'complete']);
+ }
+
+ $file_data = $this->process_file_data($q_data);
+ $result_set = $this->transfer_util->process_queue_data($file_data, $state_data, 0);
+
+ $queue_status['offset'] = $offset + $count;
+ set_site_transient('wpmdb_queue_status', $queue_status);
+
+ return $this->http->end_ajax(['queue_status' => $queue_status, 'items' => $result_set]);
+ }
+
+ /**
+ * Process data
+ *
+ * @param array $data
+ *
+ * @return array
+ */
+ public function process_file_data($data)
+ {
+ $result_set = [];
+
+ if (!empty($data)) {
+ foreach ($data as $size => $record) {
+ $display_path = $record->file['subpath'];
+ $record->file['relative_path'] = $display_path;
+
+ $result_set[] = $record->file;
+ }
+ }
+
+ return $result_set;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/QueueManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/QueueManager.php
new file mode 100644
index 000000000..7d9c1b932
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/QueueManager.php
@@ -0,0 +1,64 @@
+ new DatabaseConnection( $GLOBALS['wpdb'] ),
+ 'redis' => new RedisConnection(),
+ );
+
+ return apply_filters( 'wp_queue_connections', $connections );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Worker.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Worker.php
new file mode 100644
index 000000000..61faa194f
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Queue/Worker.php
@@ -0,0 +1,71 @@
+connection = $connection;
+ $this->attempts = $attempts;
+ }
+
+ /**
+ * Process a job on the queue.
+ *
+ * @return bool
+ */
+ public function process() {
+ $job = $this->connection->pop();
+
+ if ( ! $job ) {
+ return false;
+ }
+
+ $exception = null;
+
+ try {
+ $job->handle();
+ } catch ( Exception $exception ) {
+ $job->release();
+ }
+
+ if ( $job->attempts() >= $this->attempts ) {
+ if ( empty( $exception ) ) {
+ $exception = new WorkerAttemptsExceededException();
+ }
+
+ $job->fail();
+ }
+
+ if ( $job->failed() ) {
+ $this->connection->failure( $job, $exception );
+ } else if ( $job->released() ) {
+ $this->connection->release( $job );
+ } else {
+ $this->connection->delete( $job );
+ }
+
+ return true;
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace.php
new file mode 100644
index 000000000..48c697ec6
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace.php
@@ -0,0 +1,988 @@
+migration_state_manager = $migration_state_manager;
+ $this->table_helper = $table_helper;
+ $this->error_log = $error_log;
+ $this->util = $util;
+ $this->properties = $properties;
+ $this->pair_factory = $pairs_factory;
+ $this->rest_api_server = $rest_api_server;
+ $this->http_helper = $http_helper;
+ $this->http = $http;
+ $this->diff_interpreter = $diff_interpreter;
+ self::$form_data = $form_data;
+
+ //Setup REST API routes
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ }
+
+ public function get($prop)
+ {
+ return $this->$prop;
+ }
+
+ public function set($prop, $value)
+ {
+ return $this->$prop = $value;
+ }
+
+ public function register($args)
+ {
+ $keys = array(
+ 'table',
+ 'search',
+ 'replace',
+ 'regex',
+ 'case_sensitive',
+ 'intent',
+ 'base_domain',
+ 'site_domain',
+ 'wpmdb',
+ 'site_details',
+ );
+
+ if (!is_array($args)) {
+ throw new \InvalidArgumentException('WPMDB_Replace constructor expects the argument to be an array');
+ }
+
+ foreach ($keys as $key) {
+ if (!isset($args[$key])) {
+ throw new \InvalidArgumentException("WPMDB_Replace constructor expects '$key' key to be present in the array argument");
+ }
+ }
+
+ $this->table = $args['table'];
+ $this->search = $args['search'];
+ $this->replace = $args['replace'];
+ $this->regex = $args['regex'];
+ $this->case_sensitive = $args['case_sensitive'];
+ $this->intent = $args['intent'];
+ $this->base_domain = $args['base_domain'];
+ $this->site_domain = $args['site_domain'];
+ $this->site_details = $args['site_details'];
+ $this->json_search = '';
+ $this->json_replace = '';
+ $this->json_replace_tables = '';
+ $this->json_replace_columns = '';
+ $this->json_merged = false;
+
+ // Set diff interpreter table name
+ $this->diff_interpreter->getGroup()->setTable($this->table);
+
+ global $wpdb;
+
+ $prefix = $wpdb->base_prefix;
+
+ $this->json_replaces($prefix);
+ $this->create_pairs();
+
+ // Detect a protocol mismatch between the remote and local sites involved in the migration
+ $this->detect_protocol_mismatch();
+ return $this;
+ }
+
+ private function create_pairs($search = null, $replace = null, $json_pairs = false) {
+ if (null === $search) {
+ $search = $this->search;
+ }
+ if (null === $replace) {
+ $replace = $this->replace;
+ }
+
+ foreach ($search as $key => $pattern) {
+ if (!$json_pairs && array_key_exists($key, $this->regex) && true === $this->regex[$key]) {
+ $this->pairs[] = $this->pair_factory->create($pattern, $replace[$key], PairFactory::REGEX);
+ continue;
+ }
+ if ( array_key_exists($key, $this->case_sensitive) && true === $this->case_sensitive[$key]) {
+ $this->pairs[] = $this->pair_factory->create($pattern, $replace[$key], PairFactory::CASE_SENSITIVE);
+ continue;
+ }
+ $this->pairs[] = $this->pair_factory->create($pattern, $replace[$key], PairFactory::CASE_INSENSITIVE);
+ }
+ }
+
+ public static function parse_find_replace_pairs($intent = '', $site_url = '')
+ {
+ $find_replace_pairs = [
+ 'regex' => [],
+ 'case_sensitive' => [],
+ 'replace_old' => [],
+ 'replace_new' => []
+ ];
+
+ $tmp_find_replace_pairs = [];
+ $migration_options = self::$form_data->getFormData();
+
+
+ if(!empty($migration_options['regex'])) {
+ $find_replace_pairs['regex'] = $migration_options['regex'];
+ }
+
+ if(!empty($migration_options['case_sensitive'])) {
+ $find_replace_pairs['case_sensitive'] = $migration_options['case_sensitive'];
+ }
+
+
+ // Standard Pairs
+ if ( !empty($migration_options['search_replace']['standard_search_replace'])
+ && $migration_options['search_replace']['standard_search_visible']
+ ) {
+ $standard_pairs = $migration_options['search_replace']['standard_search_replace'];
+ foreach ($standard_pairs as $key => $pair) {
+ if (!empty(trim($pair['replace'])) && in_array($key, $migration_options['search_replace']['standard_options_enabled'], true)) {
+ $tmp_find_replace_pairs[$pair['search']] = $pair['replace'];
+ }
+ }
+ }
+
+ // Custom pairs
+ if (
+ !empty($migration_options['search_replace']['custom_search_replace'])
+ ) {
+ $standard_pairs_count = count($tmp_find_replace_pairs);
+ $custom_pairs = $migration_options['search_replace']['custom_search_replace'];
+
+ $i = 1;
+ foreach ($custom_pairs as $pair) {
+ $index = $i + $standard_pairs_count;
+ if (empty($pair['replace_old']) && empty($pair['replace_new'])) {
+ $i++;
+ continue;
+ }
+ $tmp_find_replace_pairs[$pair['replace_old']] = $pair['replace_new'];
+
+ if(empty($migration_options['regex']) && isset($pair['regex'])) {
+ $find_replace_pairs['regex'][$index] = $pair['regex'];
+ }
+
+ if(empty($migration_options['case_sensitive']) && isset($pair['case_sensitive'])) {
+ $find_replace_pairs['case_sensitive'][$index] = $pair['case_sensitive'];
+ }
+
+ $i++;
+ }
+ }
+
+ $tmp_find_replace_pairs = apply_filters('wpmdb_find_and_replace', $tmp_find_replace_pairs, $intent, $site_url);
+
+ if (!empty($tmp_find_replace_pairs)) {
+ $i = 1;
+ foreach ($tmp_find_replace_pairs as $replace_old => $replace_new) {
+ $find_replace_pairs['replace_old'][$i] = $replace_old;
+ $find_replace_pairs['replace_new'][$i] = $replace_new;
+ $i++;
+ }
+ }
+
+ return $find_replace_pairs;
+ }
+
+ /**
+ * Determine whether to apply a subdomain replace over each value in the database.
+ *
+ * @return bool
+ */
+ function is_subdomain_replaces_on()
+ {
+ if (!isset($this->subdomain_replaces_on)) {
+ $this->subdomain_replaces_on = (is_multisite() && is_subdomain_install() && !$this->has_same_base_domain() && apply_filters('wpmdb_subdomain_replace', true));
+ }
+
+ return $this->subdomain_replaces_on;
+ }
+
+
+ /**
+ * Determine if the replacement has the same base domain as the search. Produces doubled replacement strings
+ * otherwise.
+ *
+ * @return bool
+ */
+ function has_same_base_domain()
+ {
+ if ('push' !== $this->intent || 'pull' !== $this->intent) {
+ $destination_url = $this->base_domain;
+ } else {
+ $destination_url = isset($this->destination_url) ? $this->destination_url : $this->site_details['local']['site_url'];
+ }
+
+ if (stripos($destination_url, $this->site_domain)) {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Automatically replace URLs for subdomain based multisite installations
+ * e.g. //site1.example.com -> //site1.example.local for site with domain example.com
+ * NB: only handles the current network site, does not work for additional networks / mapped domains
+ *
+ * @param $new
+ *
+ * @return mixed
+ */
+ function subdomain_replaces($new)
+ {
+ if (empty($this->base_domain)) {
+ return $new;
+ }
+
+ $pattern = '|//(.*?)\\.' . preg_quote($this->site_domain, '|') . '|';
+ $replacement = '//$1.' . trim($this->base_domain);
+ $new = preg_replace($pattern, $replacement, $new);
+
+ return $new;
+ }
+
+ /**
+ * Detect a protocol mismatch between the remote and local sites involved in the migration
+ *
+ * @return bool
+ */
+ function detect_protocol_mismatch()
+ {
+ if (!isset($this->site_details['remote']) && 'import' !== $this->intent) {
+ return false;
+ }
+
+ $wpmdb_home_urls = array(
+ // TODO: rewrite unit tests that only pass site_url so that we can rely on home_url's existence
+ 'local' => isset($this->site_details['local']['home_url']) ? $this->site_details['local']['home_url'] : $this->site_details['local']['site_url'],
+ );
+
+ if ('import' !== $this->intent) {
+ $wpmdb_home_urls['remote'] = isset($this->site_details['remote']['home_url']) ? $this->site_details['remote']['home_url'] : $this->site_details['remote']['site_url'];
+ } else {
+ $this->state_data = $this->migration_state_manager->set_post_data();
+
+ if (!isset($this->state_data['import_info']) || !isset($this->state_data['import_info']['protocol'])) {
+ return false;
+ }
+ $wpmdb_home_urls['remote'] = $this->state_data['import_info']['protocol'] . ':' . $this->state_data['import_info']['URL'];
+ }
+
+ /**
+ * Filters the site_urls used to check if there is a protocol mismatch.
+ *
+ * @param array
+ */
+ $wpmdb_home_urls = apply_filters('wpmdb_replace_site_urls', $wpmdb_home_urls);
+
+ $local_url_is_https = false === stripos($wpmdb_home_urls['local'], 'https') ? false : true;
+ $remote_url_is_https = false === stripos($wpmdb_home_urls['remote'], 'https') ? false : true;
+ $local_protocol = $local_url_is_https ? 'https' : 'http';
+ $remote_protocol = $remote_url_is_https ? 'https' : 'http';
+
+ if (($local_url_is_https && !$remote_url_is_https) || (!$local_url_is_https && $remote_url_is_https)) {
+ $this->is_protocol_mismatch = true;
+ }
+
+ if ('push' === $this->intent) {
+ $this->destination_protocol = $remote_protocol;
+ $this->source_protocol = $local_protocol;
+ $this->destination_url = $wpmdb_home_urls['remote'];
+ } else {
+ $this->destination_protocol = $local_protocol;
+ $this->source_protocol = $remote_protocol;
+ $this->destination_url = $wpmdb_home_urls['local'];
+ }
+
+ return $this->is_protocol_mismatch;
+ }
+
+ /**
+ *
+ * Handles replacing the protocol if the local and destination don't have matching protocols (http > https and
+ * vice-versa).
+ *
+ * Can be filtered to disable entirely.
+ *
+ * @param string $new
+ * @param string $destination_url
+ *
+ * @return mixed
+ */
+ function do_protocol_replace($new, $destination_url)
+ {
+ /**
+ * Filters $do_protocol_replace, return false to prevent protocol replacement.
+ *
+ * @param bool true If the replace should be skipped.
+ * @param string $destination_url The URL of the target site.
+ */
+ $do_protocol_replace = apply_filters('wpmdb_replace_destination_protocol', true, $destination_url);
+
+ if (true !== $do_protocol_replace) {
+ return $new;
+ }
+
+ $parsed_destination = Util::parse_url($destination_url);
+ unset($parsed_destination['scheme']);
+
+ if (isset($parsed_destination['port'])) {
+ $parsed_destination['port'] = ':' . $parsed_destination['port'];
+ }
+
+ $protocol_search = $this->source_protocol . '://' . implode('', $parsed_destination);
+ $protocol_replace = $destination_url;
+ // JSON search & replace
+ if (
+ in_array($this->table, $this->json_replace_tables)
+ && in_array($this->column, $this->json_replace_columns)
+ ) {
+ $protocol_search = [$protocol_search, Util::json_encode_trim($protocol_search)];
+ $protocol_replace = [$protocol_replace, Util::json_encode_trim($protocol_replace)];
+ }
+ $new = str_ireplace($protocol_search, $protocol_replace, $new, $count);
+
+ return $new;
+ }
+
+
+ public function maybe_merge_json_replaces()
+ {
+ if ($this->json_merged) {
+ return false;
+ }
+
+ if (
+ !in_array($this->table, $this->json_replace_tables) ||
+ !in_array($this->column, $this->json_replace_columns)
+ ) {
+ return false;
+ }
+
+ if (empty($this->search) && empty($this->replace)) {
+ return false;
+ }
+
+ if (!is_array($this->json_search) || !is_array($this->json_replace)) {
+ return false;
+ }
+
+ //Create the json replace pairs.
+ $this->create_pairs($this->json_search, $this->json_replace, true);
+
+ //Only add json replacements once
+ $this->json_merged = true;
+
+ return true;
+ }
+
+ /**
+ * Applies find/replace pairs to a given string.
+ *
+ * @param string $subject
+ *
+ * @return string
+ */
+ public function apply_replaces($subject)
+ {
+ $original = $subject;
+
+ if (empty($this->search) && empty($this->replace)) {
+ return $subject;
+ }
+
+ if (count($this->search) !== count($this->replace)) {
+ return $subject;
+ }
+
+ $this->maybe_merge_json_replaces(); // Maybe merge in json_encoded find/replace values
+
+ foreach ($this->pairs as $pair) {
+ $subject = $pair->apply($subject);
+ }
+
+ if ($this->is_subdomain_replaces_on()) {
+ $subject = $this->subdomain_replaces($subject);
+ }
+
+ if (true === $this->is_protocol_mismatch) {
+ $subject = $this->do_protocol_replace($subject, $this->destination_url);
+ }
+
+ if ('find_replace' === $this->intent) {
+ $row = null;
+ if (is_object($this->row) ) {
+ $get_vars = function_exists('get_mangled_object_vars') ? get_mangled_object_vars($this->row) : $this->row;
+ $row = reset($get_vars);
+ }
+ $this->diff_interpreter->compute(DiffEntity::create($original, $subject, $this->column, $row));
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Take a serialized array and unserialize it replacing elements as needed and
+ * unserialising any subordinate arrays and performing the replace on those too.
+ *
+ * Mostly from https://github.com/interconnectit/Search-Replace-DB
+ *
+ * @param mixed $data Used to pass any subordinate arrays back to in.
+ * @param bool $serialized Does the array passed via $data need serialising.
+ * @param bool $parent_serialized Passes whether the original data passed in was serialized
+ * @param bool $filtered Should we apply before and after filters successively
+ *
+ * @return mixed The original array with all elements replaced as needed.
+ */
+ function recursive_unserialize_replace($data, $serialized = false, $parent_serialized = false, $filtered = true)
+ {
+ $pre = apply_filters('wpmdb_pre_recursive_unserialize_replace', false, $data, $this);
+ if (false !== $pre) {
+ return $pre;
+ }
+
+ //If the intent is find_replace we need to prefix the tables with the temp prefix and wp base table prefix.
+ global $wpdb;
+ $table_prefix = $wpdb->base_prefix;
+ if ( 'find_replace' === $this->get_intent() ) {
+
+ $table_prefix = $this->properties->temp_prefix . $table_prefix;
+ }
+
+ //Check if find and replace needs be skipped for the current table
+ $skipped_tables = apply_filters('wpmdb_skip_search_replace_tables', ['eum_logs']);
+ foreach ($skipped_tables as $skipped_table) {
+ if ($this->table === $table_prefix . $skipped_table) {
+ return $data;
+ }
+ }
+
+ if ($this->should_do_reference_check($table_prefix) && is_serialized( $data ) && preg_match('/r\:\d+;/i', $data)) {
+ $current_row = $this->get_row();
+ $first_row_key = reset($current_row);
+ $skipped = [
+ 'table' => str_replace('_mig_', '', $this->get_table()),
+ 'primary_key' => $first_row_key,
+ 'column' => $this->get_column(),
+ 'contains_match' => $this->has_skipped_values($data)
+ ];
+
+ if (property_exists($this->get_row(), 'option_name') && $this->table_is('options', $table_prefix)) {
+ $skipped['option_name'] = $this->get_row()->option_name;
+ }
+
+ error_log('WPMDB Find & Replace skipped: ' . json_encode($skipped));
+ return $data;
+ }
+
+ $is_json = false;
+ $before_fired = false;
+ $successive_filter = $filtered;
+
+ if (true === $filtered) {
+ list($data, $before_fired, $successive_filter) = apply_filters('wpmdb_before_replace_custom_data', array(
+ $data,
+ $before_fired,
+ $successive_filter,
+ ), $this);
+ }
+
+ // some unserialized data cannot be re-serialized eg. SimpleXMLElements
+ try {
+ if (is_string($data) && ($unserialized = Util::unserialize($data, __METHOD__)) !== false) {
+ // PHP currently has a bug that doesn't allow you to clone the DateInterval / DatePeriod classes.
+ // We skip them here as they probably won't need data to be replaced anyway
+ if (
+ 'object' == gettype($unserialized) &&
+ (
+ $unserialized instanceof \DateInterval ||
+ $unserialized instanceof \DatePeriod
+ )
+ ) {
+ return $data;
+ }
+ $data = $this->recursive_unserialize_replace($unserialized, true, true, $successive_filter);
+ } elseif (is_array($data)) {
+ $_tmp = array();
+ foreach ($data as $key => $value) {
+ $_tmp[$key] = $this->recursive_unserialize_replace($value, false, $parent_serialized, $successive_filter);
+ }
+
+ $data = $_tmp;
+ unset($_tmp);
+ //is_object does not return true for __PHP_Incomplete_Class until 7.2 using gettype instead
+ } elseif ('object' == gettype($data)) { // Submitted by Tina Matter
+ if ($this->is_object_cloneable($data)) {
+ $_tmp = clone $data;
+ foreach ($data as $key => $value) {
+ // Integer properties are crazy and the best thing we can do is to just ignore them.
+ // see http://stackoverflow.com/a/10333200 and https://github.com/deliciousbrains/wp-migrate-db-pro/issues/853
+ if (is_int($key)) {
+ continue;
+ }
+ $_tmp->$key = $this->recursive_unserialize_replace($value, false, $parent_serialized, $successive_filter);
+ }
+
+ $data = $_tmp;
+ unset($_tmp);
+ }
+ } elseif (Util::is_json($data, true)) {
+ $_tmp = array();
+ $data = json_decode($data, true);
+
+ foreach ($data as $key => $value) {
+ $_tmp[$key] = $this->recursive_unserialize_replace($value, false, $parent_serialized, $successive_filter);
+ }
+
+ $data = $_tmp;
+ unset($_tmp);
+ $is_json = true;
+ } elseif (is_string($data)) {
+ list($data, $do_replace) = apply_filters('wpmdb_replace_custom_data', array($data, true), $this);
+
+ if ($do_replace) {
+ $data = $this->apply_replaces($data);
+ }
+ }
+
+ if ($is_json) {
+ $flags = apply_filters('wpmdb_replace_json_encode_flags', 0, $data, $this);
+ $data = json_encode($data, $flags);
+ }
+
+ if ($serialized) {
+ $data = serialize($data);
+ }
+ } catch (\Exception $error) {
+ $error_msg = __('Failed attempting to do the recursive unserialize replace. Please contact support.', 'wp-migrate-db');
+ $error_details = $error->getMessage() . "\n\n";
+ $error_details .= var_export($data, true);
+ $this->error_log->log_error($error_msg, $error_details);
+ }
+
+ if (true === $filtered) {
+ $data = apply_filters('wpmdb_after_replace_custom_data', $data, $before_fired, $this);
+ }
+
+ return $data;
+ }
+
+ /**
+ * Search unseralized string for potential match
+ *
+ * @param string $data
+ * @return bool
+ **/
+ protected function has_skipped_values($data)
+ {
+ foreach( $this->search as $search_string) {
+ if (false !== strpos($data, $search_string)){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Table and cloumns to search for references
+ * @param string $table_prefix
+ * @return bool
+ **/
+ protected function should_do_reference_check($table_prefix)
+ {
+ if ( $this->table_is('options', $table_prefix) && 'option_value' === $this->get_column()) {
+ return true;
+ }
+ $table_column_for_check = [
+ [
+ 'table' => $table_prefix . 'duplicator_packages',
+ 'column' => 'package'
+ ],
+ [
+ 'table' => $table_prefix . 'aiowps_audit_log',
+ 'column' => 'stacktrace'
+ ]
+ ];
+ $table_column_for_check = apply_filters('wpmdb_check_table_column_for_reference', $table_column_for_check);
+ foreach($table_column_for_check as $table_column ) {
+ if (
+ array_key_exists('table', $table_column)
+ && $table_column['table'] === $this->get_table()
+ && array_key_exists('column', $table_column)
+ && $table_column['column'] === $this->get_column()
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Getter for the $table class property.
+ *
+ * @return string Name of the table currently being processed in the migration.
+ */
+ public function get_table()
+ {
+ return $this->table;
+ }
+
+ /**
+ * Getter for the $column class property.
+ *
+ * @return string Name of the column currently being processed in the migration.
+ */
+ public function get_column()
+ {
+ return $this->column;
+ }
+
+ /**
+ * Getter for the $row class property.
+ *
+ * @return string Name of the row currently being processed in the migration.
+ */
+ public function get_row()
+ {
+ return $this->row;
+ }
+
+ /**
+ * Setter for the $column class property.
+ *
+ * @param string $column Name of the column currently being processed in the migration.
+ */
+ public function set_column($column)
+ {
+ $this->column = $column;
+ }
+
+ /**
+ * Setter for the $row class property.
+ *
+ * @param string $row Name of the row currently being processed in the migration.
+ */
+ public function set_row($row)
+ {
+ $this->row = $row;
+ }
+
+ /**
+ * Multsite safe way of comparing the table currently being processed in the migration against a desired table.
+ *
+ * The table prefix should be omitted, example:
+ *
+ * $is_posts = $this->table_is( 'posts' );
+ *
+ * @TODO Cover table prefixing with Unit Tests
+ *
+ * @param string $desired_table Name of the desired table, table prefix omitted.
+ * @param string $prefix The table prefix
+ *
+ * @return boolean Whether or not the desired table is the table currently being processed.
+ */
+ public function table_is( $desired_table, $prefix = '' ) {
+ return $this->table_helper->table_is( $desired_table, $this->table, 'table', $prefix );
+ }
+
+ /**
+ * Intent of the current replace migration.
+ *
+ * Helpful for hookers who need to know what intent they are working on.
+ *
+ * @return string Intent of the current migration
+ */
+ public function get_intent()
+ {
+ return $this->intent;
+ }
+
+ /**
+ * @param string $prefix
+ */
+ protected function json_replaces($prefix)
+ {
+ $prefix = in_array($this->intent, ['find_replace', 'import']) ? '_mig_' . $prefix : $prefix;
+ $default_tables = [
+ "{$prefix}posts",
+ ];
+
+ // Account for multisite subsites.
+ if (is_multisite()) {
+ $pattern = '/^' . $prefix . '\d+_posts$/';
+ if (preg_match($pattern, $this->table)) {
+ $default_tables = [$this->table];
+ }
+ }
+
+ $this->json_replace_tables = apply_filters('wpmdb_json_replace_tables', $default_tables);
+
+ $this->json_replace_columns = apply_filters('wpmdb_json_replace_columns', [
+ 'post_content',
+ 'post_content_filtered',
+ ]);
+
+ if (empty($this->search) && empty($this->replace)) {
+ return;
+ }
+
+ if (is_array($this->search)) {
+ $this->json_search = array_map(function ($item) {
+ return Util::json_encode_trim($item);
+ }, $this->search);
+ }
+
+ if (is_array($this->replace)) {
+ $this->json_replace = array_map(function ($item) {
+ return Util::json_encode_trim($item);
+ }, $this->replace);
+ }
+ }
+
+ /**
+ * @throws \WP_CLI\ExitException
+ * @throws \DI\DependencyException
+ * @throws \DI\NotFoundException
+ */
+ public function validate_regex_pattern() {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ if (isset($_POST['pattern'])) {
+ $pattern = Util::safe_wp_unslash($_POST['pattern']);
+ if (Util::is_regex_pattern_valid( $pattern ) === false) {
+ return $this->http->end_ajax(false);
+ }
+ }
+ $key_rules = array(
+ 'pattern' => 'regex',
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data( $key_rules );
+ return $this->http->end_ajax(isset($state_data['pattern']) === true);
+ }
+
+ public function register_rest_routes() {
+ $this->rest_api_server->registerRestRoute('/regex-validate', [
+ 'methods' => 'POST',
+ 'callback' => [ $this, 'validate_regex_pattern' ],
+ ]);
+ }
+
+
+ /**
+ * Check if a given object can be cloned.
+ *
+ * @param object $object
+ *
+ * @return bool
+ */
+ private function is_object_cloneable($object) {
+ return (new \ReflectionClass(get_class($object)))->isCloneable();
+ }
+
+ /**
+ * Empties pairs array
+ */
+ public function reset_pairs() {
+ $this->pairs = [];
+ }
+
+
+ /**
+ * @return DiffInterpreter
+ */
+ public function get_diff_interpreter() {
+ return $this->diff_interpreter;
+ }
+
+
+ /**
+ * Returns an array of json serialized entities.
+ *
+ * @return array
+ */
+ public function get_diff_result() {
+ $result = [];
+ foreach($this->diff_interpreter->getGroup()->getEntities() as $entity) {
+ $result[] = $entity->jsonSerialize();
+ }
+ return $result;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/AbstractReplacePair.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/AbstractReplacePair.php
new file mode 100644
index 000000000..71ae49961
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/AbstractReplacePair.php
@@ -0,0 +1,40 @@
+pattern = $pattern;
+ $this->replace = $replace;
+ }
+
+ /**
+ * @param string $subject
+ * @return string
+ */
+ public function apply($subject)
+ {
+ return $subject;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/CaseInsensitivePair.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/CaseInsensitivePair.php
new file mode 100644
index 000000000..1a20eb255
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/CaseInsensitivePair.php
@@ -0,0 +1,21 @@
+pattern, $this->replace, $subject);
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/CaseSensitivePair.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/CaseSensitivePair.php
new file mode 100644
index 000000000..c48de438a
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/CaseSensitivePair.php
@@ -0,0 +1,23 @@
+pattern, $this->replace, $subject);
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/PairFactory.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/PairFactory.php
new file mode 100644
index 000000000..56d40ffe0
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/PairFactory.php
@@ -0,0 +1,35 @@
+pattern, $this->replace, $subject);
+
+ if (null !== $replaced) {
+ return $replaced;
+ }
+
+ return $subject;
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/ReplacePairInterface.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/ReplacePairInterface.php
new file mode 100644
index 000000000..31448aaae
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Replace/ReplacePairInterface.php
@@ -0,0 +1,12 @@
+getMessage());
+ }
+
+ return $result;
+ }
+
+ protected static function create_error_string($type, $context, $data, $key)
+ {
+ return sprintf(__('Sanitization Error: `%1$s` method was expecting %2$s for the `%3$s` field, but got something else: "%4$s"', 'wp-db-migrate-pro'), $context, $type, $key, $data);
+ }
+
+ /**
+ * Sanitize and validate data.
+ *
+ * @param string|array $data The data to the sanitized.
+ * @param string|array $key_rules The keys in the data (if data is an array) and the sanitization rule(s) to apply for each key.
+ * @param string $context Additional context data for messages etc.
+ * @param int $recursion_level How deep in the recursion are we? Optional, defaults to 0.
+ *
+ * @return mixed The sanitized data, the data if no key rules supplied or `false` if an unrecognized rule supplied.
+ * @throws SanitizationFailureException
+ */
+ private static function _sanitize_data($data, $key_rules, $context, $recursion_level = 0)
+ {
+ if (empty($data) || empty($key_rules)) {
+ return $data;
+ }
+
+ if (0 === $recursion_level && is_array($data)) {
+ // We always expect associative arrays.
+ if (!is_array($key_rules)) {
+ throw new SanitizationFailureException(sprintf(__('%1$s was not expecting data to be an array.', 'wp-db-migrate-pro'), $context));
+ }
+ foreach ($data as $key => $value) {
+ // If a key does not have a rule it's not ours and can be removed.
+ // We should not fail if there is extra data as plugins like Polylang add their own data to each ajax request.
+ if (!array_key_exists($key, $key_rules)) {
+ unset($data[$key]);
+ continue;
+ }
+ static::$field_key = $key;
+ $data[$key] = self::_sanitize_data($value, $key_rules[$key], $context, ($recursion_level + 1));
+ }
+ } elseif (is_array($key_rules)) {
+ foreach ($key_rules as $rule) {
+ $data = self::_sanitize_data($data, $rule, $context, ($recursion_level + 1));
+ }
+ } else {
+ // Neither $data or $key_rules are a first level array so can be analysed.
+ if ('array' === $key_rules) {
+ if (!is_array($data)) {
+ throw new SanitizationFailureException(self::create_error_string('an array', $context, $data, self::$field_key));
+ }
+ // @TODO - Needs sanitizing
+ } elseif ('string' === $key_rules) {
+ if (!is_string($data)) {
+ throw new SanitizationFailureException(self::create_error_string('a string', $context, $data, self::$field_key));
+ }
+ $data = filter_var($data, FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);
+ } elseif ('regex' === $key_rules) {
+ if (Util::is_regex_pattern_valid($data) === false) {
+ throw new SanitizationFailureException(self::create_error_string('a regex string', $context, $data, self::$field_key));
+ }
+ $data = $data;
+ } elseif ('key' === $key_rules) {
+ $key_name = sanitize_key($data);
+ if ($key_name !== $data) {
+ throw new SanitizationFailureException(self::create_error_string('a valid key', $context, $data, self::$field_key));
+ }
+ $data = $key_name;
+ } elseif ('text' === $key_rules) {
+ $text = sanitize_text_field($data);
+ if ($text !== trim($data)) {
+ throw new SanitizationFailureException(self::create_error_string('text', $context, $data, self::$field_key));
+ }
+ $data = $text;
+ } elseif ('serialized' === $key_rules) {
+ if (!is_string($data) || !is_serialized($data)) {
+ throw new SanitizationFailureException(self::create_error_string('serialized data', $context, $data, self::$field_key));
+ }
+ // @TODO - Needs sanitizing
+ } elseif ('json_array' === $key_rules) {
+ if (!is_string($data) || !Util::is_json($data)) {
+ throw new SanitizationFailureException(self::create_error_string('JSON data', $context, $data, self::$field_key));
+ }
+ // @TODO - Needs sanitizing
+ $data = json_decode($data, true);
+ } elseif ('json' === $key_rules) {
+ if (!is_string($data) || !Util::is_json($data)) {
+ throw new SanitizationFailureException(self::create_error_string('JSON data', $context, $data, self::$field_key));
+ }
+ // @TODO - Needs sanitizing
+ } elseif ('numeric' === $key_rules) {
+ if (!is_numeric($data)) {
+ throw new SanitizationFailureException(self::create_error_string('a valid numeric value', $context, $data, self::$field_key));
+ }
+ } elseif ('int' === $key_rules) {
+ // As we are sanitizing form data, even integers are within a string.
+ if (!is_numeric($data) || (int)$data != $data) {
+ throw new SanitizationFailureException(self::create_error_string('an integer', $context, $data, self::$field_key));
+ }
+ $data = (int)$data;
+ } elseif ('positive_int' === $key_rules) {
+ if (!is_numeric($data) || (int)$data != $data || 0 > $data) {
+ throw new SanitizationFailureException(self::create_error_string('a positive number (int)', $context, $data, self::$field_key));
+ }
+ $data = floor($data);
+ } elseif ('negative_int' === $key_rules) {
+ if (!is_numeric($data) || (int)$data !== $data || 0 < $data) {
+ throw new SanitizationFailureException(self::create_error_string('a negative number (int)', $context, $data, self::$field_key));
+ }
+ $data = ceil($data);
+ } elseif ('zero_int' === $key_rules) {
+ if (!is_numeric($data) || (int)$data !== $data || 0 !== $data) {
+ throw new SanitizationFailureException(self::create_error_string('0 (int)', $context, $data, self::$field_key));
+ }
+ $data = 0;
+ } elseif ('empty' === $key_rules) {
+ if (!empty($data)) {
+ throw new SanitizationFailureException(self::create_error_string('an empty value', $context, $data, self::$field_key));
+ }
+ } elseif ('url' === $key_rules) {
+ $url = esc_url_raw($data);
+ if (empty($url)) {
+ throw new SanitizationFailureException(self::create_error_string('URL', $context, $data, self::$field_key));
+ }
+ $data = $url;
+ } elseif ( 'bool' === $key_rules ) {
+ $bool = rest_sanitize_boolean( $data );
+
+ if ( is_bool( $bool ) ) {
+ return $bool;
+ }
+
+ if ( in_array( $bool, array('true', 'false') ) ) {
+ return $bool;
+ }
+
+ throw new SanitizationFailureException( self::create_error_string( 'a bool', $context, $data, self::$field_key ) );
+ } else {
+ throw new SanitizationFailureException(sprintf(__('Unknown sanitization rule "%1$s" supplied by %2$s', 'wp-db-migrate-pro'), $key_rules, $context));
+ }
+ }
+
+ return $data;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Settings/Settings.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Settings/Settings.php
new file mode 100644
index 000000000..c1be7af7e
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Settings/Settings.php
@@ -0,0 +1,129 @@
+util = $util;
+
+ // @TODO this shouldn't be fired every time the Settings class is called...
+ $this->load_settings();
+ $this->filesystem = $filesystem;
+ }
+
+ static function get_setting($setting)
+ {
+ if (isset(self::$static_settings[$setting])) {
+ return self::$static_settings[$setting];
+ }
+
+ throw new \InvalidArgumentException(__('Setting does not exist', 'wp-migrate-db'));
+ }
+
+ public function get_settings_for_frontend()
+ {
+ // Always get fresh settings for the frontend.
+ $this->load_settings();
+ $existing_settings = $this->settings;
+
+ if (!empty($existing_settings['licence'])) {
+ $masked_licence = $this->util->mask_licence($existing_settings['licence']);
+ $existing_settings['masked_licence'] = $masked_licence;
+ }
+
+ $existing_settings['plugins'] = $this->filesystem->get_local_plugins();
+
+ return $existing_settings;
+ }
+
+ public function get_settings()
+ {
+ // Assumes load_settings() has been called in base plugin (WPMigrateDB)
+ return $this->settings;
+ }
+
+ public function load_settings()
+ {
+ $update_settings = false;
+ $this->settings = get_site_option('wpmdb_settings');
+
+ $default_settings = array(
+ 'key' => $this->util->generate_key(),
+ 'allow_pull' => false,
+ 'allow_push' => false,
+ 'profiles' => array(),
+ 'licence' => '',
+ 'verify_ssl' => false,
+ 'whitelist_plugins' => array(),
+ 'max_request' => min(1024 * 1024, $this->util->get_bottleneck('max')),
+ 'delay_between_requests' => 0,
+ 'prog_tables_hidden' => true,
+ 'pause_before_finalize' => false,
+ 'allow_tracking' => null,
+ 'high_performance_transfers' => false
+ );
+
+ // if we still don't have settings exist this must be a fresh install, set up some default settings
+ if (false === $this->settings) {
+ $this->settings = $default_settings;
+ $update_settings = true;
+ } else {
+ /*
+ * When new settings are added an existing customer's db won't have the new settings.
+ * They're added here to circumvent array index errors in debug mode.
+ */
+ foreach ($default_settings as $key => $value) {
+ if (!isset($this->settings [$key])) {
+ $this->settings[$key] = $value;
+ $update_settings = true;
+ }
+ }
+ }
+
+ $is_compat_mode = $this->util->is_muplugin_installed();
+
+ if (!isset($this->settings['compatibility_mode']) || $is_compat_mode !== $this->settings['compatibility_mode']) {
+ //override compatibility mode
+ $this->settings['compatibility_mode'] = $is_compat_mode;
+ $update_settings = true;
+ }
+
+
+ if ($update_settings) {
+ update_site_option('wpmdb_settings', $this->settings);
+ }
+
+ $user_licence = Helpers::get_user_licence_key();
+ if ( $user_licence ) {
+ $this->settings['licence'] = $user_licence;
+ }
+
+ self::$static_settings = $this->settings;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Settings/SettingsManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Settings/SettingsManager.php
new file mode 100644
index 000000000..66eef3eaf
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Settings/SettingsManager.php
@@ -0,0 +1,274 @@
+http = $http;
+ $this->settings = $settings->get_settings();
+ $this->state_manager = $state_manager;
+ $this->error_log = $error_log;
+ $this->rest_API_server = $rest_API_server;
+ $this->http_helper = $http_helper;
+ $this->util = $util;
+ }
+
+ public function register()
+ {
+ // REST endpoints
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute(
+ '/reset-api-key',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_reset_api_key'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/save-setting',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_save_setting'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/update-max-request',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_update_max_request_size'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/update-delay-between-requests',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_update_delay_between_requests'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/whitelist-plugins',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_whitelist_plugins'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/get-log',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_get_log'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/clear-log',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_clear_log'],
+ ]
+ );
+ }
+
+ /**
+ * Handler for ajax request to reset the secret key.
+ *
+ * @return bool|null
+ */
+ function ajax_reset_api_key()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $key_rules = array(
+ 'action' => 'key',
+ );
+
+ $_POST = Sanitize::sanitize_data($_POST, $key_rules, __METHOD__);
+
+ if (false === $_POST) {
+ exit;
+ }
+
+ $this->settings['key'] = $this->util->generate_key();
+ update_site_option('wpmdb_settings', $this->settings);
+ return $this->http->end_ajax($this->settings['key']);
+ }
+
+ /**
+ * Handler for ajax request to save a setting, e.g. accept pull/push requests setting.
+ *
+ * @return bool|null
+ */
+ public function ajax_save_setting()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $key_rules = array(
+ 'checked' => 'bool',
+ 'setting' => 'key',
+ );
+
+ $state_data = $this->state_manager->set_post_data($key_rules);
+
+ $this->settings[$state_data['setting']] = $state_data['checked'];
+ update_site_option('wpmdb_settings', $this->settings);
+ $result = $this->http->end_ajax('setting saved');
+
+ return $result;
+ }
+
+ public function get_diagnostic_log() {
+ ob_start();
+ $this->error_log->output_diagnostic_info();
+ $this->error_log->output_log_file();
+ return ob_get_clean();
+ }
+
+ /**
+ * @return bool|mixed|void|null
+ */
+ public function ajax_clear_log()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ delete_site_option('wpmdb_error_log');
+
+ return $this->http->end_ajax($this->get_diagnostic_log());
+ }
+
+ /**
+ * @return bool|mixed|void|null
+ */
+ public function ajax_get_log()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $return = $this->get_diagnostic_log();
+ return $this->http->end_ajax($return);
+ }
+
+ /**
+ * Handler for updating the plugins that are not to be loaded during a request (Compatibility Mode).
+ */
+ public function ajax_whitelist_plugins()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'whitelist_plugins' => 'array',
+ );
+
+ $state_data = $this->state_manager->set_post_data($key_rules);
+
+ $this->settings['whitelist_plugins'] = (array) $state_data['whitelist_plugins'];
+ update_site_option('wpmdb_settings', $this->settings);
+
+ return $this->http->end_ajax('plugins whitelisted');
+ }
+
+ /**
+ * Updates the Maximum Request Size setting.
+ *
+ * @return void
+ * @throws \DI\DependencyException
+ * @throws \DI\NotFoundException
+ */
+ function ajax_update_max_request_size()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'max_request_size' => 'numeric',
+ );
+
+ $state_data = $this->state_manager->set_post_data($key_rules);
+
+ $this->settings['max_request'] = (int) $state_data['max_request_size'];
+ $result = update_site_option('wpmdb_settings', $this->settings);
+ $this->http->end_ajax($result);
+ }
+
+ /**
+ * Updates the Delay Between Requests setting.
+ *
+ * @return void
+ * @throws \DI\DependencyException
+ * @throws \DI\NotFoundException
+ */
+ function ajax_update_delay_between_requests()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'delay_between_requests' => 'numeric',
+ );
+ $state_data = $this->state_manager->set_post_data($key_rules);
+
+ $int_val = (int) $state_data['delay_between_requests'];
+ $this->settings['delay_between_requests'] = $int_val;
+ $result = update_site_option('wpmdb_settings', $this->settings);
+ $this->http->end_ajax($result);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Sql/Table.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Sql/Table.php
new file mode 100644
index 000000000..e5dca2c1a
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Sql/Table.php
@@ -0,0 +1,2099 @@
+rows_per_segment = apply_filters('wpmdb_rows_per_segment', 100);
+
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->form_data = $form_data;
+ $this->props = $properties;
+ $this->filesystem = $filesystem;
+ $this->util = $util;
+ $this->error_log = $error_log;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->table_helper = $table_helper;
+ $this->multisite = $multisite;
+ $this->http = $http;
+ $this->http_helper = $http_helper;
+ $this->remote_post = $remote_post;
+ $this->replace = $replace;
+ $this->full_site_export = $full_site_export;
+ }
+
+ /**
+ * Returns an array of table names with associated size in kilobytes.
+ *
+ * @return mixed
+ *
+ * NOTE: Returned array may have been altered by wpmdb_table_sizes filter.
+ */
+ function get_table_sizes()
+ {
+ global $wpdb;
+
+ static $return;
+
+ if (!empty($return)) {
+ return $return;
+ }
+
+ $return = array();
+
+ $sql = $wpdb->prepare(
+ "SELECT TABLE_NAME AS 'table',
+ ROUND( ( data_length + index_length ) / 1024, 0 ) AS 'size'
+ FROM INFORMATION_SCHEMA.TABLES
+ WHERE table_schema = %s
+ AND table_type = %s
+ ORDER BY TABLE_NAME",
+ DB_NAME,
+ 'BASE TABLE'
+ );
+
+ $results = $wpdb->get_results($sql, ARRAY_A);
+
+ if (!empty($results)) {
+ foreach ($results as $result) {
+ if ($this->get_legacy_alter_table_name() == $result['table']) {
+ continue;
+ }
+ $return[$result['table']] = $result['size'];
+ }
+ }
+
+ // "regular" is passed to the filter as the scope for backwards compatibility (a possible but never used scope was "temp").
+ return apply_filters('wpmdb_table_sizes', $return, 'regular');
+ }
+
+ /**
+ * Returns the table name where the alter statements are held during the migration (old "wp_" prefixed style).
+ *
+ * @return string
+ */
+ function get_legacy_alter_table_name()
+ {
+ static $alter_table_name;
+
+ if (!empty($alter_table_name)) {
+ return $alter_table_name;
+ }
+
+ global $wpdb;
+ $alter_table_name = apply_filters('wpmdb_alter_table_name', $wpdb->base_prefix . 'wpmdb_alter_statements');
+
+ return $alter_table_name;
+ }
+
+ /**
+ * Returns an array of table names with their associated row counts.
+ *
+ * @return array
+ */
+ function get_table_row_count()
+ {
+ global $wpdb;
+
+ $sql = $wpdb->prepare('SELECT TABLE_NAME, TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = %s ORDER BY TABLE_NAME', DB_NAME);
+ $results = $wpdb->get_results($sql, ARRAY_A);
+
+ $return = array();
+
+ foreach ($results as $result) {
+ if ($this->get_legacy_alter_table_name() == $result['TABLE_NAME']) {
+ continue;
+ }
+ $return[$result['TABLE_NAME']] = ($result['TABLE_ROWS'] == 0 ? 1 : $result['TABLE_ROWS']);
+ }
+
+ return $return;
+ }
+
+ function format_table_sizes($size)
+ {
+ $size *= 1024;
+
+ return size_format($size);
+ }
+
+ function get_lower_case_table_names_setting()
+ {
+ global $wpdb;
+
+ $setting = $wpdb->get_var("SHOW VARIABLES LIKE 'lower_case_table_names'", 1);
+
+ return empty($setting) ? '-1' : $setting;
+ }
+
+ public function get_sql_dump_info($migration_type, $info_type)
+ {
+ $session_salt = strtolower(wp_generate_password(5, false, false));
+
+ $datetime = date('YmdHis');
+ $ds = ($info_type == 'path' ? DIRECTORY_SEPARATOR : '/');
+ $dump_name = get_bloginfo() ? strtolower(preg_replace('/\s+/', '', get_bloginfo())) : sanitize_title_with_dashes(DB_NAME);
+ //Strip out any non-alphanumeric characters
+ $dump_name = preg_replace("/[^A-Za-z0-9 ]/", '', $dump_name);
+ $dump_name .= 'export' === $migration_type ? '' : '-' . $migration_type;
+ $dump_name = apply_filters('wpmdb_export_filename', sprintf('%s-%s',$dump_name, $datetime));
+ $dump_info = sprintf('%s%s%s-%s.sql', $this->filesystem->get_upload_info($info_type), $ds, $dump_name, $session_salt);
+
+ return ($info_type == 'path' ? $this->filesystem->slash_one_direction($dump_info) : $dump_info);
+ }
+
+ /**
+ * Returns SQL queries used to preserve options in the
+ * wp_options or wp_sitemeta tables during a migration.
+ *
+ * @param array $state_data
+ * @param array $temp_tables
+ * @param string $intent
+ *
+ * @return string DELETE and INSERT SQL queries separated by a newline character (\n).
+ */
+ function get_preserved_options_queries($state_data, $temp_tables, $intent = '')
+ {
+ global $wpdb;
+
+ $form_data = $this->form_data->getFormData();
+ $keep_active_plugins = $form_data['keep_active_plugins'] === '1';
+ $sql = '';
+ $sitemeta_table_name = '';
+ $options_table_names = array();
+
+ $temp_prefix = isset($state_data['temp_prefix']) ? $state_data['temp_prefix'] : $this->props->temp_prefix;
+ $table_prefix = $wpdb->base_prefix;
+ $prefix = esc_sql($temp_prefix . $table_prefix);
+
+ foreach ($temp_tables as $temp_table) {
+ $table = $wpdb->base_prefix . str_replace($prefix, '', $temp_table);
+
+ // Get sitemeta table
+ if (is_multisite() && $this->table_helper->table_is('sitemeta', $table)) {
+ $sitemeta_table_name = $temp_table;
+ }
+
+ // Get array of options tables
+ if ($this->table_helper->table_is('options', $table)) {
+ $options_table_names[] = $temp_table;
+ }
+ }
+
+ // Return if multisite but sitemeta and option tables not in migration scope
+ if (is_multisite() && true === empty($sitemeta_table_name) && true === empty($options_table_names)) {
+ return $sql;
+ }
+
+ // Return if options tables not in migration scope for non-multisite.
+ if (!is_multisite() && true === empty($options_table_names)) {
+ return $sql;
+ }
+
+ $preserved_options = array(
+ 'wpmdb_settings',
+ 'wpmdb_error_log',
+ 'wpmdb_schema_version',
+ 'upload_path',
+ 'upload_url_path',
+ 'blog_public',
+ 'wpmdb_migration_options',
+ 'wpmdb_migration_state',
+ 'wpmdb_remote_response',
+ 'wpmdb_recent_migrations',
+ 'wpmdb_saved_profiles',
+ 'wpmdb_remote_migration_state',
+ );
+
+ $preserved_sitemeta_options = $preserved_options;
+
+ if ($keep_active_plugins) {
+ $preserved_options[] = 'active_plugins';
+ $preserved_sitemeta_options[] = 'active_sitewide_plugins';
+ }
+
+ if (is_multisite()) {
+ // Get preserved data in site meta table if being replaced.
+ if (!empty($sitemeta_table_name)) {
+ $table = $wpdb->base_prefix . str_replace($prefix, '', $sitemeta_table_name);
+
+ $preserved_migration_state_options = $wpdb->get_results(
+ "SELECT `meta_key` FROM `{$table}` WHERE `meta_key` LIKE '" . MigrationState::OPTION_PREFIX . "%'",
+ OBJECT_K
+ );
+
+ if (!empty($preserved_migration_state_options)) {
+ $preserved_sitemeta_options = array_merge($preserved_sitemeta_options, array_keys($preserved_migration_state_options));
+ }
+
+ $preserved_sitemeta_options = apply_filters('wpmdb_preserved_sitemeta_options', $preserved_sitemeta_options, $intent);
+ $preserved_sitemeta_options_escaped = esc_sql($preserved_sitemeta_options);
+
+ $preserved_sitemeta_options_data = $wpdb->get_results(
+ sprintf(
+ "SELECT * FROM `{$table}` WHERE `meta_key` IN ('%s')",
+ implode("','", $preserved_sitemeta_options_escaped)
+ ),
+ ARRAY_A
+ );
+
+ $preserved_sitemeta_options_data = apply_filters('wpmdb_preserved_sitemeta_options_data', $preserved_sitemeta_options_data, $intent);
+
+ // Create preserved data queries for site meta table
+ foreach ($preserved_sitemeta_options_data as $option) {
+ $sql .= $wpdb->prepare("DELETE FROM `{$sitemeta_table_name}` WHERE `meta_key` = %s;\n", $option['meta_key']);
+ $sql .= $wpdb->prepare(
+ "INSERT INTO `{$sitemeta_table_name}` ( `meta_id`, `site_id`, `meta_key`, `meta_value` ) VALUES ( NULL , %s, %s, %s );\n",
+ $option['site_id'],
+ $option['meta_key'],
+ $option['meta_value']
+ );
+ }
+ }
+ } else {
+ $preserved_migration_state_options = $wpdb->get_results(
+ "SELECT `option_name` FROM `{$wpdb->options}` WHERE `option_name` LIKE '" . MigrationState::OPTION_PREFIX . "%'",
+ OBJECT_K
+ );
+
+ if (!empty($preserved_migration_state_options)) {
+ $preserved_options = array_merge($preserved_options, array_keys($preserved_migration_state_options));
+ }
+ }
+
+ // Get preserved data in options tables if being replaced.
+ if (!empty($options_table_names)) {
+ $preserved_options = apply_filters('wpmdb_preserved_options', $preserved_options, $intent);
+ $preserved_options_escaped = esc_sql($preserved_options);
+
+ $preserved_options_data = array();
+
+ // Get preserved data in options tables
+ foreach ($options_table_names as $option_table) {
+ $table = $wpdb->base_prefix . str_replace($prefix, '', $option_table);
+
+ $preserved_options_data[$option_table] = $wpdb->get_results(
+ sprintf(
+ "SELECT * FROM `{$table}` WHERE `option_name` IN ('%s')",
+ implode("','", $preserved_options_escaped)
+ ),
+ ARRAY_A
+ );
+ }
+
+ $preserved_options_data = apply_filters('wpmdb_preserved_options_data', $preserved_options_data, $intent);
+
+ // Create preserved data queries for options tables
+ foreach ($preserved_options_data as $key => $value) {
+ if (false === empty($value)) {
+ foreach ($value as $option) {
+ $sql .= $wpdb->prepare(
+ "DELETE FROM `{$key}` WHERE `option_name` = %s;\n",
+ $option['option_name']
+ );
+
+ $sql .= $wpdb->prepare(
+ "INSERT INTO `{$key}` ( `option_id`, `option_name`, `option_value`, `autoload` ) VALUES ( NULL , %s, %s, %s );\n",
+ $option['option_name'],
+ $option['option_value'],
+ $option['autoload']
+ );
+ }
+ }
+ }
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Preserves the active_plugins option.
+ *
+ * @param array $preserved_options
+ *
+ * @return array
+ */
+ function preserve_active_plugins_option($preserved_options)
+ {
+ $form_data = $this->form_data->getFormData();
+ $keep_active_plugins = $form_data['keep_active_plugins'] === '1';
+
+ if (empty($keep_active_plugins)) {
+ $preserved_options[] = 'active_plugins';
+ }
+
+ return $preserved_options;
+ }
+
+ /**
+ * Preserves WPMDB plugins if the "Keep active plugins" option isn't checked.
+ *
+ * @param array $preserved_options_data
+ *
+ * @return array
+ */
+ function preserve_wpmdb_plugins($preserved_options_data)
+ {
+ $form_data = $this->form_data->getFormData();
+ $keep_active_plugins = $form_data['keep_active_plugins'] === '1';
+
+ if (!empty($keep_active_plugins) || empty($preserved_options_data)) {
+ return $preserved_options_data;
+ }
+
+ foreach ($preserved_options_data as $table => $data) {
+ foreach ($data as $key => $option) {
+ if ('active_plugins' === $option['option_name']) {
+ global $wpdb;
+
+ $table_name = esc_sql($table);
+ $option_value = Util::unserialize($option['option_value']);
+ $migrated_plugins = array();
+ $wpmdb_plugins = array();
+
+ if ($result = $wpdb->get_var("SELECT option_value FROM $table_name WHERE option_name = 'active_plugins'")) {
+ $unserialized = Util::unserialize($result);
+ if (is_array($unserialized)) {
+ $migrated_plugins = $unserialized;
+ }
+ }
+
+ foreach ($option_value as $plugin_key => $plugin) {
+ if (0 === strpos($plugin, 'wp-migrate-db')) {
+ $wpmdb_plugins[] = $plugin;
+ }
+ }
+
+ $merged_plugins = array_unique(array_merge($wpmdb_plugins, $migrated_plugins));
+ $option['option_value'] = serialize($merged_plugins);
+ $preserved_options_data[$table][$key] = $option;
+ break;
+ }
+ }
+ }
+
+ return $preserved_options_data;
+ }
+
+ /**
+ * Change table prefix if needed
+ *
+ * @param string $table
+ *
+ * @return string
+ */
+ public function prefix_target_table_name($table, $state_data)
+ {
+ if($state_data['source_prefix'] === $state_data['destination_prefix']) {
+ return $table;
+ }
+ return Util::prefix_updater($table, $state_data['source_prefix'], $state_data['destination_prefix']);
+ }
+
+ /**
+ * Loops over data in the provided table to perform a migration.
+ *
+ * @TODO this has a memory leak, each iteration of the do/while loop leaks 1k or so of memory
+ *
+ * @param string $table
+ *
+ * @return mixed
+ */
+ function process_table($table, $fp = null, $state_data = [])
+ {
+ global $wpdb;
+
+ if (!empty($state_data)) {
+ $this->state_data = $state_data;
+ }
+
+ $temp_prefix = (isset($state_data['temp_prefix']) ? $state_data['temp_prefix'] : $this->props->temp_prefix);
+ $site_details = empty($state_data['site_details']) ? array() : $state_data['site_details'];
+ $subsite_migration = array_key_exists('mst_select_subsite', $state_data) && '1' === $state_data['mst_select_subsite'];
+ $target_table_name = apply_filters('wpmdb_target_table_name', $table, $state_data, $site_details, $subsite_migration );
+ if (in_array($state_data['intent'], ['push', 'pull']) && !$subsite_migration && $state_data['stage'] !== 'backup') {
+ $target_table_name = $this->prefix_target_table_name($target_table_name, $state_data);
+ }
+ $temp_table_name = $state_data["intent"] === 'import' ? $target_table_name : $temp_prefix . $target_table_name;
+ $structure_info = $this->get_structure_info($table, [], $state_data);
+ $row_start = $this->get_current_row($state_data);
+ $this->row_tracker = $row_start;
+
+ if (!is_array($structure_info)) {
+ return $structure_info;
+ }
+
+ $this->pre_process_data($table, $target_table_name, $temp_table_name, $fp, $state_data);
+ $to_search = isset($state_data['find_replace_pairs']['replace_old']) ? $state_data['find_replace_pairs']['replace_old'] : '';
+ $to_replace = isset($state_data['find_replace_pairs']['replace_new']) ? $state_data['find_replace_pairs']['replace_new'] : '';
+ $search_replace_regex = isset($state_data['find_replace_pairs']['regex']) ? $state_data['find_replace_pairs']['regex'] : '';
+ $search_replace_case_sensitive = isset($state_data['find_replace_pairs']['case_sensitive']) ? $state_data['find_replace_pairs']['case_sensitive'] : '';
+
+ $replacer = $this->replace->register(array(
+ 'table' => ('find_replace' === $state_data['stage']) ? $temp_table_name : $table,
+ 'search' => $to_search,
+ 'replace' => $to_replace,
+ 'regex' => $search_replace_regex,
+ 'case_sensitive' => $search_replace_case_sensitive,
+ 'intent' => $state_data['intent'],
+ 'base_domain' => $this->multisite->get_domain_replace(),
+ 'site_domain' => $this->multisite->get_domain_current_site(),
+ 'wpmdb' => $this,
+ 'site_details' => $site_details,
+ ));
+
+ $table_data = null;
+
+ // @TODO this has a memory leak
+ do {
+ $select_sql = $this->build_select_query($table, $row_start, $structure_info, $state_data);
+ $table_data = $wpdb->get_results($select_sql);
+
+ if (!is_array($table_data)) {
+ continue;
+ }
+
+ $this->start_query_buffer($target_table_name, $temp_table_name, $structure_info, $state_data);
+
+ // Loop over the results
+ foreach ($table_data as $row) {
+ $result = $this->process_row($table, $replacer, $row, $structure_info, $fp, $state_data);
+ if (!is_bool($result)) {
+ return $result;
+ }
+ }
+
+ $this->stow_query_buffer($fp);
+ $row_start += $this->rows_per_segment;
+ $select_sql = null;
+ $result = null;
+ } while (count($table_data) > 0);
+
+ // Finalize and return.
+ $this->post_process_data($table, $target_table_name, $fp, $state_data);
+
+ return $this->transfer_chunk($fp, $state_data);
+ }
+
+ /**
+ * Parses the provided table structure.
+ *
+ * @param array $table_structure
+ *
+ * @return array
+ */
+ function get_structure_info($table, $table_structure = array(), $state_data = [])
+ {
+ if (empty($state_data)) {
+ $state_data = Persistence::getStateData();
+ }
+
+ if (empty($table_structure)) {
+ $table_structure = $this->get_table_structure($table);
+ }
+
+ if (!is_array($table_structure)) {
+ $this->error_log->log_error($this->error_log->getError());
+ $return = array('wpmdb_error' => 1, 'body' => $this->error_log->getError());
+ $result = $this->http->end_ajax(json_encode($return));
+
+ return $result;
+ }
+
+ // $defs = mysql defaults, looks up the default for that particular column, used later on to prevent empty inserts values for that column
+ // $ints = holds a list of the possible integer types so as to not wrap them in quotation marks later in the insert statements
+ $defs = array();
+ $ints = array();
+ $bins = array();
+ $bits = array();
+ $field_set = array();
+ $this->primary_keys = array();
+ $use_primary_keys = true;
+
+ foreach ($table_structure as $struct) {
+ if ((0 === strpos($struct->Type, 'tinyint')) ||
+ (0 === strpos(strtolower($struct->Type), 'smallint')) ||
+ (0 === strpos(strtolower($struct->Type), 'mediumint')) ||
+ (0 === strpos(strtolower($struct->Type), 'int')) ||
+ (0 === strpos(strtolower($struct->Type), 'bigint'))
+ ) {
+ $defs[strtolower($struct->Field)] = (null === $struct->Default) ? 'NULL' : $struct->Default;
+ $ints[strtolower($struct->Field)] = '1';
+ } elseif (0 === strpos($struct->Type, 'binary') || apply_filters('wpmdb_process_column_as_binary', false, $struct)) {
+ $bins[strtolower($struct->Field)] = '1';
+ } elseif (0 === strpos($struct->Type, 'bit') || apply_filters('wpmdb_process_column_as_bit', false, $struct)) {
+ $bits[strtolower($struct->Field)] = '1';
+ }
+
+ $field_set[] = $this->table_helper->backquote($struct->Field);
+
+ if ('PRI' === $struct->Key && true === $use_primary_keys) {
+ if (false === strpos($struct->Type, 'int')) {
+ $use_primary_keys = false;
+ $this->primary_keys = array();
+ continue;
+ }
+ $this->primary_keys[$struct->Field] = 0;
+ }
+ }
+
+ if ( ! empty($state_data['primary_keys'])) {
+ if ( ! Util::is_json($state_data['primary_keys'])) {
+ $state_data['primary_keys'] = base64_decode(trim($state_data['primary_keys']));
+ }
+ $this->primary_keys = json_decode(stripslashes($state_data['primary_keys']), true);
+ if (false !== $this->primary_keys && ! empty($state_data['primary_keys'])) {
+ $this->first_select = false;
+ }
+ }
+
+ $return = array(
+ 'defs' => $defs,
+ 'ints' => $ints,
+ 'bins' => $bins,
+ 'bits' => $bits,
+ 'field_set' => $field_set,
+ );
+
+ return $return;
+ }
+
+ /**
+ * Returns the table structure for the provided table.
+ *
+ * @param string $table
+ *
+ * @return array|bool
+ */
+ function get_table_structure($table)
+ {
+ global $wpdb;
+
+ $table_structure = false;
+
+ if ($this->table_exists($table)) {
+ $table_structure = $wpdb->get_results('DESCRIBE ' . $this->table_helper->backquote($table));
+ }
+
+ if (!$table_structure) {
+ $this->error_log->setError(sprintf(__('Failed to retrieve table structure for table \'%s\', please ensure your database is online. (#125)', 'wp-migrate-db'), $table));
+
+ return false;
+ }
+
+ return $table_structure;
+ }
+
+ /**
+ * Checks if a given table exists.
+ *
+ * @param $table
+ *
+ * @return bool
+ */
+ function table_exists($table)
+ {
+ global $wpdb;
+
+ $table = esc_sql($table);
+
+ if ($wpdb->get_var("SHOW TABLES LIKE '$table'")) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the current row, checking the state data.
+ *
+ * @return int
+ */
+ function get_current_row($state_data = false)
+ {
+ if (!$state_data) {
+ $state_data = Persistence::getStateData();
+ }
+
+ $current_row = 0;
+
+ if (!empty($state_data['current_row'])) {
+ $temp_current_row = trim($state_data['current_row']);
+ if (!empty($temp_current_row)) {
+ $current_row = (int)$temp_current_row;
+ }
+ }
+
+ $current_row = (0 > $current_row) ? 0 : $current_row;
+
+ return $current_row;
+ }
+
+ /**
+ * Runs before processing the data in a table.
+ *
+ * @param string $table
+ * @param string $target_table_name
+ * @param string $temp_table_name
+ */
+ function pre_process_data($table, $target_table_name, $temp_table_name, $fp, $state_data)
+ {
+ if (0 !== $this->row_tracker) {
+ return;
+ }
+
+ if (in_array($state_data['intent'], array('find_replace', 'import'))) {
+ if ('backup' === $state_data['stage']) {
+ $this->build_table_header($table, $target_table_name, $temp_table_name, $fp, $state_data);
+ } elseif ('find_replace' === $state_data['intent']) {
+ $create = $this->create_temp_table($table);
+
+ if (true !== $create) {
+ $message = sprintf(__('Error creating temporary table. Table "%s" does not exist.', 'wp-migrate-db'), esc_html($table));
+
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-error-creating-temp-table',
+ $message
+ )
+ );
+ }
+ }
+ } else {
+ $this->build_table_header($table, $target_table_name, $temp_table_name, $fp, $state_data);
+ }
+
+ /**
+ * Fires just before processing the data for a table.
+ *
+ * @param string $table
+ * @param string $target_table_name
+ * @param string $temp_table_name
+ */
+ do_action('wpmdb_pre_process_table_data', $table, $target_table_name, $temp_table_name);
+ }
+
+ /**
+ * Creates the header for a table in a SQL file.
+ *
+ * @param string $table
+ * @param string $target_table_name
+ * @param string $temp_table_name
+ *
+ * @return null|bool
+ */
+ function build_table_header($table, $target_table_name, $temp_table_name, $fp, $state_data)
+ {
+ global $wpdb;
+
+ // Don't stow data until after `wpmdb_create_table_query` filter is applied as mysql_compat_filter() can return an error
+ $stow = '';
+ $is_backup = false;
+ $table_to_stow = $temp_table_name;
+
+ if ('savefile' === $state_data['intent'] || 'backup' === $state_data['stage']) {
+ $is_backup = true;
+ $table_to_stow = $target_table_name;
+ }
+
+ // Add SQL statement to drop existing table
+ if ($is_backup) {
+ $stow .= ("\n\n");
+ $stow .= ("#\n");
+ $stow .= ('# ' . sprintf(__('Delete any existing table %s', 'wp-migrate-db'), $this->table_helper->backquote($table_to_stow)) . "\n");
+ $stow .= ("#\n");
+ $stow .= ("\n");
+ }
+ $stow .= ('DROP TABLE IF EXISTS ' . $this->table_helper->backquote($table_to_stow) . ";\n");
+
+ // Table structure
+ // Comment in SQL-file
+ if ($is_backup) {
+ $stow .= ("\n\n");
+ $stow .= ("#\n");
+ $stow .= ('# ' . sprintf(__('Table structure of table %s', 'wp-migrate-db'), $this->table_helper->backquote($table_to_stow)) . "\n");
+ $stow .= ("#\n");
+ $stow .= ("\n");
+ }
+
+ $create_table = $wpdb->get_results('SHOW CREATE TABLE ' . $this->table_helper->backquote($table), ARRAY_N);
+
+ if (false === $create_table) {
+ $this->error_log->setError(__('Failed to generate the create table query, please ensure your database is online. (#126)', 'wp-migrate-db'));
+
+ return false;
+ }
+ //Replaces ANSI quotes with backticks
+ $create_table[0][1] = $this->remove_ansi_quotes( $create_table[0][1] );
+ $create_table[0][1] = str_replace( 'CREATE TABLE `' . $table . '`', 'CREATE TABLE `' . $table_to_stow . '`', $create_table[0][1] );
+ $create_table[0][1] = str_replace( 'TYPE=', 'ENGINE=', $create_table[0][1] );
+
+ $alter_table_query = '';
+ $create_table[0][1] = $this->process_sql_constraint($create_table[0][1], $target_table_name, $alter_table_query);
+
+ $create_table[0][1] = apply_filters('wpmdb_create_table_query', $create_table[0][1], $table_to_stow, $this->dynamic_props->target_db_version, $state_data['intent'], $state_data['stage']);
+ $stow .= ($create_table[0][1] . ";\n");
+
+ $this->stow($stow, false, $fp);
+
+ if (!empty($alter_table_query)) {
+ $alter_table_name = $this->get_alter_table_name();
+ $insert = sprintf("INSERT INTO %s ( `query` ) VALUES ( '%s' );\n", $this->table_helper->backquote($alter_table_name), esc_sql($alter_table_query));
+
+ if ($is_backup) {
+ $process_chunk_result = $this->process_chunk($insert);
+ if (true !== $process_chunk_result) {
+ $result = $this->http->end_ajax($process_chunk_result);
+
+ return $result;
+ }
+ } else {
+ $this->stow($insert, false, $fp);
+ }
+ }
+
+ $alter_data_queries = array();
+ $alter_data_queries = apply_filters('wpmdb_alter_data_queries', $alter_data_queries, $table_to_stow, $state_data['intent'], $state_data['stage']);
+
+ if (!empty($alter_data_queries)) {
+ $alter_table_name = $this->get_alter_table_name();
+ $insert = '';
+ foreach ($alter_data_queries as $alter_data_query) {
+ $insert .= sprintf("INSERT INTO %s ( `query` ) VALUES ( '%s' );\n", $this->table_helper->backquote($alter_table_name), esc_sql($alter_data_query));
+ }
+ if ($is_backup) {
+ $process_chunk_result = $this->process_chunk($insert);
+ if (true !== $process_chunk_result) {
+ $result = $this->http->end_ajax($process_chunk_result);
+
+ return $result;
+ }
+ } else {
+ $this->stow($insert, false, $fp);
+ }
+ }
+
+ // Comment in SQL-file
+ if ($is_backup) {
+ $this->stow("\n\n", false, $fp);
+ $this->stow("#\n", false, $fp);
+ $this->stow('# ' . sprintf(__('Data contents of table %s', 'wp-migrate-db'), $this->table_helper->backquote($table_to_stow)) . "\n", false, $fp);
+ $this->stow("#\n", false, $fp);
+ }
+ }
+
+ function process_sql_constraint($create_query, $table, &$alter_table_query)
+ {
+ if (preg_match('@CONSTRAINT|FOREIGN[\s]+KEY@', $create_query)) {
+ $sql_constraints_query = '';
+
+ $nl_nix = "\n";
+ $nl_win = "\r\n";
+ $nl_mac = "\r";
+
+ if (strpos($create_query, $nl_win) !== false) {
+ $crlf = $nl_win;
+ } elseif (strpos($create_query, $nl_mac) !== false) {
+ $crlf = $nl_mac;
+ } else {
+ $crlf = $nl_nix;
+ }
+
+ // Split the query into lines, so we can easily handle it.
+ // We know lines are separated by $crlf (done few lines above).
+ $sql_lines = explode($crlf, $create_query);
+ $sql_count = count($sql_lines);
+
+ // lets find first line with constraints
+ for ($i = 0; $i < $sql_count; $i++) {
+ if (preg_match(
+ '@^[\s]*(CONSTRAINT|FOREIGN[\s]+KEY)@',
+ $sql_lines[$i]
+ )) {
+ break;
+ }
+ }
+
+ // If we really found a constraint
+ if ($i != $sql_count) {
+ // remove, from the end of create statement
+ $sql_lines[$i - 1] = preg_replace(
+ '@,$@',
+ '',
+ $sql_lines[$i - 1]
+ );
+
+ // let's do the work
+ $sql_constraints_query .= 'ALTER TABLE ' . $this->table_helper->backquote($table) . $crlf;
+
+ $first = true;
+ for ($j = $i; $j < $sql_count; $j++) {
+ if (preg_match(
+ '@CONSTRAINT|FOREIGN[\s]+KEY@',
+ $sql_lines[$j]
+ )) {
+ if (strpos($sql_lines[$j], 'CONSTRAINT') === false) {
+ $tmp_str = preg_replace(
+ '/(FOREIGN[\s]+KEY)/',
+ 'ADD \1',
+ $sql_lines[$j]
+ );
+ $sql_constraints_query .= $tmp_str;
+ } else {
+ $tmp_str = preg_replace(
+ '/(CONSTRAINT)/',
+ 'ADD \1',
+ $sql_lines[$j]
+ );
+ $sql_constraints_query .= $tmp_str;
+ preg_match(
+ '/(CONSTRAINT)([\s])([\S]*)([\s])/',
+ $sql_lines[$j],
+ $matches
+ );
+ }
+ $first = false;
+ } else {
+ break;
+ }
+ }
+
+ $sql_constraints_query .= ";\n";
+
+ $create_query = implode(
+ $crlf,
+ array_slice($sql_lines, 0, $i)
+ )
+ . $crlf
+ . implode(
+ $crlf,
+ array_slice($sql_lines, $j, $sql_count - 1)
+ );
+ unset($sql_lines);
+
+ $alter_table_query = $sql_constraints_query;
+
+ return $create_query;
+ }
+ }
+
+ return $create_query;
+ }
+
+ /**
+ * Write query line to chunk, file pointer, or buffer depending on migration stage/action.
+ *
+ * @param string $query_line
+ * @param bool $replace
+ *
+ * @return bool
+ */
+ function stow($query_line, $replace = true, $fp = null)
+ {
+ $state_data = !empty($this->state_data) ? $this->state_data : Persistence::getStateData();
+ $form_data = $this->form_data->getFormData();
+
+ $this->migration_state_manager->set_post_data();
+ $this->current_chunk .= $query_line;
+
+ if (0 === strlen($query_line)) {
+ return true;
+ }
+
+ if ('savefile' === $state_data['intent'] || in_array($state_data['stage'], array('backup', 'import'))) {
+ $is_full_site_export = isset($state_data['stages']) && json_decode($state_data['stages']) !== ['tables'] ? true : false;
+ if (Util::gzip() && (isset($form_data['gzip_file']) && $form_data['gzip_file']) && !$is_full_site_export) {
+ if (!gzwrite($fp, $query_line)) {
+ $this->error_log->setError(__('Failed to write the gzipped SQL data to the file. (#127)', 'wp-migrate-db'));
+
+ return false;
+ }
+ } else {
+ // TODO: Use WP_Filesystem API.
+ if (false === @fwrite($fp, $query_line)) {
+ $this->error_log->setError(__('Failed to write the SQL data to the file. (#128)', 'wp-migrate-db'));
+
+ return false;
+ }
+ }
+ } elseif ($state_data['intent'] == 'pull') {
+ echo apply_filters('wpmdb_before_response', $query_line);
+ } elseif ('find_replace' === $state_data['stage']) {
+ return $this->process_chunk($query_line);
+ }
+ }
+
+ function process_chunk($chunk)
+ {
+ // prepare db
+ global $wpdb;
+ $this->util->set_time_limit();
+
+ $queries = array_filter(explode(";\n", $chunk));
+ array_unshift($queries, "SET sql_mode='NO_AUTO_VALUE_ON_ZERO';");
+
+ ob_start();
+ $wpdb->show_errors();
+ if (empty($wpdb->charset)) {
+ $charset = (defined('DB_CHARSET') ? DB_CHARSET : 'utf8');
+ $wpdb->charset = $charset;
+ $wpdb->set_charset($wpdb->dbh, $wpdb->charset);
+ }
+
+ foreach ($queries as $query) {
+ if (false === $wpdb->query($query)) {
+ $return = ob_get_clean();
+ $return = array('wpmdb_error' => 1, 'body' => $return);
+
+ $invalid_text = $this->maybe_strip_invalid_text_and_retry($query);
+ if (false !== $invalid_text) {
+ $return = $invalid_text;
+ }
+
+ if (true !== $return) {
+ return $this->http->end_ajax(new WP_Error('wp-migrate-db-strip-invalid', $return));
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if query failed due to invalid text and retry stripped query if WPMDB_STRIP_INVALID is defined as true
+ *
+ * @param string $query
+ * @param string $context
+ *
+ * @return array|bool|WP_Error
+ */
+ function maybe_strip_invalid_text_and_retry($query, $context = 'default')
+ {
+ global $wpdb;
+ $return = true;
+ // For insert/update queries, check if it's due to invalid text
+ if (!$wpdb->last_error && (strstr($query, 'INSERT') || strstr($query, 'UPDATE'))) {
+ // Only instantiate WPMDB_WPDB if needed
+ if (!$this->wpdb) {
+ $this->wpdb = Util::make_wpmdb_wpdb_instance();
+ }
+ if ($this->wpdb->query_has_invalid_text($query)) {
+ if (!(defined('WPMDB_STRIP_INVALID_TEXT') && WPMDB_STRIP_INVALID_TEXT)) {
+ $table = $this->wpdb->get_table_from_query($query);
+ $table = str_replace($this->props->temp_prefix, '', $table);
+
+ if ('import' === $context) {
+ $message = sprintf(__('The imported table `%1s` contains characters which are invalid in the target schema.
If this is a WP Migrate export file, ensure that the `Compatible with older versions of MySQL` setting under `Advanced Options` is unchecked and try exporting again.
See our documentation for more information.', 'wp-migrate-db'), $table, 'https://deliciousbrains.com/wp-migrate-db-pro/doc/invalid-text/#imports');
+ $return = new WP_Error('import_sql_execution_failed', $message);
+ } else {
+ $message = sprintf(__('The table `%1s` contains characters which are invalid in the target database. See our documentation for more information.', 'wp-migrate-db'), $table, 'https://deliciousbrains.com/wp-migrate-db-pro/doc/invalid-text/');
+
+ return $message;
+ }
+
+ $this->error_log->log_error($message);
+ error_log($message . ":\n" . $query);
+ } else {
+ if (false === $wpdb->query($this->wpdb->last_stripped_query)) {
+ $error = ob_get_clean();
+
+ $return = new WP_Error('strip_invalid_text_query_failed', 'Failed to import the stripped SQL query: ' . $error);
+ } else {
+ $return = true;
+ }
+ }
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Returns the table name where the alter statements are held during the migration.
+ *
+ * @return string
+ */
+ function get_alter_table_name()
+ {
+ static $alter_table_name;
+
+ if (!empty($alter_table_name)) {
+ return $alter_table_name;
+ }
+
+ $alter_table_name = apply_filters('wpmdb_alter_table_name', $this->props->temp_prefix . 'wpmdb_alter_statements');
+
+ return $alter_table_name;
+ }
+
+ /**
+ * Creates a temporary table with a copy of the existing table's data.
+ *
+ * @param $table
+ *
+ * @return bool|mixed
+ */
+ function create_temp_table($table)
+ {
+ if ($this->table_exists($table)) {
+ $src_table = $this->table_helper->backquote($table);
+ $temp_table = $this->table_helper->backquote($this->props->temp_prefix . $table);
+ $query = "DROP TABLE IF EXISTS {$temp_table};\n";
+ $query .= "CREATE TABLE {$temp_table} LIKE {$src_table};\n";
+ $query .= "INSERT INTO {$temp_table} SELECT * FROM {$src_table};";
+
+ return $this->process_chunk($query);
+ }
+
+ return false;
+ }
+
+ /**
+ * Builds the SELECT query to get data to migrate.
+ *
+ * @param string $table
+ * @param int $row_start
+ * @param array $structure_info
+ *
+ * @return string
+ */
+ function build_select_query($table, $row_start, $structure_info, $state_data)
+ {
+ $form_data = $this->form_data->getFormData();
+
+ global $wpdb;
+
+ $join = array();
+ $where = 'WHERE 1=1';
+ $order_by = '';
+ $prefix = ('import' === $state_data['intent']) ? $this->props->temp_prefix . $wpdb->base_prefix : '';
+
+ // We need ORDER BY here because with LIMIT, sometimes it will return
+ // the same results from the previous query and we'll have duplicate insert statements
+ if ('import' !== $state_data['intent'] && 'backup' != $state_data['stage'] && false === empty($form_data['exclude_spam'])) {
+ if ($this->table_helper->table_is('comments', $table, 'table', $prefix)) {
+ $where .= ' AND comment_approved != \'spam\'';
+ } elseif ($this->table_helper->table_is('commentmeta', $table, 'table', $prefix)) {
+ $tables = $this->get_ms_compat_table_names(array('commentmeta', 'comments'), $table);
+ $join[] = sprintf('INNER JOIN %1$s ON %1$s.comment_ID = %2$s.comment_id', $this->table_helper->backquote($tables['comments_table']), $this->table_helper->backquote($tables['commentmeta_table']));
+ $where .= sprintf(' AND %1$s.comment_approved != \'spam\'', $this->table_helper->backquote($tables['comments_table']));
+ }
+ }
+
+ if ('import' !== $state_data['intent'] && 'backup' != $state_data['stage'] && isset($form_data['exclude_post_types']) && !empty($form_data['select_post_types'])) {
+ $post_types = '\'' . implode('\', \'', $form_data['select_post_types']) . '\'';
+ if ($this->table_helper->table_is('posts', $table, 'table', $prefix)) {
+ $where .= ' AND `post_type` IN ( ' . $post_types . ' )';
+ } elseif ($this->table_helper->table_is('postmeta', $table, 'table', $prefix)) {
+ $tables = $this->get_ms_compat_table_names(array('postmeta', 'posts'), $table);
+ $join[] = sprintf('INNER JOIN %1$s ON %1$s.ID = %2$s.post_id', $this->table_helper->backquote($tables['posts_table']), $this->table_helper->backquote($tables['postmeta_table']));
+ $where .= sprintf(' AND %1$s.post_type IN ( ' . $post_types . ' )', $this->table_helper->backquote($tables['posts_table']));
+ } elseif ($this->table_helper->table_is('comments', $table, 'table', $prefix)) {
+ $tables = $this->get_ms_compat_table_names(array('comments', 'posts'), $table);
+ $join[] = sprintf('INNER JOIN %1$s ON %1$s.ID = %2$s.comment_post_ID', $this->table_helper->backquote($tables['posts_table']), $this->table_helper->backquote($tables['comments_table']));
+ $where .= sprintf(' AND %1$s.post_type IN ( ' . $post_types . ' )', $this->table_helper->backquote($tables['posts_table']));
+ } elseif ($this->table_helper->table_is('commentmeta', $table, 'table', $prefix)) {
+ $tables = $this->get_ms_compat_table_names(array('commentmeta', 'posts', 'comments'), $table);
+ $join[] = sprintf('INNER JOIN %1$s ON %1$s.comment_ID = %2$s.comment_id', $this->table_helper->backquote($tables['comments_table']), $this->table_helper->backquote($tables['commentmeta_table']));
+ $join[] = sprintf('INNER JOIN %2$s ON %2$s.ID = %1$s.comment_post_ID', $this->table_helper->backquote($tables['comments_table']), $this->table_helper->backquote($tables['posts_table']));
+ $where .= sprintf(' AND %1$s.post_type IN ( ' . $post_types . ' )', $this->table_helper->backquote($tables['posts_table']));
+ }
+ }
+
+
+ if ('import' !== $state_data['intent'] && 'backup' != $state_data['stage'] && true === apply_filters('wpmdb_exclude_transients', true) && isset($form_data['exclude_transients']) && '1' === $form_data['exclude_transients'] && ($this->table_helper->table_is('options', $table, 'table', $prefix) || (isset($wpdb->sitemeta) && $wpdb->sitemeta == $table))) {
+ $col_name = 'option_name';
+
+ if (isset($wpdb->sitemeta) && $wpdb->sitemeta == $table) {
+ $col_name = 'meta_key';
+ }
+
+ $where .= " AND `{$col_name}` NOT LIKE '\_transient\_%' AND `{$col_name}` NOT LIKE '\_site\_transient\_%'";
+ }
+
+ // don't export/migrate wpmdb specific option rows unless we're performing a backup
+ if ('backup' != $state_data['stage'] && ($this->table_helper->table_is('options', $table, 'table', $prefix) || (isset($wpdb->sitemeta) && $wpdb->sitemeta == $table))) {
+ $col_name = 'option_name';
+
+ if (isset($wpdb->sitemeta) && $wpdb->sitemeta == $table) {
+ $col_name = 'meta_key';
+ }
+
+ $where .= " AND `{$col_name}` != 'wpmdb_settings'";
+ $where .= " AND `{$col_name}` != 'wpmdb_saved_profiles'";
+ $where .= " AND `{$col_name}` != 'wpmdb_recent_migrations'";
+ $where .= " AND `{$col_name}` != 'wpmdb_remote_migration_state'";
+ $where .= " AND `{$col_name}` != 'wpmdb_migration_state'";
+ $where .= " AND `{$col_name}` != 'wpmdb_migration_options'";
+ $where .= " AND `{$col_name}` != 'wpmdb_remote_response'";
+ $where .= " AND `{$col_name}` != 'wpmdb_error_log'";
+ $where .= " AND `{$col_name}` != 'wpmdb_file_ignores'";
+ $where .= " AND `{$col_name}` != 'wpmdb_schema_version'";
+ $where .= " AND `{$col_name}` NOT LIKE 'wpmdb_state_%'";
+ }
+
+ $limit = "LIMIT {$row_start}, {$this->rows_per_segment}";
+
+ if (!empty($this->primary_keys)) {
+ $primary_keys_keys = array_keys($this->primary_keys);
+ $primary_keys_keys = array_map(array($this->table_helper, 'backquote'), $primary_keys_keys);
+
+ $order_by = 'ORDER BY ' . implode(',', $primary_keys_keys);
+ $limit = "LIMIT {$this->rows_per_segment}";
+
+ if (false === $this->first_select) {
+ $where .= ' AND ';
+
+ $temp_primary_keys = $this->primary_keys;
+ $primary_key_count = count($temp_primary_keys);
+
+ // build a list of clauses, iteratively reducing the number of fields compared in the compound key
+ // e.g. (a = 1 AND b = 2 AND c > 3) OR (a = 1 AND b > 2) OR (a > 1)
+ $clauses = array();
+ for ($j = 0; $j < $primary_key_count; $j++) {
+ // build a subclause for each field in the compound index
+ $subclauses = array();
+ $i = 0;
+ foreach ($temp_primary_keys as $primary_key => $value) {
+ // only the last field in the key should be different in this subclause
+ $operator = (count($temp_primary_keys) - 1 == $i ? '>' : '=');
+ $subclauses[] = sprintf('%s %s %s', $this->table_helper->backquote($primary_key), $operator, $wpdb->prepare('%s', $value));
+ ++$i;
+ }
+
+ // remove last field from array to reduce fields in next clause
+ array_pop($temp_primary_keys);
+
+ // join subclauses into a single clause
+ // NB: AND needs to be wrapped in () as it has higher precedence than OR
+ $clauses[] = '( ' . implode(' AND ', $subclauses) . ' )';
+ }
+ // join clauses into a single clause
+ // NB: OR needs to be wrapped in () as it has lower precedence than AND
+ $where .= '( ' . implode(' OR ', $clauses) . ' )';
+ }
+
+ $this->first_select = false;
+ }
+
+ $sel = $this->table_helper->backquote($table) . '.*';
+ if (!empty($structure_info['bins'])) {
+ foreach ($structure_info['bins'] as $key => $bin) {
+ $hex_key = strtolower($key) . '__hex';
+ $sel .= ', HEX(' . $this->table_helper->backquote($key) . ') as ' . $this->table_helper->backquote($hex_key);
+ }
+ }
+ if (!empty($structure_info['bits'])) {
+ foreach ($structure_info['bits'] as $key => $bit) {
+ $bit_key = strtolower($key) . '__bit';
+ $sel .= ', ' . $this->table_helper->backquote($key) . '+0 as ' . $this->table_helper->backquote($bit_key);
+ }
+ }
+ $join = implode(' ', array_unique($join));
+ $join = apply_filters('wpmdb_rows_join', $join, $table);
+ $where = apply_filters('wpmdb_rows_where', $where, $table);
+ $order_by = apply_filters('wpmdb_rows_order_by', $order_by, $table);
+ $limit = apply_filters('wpmdb_rows_limit', $limit, $table);
+
+ $sql = 'SELECT ' . $sel . ' FROM ' . $this->table_helper->backquote($table) . " $join $where $order_by $limit";
+ $sql = apply_filters('wpmdb_rows_sql', $sql, $table);
+
+ return $sql;
+ }
+
+ /**
+ * Return multisite-compatible names for requested
+ * tables, based on queried table name
+ *
+ * @param array $tables List of table names required
+ * @param string $queried_table Name of table from which to derive the blog ID
+ *
+ * @return array List of table names altered for multisite compatibility
+ */
+ function get_ms_compat_table_names($tables, $queried_table)
+ {
+ $state_data = $this->migration_state_manager->set_post_data();
+ global $wpdb;
+
+ $temp_prefix = ('import' === $state_data['intent']) ? $this->props->temp_prefix : '';
+ $prefix = $temp_prefix . $wpdb->base_prefix;
+ $prefix_escaped = preg_quote($prefix, '/');
+
+ // if multisite, extract blog ID from queried table name and add to prefix
+ // won't match for primary blog because it uses standard table names, i.e. blog_id will never be 1
+ if (is_multisite() && preg_match('/^' . $prefix_escaped . '([0-9]+)_/', $queried_table, $matches)) {
+ $blog_id = $matches[1];
+ $prefix .= $blog_id . '_';
+ }
+
+ // build table names
+ $ms_compat_table_names = array();
+
+ foreach ($tables as $table) {
+ $ms_compat_table_names[$table . '_table'] = $prefix . $table;
+ }
+
+ return $ms_compat_table_names;
+ }
+
+ /**
+ * Initializes the query buffer and template.
+ *
+ * @param $target_table_name
+ * @param $temp_table_name
+ * @param $structure_info
+ *
+ * @return null
+ */
+ function start_query_buffer($target_table_name, $temp_table_name, $structure_info, $state_data)
+ {
+ if ('find_replace' !== $state_data['stage']) {
+ $fields = implode(', ', $structure_info['field_set']);
+ $table_to_insert = $temp_table_name;
+
+ if ('savefile' === $state_data['intent'] || 'backup' === $state_data['stage']) {
+ $table_to_insert = $target_table_name;
+ }
+
+ $this->query_template = 'INSERT INTO ' . $this->table_helper->backquote($table_to_insert) . ' ( ' . $fields . ") VALUES\n";
+ } else {
+ $this->query_template = '';
+ }
+
+ $this->query_buffer = $this->query_template;
+ }
+
+ /**
+ * Processes the data in a given row.
+ *
+ * @param string $table
+ * @param object $replacer
+ * @param array $row
+ * @param array $structure_info
+ *
+ * @return array|void
+ */
+ function process_row($table, $replacer, $row, $structure_info, $fp, $state_data)
+ {
+ $form_data = $this->form_data->getFormData();
+
+ global $wpdb;
+
+ $skip_row = false;
+ $updates_pending = false;
+ $update_sql = array();
+ $where_sql = array();
+ $values = array();
+ $query = '';
+
+ if (!apply_filters('wpmdb_table_row', $row, $table, $state_data)) {
+ $skip_row = true;
+ }
+
+ if (!$skip_row) {
+ $replacer->set_row($row);
+
+ foreach ($row as $key => $value) {
+ $data_to_fix = $value;
+
+ if ('find_replace' === $state_data['stage'] && in_array($key, array_keys($this->primary_keys))) {
+ $where_sql[] = $this->table_helper->backquote($key) . ' = "' . $this->mysql_escape_mimic($data_to_fix) . '"';
+ continue;
+ }
+
+ $replacer->set_column($key);
+
+ if (isset($structure_info['ints'][strtolower($key)]) && $structure_info['ints'][strtolower($key)]) {
+ // make sure there are no blank spots in the insert syntax,
+ // yet try to avoid quotation marks around integers
+ $value = (null === $value || '' === $value) ? $structure_info['defs'][strtolower($key)] : $value;
+ $values[] = ('' === $value) ? "''" : $value;
+ continue;
+ }
+
+ $test_bit_key = strtolower($key) . '__bit';
+ // Correct null values IF we're not working with a BIT type field, they're handled separately below
+ if (null === $value && !property_exists($row, $test_bit_key)) {
+ $values[] = 'NULL';
+ continue;
+ }
+
+ // If we have binary data, substitute in hex encoded version and remove hex encoded version from row.
+ $hex_key = strtolower($key) . '__hex';
+ if (isset($structure_info['bins'][strtolower($key)]) && $structure_info['bins'][strtolower($key)] && isset($row->$hex_key)) {
+ $value = "UNHEX('" . $row->$hex_key . "')";
+ $values[] = $value;
+ unset($row->$hex_key);
+ continue;
+ }
+
+ // If we have bit data, substitute in properly bit encoded version.
+ $bit_key = strtolower($key) . '__bit';
+ if (isset($structure_info['bits'][strtolower($key)]) && $structure_info['bits'][strtolower($key)] && (isset($row->$bit_key) || null === $row->$bit_key)) {
+ $value = null === $row->$bit_key ? 'NULL' : "b'" . $row->$bit_key . "'";
+ $values[] = $value;
+ unset($row->$bit_key);
+ continue;
+ }
+
+ if (is_multisite() && in_array($table, array($wpdb->site, $wpdb->blogs, $this->props->temp_prefix . $wpdb->blogs, $this->props->temp_prefix . $wpdb->site))) {
+ if ('backup' !== $state_data['stage']) {
+ if ('path' == $key) {
+ $old_path_current_site = $this->util->get_path_current_site();
+ $new_path_current_site = '';
+
+ if (!empty($state_data['path_current_site'])) {
+ $new_path_current_site = $state_data['path_current_site'];
+ } elseif ('find_replace' === $state_data['stage']) {
+ $new_path_current_site = $this->util->get_path_current_site();
+ } elseif (!empty($form_data['replace_new'][1])) {
+ $new_path_current_site = $this->util->get_path_from_url($form_data['replace_new'][1]);
+ }
+
+ $new_path_current_site = apply_filters('wpmdb_new_path_current_site', $new_path_current_site);
+
+ if (!empty($new_path_current_site) && $old_path_current_site != $new_path_current_site) {
+ $pos = strpos($value, $old_path_current_site);
+ $value = substr_replace($value, $new_path_current_site, $pos, strlen($old_path_current_site));
+ }
+ }
+
+ if ('domain' == $key) { // wp_blogs and wp_sites tables
+ if (!empty($state_data['domain_current_site'])) {
+ $main_domain_replace = $state_data['domain_current_site'];
+ } elseif ('find_replace' === $state_data['stage'] || in_array($state_data['intent'], ['savefile', 'push'])) {
+ $main_domain_replace = $this->multisite->get_domain_replace() ? $this->multisite->get_domain_replace() : $this->multisite->get_domain_current_site();
+ } elseif (!empty($form_data['replace_new'][1])) {
+ $url = Util::parse_url($form_data['replace_new'][1]);
+ $main_domain_replace = $url['host'];
+ }
+
+ $domain_replaces = array();
+ $main_domain_find = $this->multisite->get_domain_current_site();
+
+ if ('find_replace' === $state_data['stage']) {
+ // Check if the domain field in the DB is being searched for in the find & replace
+ $old_domain_find = sprintf('/^(\/\/|http:\/\/|https:\/\/|)%s/', $data_to_fix);
+
+ if (preg_grep($old_domain_find, $this->dynamic_props->find_replace_pairs['replace_old'])) {
+ $main_domain_find = $data_to_fix;
+ }
+ }
+
+ $main_domain_find = sprintf('/%s/', preg_quote($main_domain_find, '/'));
+ if (isset($main_domain_replace)) {
+ $domain_replaces[$main_domain_find] = $main_domain_replace;
+ }
+
+ $domain_replaces = apply_filters('wpmdb_domain_replaces', $domain_replaces);
+
+ $value = preg_replace(array_keys($domain_replaces), array_values($domain_replaces), $value);
+ }
+ }
+ }
+
+ if ('guid' != $key || (false === empty($form_data['replace_guids']) && $this->table_helper->table_is('posts', $table))) {
+ if ($state_data['stage'] != 'backup') {
+ $value = $replacer->recursive_unserialize_replace($value);
+ }
+ }
+
+ if ('find_replace' === $state_data['stage']) {
+ $value = $this->mysql_escape_mimic($value);
+ $data_to_fix = $this->mysql_escape_mimic($data_to_fix);
+
+ if ($value !== $data_to_fix) {
+ $update_sql[] = $this->table_helper->backquote($key) . ' = "' . $value . '"';
+ $updates_pending = true;
+ }
+ } else {
+ // \x08\\x09, not required
+ $multibyte_search = array("\x00", "\x0a", "\x0d", "\x1a");
+ $multibyte_replace = array('\0', '\n', '\r', '\Z');
+
+ $value = $this->table_helper->sql_addslashes($value);
+ $value = str_replace($multibyte_search, $multibyte_replace, $value);
+ }
+
+ if (isset($state_data['destination_prefix'], $state_data['source_prefix']) && $state_data['destination_prefix'] !== $state_data['source_prefix']) {
+ $value = $this->handle_different_prefix($key, $value, $table);
+ }
+
+ $values[] = "'" . $value . "'";
+ }
+
+ // Determine what to do with updates.
+ if ('find_replace' === $state_data['stage']) {
+ if ($updates_pending && !empty($where_sql)) {
+ $table_to_update = $table;
+
+ if ('import' !== $state_data['intent']) {
+ $table_to_update = $this->table_helper->backquote($this->props->temp_prefix . $table);
+ }
+
+ $query .= 'UPDATE ' . $table_to_update . ' SET ' . implode(', ', $update_sql) . ' WHERE ' . implode(' AND ', array_filter($where_sql)) . ";\n";
+ }
+ } else {
+ $query .= '(' . implode(', ', $values) . '),' . "\n";
+ }
+ }
+
+ $chunk_size = $state_data['intent'] === 'pull' ? $this->dynamic_props->maximum_chunk_size : $this->util->get_bottleneck();
+
+
+ if ((strlen($this->current_chunk) + strlen($query) + strlen($this->query_buffer) + 30) > $chunk_size) {
+ if ($this->query_buffer == $this->query_template) {
+ $this->query_buffer .= $query;
+
+ ++$this->row_tracker;
+
+ if (!empty($this->primary_keys)) {
+ foreach ($this->primary_keys as $primary_key => $value) {
+ $this->primary_keys[$primary_key] = $row->$primary_key;
+ }
+ }
+ }
+
+ $this->stow_query_buffer($fp);
+
+ //time this method
+ return $this->transfer_chunk($fp, $state_data);
+ }
+
+ if (($this->query_size + strlen($query)) > $this->max_insert_string_len) {
+ $this->stow_query_buffer($fp);
+ }
+
+ $this->query_buffer .= $query;
+ $this->query_size += strlen($query);
+
+ ++$this->row_tracker;
+
+ if (!empty($this->primary_keys)) {
+ foreach ($this->primary_keys as $primary_key => $value) {
+ $this->primary_keys[$primary_key] = $row->$primary_key;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
+ *
+ * @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
+ *
+ * @param string $input The string to escape.
+ *
+ * @return string
+ */
+ function mysql_escape_mimic($input)
+ {
+ if (is_array($input)) {
+ return array_map(__METHOD__, $input);
+ }
+ if (!empty($input) && is_string($input)) {
+ return str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $input);
+ }
+
+ return $input;
+ }
+
+ /**
+ * Responsible for stowing a chunk of processed data.
+ */
+ function stow_query_buffer($fp)
+ {
+ if ($this->query_buffer !== $this->query_template) {
+ $this->query_buffer = rtrim($this->query_buffer, "\n,");
+ $this->query_buffer .= " ;\n";
+ $this->stow($this->query_buffer, false, $fp);
+ $this->query_buffer = $this->query_template;
+ $this->query_size = 0;
+ }
+ }
+
+ /**
+ * Called once our chunk buffer is full, will transfer the SQL to the remote server for importing
+ *
+ */
+ function transfer_chunk($fp, $state_data)
+ {
+ if (in_array($state_data['intent'], array('savefile', 'find_replace', 'import')) || 'backup' === $state_data['stage']) {
+ if ('find_replace' === $state_data['stage']) {
+ $this->process_chunk($this->query_buffer);
+ } else {
+ $this->filesystem->close($fp);
+ }
+
+ $result = array(
+ 'current_row' => $this->row_tracker,
+ 'primary_keys' => json_encode($this->primary_keys),
+ );
+
+ if ($state_data['intent'] == 'savefile' && $state_data['last_table'] == '1') {
+ $result['dump_filename'] = $state_data['dump_filename'];
+ $result['dump_path'] = $state_data['dump_path'];
+ $result['full_site_export'] = $state_data['full_site_export'];
+ if ($state_data['full_site_export'] === true ) {
+ $result['export_path'] = $state_data['export_path'];
+ $move_into_zip = $this->full_site_export->move_into_zip($state_data['dump_path'], $state_data['export_path']);
+
+ if ($move_into_zip === false) {
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-error-moving-sql-file',
+ __('Error moving SQL file into ZIP archive', 'wp-migrate-db')
+ )
+ );
+ }
+ }
+ }
+
+ if ($this->row_tracker === -1) {
+ $result['current_row'] = '-1';
+ }
+
+ if ('find_replace' === $state_data['stage']) {
+ $result['replace_data'] = json_encode($this->replace->get_diff_result());
+ }
+
+ $result = $this->http->end_ajax($result);
+
+ return $result;
+ }
+
+ if ($state_data['intent'] === 'pull') {
+ $str = $this->row_tracker . '##MDB_SEPARATOR##' . json_encode($this->primary_keys);
+ $result = $this->http->end_ajax($str, '', true);
+
+ return $result;
+ }
+
+ $chunk_gzipped = '0';
+ if (isset($state_data['gzip']) && $state_data['gzip'] == '1' && Util::gzip()) {
+ $this->current_chunk = gzcompress($this->current_chunk);
+ $chunk_gzipped = '1';
+ }
+
+ $data = array(
+ 'action' => 'wpmdb_process_chunk',
+ 'table' => $state_data['table'],
+ 'chunk_gzipped' => $chunk_gzipped,
+ 'chunk' => $this->current_chunk,
+ // NEEDS TO BE the last element in this array because of adding it back into the array in ajax_process_chunk()
+ );
+
+ $data['sig'] = $this->http_helper->create_signature($data, $state_data['key']);
+
+ $ajax_url = $this->util->ajax_url();
+ $response = $this->remote_post->post($ajax_url, $data, __FUNCTION__);
+
+ HandleRemotePostError::handle('wpmdb-transfer-chunk-error', $response);
+
+ $result = $this->http->end_ajax(
+ [
+ 'current_row' => $this->row_tracker,
+ 'primary_keys' => json_encode($this->primary_keys),
+ ]
+ );
+
+ return $result;
+ }
+
+ /**
+ * Runs after processing data in a table.
+ *
+ * @param string $table
+ * @param string $target_table_name
+ */
+ function post_process_data($table, $target_table_name, $fp, $state_data)
+ {
+ if ('savefile' === $state_data['intent'] || 'backup' === $state_data['stage']) {
+ $this->build_table_footer($table, $target_table_name, $fp, $state_data);
+ }
+
+ /**
+ * Fires just after processing the data for a table.
+ *
+ * @param string $table
+ * @param string $target_table_name
+ */
+ do_action('wpmdb_post_process_table_data', $table, $target_table_name);
+
+ $this->row_tracker = -1;
+ }
+
+ /**
+ * Creates the footer for a table in a SQL file.
+ *
+ * @param $table
+ * @param $target_table_name
+ *
+ * @return null
+ */
+ function build_table_footer($table, $target_table_name, $fp, $state_data)
+ {
+ global $wpdb;
+
+ $stow = "\n";
+ $stow .= "#\n";
+ $stow .= '# ' . sprintf(__('End of data contents of table %s', 'wp-migrate-db'), $this->table_helper->backquote($target_table_name)) . "\n";
+ $stow .= "# --------------------------------------------------------\n";
+ $stow .= "\n";
+ $this->stow($stow, false, $fp);
+
+
+ if ($state_data['last_table'] == '1') {
+ $stow = "#\n";
+ $stow .= "# Add constraints back in and apply any alter data queries.\n";
+ $stow .= "#\n\n";
+ $stow .= $this->get_alter_queries();
+ $this->stow($stow, false, $fp);
+
+ $alter_table_name = $this->get_alter_table_name();
+
+ $wpdb->query('DROP TABLE IF EXISTS ' . $this->table_helper->backquote($alter_table_name) . ';');
+
+ if ('backup' == $state_data['stage']) {
+ // Re-create our table to store 'ALTER' queries so we don't get duplicates.
+ $create_alter_table_query = $this->get_create_alter_table_query();
+ $process_chunk_result = $this->process_chunk($create_alter_table_query);
+ if (true !== $process_chunk_result) {
+ $result = $this->http->end_ajax($process_chunk_result);
+
+ return $result;
+ }
+ }
+ }
+ }
+
+ function get_alter_queries( $state_data = false )
+ {
+ global $wpdb;
+
+ $alter_table_name = $this->get_alter_table_name();
+ $alter_queries = array();
+ $sql = '';
+
+ if ($alter_table_name === $wpdb->get_var("SHOW TABLES LIKE '$alter_table_name'")) {
+ $alter_queries = $wpdb->get_results("SELECT * FROM `{$alter_table_name}`", ARRAY_A);
+ $alter_queries = apply_filters('wpmdb_get_alter_queries', $alter_queries, $state_data );
+ }
+
+ if (!empty($alter_queries)) {
+ foreach ($alter_queries as $alter_query) {
+ $sql .= $alter_query['query'] . "\n";
+ }
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Returns a fragment of SQL for creating the table where the alter statements are held during the migration.
+ *
+ * @return string
+ */
+ function get_create_alter_table_query()
+ {
+ if (!is_null($this->create_alter_table_query)) {
+ return $this->create_alter_table_query;
+ }
+
+ $legacy_alter_table_name = $this->get_legacy_alter_table_name();
+ $this->create_alter_table_query = sprintf("DROP TABLE IF EXISTS `%s`;\n", $legacy_alter_table_name);
+
+ $alter_table_name = $this->get_alter_table_name();
+ $this->create_alter_table_query .= sprintf("DROP TABLE IF EXISTS `%s`;\n", $alter_table_name);
+ $this->create_alter_table_query .= sprintf("CREATE TABLE `%s` ( `query` LONGTEXT NOT NULL );\n", $alter_table_name);
+ $this->create_alter_table_query = apply_filters('wpmdb_create_alter_table_query', $this->create_alter_table_query);
+
+ return $this->create_alter_table_query;
+ }
+
+ function delete_temporary_tables($prefix)
+ {
+ $tables = $this->get_tables();
+ $delete_queries = '';
+
+ foreach ($tables as $table) {
+ if (0 !== strpos($table, $prefix)) {
+ continue;
+ }
+ $delete_queries .= sprintf("DROP TABLE %s;\n", $this->table_helper->backquote($table));
+ }
+
+ $this->process_chunk($delete_queries);
+ }
+
+ /**
+ * Get only the tables beginning with our DB prefix or temporary prefix, also skip views and legacy wpmdb_alter_statements table.
+ *
+ * @param string $scope
+ *
+ * @return array
+ */
+ function get_tables($scope = 'regular')
+ {
+ global $wpdb;
+ $prefix = ($scope == 'temp' ? $this->props->temp_prefix : $wpdb->base_prefix);
+ $tables = $wpdb->get_results('SHOW FULL TABLES', ARRAY_N);
+ $clean_tables = array();
+
+ foreach ($tables as $table) {
+ if ((($scope == 'temp' || $scope == 'prefix') && 0 !== strpos($table[0], $prefix)) || $table[1] == 'VIEW') {
+ continue;
+ }
+ if ($this->get_legacy_alter_table_name() == $table[0]) {
+ continue;
+ }
+ $clean_tables[] = $table[0];
+ }
+
+ return apply_filters('wpmdb_tables', $clean_tables, $scope);
+ }
+
+ function db_backup_header($fp)
+ {
+ $state_data = $this->migration_state_manager->set_post_data();
+ $form_data = $this->form_data->getFormData();
+ $search_replace_values = Replace::parse_find_replace_pairs();
+
+ global $wpdb;
+
+ $charset = (defined('DB_CHARSET') ? DB_CHARSET : 'utf8');
+ $this->stow('# ' . __('WordPress MySQL database migration', 'wp-migrate-db') . "\n", false, $fp);
+ $this->stow("#\n", false, $fp);
+ $this->stow('# ' . sprintf(__('Generated: %s', 'wp-migrate-db'), date('l j. F Y H:i T')) . "\n", false, $fp);
+ $this->stow('# ' . sprintf(__('Hostname: %s', 'wp-migrate-db'), DB_HOST) . "\n", false, $fp);
+ $this->stow('# ' . sprintf(__('Database: %s', 'wp-migrate-db'), $this->table_helper->backquote(DB_NAME)) . "\n", false, $fp);
+
+ $home_url = apply_filters('wpmdb_backup_header_url', Util::home_url());
+ $url = preg_replace('(^https?:)', '', $home_url, 1);
+ $key = array_search($url, $search_replace_values['replace_old']);
+
+ if (false !== $key) {
+ $url = $search_replace_values['replace_new'][$key];
+ } else {
+ // Protocol might have been added in
+ $key = array_search($home_url, $search_replace_values['replace_old']);
+
+ if (false !== $key) {
+ $url = $search_replace_values['replace_new'][$key];
+ }
+ }
+
+ $this->stow('# URL: ' . esc_html(addslashes($url)) . "\n", false, $fp);
+
+ $path = Util::get_absolute_root_file_path();
+ $key = array_search($path, $search_replace_values['replace_old']);
+
+ if (false !== $key) {
+ $path = $search_replace_values['replace_new'][$key];
+ }
+
+ $this->stow('# Path: ' . esc_html(addslashes($path)) . "\n", false, $fp);
+
+ $included_tables = $this->get_tables('prefix');
+
+ if (in_array($state_data['intent'], ['savefile', 'backup_local']) && isset($form_data['table_migrate_option']) && 'migrate_select' === $form_data['table_migrate_option']) {
+ $included_tables = $form_data['select_tables'];
+ }
+
+ $included_tables = apply_filters('wpmdb_backup_header_included_tables', $included_tables);
+
+ $this->stow('# Tables: ' . implode(', ', $included_tables) . "\n", false, $fp);
+ $this->stow('# Table Prefix: ' . $wpdb->base_prefix . "\n", false, $fp);
+ $this->stow('# Post Types: ' . implode(', ', $this->get_post_types()) . "\n", false, $fp);
+
+ $protocol = 'http';
+ if ('https' === substr($home_url, 0, 5)) {
+ $protocol = 'https';
+ }
+
+ $this->stow('# Protocol: ' . $protocol . "\n", false, $fp);
+
+ $is_multisite = is_multisite() ? 'true' : 'false';
+ $this->stow('# Multisite: ' . $is_multisite . "\n", false, $fp);
+
+ $is_subsite_export = apply_filters('wpmdb_backup_header_is_subsite_export', 'false');
+ $this->stow('# Subsite Export: ' . $is_subsite_export . "\n", false, $fp);
+
+ $this->stow("# --------------------------------------------------------\n\n", false, $fp);
+ $this->stow("/*!40101 SET NAMES $charset */;\n\n", false, $fp);
+ $this->stow("SET sql_mode='NO_AUTO_VALUE_ON_ZERO';\n\n", false, $fp);
+ }
+
+ /**
+ * Return array of post type slugs stored within DB.
+ *
+ * @return array List of post types
+ */
+ function get_post_types()
+ {
+ global $wpdb;
+
+ if (is_multisite()) {
+ $tables = $this->get_tables('prefix');
+ $sql = "SELECT DISTINCT `post_type` FROM `{$wpdb->base_prefix}posts` ;";
+ $post_types = $wpdb->get_results($sql, ARRAY_A);
+ $prefix_escaped = preg_quote($wpdb->base_prefix, '/');
+
+ foreach ($tables as $table) {
+ if (0 == preg_match('/' . $prefix_escaped . '[0-9]+_posts$/', $table)) {
+ continue;
+ }
+ $blog_id = str_replace(array($wpdb->base_prefix, '_posts'), array('', ''), $table);
+ $sql = "SELECT DISTINCT `post_type` FROM `{$wpdb->base_prefix}" . $blog_id . '_posts` ;';
+ $site_post_types = $wpdb->get_results($sql, ARRAY_A);
+ if (is_array($site_post_types)) {
+ $post_types = array_merge($post_types, $site_post_types);
+ }
+ }
+ } else {
+ $post_types = $wpdb->get_results(
+ "SELECT DISTINCT `post_type`
+ FROM `{$wpdb->base_prefix}posts`
+ WHERE 1;",
+ ARRAY_A
+ );
+ }
+
+ $return = array('revision');
+
+ foreach ($post_types as $post_type) {
+ $return[] = $post_type['post_type'];
+ }
+
+ return apply_filters('wpmdb_post_types', array_values(array_unique($return)));
+ }
+
+ function empty_current_chunk()
+ {
+ $this->current_chunk = '';
+ }
+
+
+ /**
+ * Removes ANSI quotes from a given string and replaces it with backticks `
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ public function remove_ansi_quotes($string)
+ {
+ if (!is_string($string)) {
+ return $string;
+ }
+
+ return str_replace('"', '`', $string);
+ }
+
+ /**
+ * Changes db prefix for values that use prefixes
+ *
+ * @param string $key
+ *
+ * @param string $value
+ *
+ * @param string $table
+ *
+ * @return string
+ */
+ private function handle_different_prefix($key, $value, $table)
+ {
+ $source_prefix = $this->state_data['source_prefix'];
+ $destination_prefix = $this->state_data['destination_prefix'];
+ if ('meta_key' === $key && $this->table_helper->table_is('usermeta', $table)) {
+ if (strpos($value , $source_prefix) === 0) {
+ $value = Util::prefix_updater($value, $source_prefix, $destination_prefix);
+ }
+ }
+ if ('option_name' === $key && $this->is_user_roles($source_prefix, $value) && $this->table_helper->table_is('options', $table)) {
+ $value = Util::prefix_updater($value, $source_prefix, $destination_prefix);
+ }
+ return $value;
+ }
+
+ /**
+ * Checks if value is user_roles for both single site
+ * and multisite options values
+ *
+ * @param string $source_prefix
+ *
+ * @param string $value
+ *
+ * @return bool
+ */
+ private function is_user_roles( $source_prefix, $value)
+ {
+ return $source_prefix . 'user_roles' === $value || preg_match('/^' . $source_prefix . '[0-9]+_' . 'user_roles' . '$/', $value);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Sql/TableHelper.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Sql/TableHelper.php
new file mode 100644
index 000000000..a15a35e1a
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Sql/TableHelper.php
@@ -0,0 +1,258 @@
+form_data = $form_data;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->http = $http;
+ }
+
+
+ /**
+ * Add backquotes to tables and db-names in
+ * SQL queries. Taken from phpMyAdmin.
+ *
+ * @param $a_name
+ *
+ * @return array|string
+ */
+ function backquote($a_name)
+ {
+ if (!empty($a_name) && $a_name != '*') {
+ if (is_array($a_name)) {
+ $result = array();
+ reset($a_name);
+ foreach ($a_name as $key => $val) {
+ $result[$key] = '`' . $val . '`';
+ }
+
+ return $result;
+ } else {
+ return '`' . $a_name . '`';
+ }
+ } else {
+ return $a_name;
+ }
+ }
+
+ /**
+ * Better addslashes for SQL queries.
+ * Taken from phpMyAdmin.
+ *
+ * @param string $a_string
+ * @param bool $is_like
+ *
+ * @return mixed
+ */
+ function sql_addslashes($a_string = '', $is_like = false)
+ {
+ if ($is_like) {
+ $a_string = str_replace('\\', '\\\\\\\\', $a_string);
+ } else {
+ $a_string = str_replace('\\', '\\\\', $a_string);
+ }
+
+ return str_replace('\'', '\\\'', $a_string);
+ }
+
+ /**
+ * Ensures that the given create table sql string is compatible with the target database server version.
+ *
+ * @param string $create_table
+ * @param string $table
+ * @param string $db_version
+ * @param string $action
+ * @param string $stage
+ *
+ * @return mixed
+ */
+ function mysql_compat_filter($create_table, $table, $db_version, $action, $stage)
+ {
+ if (empty($db_version) || empty($action) || empty($stage)) {
+ return $create_table;
+ }
+
+ if (version_compare($db_version, '5.6', '<')) {
+ // Convert utf8m4_unicode_520_ci collation to utf8mb4_unicode_ci if less than mysql 5.6
+ $create_table = str_replace('utf8mb4_unicode_520_ci', 'utf8mb4_unicode_ci', $create_table);
+ $create_table = str_replace('utf8_unicode_520_ci', 'utf8_unicode_ci', $create_table);
+ } elseif (apply_filters('wpmdb_convert_to_520', true)) {
+ $create_table = str_replace('utf8mb4_unicode_ci', 'utf8mb4_unicode_520_ci', $create_table);
+ $create_table = str_replace('utf8_unicode_ci', 'utf8_unicode_520_ci', $create_table);
+ }
+
+ if (version_compare($db_version, '5.5.3', '<')) {
+ // Remove index comments introduced in MySQL 5.5.3.
+ // Following regex matches any PRIMARY KEY or KEY statement on a table definition that has a COMMENT statement attached.
+ // The regex is then reset (\K) to return just the COMMENT, its string and any leading whitespace for replacing with nothing.
+ $create_table = preg_replace('/(?-i)KEY\s.*`.*`\).*\K\sCOMMENT\s\'.*\'/', '', $create_table);
+
+ // Replace utf8mb4 introduced in MySQL 5.5.3 with utf8. As of WordPress 4.2 utf8mb4 is used by default on supported MySQL versions
+ // but causes migrations to fail when the remote site uses MySQL < 5.5.3.
+ $abort_utf8mb4 = false;
+ if ('savefile' !== $action && 'backup' !== $stage) {
+ $abort_utf8mb4 = true;
+ }
+ // Escape hatch if user knows that site content is utf8 safe.
+ $abort_utf8mb4 = apply_filters('wpmdb_abort_utf8mb4_to_utf8', $abort_utf8mb4);
+
+ $replace_count = 0;
+ $create_table = preg_replace('/(COLLATE\s)utf8mb4/', '$1utf8', $create_table, -1, $replace_count); // Column collation
+
+ if (false === $abort_utf8mb4 || 0 === $replace_count) {
+ $create_table = preg_replace('/(COLLATE=)utf8mb4/', '$1utf8', $create_table, -1, $replace_count); // Table collation
+ }
+
+ if (false === $abort_utf8mb4 || 0 === $replace_count) {
+ $create_table = preg_replace('/(CHARSET\s?=\s?)utf8mb4/', '$1utf8', $create_table, -1, $replace_count); // Table charset
+ }
+
+ if (true === $abort_utf8mb4 && 0 !== $replace_count) {
+ $return = sprintf(__('The source site supports utf8mb4 data but the target does not, aborting migration to avoid possible data corruption. Please see %1$s for more information. (#148)', 'wp-migrate-db-pro'), sprintf('%s', 'https://deliciousbrains.com/wp-migrate-db-pro/doc/source-site-supports-utf8mb4/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin', __('our documentation', 'wp-migrate-db-pro')));
+ $return = array('wpmdb_error' => 1, 'body' => $return);
+ $result = $this->http->end_ajax(json_encode($return));
+
+ return $result;
+ }
+ }
+
+ return $create_table;
+ }
+
+ function format_dump_name($dump_name)
+ {
+ $state_data = $this->migration_state_manager->set_post_data();
+ $form_data = $this->form_data->getFormData();
+ $extension = '.sql';
+ $is_full_site_export = isset($state_data['full_site_export']) ? $state_data['full_site_export'] : false;
+
+ if (empty($form_data) && empty($state_data)) {
+ return $dump_name . $extension;
+ }
+
+ if ('backup' === $state_data['stage']) {
+ return $dump_name . $extension;
+ }
+
+ if ('import' === $state_data['intent']) {
+ if (isset($state_data['import_info']['import_gzipped']) && true === $state_data['import_info']['import_gzipped']) {
+ $extension .= '.gz';
+ }
+ } else {
+ if (Util::gzip() && $form_data['gzip_file'] && !$is_full_site_export) {
+ $extension .= '.gz';
+ }
+ }
+
+ return $dump_name . $extension;
+ }
+
+ /**
+ * Check that the given table is of the desired type,
+ * including single and multisite installs.
+ * eg: wp_posts, wp_2_posts
+ *
+ * The scope argument can take one of the following:
+ *
+ * 'table' - Match on the un-prefixed table name, this is the default.
+ * 'all' - Match on 'blog' and 'global' tables. No old tables are returned.
+ * 'blog' - Match the blog-level tables for the queried blog.
+ * 'global' - Match the global tables for the installation, matching multisite tables only if running multisite.
+ * 'ms_global' - Match the multisite global tables, regardless if current installation is multisite.
+ * 'non_ms_global' - Match the non multisite global tables, regardless if current installation is multisite.
+ * 'old' - Matches tables which are deprecated.
+ *
+ * @param string $desired_table Can be empty to match on tables from scopes other than 'table'.
+ * @param string $given_table
+ * @param string $scope Optional type of table to match against, default is 'table'.
+ * @param string $new_prefix Optional new prefix already added to $given_table.
+ * @param int $blog_id Optional Only used with 'blog' scope to test against a specific subsite's tables other than current for $wpdb.
+ * @param string $source_prefix Optional prefix from source site already added to $given_table.
+ *
+ * @return boolean
+ */
+ function table_is($desired_table, $given_table, $scope = 'table', $new_prefix = '', $blog_id = 0, $source_prefix = '')
+ {
+ global $wpdb;
+
+ $scopes = array('all', 'blog', 'global', 'ms_global', 'non_ms_global', 'old');
+
+ if (!in_array($scope, $scopes)) {
+ $scope = 'table';
+ }
+
+ if (empty($desired_table) && 'table' === $scope) {
+ return false;
+ }
+
+ if (!empty($new_prefix) && 0 === stripos($given_table, $new_prefix)) {
+ $given_table = substr_replace($given_table, $wpdb->base_prefix, 0, strlen($new_prefix));
+ }
+
+ $match = false;
+ $prefix_escaped = $source_prefix ? preg_quote($source_prefix, '/') : preg_quote($wpdb->base_prefix, '/');
+ $desired_table_escaped = preg_quote($desired_table, '/');
+
+ if ('table' === $scope) {
+ if ($wpdb->{$desired_table} == $given_table ||
+ preg_match('/^' . $prefix_escaped . '[0-9]+_' . $desired_table_escaped . '$/', $given_table)
+ ) {
+ $match = true;
+ }
+ } else {
+ if ('non_ms_global' === $scope) {
+ $tables = array_diff_key($wpdb->tables('global', true, $blog_id), $wpdb->tables('ms_global', true, $blog_id));
+ } else {
+ $tables = $wpdb->tables($scope, true, $blog_id);
+ }
+
+ if (!empty($desired_table)) {
+ $tables = array_intersect_key($tables, array($desired_table => ''));
+ }
+
+ if (!empty($tables)) {
+ if ($source_prefix) {
+ $local_prefix = preg_quote($wpdb->base_prefix, '/');
+ $tables = Util::change_tables_prefix($tables, $local_prefix, $source_prefix);
+ }
+ foreach ($tables as $table_name) {
+ if (!empty($table_name) && strtolower($table_name) === strtolower($given_table)) {
+ $match = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return $match;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/Manager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/Manager.php
new file mode 100644
index 000000000..07709c38a
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/Manager.php
@@ -0,0 +1,33 @@
+get(ThemePluginFilesAddon::class);
+ $theme_plugin->register();
+ $theme_plugin->set_licensed($licensed);
+
+ $container->get(ThemePluginFilesLocal::class)->register();
+
+ add_filter('wpmdb_addon_registered_tpf', '__return_true');
+
+ return $theme_plugin;
+ }
+
+ public function get_license_response_key()
+ {
+ return 'wp-migrate-db-pro-theme-plugin-files';
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesAddon.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesAddon.php
new file mode 100644
index 000000000..c80c97de2
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesAddon.php
@@ -0,0 +1,554 @@
+plugin_slug = $properties->plugin_slug;
+ $this->plugin_version = $properties->plugin_version;
+ $this->plugin_folder_name = $properties->plugin_folder_name . '/';
+ $this->plugins_url = trailingslashit( $this->plugins_url . '/class/Pro/TPF' );
+ $this->template_path = $this->plugin_dir_path . 'template/';
+
+ $this->transfer_helpers = $transfer_helpers;
+
+ // Fields that can be saved in a 'profile'
+ $this->accepted_fields = [
+ 'migrate_themes',
+ 'migrate_plugins',
+ 'migrate_muplugins',
+ 'migrate_others',
+ 'migrate_core',
+ 'select_plugins',
+ 'select_muplugins',
+ 'select_themes',
+ 'select_others',
+ 'select_core',
+ 'file_ignores',
+ ];
+
+ $this->filesystem = $filesystem;
+ $this->profile_manager = $profile_manager;
+ $this->util = $util;
+ $this->theme_plugin_files_finalize = $theme_plugin_files_finalize;
+ $this->plugin_helper = $plugin_helper;
+ }
+
+ public function register()
+ {
+
+ // Register Queue manager actions
+ WPMDBDI::getInstance()->get(Manager::class)->register();
+ $this->addon_name = $this->addon->get_plugin_name('wp-migrate-db-pro-theme-plugin-files/wp-migrate-db-pro-theme-plugin-files.php');
+ add_filter('wpmdb_before_finalize_migration', [$this->theme_plugin_files_finalize, 'maybe_finalize_tp_migration']);
+ add_action('wpmdb_migration_complete', [$this->theme_plugin_files_finalize, 'cleanup_transfer_migration']);
+ add_action('wpmdb_respond_to_push_cancellation', [$this->theme_plugin_files_finalize, 'remove_tmp_files_remote']);
+ add_action('wpmdb_cancellation', [$this->theme_plugin_files_finalize, 'cleanup_transfer_migration']);
+ add_action('wpmdb_load_assets', [$this, 'load_assets']);
+ add_action('wpmdb_before_verify_connection_to_remote_site', [$this, 'cleanup_migration_cookie']);
+ add_filter('wpmdb_diagnostic_info', [$this, 'diagnostic_info']);
+ add_filter('wpmdb_establish_remote_connection_data', [$this, 'establish_remote_connection_data']);
+ add_filter('wpmdb_data', [$this, 'js_variables']);
+ add_filter('wpmdb_site_details', [$this, 'filter_site_details'], 10, 2);
+ }
+
+ /**
+ * Load media related assets in core plugin
+ */
+ public function load_assets()
+ {
+ $plugins_url = trailingslashit(plugins_url($this->plugin_folder_name));
+ $version = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? time() : $this->plugin_version;
+ $ver_string = '-' . str_replace('.', '', $this->plugin_version);
+
+ // $src = $plugins_url . 'asset/build/css/styles.css';
+ // wp_enqueue_style( 'wp-migrate-db-pro-theme-plugin-files-styles', $src, array( 'wp-migrate-db-pro-styles' ), $version );
+
+ // $src = $plugins_url . "asset/build/js/bundle{$ver_string}.js";
+ $src = $plugins_url . 'frontend/public/noop.js';
+ wp_enqueue_script('wp-migrate-db-pro-theme-plugin-files-script', $src, [
+ 'jquery',
+ 'wp-migrate-db-pro-script-v2',
+ ], $version, true);
+
+ wp_localize_script('wp-migrate-db-pro-theme-plugin-files-script', 'wpmdbtp_settings', [
+ 'strings' => $this->get_strings(),
+ ]);
+ wp_localize_script('wp-migrate-db-pro-theme-plugin-files-script', 'wpmdbtp', [
+ 'enabled' => true,
+ ]);
+ }
+
+ /**
+ * Get translated strings for javascript and other functions
+ *
+ * @return array Array of translations
+ */
+ public function get_strings()
+ {
+ $strings = [
+ 'themes' => __('Themes', 'wp-migrate-db'),
+ 'plugins' => __('Plugins', 'wp-migrate-db'),
+ 'theme_and_plugin_files' => __('Themes & Plugins', 'wp-migrate-db'),
+ 'theme_active' => __('(active)', 'wp-migrate-db'),
+ 'select_themes' => __('Please select themes for migration.', 'wp-migrate-db'),
+ 'select_plugins' => __('Please select plugins for migration.', 'wp-migrate-db'),
+ 'remote' => __('remote', 'wp-migrate-db'),
+ 'local' => __('local', 'wp-migrate-db'),
+ 'failed_to_transfer' => __('Failed to transfer file.', 'wp-migrate-db'),
+ 'file_transfer_error' => __('Themes & Plugins Transfer Error', 'wp-migrate-db'),
+ 'loading_transfer_queue' => __('Loading transfer queue', 'wp-migrate-db'),
+ 'current_transfer' => __('Transferring: ', 'wp-migrate-db'),
+ 'cli_migrating_push' => __('Uploading files', 'wp-migrate-db'),
+ 'cli_migrating_pull' => __('Downloading files', 'wp-migrate-db'),
+ ];
+
+ if (is_null($this->strings)) {
+ $this->strings = $strings;
+ }
+
+ return $this->strings;
+ }
+
+ /**
+ * Retrieve a specific translated string
+ *
+ * @param string $key Array key
+ *
+ * @return string Translation
+ */
+ public function get_string($key)
+ {
+ $strings = $this->get_strings();
+
+ return (isset($strings[$key])) ? $strings[$key] : '';
+ }
+
+ /**
+ * Add media related javascript variables to the page
+ *
+ * @param array $data
+ *
+ * @return array
+ */
+ public function js_variables($data)
+ {
+ $data['theme_plugin_files_version'] = $this->plugin_version;
+ $data['tpf_is_licensed'] = $this->licensed ? '1' : '0';
+
+ return $data;
+ }
+
+ /**
+ * Adds extra information to the core plugin's diagnostic info
+ */
+ public function diagnostic_info($diagnostic_info)
+ {
+ $diagnostic_info['themes-plugins'] = [
+ 'Themes & Plugins',
+ 'Transfer Bottleneck' => size_format($this->transfer_helpers->get_transfer_bottleneck()),
+ 'Themes Permissions' => decoct(fileperms($this->filesystem->slash_one_direction(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'themes')) & 0777),
+ 'Plugins Permissions' => decoct(fileperms($this->filesystem->slash_one_direction(WP_PLUGIN_DIR)) & 0777),
+ 'Must-Use Plugins Permissions' => decoct(fileperms($this->filesystem->slash_one_direction(WPMU_PLUGIN_DIR)) & 0777),
+ 'WP-Content Permissions' => decoct(fileperms($this->filesystem->slash_one_direction(WP_CONTENT_DIR)) & 0777),
+ 'WP-Core Permissions' => decoct(fileperms($this->filesystem->slash_one_direction(ABSPATH)) & 0777),
+ ];
+
+ return $diagnostic_info;
+ }
+
+ /**
+ * Check the remote site has the media addon setup
+ *
+ * @param array $data Connection data
+ *
+ * @return array Updated connection data
+ */
+ public function establish_remote_connection_data($data)
+ {
+ $data['theme_plugin_files_available'] = '1';
+ $data['theme_plugin_files_version'] = $this->plugin_version;
+
+ //@TODO - move to core plugin
+ if (function_exists('ini_get')) {
+ $max_file_uploads = ini_get('max_file_uploads');
+ }
+
+ $max_file_uploads = (empty($max_file_uploads)) ? 20 : $max_file_uploads;
+ $data['theme_plugin_files_max_file_uploads'] = apply_filters('wpmdbtp_max_file_uploads', $max_file_uploads);
+
+ return $data;
+ }
+
+ /**
+ * Gets all active themes, including parents
+ *
+ * @return array stylesheet strings
+ */
+ private function get_active_themes()
+ {
+ $active_themes = [];
+ if (is_multisite()) {
+ $sites = get_sites();
+ foreach($sites as $site) {
+ $site_stylesheet = get_blog_option($site->blog_id, 'stylesheet');
+ if (!in_array($site_stylesheet, $active_themes)) {
+ $active_themes[] = $site_stylesheet;
+ }
+ $site_template = get_blog_option($site->blog_id, 'template');
+ if (!in_array($site_template, $active_themes)) {
+ $active_themes[] = $site_template;
+ }
+ }
+ return $active_themes;
+ }
+
+ $active_themes[] = get_option('stylesheet');
+ $site_template = get_option('template');
+ if (!in_array($site_template, $active_themes)) {
+ $active_themes[] = $site_template;
+ }
+ return $active_themes;
+ }
+
+ /**
+ * @return array
+ */
+ public function get_local_themes()
+ {
+ $theme_list = [];
+ $themes = wp_get_themes();
+ $active_themes = $this->get_active_themes();
+ foreach ($themes as $key => $theme) {
+ $set_active = in_array($key, $active_themes);
+ $theme_list[$key] = [
+ [
+ 'name' => html_entity_decode($theme->Name),
+ 'active' => $set_active,
+ 'version' => html_entity_decode($theme->Version),
+ 'path' => $this->filesystem->slash_one_direction(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $key),
+ ],
+ ];
+ }
+
+ return $theme_list;
+ }
+
+ /**
+ * Get must-use plugin files
+ * @return bool|array
+ */
+ public function get_local_muplugin_files()
+ {
+ if (!defined('WPMU_PLUGIN_DIR') || !is_dir(WPMU_PLUGIN_DIR)) {
+ return false;
+ }
+ $wpmu_plugin_dir = $this->filesystem->slash_one_direction(WPMU_PLUGIN_DIR);
+ $wpmu_plugin_tree = scandir($wpmu_plugin_dir);
+ $to_exclude = [
+ '.',
+ '..',
+ '.DS_Store',
+ 'wp-migrate-db-pro-compatibility.php',
+ 'index.php'
+ ];
+
+ return $this->prepare_files_list(
+ $wpmu_plugin_tree,
+ $to_exclude,
+ $this->filesystem->slash_one_direction(WPMU_PLUGIN_DIR)
+ );
+ }
+
+
+ /**
+ * Gets all wp-content files not included in other stages
+ * @return bool|array
+ */
+ public function get_local_other_files()
+ {
+ if (!defined('WP_CONTENT_DIR') || !is_dir(WP_CONTENT_DIR)) {
+ return false;
+ }
+ $wp_content_dir = $this->filesystem->slash_one_direction(WP_CONTENT_DIR);
+ $wp_content_tree = scandir($wp_content_dir);
+ $to_exclude = [
+ '.',
+ '..',
+ '.DS_Store',
+ 'index.php',
+ 'debug.log',
+ 'plugins',
+ 'mu-plugins',
+ 'themes',
+ 'uploads',
+ 'upgrade'
+ ];
+
+ return $this->prepare_files_list(
+ $wp_content_tree,
+ $to_exclude,
+ $this->filesystem->slash_one_direction(WP_CONTENT_DIR)
+ );
+ }
+
+ /**
+ * Gets all core files not including wp-content
+ * @return bool|array
+ */
+ public function get_local_core_files()
+ {
+ if (!defined('ABSPATH') || !is_dir(ABSPATH)) {
+ return false;
+ }
+ $wp_core = [
+ 'wp-admin',
+ 'wp-includes',
+ 'index.php',
+ 'license.txt',
+ 'readme.html',
+ 'wp-activate.php',
+ 'wp-blog-header.php',
+ 'wp-comments-post.php',
+ 'wp-config-sample.php',
+ 'wp-config.php',
+ 'wp-cron.php',
+ 'wp-links-opml.php',
+ 'wp-load.php',
+ 'wp-login.php',
+ 'wp-mail.php',
+ 'wp-settings.php',
+ 'wp-signup.php',
+ 'wp-trackback.php',
+ 'xmlrpc.php'
+ ];
+
+ return $this->prepare_files_list(
+ $wp_core,
+ [],
+ $this->filesystem->slash_one_direction(ABSPATH)
+ );
+ }
+
+ /**
+ * Prepare files for select list
+ *
+ * @param array $files Total contents of directory
+ * @param array $to_exclude Files and directories to exclude
+ * @param array $base_path Path to directory
+ * @return array
+ **/
+ public function prepare_files_list($all_files, $to_exclude, $base_path)
+ {
+ $files = array_diff($all_files, $to_exclude);
+ sort($files);
+ $formatted_files = [];
+ foreach ($files as $file) {
+ $path = $base_path . DIRECTORY_SEPARATOR . $file;
+ if (is_dir($path) && Util::is_empty_dir($path)) {
+ continue;
+ }
+ $formatted_files[$file] = [
+ [
+ 'name' => $file,
+ 'path' => $path,
+ ]
+ ];
+
+ }
+
+ return $formatted_files;
+ }
+
+
+
+ /**
+ * @return array
+ */
+ // @TODO Refactor to use core version - used for Compatibility Mode
+ public function get_plugin_paths()
+ {
+ $plugin_root = $this->filesystem->slash_one_direction(WP_PLUGIN_DIR);
+
+ $plugins_dir = @opendir($plugin_root);
+ $plugin_files = [];
+
+ if ($plugins_dir) {
+ while (false !== ($file = readdir($plugins_dir))) {
+ if ('.' === $file[0] || 'index.php' === $file) {
+ continue;
+ }
+
+ if (stristr($file, 'wp-migrate-db')) {
+ continue;
+ }
+
+ if (is_dir($plugin_root . DIRECTORY_SEPARATOR . $file)) {
+ $plugin_files[$file] = $plugin_root . DIRECTORY_SEPARATOR . $file;
+ } else {
+ if ('.php' === substr($file, -4)) {
+ $plugin_files[$file] = $plugin_root . DIRECTORY_SEPARATOR . $file;
+ }
+ }
+ }
+ closedir($plugins_dir);
+ }
+
+ return $plugin_files;
+ }
+
+ /**
+ * @param string $plugin
+ *
+ * @return bool
+ */
+ public function check_plugin_exclusions($plugin)
+ {
+ // Exclude MDB plugins
+ $plugin_exclusions = apply_filters('wpmdbtp_plugin_list', ['wp-migrate-db']);
+
+ foreach ($plugin_exclusions as $exclusion) {
+ if (stristr($plugin, $exclusion)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param $site_details
+ *
+ * @return mixed
+ */
+ public function filter_site_details($site_details, $state_data)
+ {
+ $intent = isset($state_data, $state_data['intent']) ? $state_data['intent'] : '';
+ if(in_array($intent, ['find_replace', 'savefile', 'import'])) {
+ return $site_details;
+ }
+ //check it we need this step here maybe with state data.
+ if (isset($site_details['plugins'])) {
+ return $site_details;
+ }
+
+ if (array_key_exists('max_request', $site_details) && array_key_exists('transfer_bottleneck', $site_details)) {
+ return $site_details;
+ }
+
+ $exclude_wpdb_plugins = in_array($intent, ['savefile', '']) ? false : true;
+ $site_details['plugins'] = $this->filesystem->get_local_plugins($exclude_wpdb_plugins);
+ $site_details['plugins_path'] = $this->filesystem->slash_one_direction(WP_PLUGIN_DIR);
+ $site_details['muplugins'] = $this->get_local_muplugin_files();
+ $site_details['muplugins_path'] = $this->filesystem->slash_one_direction(WPMU_PLUGIN_DIR);
+ $site_details['themes'] = $this->get_local_themes();
+ $site_details['themes_path'] = $this->filesystem->slash_one_direction(WP_CONTENT_DIR) . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR;
+ $site_details['others'] = $this->get_local_other_files();
+ $site_details['content_dir'] = $this->filesystem->slash_one_direction(WP_CONTENT_DIR);
+ $site_details['core'] = $this->get_local_core_files();
+ $site_details['transfer_bottleneck'] = $this->transfer_helpers->get_transfer_bottleneck();
+ $site_details['max_request_size'] = $this->util->get_bottleneck();
+ $site_details['php_os'] = PHP_OS;
+ if (in_array($intent, ['push', 'pull'])) {
+ $stages = !empty($state_data['stages']) ? json_decode($state_data['stages']) : [];
+ $tpf_stages = array_intersect($stages, ['theme_files', 'plugin_files', 'muplugin_files', 'other_files']);
+ $to_test = empty($tpf_stages) ? ['theme_files'] : $tpf_stages;
+ $folder_writable = $this->transfer_helpers->is_tmp_folder_writable(reset($to_test));
+ $site_details['local_tmp_folder_check'] = $folder_writable;
+ $site_details['local_tmp_folder_writable'] = $folder_writable['status'];
+ }
+
+ return $site_details;
+ }
+
+ /**
+ * Remove cookie data stored in wp_options during migration
+ *
+ **/
+ public function cleanup_migration_cookie()
+ {
+ Persistence::removeRemoteWPECookie();
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesFinalize.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesFinalize.php
new file mode 100644
index 000000000..798f5a38f
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesFinalize.php
@@ -0,0 +1,335 @@
+form_data = $form_data;
+ $this->filesystem = $filesystem;
+ $this->transfer_helpers = $transfer_helpers;
+ $this->error_log = $error_log;
+ $this->http = $http;
+ $this->state_data_container = $state_data_container;
+ $this->manager = $manager;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->plugin_helper = $plugin_helper;
+ }
+
+ public function maybe_finalize_tp_migration()
+ {
+ // @todo - revisit
+ $form_data = $this->form_data->getCurrentMigrationData();
+ $intent = $form_data['intent'];
+
+ $state_data = $intent === 'push' ? Persistence::getRemoteStateData() : Persistence::getStateData();
+
+ if (!isset($state_data['stage'])) {
+ return false;
+ }
+
+ if (!in_array($state_data['stage'], array('themes', 'plugins', 'muplugins','others' ))) {
+ return false;
+ }
+
+ // Check that the number of files transferred is correct, throws exception
+ $this->verify_file_transfer($state_data);
+
+ $form_data = Persistence::getMigrationOptions();
+
+ $current_migration = $form_data['current_migration'];
+
+ if (empty(array_intersect(['theme_files', 'plugin_files', 'muplugin_files', 'other_files'], $current_migration['stages']))) {
+ return;
+ }
+
+ $files_to_migrate = array(
+ 'themes' => isset($current_migration['stages'], $state_data['theme_folders']) && in_array('theme_files', $current_migration['stages']) ? $state_data['theme_folders'] : [],
+ 'plugins' => isset($current_migration['stages'], $state_data['plugin_folders']) && in_array('plugin_files', $current_migration['stages']) ? $state_data['plugin_folders'] : [],
+ 'muplugins' => isset($current_migration['stages'], $state_data['muplugin_folders']) && in_array('muplugin_files', $current_migration['stages']) ? $state_data['muplugin_folders'] : [],
+ 'others' => isset($current_migration['stages'], $state_data['other_folders']) && in_array('other_files', $current_migration['stages']) ? $state_data['other_folders'] : [],
+ );
+ $migration_id = $intent === 'push' ? $state_data['remote_state_id'] : $state_data['migration_state_id'];
+
+ foreach ($files_to_migrate as $stage => $folder) {
+ $paths = [
+ 'themes' => WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'themes',
+ 'plugins' => WP_PLUGIN_DIR,
+ 'muplugins' => WPMU_PLUGIN_DIR,
+ 'others' => WP_CONTENT_DIR
+ ];
+ $dest_path = trailingslashit($paths[$stage]);
+
+ $tmp_path = Util::get_temp_dir($stage);
+ if ('pull' === $intent) {
+ $file_folders_list = $this->get_files_for_stage($stage, $state_data, $form_data);
+ }
+
+ foreach ($folder as $file_folder) {
+ if ( 'pull' === $intent && !$this->in_file_folders_list($file_folders_list, $file_folder )) {
+ continue;
+ }
+ if (in_array($stage, ['plugins', 'muplugins', 'others'])) {
+ $folder_name = basename(str_replace('\\', '/', $file_folder));
+ } else { //Themes
+ $manifest = $this->transfer_helpers->load_manifest($stage, $migration_id);
+ $folder_name = $this->transfer_helpers->get_folder_name($file_folder, $dest_path, $tmp_path, $manifest['manifest']);
+
+ if (!$folder_name) {
+ return $this->http->end_ajax(new \WP_Error('wpmdb_no_folder_name', sprintf(__('Unable to determine folder name for theme %s'), $folder)));
+ }
+ }
+
+ $dest_folder = $dest_path . $folder_name;
+ $tmp_source = $tmp_path . $folder_name;
+
+ if (is_link($dest_folder)) {
+ $dest_folder = readlink($dest_folder);
+ }
+
+ $return = $this->move_folder_into_place($tmp_source, $dest_folder, $stage);
+
+ if (is_wp_error($return)) {
+ return $this->transfer_helpers->ajax_error($return->get_error_message());
+ }
+ }
+ }
+ }
+
+ /**
+ * @param string $stage
+ * @param array $state_data
+ *
+ * @return array
+ */
+ public function get_files_for_stage($stage, $state_data, $form_data) {
+ if ('pull' === $form_data['current_migration']['intent']) {
+ return $state_data['site_details']['remote'][$stage];
+ }
+ return $state_data[$stage . '_folders'];
+ }
+
+ /**
+ * @param array $file_folders_list
+ * @param string $file_folder
+ *
+ * @return bool
+ */
+ public function in_file_folders_list($file_folders_list, $file_folder) {
+ foreach ($file_folders_list as $list_item) {
+ if ($list_item[0]['path'] === $file_folder) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param string $source
+ * @param string $dest
+ * @param string $stage
+ *
+ * @return bool|\WP_Error
+ */
+ public function move_folder_into_place(
+ $source,
+ $dest,
+ $stage
+ ) {
+ $fs = $this->filesystem;
+ $dest_backup = false;
+ $singular_stages = [
+ 'themes' => 'Theme',
+ 'plugins' => 'Plugin',
+ 'muplugins' => 'Must-Use Plugin',
+ 'others' => 'Other'
+ ];
+
+ if (!$fs->file_exists($source)) {
+ $message = sprintf(__('Temporary file not found when finalizing %s Files migration: %s ', 'wp-migrate-db'), $singular_stages[$stage], $source);
+ $this->error_log->log_error($message);
+
+ return new \WP_Error('wpmdbpro_theme_plugin_files_error', $message);
+ }
+
+ if ($fs->file_exists($dest)) {
+ if (!$fs->is_writable($dest)) {
+ $message = sprintf(__('Unable to overwrite destination file when finalizing Theme & Plugin Files migration: %s', 'wp-migrate-db'), $source);
+ $this->error_log->log_error($message);
+ error_log($message);
+
+ return new \WP_Error('wpmdbpro_theme_plugin_files_error', $message);
+ }
+
+ $backup_dir = Util::get_temp_dir($stage) . 'backups' . DIRECTORY_SEPARATOR;
+ if (!$fs->is_dir($backup_dir)) {
+ $fs->mkdir($backup_dir);
+ }
+ $dest_backup = $backup_dir . basename($dest) . '.' . time() . '.bak';
+ $dest_backup = $fs->move($dest, $dest_backup) ? $dest_backup : false;
+ }
+
+ if (!$fs->move($source, $dest)) {
+ $message = sprintf(__('Unable to move file into place when finalizing Theme & Plugin Files migration. Source: %s | Destination: %s', 'wp-migrate-db'), $source, $dest);
+ $this->error_log->log_error($message);
+ error_log($message);
+
+ // attempt to restore backup
+ if ($dest_backup) {
+ $fs->move($dest_backup, $dest);
+ }
+
+ return new \WP_Error('wpmdbpro_theme_plugin_files_error', $message);
+ }
+
+ return true;
+ }
+
+ /**
+ * Runs on local site
+ */
+ public function cleanup_transfer_migration()
+ {
+ $stages = $this->form_data->getMigrationStages();
+
+ if (!$stages) {
+ return;
+ }
+
+ if (!empty(array_intersect(['theme_files', 'plugin_files', 'muplugin_files', 'other_files', 'core_files'], $stages))) {
+ $this->plugin_helper->cleanup_transfer_migration('themes');
+ }
+ }
+
+ /**
+ * Runs on remote site, on `wpmdb_respond_to_push_cancellation` hook
+ */
+ public function remove_tmp_files_remote()
+ {
+ $stages = $this->form_data->getMigrationStages();
+
+ if (!$stages) {
+ return;
+ }
+ // Only run if no media files stage
+ if (!empty(array_intersect(['theme_files', 'plugin_files', 'muplugin_files', 'other_files'], $stages))) {
+ $this->plugin_helper->remove_tmp_files('themes', 'remote');
+ }
+ }
+
+ /**
+ *
+ * Fires on the `wpmdb_before_finalize_migration` hook
+ *
+ * @return bool
+ * @throws \Exception
+ */
+ public function verify_file_transfer($state_data)
+ {
+ if (isset($state_data['stage']) && !in_array($state_data['stage'], array('themes', 'plugins', 'muplugins', 'others'))) {
+ return false;
+ }
+
+ $stages = array();
+ $form_data = $this->form_data->getCurrentMigrationData();
+
+ if (isset($form_data['stages']) && in_array('theme_files', $form_data['stages']) && isset($state_data['theme_folders'])) {
+ $stages[] = 'themes';
+ }
+
+ if (isset($form_data['stages']) && in_array('plugin_files', $form_data['stages']) && isset($state_data['plugin_folders'])) {
+ $stages[] = 'plugins';
+ }
+
+ if (isset($form_data['stages']) && in_array('muplugin_files', $form_data['stages']) && isset($state_data['muplugin_folders'])) {
+ $stages[] = 'muplugins';
+ }
+
+ if (isset($form_data['stages']) && in_array('other_files', $form_data['stages']) && isset($state_data['other_folders'])) {
+ $stages[] = 'others';
+ }
+
+ $migration_key = isset($state_data['type']) && 'push' === $state_data['type'] ? $state_data['remote_state_id'] : $state_data['migration_state_id'];
+
+ foreach ($stages as $stage) {
+ $filename = '.' . $migration_key . '-manifest';
+ $manifest_path = Util::get_temp_dir($stage) . $filename;
+ $queue_info = json_decode(file_get_contents($manifest_path), true);
+
+ if (!$queue_info) {
+ throw new \Exception(sprintf(__('Unable to verify file migration, %s does not exist.'), $manifest_path));
+ }
+
+ if (!isset($queue_info['total'])) {
+ continue;
+ }
+
+ try {
+ // Throws exception
+ $this->transfer_helpers->check_manifest($queue_info['manifest'], $stage);
+ } catch (\Exception $e) {
+ return $this->http->end_ajax(json_encode(array('wpmdb_error' => 1, 'body' => $e->getMessage())));
+ }
+ }
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesLocal.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesLocal.php
new file mode 100644
index 000000000..5eb358133
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/ThemePluginFilesLocal.php
@@ -0,0 +1,333 @@
+util = $common_util;
+ $this->queueManager = $queue_manager;
+ $this->transfer_util = $util;
+ $this->file_processor = $file_processor;
+ $this->transfer_manager = $transfer_manager;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->http = $http;
+ $this->check = $check;
+ $this->filesystem = $filesystem;
+ $this->rest_API_server = $rest_API_server;
+ $this->http_helper = $http_helper;
+ $this->queue_helper = $queue_helper;
+ }
+
+ public function register()
+ {
+ add_action('wpmdb_initiate_migration', array($this->check, 'transfer_check'));
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute(
+ '/tpf-initiate-file-migration',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_initiate_file_migration'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/tpf-get-queue-items',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_get_queue_items'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/tpf-transfer-files',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_transfer_files'],
+ ]
+ );
+ }
+
+ /**
+ *
+ * @TODO Break this up into smaller, testable functions
+ * @return bool|null
+ */
+ public function ajax_initiate_file_migration()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $this->util->set_time_limit();
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'stage' => 'string',
+ 'plugins_excludes' => 'json',
+ 'themes_excludes' => 'json',
+ 'muplugins_excludes' => 'json',
+ 'others_excludes' => 'json',
+ 'core_excludes' => 'json',
+ 'migration_state_id' => 'key',
+ 'folders' => 'json_array',
+ 'theme_folders' => 'json_array',
+ 'themes_option' => 'string',
+ 'plugin_folders' => 'json_array',
+ 'plugins_option' => 'string',
+ 'muplugin_folders' => 'json_array',
+ 'muplugins_option' => 'string',
+ 'other_folders' => 'json_array',
+ 'others_option' => 'string',
+ 'core_folders' => 'json_array',
+ 'core_option' => 'string',
+ 'is_cli_migration' => 'int',
+ 'nonce' => 'key',
+ );
+
+ $state_data = Persistence::setPostData($key_rules, __METHOD__);
+ $current_option = $state_data[$state_data['stage']. '_option'];
+ if (empty($state_data['folders']) && $current_option !== 'except' ) {
+ return $this->transfer_util->ajax_error(__('Error: empty folder list supplied.', 'wp-migrate-db'));
+ }
+
+ $excludes = isset($state_data[$state_data['stage']. '_excludes']) ? trim($state_data[$state_data['stage']. '_excludes'], '" \t\n\r\0\x0B') : [];
+ $split_excludes = [];
+ if (!is_array($excludes)) {
+ $split_excludes = preg_split('/\r\n|\r|\n/', stripcslashes($excludes)); //stripcslashes() makes the $excludes string double quoted so we can use preg_split()
+ }
+
+ //Cleanup partial chunk files.
+ $this->transfer_util->cleanup_temp_chunks(WP_CONTENT_DIR . DIRECTORY_SEPARATOR, 'tmpchunk');
+
+ //State data populated
+ $files = $state_data['folders'];
+
+ if (!is_array($files)) {
+ return $this->transfer_util->ajax_error(__('Invalid folder list supplied (invalid array)', 'wp-migrate-db'));
+ }
+
+ // @TODO this needs to be implemented for remotes on a pull
+ $verified_folders = $this->verify_files_for_migration($files);
+
+ if (empty($state_data['is_cli_migration'])) {
+ Util::enable_scandir_bottleneck();
+ }
+
+ if ('pull' === $state_data['intent']) {
+ // Set up local meta data
+ $file_list = $this->transfer_util->get_remote_files($files, 'wpmdbtp_respond_to_get_remote_' . $state_data['stage'], $split_excludes);
+ } else {
+ // Push = get local files
+ $paths = [
+ 'themes' => WP_CONTENT_DIR . '/themes/',
+ 'plugins' => WP_PLUGIN_DIR,
+ 'muplugins' => WPMU_PLUGIN_DIR,
+ 'others' => WP_CONTENT_DIR,
+ 'core' => ABSPATH
+ ];
+
+ $abs_path = $paths[$state_data['stage']];
+ $file_list = $this->file_processor->get_local_files($verified_folders, $abs_path, $split_excludes, $state_data['stage'], null, null,'push');
+ }
+
+ if (is_wp_error($file_list)) {
+ return $this->http->end_ajax($file_list);
+ }
+ $full_site_export = isset($state_data['full_site_export']) ? $state_data['full_site_export'] : false ;
+ $queue_status = $this->queue_helper->populate_queue($file_list, $state_data['intent'], $state_data['stage'], $state_data['migration_state_id'], $full_site_export);
+ set_site_transient('wpmdb_queue_status', $queue_status);
+
+ if (isset($file_list['meta']['scan_completed'])) {
+ if (true === $file_list['meta']['scan_completed']) {
+ return $this->http->end_ajax(['queue_status' => $queue_status]);
+ }
+ return $this->http->end_ajax(
+ [
+ 'recursive_queue' => true,
+ 'items_count' => $queue_status['total']
+ ]);
+ }
+
+ return $this->http->end_ajax(['queue_status' => $queue_status]);
+ }
+
+ /**
+ * Get queue items in batches to populate the UI
+ *
+ * @return mixed|null
+ */
+ public function ajax_get_queue_items()
+ {
+ return $this->queue_helper->get_queue_items();
+ }
+
+ /**
+ * @return null
+ */
+ public function ajax_transfer_files()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $this->util->set_time_limit();
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'stage' => 'string',
+ 'offset' => 'numeric',
+ 'folders' => 'json_array',
+ 'theme_folders' => 'json_array',
+ 'themes_option' => 'string',
+ 'plugin_folders' => 'json_array',
+ 'plugins_option' => 'string',
+ 'muplugin_folders' => 'json_array',
+ 'muplugins_option' => 'string',
+ 'other_folders' => 'json_array',
+ 'others_option' => 'string',
+ 'core_folders' => 'json_array',
+ 'core_option' => 'string',
+ 'migration_state_id' => 'key',
+ 'payloadSize' => 'numeric',
+ 'stabilizePayloadSize' => 'bool',
+ 'stepDownSize' => 'bool',
+ 'nonce' => 'key',
+ 'retries' => 'numeric',
+ 'forceHighPerformanceTransfers' => 'bool',
+ );
+
+ $state_data = Persistence::setPostData($key_rules, __METHOD__);
+
+ $count = apply_filters('wpmdbtp_file_batch_size', 1000);
+ $data = $this->queueManager->list_jobs($count);
+
+ if (is_wp_error($data)) {
+ return $this->http->end_ajax($data);
+ }
+
+ $processed = $this->transfer_util->process_file_data($data);
+
+ if (empty($data)) {
+ do_action('wpmdbtp_file_transfer_complete');
+
+ // Clear out queue in case there is a next step
+ $this->queueManager->truncate_queue();
+
+ return $this->http->end_ajax(['status' => 'complete']);
+ }
+
+ $remote_url = isset($state_data['url']) ? $state_data['url'] : '';
+ $processed = $this->transfer_manager->manage_file_transfer($remote_url, $processed, $state_data);
+
+ $result = [
+ 'status' => $processed,
+ ];
+
+ if (isset($processed['error'], $processed['message']) && true === $processed['error']) {
+ $result = new \WP_Error(400, $processed['message']);
+ }
+
+ //Client should check error status for files and if a 500 is encountered kill the migration stage
+ return $this->http->end_ajax($result);
+ }
+
+ public function verify_files_for_migration($files)
+ {
+ $paths = [];
+
+ foreach ($files as $file) {
+ if ($this->filesystem->file_exists($file)) {
+ $paths[] = $file;
+ }
+ }
+
+ return $paths;
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/TransferCheck.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/TransferCheck.php
new file mode 100644
index 000000000..72603a51f
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/TPF/TransferCheck.php
@@ -0,0 +1,84 @@
+form_data = $form_data;
+ $this->http = $http;
+ $this->error_log = $error_log;
+ }
+
+ /**
+ *
+ * Fires on `wpmdb_initiate_migration`
+ *
+ * @param $state_data
+ *
+ * @return null
+ */
+ public function transfer_check( $state_data ) {
+ $message = null;
+
+ // ***+=== @TODO - revisit usage of parse_migration_form_data
+ $form_data = $this->form_data->parse_and_save_migration_form_data($state_data['form_data'] );
+ if (empty(array_intersect(['theme_files', 'plugin_files', 'muplugins', 'other_files'], $form_data['current_migration']['stages']))) {
+ return;
+ }
+
+ if ( ! isset( $state_data['intent'] ) ) {
+ $this->error_log->log_error( 'Unable to determine migration intent - $state_data[\'intent\'] empty' );
+
+ return $this->http->end_ajax( json_encode( [
+ 'wpmdb_error' => 1,
+ 'body' => __( 'A problem occurred starting the Themes & Plugins migration.', 'wp-migrate-db' ),
+ ] ) );
+ }
+
+ $key = 'push' === $state_data['intent'] ? 'remote' : 'local';
+ $site_details = $state_data['site_details'][ $key ];
+ $tmp_folder_writable = isset($site_details['local_tmp_folder_writable']) ? $site_details['local_tmp_folder_writable'] : null;
+
+ // $tmp_folder_writable is `null` if remote doesn't have T&P addon installed
+ if ( false !== $tmp_folder_writable || false !== Persistence::getRemoteWPECookie() ) {
+ return;
+ }
+
+ $tmp_folder_error_message = isset( $site_details['local_tmp_folder_check']['message'] ) ? $site_details['local_tmp_folder_check']['message'] : '';
+
+ $error_message = __( 'Unfortunately it looks like we can\'t migrate your themes or plugins. However, running a migration without themes and plugins should work. Please uncheck the Themes checkbox, uncheck the Plugins checkbox, and try your migration again.', 'wp-migrate-db' );
+ $link = 'https://deliciousbrains.com/wp-migrate-db-pro/doc/theme-plugin-files-errors/';
+ $more = __( 'More Details »', 'wp-migrate-db' );
+
+ $message = sprintf( '%s
%s %s
', $error_message, $tmp_folder_error_message, $link, $more );
+
+ return $this->http->end_ajax( json_encode( [
+ 'wpmdb_error' => 1,
+ 'body' => $message,
+ ] ) );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Abstracts/TransferManagerAbstract.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Abstracts/TransferManagerAbstract.php
new file mode 100644
index 000000000..3443e1aaa
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Abstracts/TransferManagerAbstract.php
@@ -0,0 +1,61 @@
+ajax_initiate_file_migration
+ *
+ */
+ public function manage_file_transfer($remote_url, $processed, $state_data)
+ {
+ if ('pull' === $state_data['intent']) {
+ return $this->handle_pull($processed, $state_data, $remote_url);
+ }
+ if ('savefile' === $state_data['intent']) {
+ return $this->handle_savefile($processed, $state_data);
+ }
+
+ return $this->handle_push($processed, $state_data, $remote_url);
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Chunker.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Chunker.php
new file mode 100644
index 000000000..386613c0a
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Chunker.php
@@ -0,0 +1,159 @@
+util = $util;
+ }
+
+ /**
+ * @param string $id
+ *
+ * @return string
+ */
+ public static function get_chunk_path( $id ) {
+ $chunk_path = apply_filters( 'wpmdb_tranfers_chunk_folder', WP_CONTENT_DIR . DIRECTORY_SEPARATOR );
+
+ return $chunk_path . ".{$id}-tmpchunk";
+ }
+
+ /**
+ *
+ * Creates a temporary file at file path specified by self::get_chunk_path() and chucks $chunk_data into said file to be transferred to the remote
+ *
+ * @param array $chunk_data
+ * @param int $chunk_size
+ * @param array $state_data
+ *
+ * @return array
+ */
+ public function create_chunk( $chunk_data, $chunk_size, $state_data ) {
+ $file_path = $chunk_data['file_path'];
+ $stored_offset = $chunk_data['bytes_offset'];
+
+ $chunk_path = self::get_chunk_path( $state_data['migration_state_id'] );
+
+ $chunk_handle = fopen( $chunk_path, 'wb' );
+ $file_handle = fopen( $file_path, 'rb' );
+ $file_size = fstat($file_handle);
+
+ if ( 0 !== $stored_offset ) {
+ fseek( $file_handle, $stored_offset );
+ }
+
+ //Copying the stream directly prevents memory exhaustion
+ stream_copy_to_stream($file_handle, $chunk_handle, $chunk_size);
+
+ fclose( $chunk_handle );
+
+ return [$chunk_path, $file_size['size']];
+ }
+
+ /**
+ * Checks if a file is too large to push
+ *
+ * @param array $state_data
+ * @param int $bottleneck
+ * @param string $file_path
+ * @param array $file
+ * @param int $chunks
+ *
+ * @return array|bool
+ */
+ public function chunk_it( $state_data, $bottleneck, $file_path, $file, $chunks ) {
+ $chunked = true;
+
+ if ( 'pull' === $state_data['intent'] ) {
+ return false;
+ }
+
+ //Check if we're currently chunking, existing chunk data stored as a option
+ $chunk_option_name = 'wpmdb_file_chunk_' . $state_data['migration_state_id'];
+ $chunk_option = get_site_option( $chunk_option_name );
+
+ // If we haven't sent a previous chunk
+ if ( empty( $chunk_option ) ) {
+ $chunk_data = $this->assemble_chunk_data( $chunked, $file, $file_path, $bottleneck, $chunks, 1 );
+ } else {
+ $chunk_data = $chunk_option;
+ }
+
+ // --- File chunking begins ---
+ $chunked = true;
+ $file['chunked'] = true;
+
+ // Actually creates the chunk of data and saves it to a `wp-content/.-tmpchunk` file
+ list( $file_path, $file ) = $this->modify_file_data_for_chunk( $file, $chunk_data, $bottleneck, $state_data );
+
+ // Get the size of the .-tmpchunk file in /wp-content
+ $actual_chunk_size = filesize( $file_path );
+
+ $chunk_data['bytes_offset'] += $actual_chunk_size;
+
+ $file['bytes_offset'] = $chunk_data['bytes_offset'];
+ $data = $this->assemble_chunk_data( $chunked, $file, $file_path, $actual_chunk_size, $chunks, $chunk_data['chunk_number'], $chunk_data['bytes_offset'] );
+
+ // Update chunk number after chunk has been created
+ $chunk_data['chunk_number'] ++;
+
+
+ return [$data, $chunk_data];
+ }
+
+ /**
+ *
+ * Return a standard format array
+ *
+ * @param $chunked
+ * @param $file
+ * @param $file_path
+ *
+ * @return array
+ */
+ public function assemble_chunk_data( $chunked, $file, $file_path, $chunk_size, $chunks, $chunk_number, $bytes_offset = 0 ) {
+ $chunk_data = array(
+ 'chunked' => $chunked,
+ 'file' => $file,
+ 'file_path' => $file_path,
+ 'chunk_size' => $chunk_size,
+ 'chunks' => $chunks,
+ 'chunk_number' => $chunk_number,
+ 'bytes_offset' => $bytes_offset
+ );
+
+ return $chunk_data;
+ }
+
+ /**
+ * @param $file
+ * @param $chunk_data
+ * @param $chunk_size
+ *
+ * @return array
+ */
+ public function modify_file_data_for_chunk( $file, $chunk_data, $chunk_size, $state_data ) {
+ list($file_path, $file_size) = $this->create_chunk( $chunk_data, $chunk_size, $state_data );
+ $file['chunk_path'] = $file_path;
+ $file['chunks'] = $chunk_data['chunks'];
+ $file['chunk_number'] = $chunk_data['chunk_number'];
+ $file['percent_transferred'] = round( ( $chunk_data['bytes_offset'] + filesize( $file_path ) ) / (int) $file_size,
+ 2 );
+ $file['bytes_transferred'] = $chunk_size;
+
+ return array( $file_path, $file );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Excludes.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Excludes.php
new file mode 100644
index 000000000..d114c3ea9
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Excludes.php
@@ -0,0 +1,91 @@
+filesystem = $filesystem;
+ $this->http = $http;
+ $this->recursive_scanner = $recursive_scanner;
+ }
+
+ /**
+ * Given an array of directory paths, loops over each dir and returns an array of files and metadata
+ *
+ * @param array $directories
+ * @param string $abs_path
+ * @param array $excludes
+ * @param string $stage
+ * @param string|null $date
+ * @param string|null $timezone
+ * @param string|null $intent
+ *
+ * @return array
+ */
+ public function get_local_files($directories, $abs_path = '', $excludes = array(), $stage = '', $date = null, $timezone = 'UTC', $intent = null)
+ {
+ $count = 0;
+ $total_size = 0;
+ $files = [];
+ $manifest = [];
+
+ $scan_completed = false;
+ $directories = $this->recursive_scanner->unset_manifest_file($directories);
+ $dirs_count = count($directories);
+ if ($dirs_count === 0 ) {
+ $scan_completed = true;
+ }
+
+ $this->recursive_scanner->set_excludes($excludes);
+ $this->recursive_scanner->set_intent($intent);
+
+ foreach ($directories as $directory => $current_dir) {
+ $file_size = 0;
+ $is_single = false;
+ $files_in_directory = [];
+ $processed_files = [];
+
+ if (!$this->filesystem->file_exists($current_dir)) {
+ if($directory >= $dirs_count-1) {
+ $scan_completed = true;
+ }
+ continue;
+ }
+
+ if (!$this->filesystem->is_dir($current_dir)) {
+ $is_single = true;
+ }
+
+ $nice_name = $this->get_item_nice_name($stage, $current_dir, $is_single);
+
+ if ($is_single) {
+ $basename = wp_basename($current_dir);
+ $file_info = $this->filesystem->get_file_info(wp_basename($current_dir), $abs_path, false, $stage);
+ $files_in_directory[$basename] = $file_info;
+ } else {
+ $files_in_directory = $this->get_files_by_path($current_dir, $stage);
+ }
+
+ if (is_wp_error($files_in_directory)) {
+ return $files_in_directory;
+ }
+
+ $files_count = count($files_in_directory);
+ $files_keys = array_keys($files_in_directory);
+ for ($file_index = 0; $file_index < $files_count; $file_index++) {
+ $file_key = $files_keys[$file_index];
+ $file = $files_in_directory[$file_key];
+ $not_excluded = $this->check_file_against_excludes($file, $excludes);
+ $date_compare = true;
+
+ if ($not_excluded) {
+ $date_compare = $this->check_file_against_date($file, $date, $timezone);
+ }
+
+ if (is_wp_error($date_compare)) {
+ return $date_compare;
+ }
+
+ if (
+ $not_excluded === false ||
+ $date_compare === false
+ ) {
+ unset($files_in_directory[$file_key]);
+ continue;
+ }
+
+ //Check for manifest files, don't want those suckers
+ if (preg_match("/(([a-z0-9]+-){5})manifest/", $file_key)) {
+ unset($files_in_directory[$file_key]);
+ continue;
+ }
+
+ $file_size += $file['size'];
+ $total_size += $file['size'];
+ $manifest[] = $file['subpath'];
+ $processed_files[] = $file;
+ $count++;
+ }
+
+ $filtered_files = $this->filter_folder_data($processed_files, $file_size, $current_dir, $nice_name);
+
+ if (!empty($filtered_files)) {
+ $files[$current_dir] = $filtered_files;
+ }
+
+ if($this->recursive_scanner->is_enabled()) {
+ if ($this->recursive_scanner->reached_bottleneck() || !$this->recursive_scanner->is_scan_complete($current_dir)) {
+ break;
+ }
+ }
+
+ if($directory >= $dirs_count-1) {
+ $scan_completed = true;
+ }
+ }
+
+ $return = [
+ 'meta' => [
+ 'count' => $count,
+ 'size' => $total_size,
+ 'manifest' => $manifest,
+ 'scan_completed' => $scan_completed
+ ],
+ 'files' => $files,
+ ];
+
+ return $return;
+ }
+
+ /**
+ * @param string $stage
+ * @param array $directory
+ * @param bool $is_single
+ *
+ * @return string
+ */
+ public function get_item_nice_name($stage, $directory, $is_single = false)
+ {
+ $directory_info = 'themes' === $stage ? wp_get_themes() : get_plugins();
+ $exploded = explode(DIRECTORY_SEPARATOR, $directory);
+ $directory_key = $exploded[count($exploded) - 1];
+ $nice_name = '';
+
+ if ('media_files' === $stage) {
+ return $directory_key;
+ }
+
+ if ('themes' === $stage) {
+ if (isset($directory_info[$directory_key])) {
+ $nice_name = html_entity_decode($directory_info[$directory_key]->Name);
+ }
+ } else {
+ foreach ($directory_info as $key => $info) {
+ $pattern = '/^' . $directory_key;
+
+ if (!$is_single) {
+ $pattern .= '(\/|\\\)'; // Account for Windows slashes
+ }
+
+ $pattern .= '/';
+
+ if (1 === preg_match($pattern, $key)) {
+ $nice_name = html_entity_decode($info['Name']);
+ break;
+ }
+ }
+ }
+
+ return $nice_name;
+ }
+
+ /**
+ * @param string $abs_path
+ * @param string $filename
+ * @param int $size
+ * @param array $files
+ * @param int $count
+ *
+ * @return array
+ */
+ public function handle_single_file($abs_path, $filename, $size, $files, $count, $nice_name, $fix_path = false)
+ {
+ $file = wp_basename($filename);
+
+ $file_info = $this->filesystem->get_file_info($file, $abs_path);
+
+ $size += $file_info['size'];
+
+ $filtered_files = $this->filter_folder_data([$file_info], $size, $filename, $nice_name);
+ $files[$filename][$file_info['name']] = $filtered_files[0];
+ ++$count;
+
+ return array($size, $filtered_files, $files, $count);
+ }
+
+ /**
+ * @param array $files_in_directory
+ * @param int $size
+ *
+ * @return array
+ */
+ public function filter_folder_data($files_in_directory, $size, $folder_path, $nice_name)
+ {
+ $filtered_files = [];
+
+ foreach ($files_in_directory as $key => $files) {
+ $filtered_files[$key] = $files;
+ $filtered_files[$key]['folder_size'] = $size;
+ $filtered_files[$key]['folder_abs_path'] = $folder_path;
+ $filtered_files[$key]['nice_name'] = $nice_name;
+ }
+
+ return $filtered_files;
+ }
+
+ /**
+ * @param string $directory
+ * @param string $stage
+ *
+ * @return array|bool
+ */
+ public function get_files_by_path($directory, $stage = '')
+ {
+ // @TODO potentially filter this list
+ if($this->recursive_scanner->is_enabled()) {
+ $this->recursive_scanner->initialize($directory);
+ $files = $this->recursive_scanner->scan($directory, $stage);
+ } else {
+ $files = $this->filesystem->scandir_recursive($directory, $stage);
+ }
+
+ if (is_wp_error($files)) {
+ return $this->http->end_ajax($files);
+ }
+
+ return $files;
+ }
+
+ /**
+ * @param array $file
+ * @param array $excludes
+ *
+ * @return bool
+ */
+ public function check_file_against_excludes($file, $excludes)
+ {
+ if (empty($excludes)) {
+ return true;
+ }
+
+ $testMatch = Excludes::shouldExcludeFile($file['absolute_path'], $excludes);
+
+ if (!empty($testMatch['exclude'])) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Compare file modified date against a date and timezone
+ *
+ * Debug: $date = $date->format('Y-m-d H:i:sP');
+ *
+ * @param $file
+ * @param $date
+ * @param $clientTimezone
+ *
+ * @return bool
+ */
+ public function check_file_against_date($file, $date, $clientTimezone)
+ {
+ if (is_null($date)) {
+ return true;
+ }
+
+ $serverdate = new \DateTime();
+ $serverTimeZone = $serverdate->getTimezone();
+
+ $date = new \DateTime($date, new \DateTimeZone($clientTimezone));// Create client date object with associated timezone so we can compare against filemtime() which uses the server timezone
+
+ $date->setTimezone(new \DateTimeZone($serverTimeZone->getName()));
+ $abs_path = $file['absolute_path'];
+
+ if (!file_exists($abs_path)) {
+ return $this->http->end_ajax(new \WP_Error('wpmdb-file-does-not-exist', sprintf(__('File %s does not exist', 'wp-migrate-db'), $abs_path)));
+ }
+
+ $timestamp = $date->getTimestamp();
+ $fileMTime = filemtime($abs_path);
+
+ if ($fileMTime <= $timestamp) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Filters/FilterInterface.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Filters/FilterInterface.php
new file mode 100644
index 000000000..ee9e59adb
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Filters/FilterInterface.php
@@ -0,0 +1,8 @@
+constants_filter($line);
+ $line = $this->db_prefix_filter($line);
+
+ //Write the line to the temporary file
+ fwrite($tmp_file, $line);
+ }
+
+ fclose($original_file_handle);
+ fclose($tmp_file);
+
+ //adjust the absolute path to the temporary file
+ $file['absolute_path'] = $path;
+ return $file;
+ }
+
+ public function can_filter($file, $state_data)
+ {
+ $this->state_data = $state_data;
+
+ //Only filter wp-config.php for a subsite export
+ return $file['name'] === 'wp-config.php'
+ && isset($state_data['stages'], $state_data['mst_selected_subsite'])
+ && json_decode($state_data['stages']) !== ['tables']
+ && 'savefile' === $state_data['intent']
+ && '0' !== $state_data['mst_selected_subsite'];
+ }
+
+ /**
+ * Filters the multisite constants
+ *
+ * @param $line
+ *
+ * @return mixed|string
+ */
+ private function constants_filter($line) {
+ //Targeted multisite constants
+ $constants = ['WP_ALLOW_MULTISITE', 'MULTISITE', 'SUBDOMAIN_INSTALL', 'DOMAIN_CURRENT_SITE', 'PATH_CURRENT_SITE', 'SITE_ID_CURRENT_SITE', 'BLOG_ID_CURRENT_SITE'];
+ //If the line contains the MULTISITE constants, comment it
+ foreach ($constants as $constant) {
+ if (preg_match("~\b$constant\b~",$line)) {
+ $line = '// ' . $line;
+ }
+ }
+
+ return $line;
+ }
+
+ /**
+ * Filters the DB prefix variable
+ *
+ * @param string $line
+ * @return string
+ */
+ private function db_prefix_filter($line)
+ {
+ //If the line contains the $table_prefix variable, match it and extract the value
+ $regex = '~[$]\btable_prefix\b[=]((?:\'|").*(?:\'|"))~';
+ $adjusted_line = str_replace(' ', '', $line);
+ preg_match($regex, $adjusted_line, $matches);
+
+ //If the matched prefix is not the same as the original prefix, comment it and add the new prefix
+ if (isset($matches[0], $matches[1], $this->state_data['new_prefix']) && str_replace(['"', "'"], "",
+ $matches[1]) !== $this->state_data['new_prefix']) {
+ $adjusted_line = "// $adjusted_line \n";
+ $adjusted_line .= '$table_prefix = \'' . $this->state_data['new_prefix'] . '\';';
+
+ return $adjusted_line;
+ }
+
+ return $line;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/PluginHelper.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/PluginHelper.php
new file mode 100644
index 000000000..b3449dc8f
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/PluginHelper.php
@@ -0,0 +1,338 @@
+filesystem = $filesystem;
+ $this->http = $http;
+ $this->http_helper = $http_helper;
+ $this->settings = $settings->get_settings();
+ $this->migration_state_manager = $migration_state_manager;
+ $this->scramble = $scramble;
+ $this->file_processor = $file_processor;
+ $this->transfer_util = $transfer_util;
+ $this->properties = $properties;
+ $this->queue_manager = $queue_manager;
+ $this->manager = $manager;
+ $this->state_data_container = $state_data_container;
+ }
+
+
+ /**
+ * @param $stage
+ *
+ * @return mixed|null
+ */
+ public function respond_to_get_remote_folders($stage)
+ {
+ $key_rules = array(
+ 'action' => 'key',
+ 'intent' => 'key',
+ 'folders' => 'json',
+ 'excludes' => 'json',
+ 'stage' => 'string',
+ 'sig' => 'string',
+ 'date' => 'string',
+ 'timezone' => 'string',
+ 'is_cli_migration' => 'int',
+ );
+
+ $_POST['folders'] = stripslashes($_POST['folders']);
+ $_POST['excludes'] = stripslashes($_POST['excludes']);
+
+ $state_data = Persistence::setRemotePostData($key_rules, __METHOD__);
+
+ // Check for CLI migration and skip enabling recursive scanner if necessary.
+ if (!isset($state_data['is_cli_migration']) || 0 === (int)$state_data['is_cli_migration']) {
+ Util::enable_scandir_bottleneck();
+ }
+
+ $filtered_post = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'action',
+ 'intent',
+ 'folders',
+ 'excludes',
+ 'stage',
+ 'is_cli_migration'
+ )
+ );
+ $verification = $this->http_helper->verify_signature($filtered_post, $this->settings['key']);
+
+ if (!$verification) {
+ return $this->http->end_ajax(new \WP_Error('wpmdbtpf_invalid_post_data', __('Could not validate $_POST data.') . ' (#100tp)'));
+ }
+
+ $abs_path = '';
+
+ if ('plugins' === $stage) {
+ $abs_path = WP_PLUGIN_DIR;
+ }
+
+ if ('muplugins' === $stage) {
+ $abs_path = WPMU_PLUGIN_DIR;
+ }
+
+ if ('others' === $stage) {
+ $abs_path = WP_CONTENT_DIR;
+ }
+
+ if ('themes' === $stage) {
+ $abs_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR;
+ }
+
+ if ('media_files' === $stage) {
+ $uploads_path = Util::get_wp_uploads_dir();
+ $uploads_name = wp_basename($uploads_path);
+ $abs_path = apply_filters('wpmdb_mf_media_upload_path', WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $uploads_name, $state_data);
+ }
+
+ $slashed = $this->filesystem->slash_one_direction($abs_path);
+ $date = isset($_POST['date']) ? $state_data['date'] : null;
+ $timezone = !empty($_POST['timezone']) ? $state_data['timezone'] : 'UTC';
+
+ $folders = json_decode($state_data['folders'], true);
+
+ if ('media_files' === $stage) {
+ $folders = apply_filters('wpmdb_mf_remote_uploads_folder', $folders, $state_data);
+ }
+
+ $items = $folders;
+
+ if ($stage === 'media_files' && isset($folders[0])) {
+ $items = $this->get_top_level_items($folders[0]);
+ }
+
+ $files = $this->file_processor->get_local_files($items, $slashed, json_decode($state_data['excludes'], true), $stage, $date, $timezone, 'pull');
+
+
+ $files = ZipAndEncode::encode(json_encode($files));
+
+ return $this->http->end_ajax($files);
+ }
+
+ /**
+ *
+ * Respond to request to save queue status
+ *
+ * @return mixed|null
+ */
+ public function respond_to_save_queue_status()
+ {
+ $key_rules = array(
+ 'action' => 'key',
+ 'remote_state_id' => 'key',
+ 'stage' => 'string',
+ 'intent' => 'string',
+ 'sig' => 'string',
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data($key_rules);
+
+ $filtered_post = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'action',
+ 'remote_state_id',
+ 'intent',
+ 'stage',
+ )
+ );
+
+ $settings = $this->settings;
+
+ if (!$this->http_helper->verify_signature($filtered_post, $settings['key'])) {
+ return $this->transfer_util->ajax_error($this->properties->invalid_content_verification_error . ' (#100tp)', $filtered_post);
+ }
+
+ if (empty($_POST['queue_status'])) {
+ return $this->transfer_util->ajax_error(__('Saving queue status to remote failed.'));
+ }
+
+ $queue_status = filter_var($_POST['queue_status'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
+ $queue_data = json_decode(gzdecode(base64_decode($queue_status)), true);
+
+ if ($queue_data) {
+ try {
+ $queue_data = $this->transfer_util->concat_existing_remote_items($queue_data, $state_data['stage'], $state_data['remote_state_id']);
+ $this->transfer_util->save_queue_status($queue_data, $state_data['stage'], $state_data['remote_state_id']);
+ } catch (\Exception $e) {
+ return $this->http->end_ajax(new \WP_Error('wpmdb_failed_save_queue', $e->getMessage()));
+ }
+
+ return $this->http->end_ajax(json_encode(true));
+ }
+ }
+
+ public function get_top_level_items($dir)
+ {
+ $file_data = $this->filesystem->scandir($dir);
+
+ $items = [];
+
+ if (!$file_data) {
+ return false;
+ }
+
+ foreach ($file_data as $item) {
+ $items[] = $item['absolute_path'];
+ }
+
+ return $items;
+ }
+
+ public function cleanup_transfer_migration($stage)
+ {
+ $this->manager->drop_tables();
+
+ $this->remove_tmp_files($stage);
+ }
+
+ /**
+ *
+ * @param $stage
+ */
+ public function remove_tmp_files($stage, $env = 'local')
+ {
+ if (in_array($stage, ['themes', 'plugins', 'muplugins', 'others', 'core'])) {
+ $this->transfer_util->remove_tmp_folder('themes');
+ $this->transfer_util->remove_tmp_folder('plugins');
+ $this->transfer_util->remove_tmp_folder('muplugins');
+ $this->transfer_util->remove_tmp_folder('others');
+ $this->transfer_util->remove_tmp_folder('core');
+ }
+
+ if ($stage === 'media_files') {
+ $this->transfer_util->remove_tmp_folder($stage);
+ }
+
+ $id = null;
+
+ if ($env === 'local') {
+ $state_data = Persistence::getStateData();
+ $id = isset($state_data['migration_state_id']) ? $state_data['migration_state_id'] : null;
+ } else {
+ $state_data = Persistence::getRemoteStateData();
+ $id = isset($state_data['remote_state_id']) ? $state_data['remote_state_id'] : null;
+ }
+
+ if ($id) {
+ $this->remove_chunk_data($id, $env);
+ $this->remove_folder_options($id);
+ }
+
+ return;
+ }
+
+ public function remove_folder_options($id)
+ {
+ delete_site_option('wpmdb_folder_transfers_media_files_' . $id);
+ delete_site_option('wpmdb_folder_transfers_themes_' . $id);
+ delete_site_option('wpmdb_folder_transfers_plugins_' . $id);
+ delete_site_option('wpmdb_folder_transfers_muplugins_' . $id);
+ delete_site_option('wpmdb_folder_transfers_others_' . $id);
+ }
+
+ public function remove_chunk_data($id, $env)
+ {
+ if (!$id || $env !== 'local') {
+ return;
+ }
+
+ $chunk_file = Chunker::get_chunk_path($id);
+ if ($this->filesystem->file_exists($chunk_file)) {
+ $this->filesystem->unlink($chunk_file);
+ }
+
+ $chunk_option_name = 'wpmdb_file_chunk_' . $id;
+ delete_site_option($chunk_option_name);
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/TransferManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/TransferManager.php
new file mode 100644
index 000000000..022091e65
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/TransferManager.php
@@ -0,0 +1,70 @@
+queueManager = $manager;
+ $this->util = $util;
+ $this->http = $http;
+ $this->full_site_export = $full_site_export;
+ }
+
+ /**
+ * @param array $processed
+ * @param array $state_data
+ *
+ * @return array
+ */
+ public function handle_savefile($processed, $state_data)
+ {
+ $added_to_zip = $this->full_site_export->add_batch_to_zip($processed, $state_data);
+
+ if (is_wp_error($added_to_zip)) {
+ return $this->http->end_ajax($added_to_zip);
+ }
+
+ $this->queueManager->delete_data_from_queue($added_to_zip['count']);
+
+ return $this->util->process_queue_data($processed, $state_data, $added_to_zip['total_size']);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Util.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Util.php
new file mode 100644
index 000000000..c32eccae4
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Transfers/Files/Util.php
@@ -0,0 +1,802 @@
+filesystem = $filesystem;
+ $this->http = $http;
+ $this->error_log = $error_log;
+ $this->http_helper = $http_helper;
+ $this->remote_post = $remote_post;
+ $this->settings = $settings->get_settings();
+ $this->migration_state_manager = $migration_state_manager;
+ $this->util = $util;
+
+ add_filter( 'wpmdb_theoretical_transfer_bottleneck', function ( $bottleneck ) {
+ return $this->get_transfer_bottleneck();
+ } );
+ }
+
+ public function get_remote_files(array $directories, $action, $excludes, $date = null, $timezone = null)
+ {
+ // POST to remote to get list of files
+ $state_data = $this->migration_state_manager->set_post_data();
+
+ $data = array();
+ $data['action'] = $action;
+ $data['intent'] = $state_data['intent'];
+ $data['folders'] = json_encode($directories);
+ $data['excludes'] = json_encode($excludes);
+ $data['stage'] = $state_data['stage'];
+ $data['is_cli_migration'] = isset($state_data['is_cli_migration']) ? (int)$state_data['is_cli_migration'] : 0;
+ $data['sig'] = $this->http_helper->create_signature($data, $state_data['key']);
+
+ if (!is_null($date)) {
+ $data['date'] = $date;
+ }
+
+ if (!is_null($timezone)) {
+ $data['timezone'] = $timezone;
+ }
+
+ $ajax_url = trailingslashit($state_data['url']) . 'wp-admin/admin-ajax.php';
+ $response = $this->remote_post->post($ajax_url, $data, __FUNCTION__);
+ $response = $this->remote_post->verify_remote_post_response($response);
+ $response['data'] = json_decode(ZipAndEncode::decode($response['data']), true);
+
+ if (isset($response['wpmdb_error'])) {
+ return $response;
+ }
+
+ if (!$response['success']) {
+ return $this->http->end_ajax(new \WP_Error('wpmdbtransfers_invalid_file_list', $response['data']));
+ }
+
+ return $response['data'];
+ }
+
+ /**
+ * @param array $queue_status
+ * @param string $action
+ *
+ * @return array|bool|mixed|object|string
+ * @throws \Exception
+ */
+ public function save_queue_status_to_remote(array $queue_status, $action)
+ {
+ $state_data = $this->migration_state_manager->set_post_data();
+
+ $data = array();
+ $data['action'] = $action;
+ $data['intent'] = $state_data['intent'];
+ $data['stage'] = $state_data['stage'];
+ $data['remote_state_id'] = $state_data['migration_state_id'];
+ $data['sig'] = $this->http_helper->create_signature($data, $state_data['key']);
+
+ $data['queue_status'] = base64_encode(gzencode(json_encode($queue_status)));
+
+ $ajax_url = trailingslashit($state_data['url']) . 'wp-admin/admin-ajax.php';
+ $response = $this->remote_post_and_verify($ajax_url, $data);
+
+ return $response;
+ }
+
+ /**
+ * Fire POST at remote and check for the 'wpmdb_error' key in response
+ *
+ * @param string $ajax_url
+ * @param array $data
+ *
+ * @return array|bool|mixed|object|string
+ * @throws \Exception
+ */
+ public function remote_post_and_verify($ajax_url, $data, $headers = array())
+ {
+ $requests_options = $this->get_requests_options();
+ $response = null;
+
+ try {
+ $response = \Requests::post($ajax_url, $headers, $data, $requests_options);
+ } catch (\Exception $e) {
+ $this->catch_general_error($e->getMessage());
+ }
+
+ $response_body = json_decode($response->body, true);
+
+ if (isset($response_body['wpmdb_error'])) {
+ throw new \Exception($response_body['body']);
+ }
+
+ return $response;
+ }
+
+ /**
+ * @param $msg
+ * @param array $data
+ *
+ * @return mixed
+ */
+ public function ajax_error($msg, $data = array())
+ {
+ $this->error_log->log_error($msg, $data);
+
+ return $this->http->end_ajax(new \WP_Error('wpmdb_transfer_error', $msg));
+ }
+
+ /**
+ *
+ * Handles individual file transfer errors
+ *
+ * @param string $message
+ *
+ * @return array
+ */
+ public function fire_transfer_errors($message)
+ {
+ error_log($message);
+ $this->error_log->log_error($message);
+
+ return [
+ 'error' => true,
+ 'message' => $message,
+ ];
+ }
+
+ /**
+ *
+ *
+ * @param $message
+ *
+ * @return null
+ */
+ public function catch_general_error($message)
+ {
+ $return = [
+ 'wpmdb_error' => true,
+ 'msg' => $message,
+ ];
+
+ $this->error_log->log_error($message);
+ $this->http->end_ajax(json_encode($return));
+ }
+
+ /**
+ * @return array
+ */
+ public function get_requests_options()
+ {
+ // Listen to SSL verify setting
+ $wpmdb_settings = $this->settings;
+ $sslverify = 1 === $wpmdb_settings['verify_ssl'];
+ $requests_options = [];
+
+ // Make Requests cURL transport wait 45s for timeouts
+ $hooks = new \Requests_Hooks();
+ $hooks->register(
+ 'curl.before_send',
+ function ($handle) {
+ $remote_cookie = Persistence::getRemoteWPECookie();
+ if (false !== $remote_cookie) {
+ curl_setopt($handle, CURLOPT_COOKIE, 'wpe-auth=' . $remote_cookie);
+ }
+ curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 45);
+ curl_setopt($handle, CURLOPT_TIMEOUT, 45);
+ curl_setopt($handle, CURLOPT_ENCODING, 'gzip,deflate');
+ }
+ );
+
+ $requests_options['hooks'] = $hooks;
+
+ if (!$sslverify) {
+ $requests_options['verify'] = false;
+ }
+
+ return $requests_options;
+ }
+
+ /**
+ * Check's that files migrated match the .manifest file. Always fires at the migration destination
+ *
+ * @param array $files
+ * @param string $stage
+ *
+ * @throws \Exception
+ */
+ public function check_manifest($files, $stage)
+ {
+ $failures = [];
+
+ foreach ($files as $file) {
+ $file_path = Common_Util::get_stage_base_dir($stage);
+ if (!file_exists($file_path)) {
+ $failures[] = $file_path;
+ }
+ }
+
+ if (!empty($failures)) {
+ throw new \Exception(sprintf(__('The following files failed to transfer:
%s', 'wp-migrate-db'), implode('
', $failures)));
+ }
+ }
+
+ /**
+ * Merges a stored queue status (if exists) with the provided one.
+ *
+ * @param array $queue_status
+ * @param string $stage
+ * @param string $migration_state_id
+ * @return array
+ */
+ public function concat_existing_remote_items($queue_status, $stage, $migration_state_id)
+ {
+ $stored_queue = $this->get_queue_status($stage, $migration_state_id);
+ if (false !== $stored_queue) {
+ $queue_status['total'] += $stored_queue['total'];
+ $queue_status['size'] += $stored_queue['size'];
+ $queue_status['manifest'] = array_merge($stored_queue['manifest'], $queue_status['manifest']);
+
+ $this->remove_tmp_folder($stage);
+ }
+
+ return $queue_status;
+ }
+
+ /**
+ * Saves queue data to the manifest file
+ *
+ * @param array $data
+ * @param $stage
+ * @param string $migration_state_id
+ * @param bool $full_site_export
+ *
+ * @return bool|int
+ * @throws \Exception
+ */
+ public function save_queue_status(array $data, $stage, $migration_state_id, $full_site_export = false)
+ {
+ $tmp_path = $this->get_queue_tmp_path($stage, $full_site_export);
+
+ if (!$this->filesystem->mkdir($tmp_path)) {
+ throw new \Exception(sprintf(__('Unable to create folder for file transfers: %s'), $tmp_path));
+ }
+
+ $filename = $this->get_queue_manifest_file_name($migration_state_id);
+ $manifest = @file_put_contents($tmp_path . DIRECTORY_SEPARATOR . $filename, json_encode($data));
+
+ if (!$manifest) {
+ throw new \Exception(sprintf(__('Unable to create the transfer manifest file. Verify the web server can write to this file/folder: `%s`'), $tmp_path . DIRECTORY_SEPARATOR . '.manifest'));
+ }
+
+ return $manifest;
+ }
+
+ /**
+ * Get stored queue manifest array.
+ *
+ * @param string $stage
+ * @param string $migration_state_id
+ * @param bool $full_site_export
+ *
+ * @return array|false
+ */
+ public function get_queue_status($stage, $migration_state_id, $full_site_export = false)
+ {
+ $filename = $this->get_queue_manifest_file_name($migration_state_id);
+ $tmp_path = $this->get_queue_tmp_path($stage, $full_site_export);
+ $file_path = $tmp_path . DIRECTORY_SEPARATOR . $filename;
+ $manifest = is_file($file_path) ? @file_get_contents($file_path) : false;
+
+ if (false !== $manifest) {
+ return json_decode($manifest, true);
+ }
+
+ return false;
+ }
+
+ /**
+ * Get queue tmp path.
+ *
+ * @param string $stage
+ * @param bool $full_site_export
+ * @return string
+ */
+ private function get_queue_tmp_path($stage, $full_site_export = false)
+ {
+ //@todo avoid passing full_site_export down to here.
+ if ($full_site_export === true || $stage === 'media_files') {
+ return self::get_wp_uploads_dir();
+ }
+ return self::get_temp_dir($stage);
+
+ }
+
+ /**
+ * Get manifest file name.
+ *
+ * @param $migration_state_id
+ * @return string
+ */
+ private function get_queue_manifest_file_name($migration_state_id)
+ {
+ return '.' . $migration_state_id . '-manifest';
+ }
+
+ public function cleanup_media_migration()
+ {
+ $uploads = self::get_wp_uploads_dir();
+ $this->remove_manifests($uploads);
+
+ return true;
+ }
+
+ /**
+ * Will look for a tmp folder to remove based on the $stage param (themes, plugins)
+ *
+ * @param $stage
+ *
+ * @return bool
+ */
+ public function remove_tmp_folder($stage)
+ {
+ $fs = $this->filesystem;
+
+ if ($stage === 'media_files') {
+ return $this->cleanup_media_migration();
+ }
+
+ $tmp_folder = self::get_temp_dir($stage);
+ if ($fs->file_exists($tmp_folder)) {
+ if ($fs->is_dir($tmp_folder)) {
+ return $fs->rmdir($tmp_folder, true);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ *
+ * Verify a file is the correct size
+ *
+ * @param string $filepath
+ * @param int $expected_size
+ *
+ * @return bool
+ */
+ public function verify_file($filepath, $expected_size)
+ {
+ if (!file_exists($filepath)) {
+ return false;
+ }
+
+ $filesystem_size = filesize($filepath);
+ if ($filesystem_size !== (int)$expected_size) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function enqueue_files($files, $queue_manager)
+ {
+ foreach ($files as $file) {
+ $queue_manager->enqueue_file($file);
+ }
+ }
+
+ /**
+ * Determine folder tranferred numbers for client.
+ *
+ * @param array $data
+ * @param int $bytes_transferred
+ * @param array $state_data
+ *
+ * @return array
+ */
+ public function process_queue_data($data, $state_data, $bytes_transferred = 0)
+ {
+ $result_set = [];
+
+ if (empty($data)) {
+ return array($result_set, 0);
+ }
+
+
+ $stage = $state_data['stage'];
+ // Could be empty - stores progress of folder migrations between requests. Generally, the size of batch is 100 files and each file could be from a separate folder
+ $folder_transfer_status = get_site_option("wpmdb_folder_transfers_{$stage}_" . $state_data['migration_state_id']);
+
+ if (empty($folder_transfer_status)) {
+ $folder_transfer_status = [];
+ }
+
+ $total_transferred = 0;
+ $batch_size = 0;
+
+ foreach ($data as $key => $record) {
+ $is_chunked = isset($record['chunked']) && $record['chunked'];
+ $dirname = $record['folder_name'];
+ $keys = array_keys($result_set);
+
+ // This method is called in WPMDBPro_Theme_Plugin_Files_Local::ajax_initiate_file_migration()
+ // $bytes_transferred = 0 and we don't need to iterate over _all_ the files
+ if (0 === $bytes_transferred && \in_array($dirname, $keys)) {
+ continue;
+ }
+
+ if (0 !== $bytes_transferred) {
+ if (!isset($folder_transfer_status[$dirname])) {
+ $batch_size = 0;
+
+ $folder_transfer_status[$dirname] = [
+ 'folder_transferred' => 0,
+ 'folder_percent_transferred' => 0,
+ ];
+ }
+
+ $item_size = $record['size'];
+
+ if ($is_chunked) {
+ $item_size = $record['chunk_size'];
+ }
+
+ $folder_transfer_status[$dirname]['folder_transferred'] += $item_size;
+
+ if (!$is_chunked) {
+ $batch_size += $item_size;
+ } else {
+ $batch_size = $item_size;
+ }
+
+ $transferred_percentage = $record['folder_size'] > 0 ? $folder_transfer_status[$dirname]['folder_transferred'] / $record['folder_size'] : 0;
+ $folder_transfer_status[$dirname]['folder_percent_transferred'] = $transferred_percentage;
+ }
+
+ $is_dir = $this->filesystem->is_dir($record['folder_abs_path'] . DIRECTORY_SEPARATOR . $dirname);
+
+ $result_set[$dirname] = [
+ 'nice_name' => $record['nice_name'],
+ 'relative_path' => DIRECTORY_SEPARATOR . $dirname,
+ 'is_dir' => $is_dir,
+ 'absolute_path' => $record['folder_abs_path'],
+ 'item_size' => $record['size'],
+ 'size' => $record['folder_size'],
+ 'batch_size' => $batch_size,
+ 'folder_transferred' => isset($folder_transfer_status[$dirname]['folder_transferred']) ? $folder_transfer_status[$dirname]['folder_transferred'] : 0,
+ 'folder_percent_transferred' => isset($folder_transfer_status[$dirname]['folder_percent_transferred']) ? $folder_transfer_status[$dirname]['folder_percent_transferred'] : 0,
+ 'total_transferred' => $bytes_transferred,
+ ];
+ }
+
+ //Updates folder status transient
+ $this->update_folder_status($state_data, $result_set, $bytes_transferred);
+
+ // Maybe compute folder percent transferred here?
+ return $result_set;
+ }
+
+ /**
+ * @param array $state_data
+ * @param array $result_set
+ * @param int $bytes_transferred
+ *
+ * @return bool
+ */
+ public function update_folder_status($state_data, $result_set, $bytes_transferred)
+ {
+ if (0 === $bytes_transferred) {
+ return false;
+ }
+
+ $folders_in_progress = [];
+
+ foreach ($result_set as $key => $folder) {
+ if ($folder['folder_transferred'] < $folder['size']) {
+ $folders_in_progress[$key] = $folder;
+ }
+ }
+
+ $stage = $state_data['stage'];
+ if (empty($folders_in_progress) && 0 !== $bytes_transferred) {
+ delete_site_option("wpmdb_folder_transfers_{$stage}_" . $state_data['migration_state_id']);
+ } else {
+ update_site_option("wpmdb_folder_transfers_{$stage}_" . $state_data['migration_state_id'], $folders_in_progress);
+ }
+
+ return true;
+ }
+
+ public function cleanup_temp_chunks($dir, $suffix)
+ {
+ $iterator = new DirectoryIterator($dir);
+
+ foreach ($iterator as $fileInfo) {
+ if (!$fileInfo->isDot()) {
+ $name = $fileInfo->getFilename();
+
+ if (preg_match("/(([a-z0-9]+-){5})$suffix/", $name) && $fileInfo->isFile()) {
+ $this->filesystem->unlink($dir . $name);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public function get_folder_name($local_path, $dest_path, $temp_path, $manifest)
+ {
+ $last = basename(str_replace('\\', '/', $local_path));
+
+ if ($this->filesystem->file_exists($temp_path . $last)) {
+ return $last;
+ }
+
+ foreach ($manifest as $key => $item) {
+ $manifest_item = explode(DIRECTORY_SEPARATOR, $item);
+ unset($manifest_item[count($manifest_item) - 1]);
+ $imploded = implode(DIRECTORY_SEPARATOR, $manifest_item);
+
+ if (stripos($item, 'style.css') !== false && stripos($local_path, $imploded) !== false) {
+ $pieces = explode(DIRECTORY_SEPARATOR, $item);
+
+ if (isset($pieces[1]) && $this->filesystem->file_exists($temp_path . $pieces[1])) {
+ return $pieces[1];
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public function load_manifest($stage, $migration_id)
+ {
+ $filename = '.' . $migration_id . '-manifest';
+ $manifest_path = self::get_temp_dir($stage) . $filename;
+ $contents = file_get_contents($manifest_path);
+
+ if (!$contents) {
+ $this->http->end_ajax(new \WP_Error('wpmdb_load_manifest_failed', __("Failed to load manifest file.")));
+ }
+
+ $queue_info = json_decode($contents, true);
+
+ if (!$queue_info) {
+ $this->http->end_ajax(new \WP_Error('wpmdb_parse_manifest_failed', __("Failed to parse manifest file.")));
+ }
+
+ return $queue_info;
+ }
+
+ /**
+ * Process data
+ *
+ * @param array $data
+ *
+ * @return array
+ */
+ public function process_file_data($data)
+ {
+ $result_set = [];
+
+ if (!empty($data)) {
+ foreach ($data as $size => $record) {
+ $display_path = $record->file['subpath'];
+ $record->file['relative_path'] = $display_path;
+
+ $result_set[] = $record->file;
+ }
+ }
+
+ return $result_set;
+ }
+
+ public static function get_wp_uploads_dir()
+ {
+ $upload_info = wp_get_upload_dir();
+
+ return $upload_info['basedir'];
+ }
+
+ /**
+ * @param $directory
+ */
+ public function remove_manifests($directory)
+ {
+ $iterator = new DirectoryIterator($directory);
+
+ foreach ($iterator as $fileInfo) {
+ if (!$fileInfo->isDot()) {
+ $name = $fileInfo->getFilename();
+
+ if (preg_match("/(([a-z0-9]+-){5})manifest/", $name) && $fileInfo->isFile()) {
+ $this->filesystem->unlink($directory . DIRECTORY_SEPARATOR . $name);
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ * @return int
+ */
+ public function get_transfer_bottleneck()
+ {
+ $bottleneck = $this->util->get_max_upload_size();
+
+ // Subtract 250 KB from min for overhead
+ $bottleneck -= 250000;
+
+ return $bottleneck;
+ }
+
+ /**
+ * Enables the bottleneck-ed recursive file scanner.
+ */
+ public static function enable_scandir_bottleneck() {
+ add_filter('wpmdb_bottleneck_dir_scan', function ($bottleneck) {
+ return true;
+ });
+ }
+
+ /**
+ * @param string $base
+ *
+ * @return array
+ */
+ public function is_tmp_folder_writable( $base = 'theme_files' ) {
+ $options_to_dirs = [
+ 'theme_files' => 'themes',
+ 'plugin_files' => 'plugins',
+ 'muplugin_files' => 'muplugins',
+ 'other_files' => 'others'
+ ];
+ $tmp = self::get_temp_dir($options_to_dirs[$base]);
+ $test_file = $tmp . '/test.php';
+ $renamed_file = $tmp . '/test-2.php';
+
+ $return = [
+ 'status' => true,
+ ];
+
+ if ( ! $this->filesystem->mkdir( $tmp ) ) {
+ $message = sprintf( __( 'File transfer error - Unable to create a temporary folder. (%s)', 'wp-migrate-db' ), $tmp );
+ $this->error_log->log_error( $message );
+
+ return [
+ 'status' => false,
+ 'message' => $message,
+ ];
+ }
+
+ if ( method_exists('WpeCommon', 'get_wpe_auth_cookie_value') ) {
+ return $return;
+ }
+
+ if ( ! $this->filesystem->touch( $test_file ) ) {
+ $message = sprintf( __( 'File transfer error - Unable to create a PHP file on the server. (%s)', 'wp-migrate-db' ), $test_file );
+ $this->error_log->log_error( $message );
+
+ return [
+ 'status' => false,
+ 'message' => $message,
+ ];
+ }
+
+ if ( ! file_put_contents( $test_file, 'test' ) ) {
+ $message = sprintf( __( 'File transfer error - Unable to update file contents using using PHP\'s file_put_contents() function. (%s)', 'wp-migrate-db' ), $test_file );
+ $this->error_log->log_error( $message );
+
+ return [
+ 'status' => false,
+ 'message' => $message,
+ ];
+ }
+
+ if ( ! rename( $test_file, $renamed_file ) ) {
+ $message = sprintf( __( 'File transfer error - Unable to move file to the correct location using PHP\'s rename() function. (%s)', 'wp-migrate-db' ), $renamed_file );
+ $this->error_log->log_error( $message );
+
+ return [
+ 'status' => false,
+ 'message' => $message,
+ ];
+ }
+
+ //Clean up
+ if ( ! $this->remove_tmp_folder( $options_to_dirs[$base] ) ) {
+ $message = sprintf( __( 'File transfer error - Unable to delete file using PHP\'s unlink() function. (%s)', 'wp-migrate-db' ), $renamed_file );
+ $this->error_log->log_error( $message );
+
+ return [
+ 'status' => false,
+ 'message' => $message,
+ ];
+ }
+
+ return $return;
+ }
+
+
+ /**
+ * Where to store files as they're being transferred
+ *
+ * @param string $stage
+ * @return bool|mixed|void
+ */
+ public static function get_temp_dir($stage) {
+ $temp_dir = Common_Util::get_stage_base_dir($stage) . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
+ return apply_filters('wpmdb_transfers_temp_dir', $temp_dir);
+ }
+
+ /**
+ * Sanitizes a provided file path.
+ * If the filename includes a path, it will get split and only the last part will be sanitized.
+ *
+ * @param string $file_path
+ *
+ * @return string
+ */
+ public static function sanitize_file_path($file_path) {
+ //split path
+ $split = explode(DIRECTORY_SEPARATOR, $file_path);
+
+ //sanitize last part
+ $file_path = array_pop($split);
+ $split[] = sanitize_file_name($file_path);
+ return implode(DIRECTORY_SEPARATOR, $split);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/UI/Notice.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/UI/Notice.php
new file mode 100644
index 000000000..a50a66d72
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/UI/Notice.php
@@ -0,0 +1,53 @@
+ID, 'wpmdb_dismiss_' . $notice)) {
+ return false;
+ }
+
+ $notice_links['dismiss'] = $notice;
+
+ if ('SHOW_ONCE' === $dismissLink) {
+ update_user_meta($current_user->ID, 'wpmdb_dismiss_' . $notice, true);
+ }
+ }
+
+ if ($reminderTime) {
+ $reminder_set = get_user_meta($current_user->ID, 'wpmdb_reminder_' . $notice, true);
+//
+ if ($reminder_set) {
+ if (strtotime('now') < $reminder_set) {
+ return false;
+ }
+ }
+
+ $notice_links['reminder'] = $notice;
+ $notice_links['reminder_time'] = $reminderTime;
+ }
+
+ return (count($notice_links) > 0) ? $notice_links : true;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/UI/TemplateBase.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/UI/TemplateBase.php
new file mode 100644
index 000000000..557334110
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/UI/TemplateBase.php
@@ -0,0 +1,264 @@
+props = $properties;
+ $this->settings = $settings->get_settings();
+ $this->util = $util;
+ $this->profile = $profile;
+ $this->filesystem = $filesystem;
+
+ if (is_multisite()) {
+ $this->lock_url_find_replace_row = true;
+ }
+
+ $this->table = $table;
+
+ $this->plugin_tabs = [
+ [
+ 'slug' => 'migrate',
+ 'title' => _x('Migrate', 'Configure a migration or export', 'wp-migrate-db'),
+ ],
+ [
+ 'slug' => 'settings',
+ 'title' => _x('Settings', 'Plugin configuration and preferences', 'wp-migrate-db'),
+ ],
+ [
+ 'slug' => 'addons',
+ 'title' => _x('Addons', 'Plugin extensions', 'wp-migrate-db'),
+ ],
+ [
+ 'slug' => 'help',
+ 'title' => _x('Help', 'Get help or contact support', 'wp-migrate-db'),
+ ],
+ ];
+ }
+
+ function template_compatibility()
+ {
+ $args = array(
+ 'plugin_compatibility_checked' => ($this->util->is_muplugin_installed() ? true : false),
+ );
+ $this->template('compatibility', 'common', $args);
+ }
+
+ function template_max_request_size()
+ {
+ $this->template('max-request-size', 'common');
+ }
+
+ function template_debug_info()
+ {
+ $this->template('debug-info', 'common');
+ }
+
+ function template_exclude_post_revisions($loaded_profile)
+ {
+ $args = array(
+ 'loaded_profile' => $loaded_profile,
+ );
+ $this->template('exclude-post-revisions', 'wpmdb', $args);
+ }
+
+ function template_wordpress_org_support()
+ {
+ $this->template('wordpress-org-support', 'wpmdb');
+ }
+
+ function template_progress_upgrade()
+ {
+ $this->template('progress-upgrade', 'wpmdb');
+ }
+
+ function template_sidebar()
+ {
+ $this->template('sidebar', 'wpmdb');
+ }
+
+ /**
+ * Load Tools HTML template for tools menu on sites in a Network to help users find WPMDB in Multisite
+ *
+ */
+ function subsite_tools_options_page()
+ {
+ $this->template('options-tools-subsite');
+ }
+
+ function template_part($methods, $args = false)
+ {
+ $methods = array_diff($methods, $this->props->unhook_templates);
+
+ foreach ($methods as $method) {
+ $method_name = 'template_' . $method;
+
+ if (method_exists($this, $method_name)) {
+ call_user_func(array($this, $method_name), $args);
+ }
+ }
+ }
+
+ public function plugin_tabs()
+ {
+ $i = 0;
+ foreach ($this->plugin_tabs as $tab) {
+ $active = 0 === $i ? ' nav-tab-active' : '';
+ $tpl = '%s';
+ printf($tpl, $active, $tab['slug'], $tab['slug'], $tab['title']);
+ $i++;
+ }
+ }
+
+ /**
+ * Returns HTML for setting a checkbox as checked depending on supplied option value.
+ *
+ * @param string|array $option Options value or array containing $option_name as key.
+ * @param string $option_name If $option is an array, the key that contains the value to be checked.
+ */
+ public function maybe_checked($option, $option_name = '')
+ {
+ if (is_array($option) && !empty($option_name) && !empty($option[$option_name])) {
+ $option = $option[$option_name];
+ }
+ echo esc_html((!empty($option) && '1' == $option) ? ' checked="checked"' : '');
+ }
+
+ public function template($template, $dir = '', $args = array(), $template_path = '')
+ {
+ // @TODO: Refactor to remove extract().
+ extract($args, EXTR_OVERWRITE);
+ $dir = !empty($dir) ? trailingslashit($dir) : $dir;
+ $base_path = !empty($template_path) ? $template_path : $this->props->template_dir;
+
+ $path = $base_path . $dir . $template . '.php';
+ include $path;
+ }
+
+ /**
+ * @param $template
+ * @param string $dir
+ * @param array $args
+ * @param string $template_path
+ *
+ * @return false|string
+ *
+ * @TODO !!! DO NOT USE PHP TEMPLATES GOING FORWARD !!!
+ */
+ public function template_to_string($template, $dir = '', $args = array(), $template_path = '')
+ {
+ // @TODO: Refactor to remove extract().
+
+ if (is_array($args) && !empty($args)) {
+ extract($args, EXTR_OVERWRITE);
+ }
+
+ $dir = !empty($dir) ? trailingslashit($dir) : $dir;
+ $base_path = !empty($template_path) ? $template_path : $this->props->template_dir;
+
+ $path = $base_path . $dir . $template . '.php';
+
+ $str = $this->render_php($path, $args);
+ return $str;
+ }
+
+ public function options_page()
+ {
+ $this->template('options');
+ }
+
+ public function options_page_outdated_wp()
+ {
+ $this->template('options-page-outdated-wp');
+ }
+
+ function mixed_case_table_name_warning($migration_type)
+ {
+ ob_start();
+ ?>
+
+
+
+ local site has the MySQL setting lower_case_table_names
set to 1
.", 'wp-migrate-db'); ?>
+
+ remote site has the MySQL setting lower_case_table_names
set to 1
.", 'wp-migrate-db'); ?>
+
+
+
+
+ our documentation, proceed with caution.', 'wp-migrate-db'), 'https://deliciousbrains.com/wp-migrate-db-pro/doc/mixed-case-table-names/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin'); ?>
+ current_migration->forceHighPerformanceTransfers = true;
+ }
+
+ //If pro, update profiles with addons license status
+ if (Util::isPro()) {
+ $available_addons = WPMDBDI::getInstance()->get(\DeliciousBrains\WPMDB\Pro\License::class)->get_available_addons_list(get_current_user_id());
+ $licensed_array = $available_addons ? array_keys($available_addons) : [];
+
+ if (property_exists($profile, 'media_files') && in_array('wp-migrate-db-pro-media-files', $licensed_array,
+ true)) {
+ $profile->media_files->is_licensed = true;
+ }
+
+ if (property_exists($profile, 'multisite_tools') && in_array('wp-migrate-db-pro-multisite-tools',
+ $licensed_array, true)) {
+ $profile->multisite_tools->is_licensed = true;
+ }
+
+ if (property_exists($profile, 'theme_plugin_files') && in_array('wp-migrate-db-pro-theme-plugin-files',
+ $licensed_array, true)) {
+ $profile->theme_plugin_files->is_licensed = true;
+ }
+ }
+ }
+
+ public function get_target_schema_version()
+ {
+ return "3.7.0";
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Upgrades/Routines/Routine_2_6_2.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Upgrades/Routines/Routine_2_6_2.php
new file mode 100644
index 000000000..67f929e1e
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Upgrades/Routines/Routine_2_6_2.php
@@ -0,0 +1,34 @@
+current_migration->post_types_selected);
+
+ if ($has_revision && in_array('exclude_post_revisions', $profile->current_migration->advanced_options_selected)) {
+ $profile->current_migration->advanced_options_selected = array_diff($profile->current_migration->advanced_options_selected, ['exclude_post_revisions']);
+ }
+ //check exclude revisions if revision is not in the selected list
+ if (!$has_revision && 'selected' === $profile->current_migration->post_types_option && !in_array('exclude_post_revisions', $profile->current_migration->advanced_options_selected)) {
+ $profile->current_migration->advanced_options_selected[] = 'exclude_post_revisions';
+ }
+ //remove revision from selected post type list
+ if ($has_revision && in_array('revision', $profile->current_migration->post_types_selected)) {
+ $profile->current_migration->post_types_selected = array_values(array_filter($profile->current_migration->post_types_selected, function($value) { return $value !== 'revision'; }));
+ }
+ }
+ }
+
+ public function get_target_schema_version()
+ {
+ return "3.8.0";
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Upgrades/UpgradeRoutinesManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Upgrades/UpgradeRoutinesManager.php
new file mode 100644
index 000000000..16f563f21
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Upgrades/UpgradeRoutinesManager.php
@@ -0,0 +1,123 @@
+assets = $assets;
+ $this->profile_manager = $profile_manager;
+ }
+
+ /**
+ * @return RoutineInterface[]
+ */
+ private function get_routines() {
+ // New routines should be added here, in order, newer last.
+ return [
+ new Routine_2_6_0(),
+ new Routine_2_6_2(),
+ ];
+ }
+
+ /**
+ * Iterate through all upgrade routines and apply them to all profiles.
+ *
+ * @return void
+ */
+ public function perform_upgrade_routines()
+ {
+ $routines = $this->get_routines();
+ $target_version = end($routines)->get_target_schema_version();
+
+ //Iterate through all profiles and apply all upgrade routines
+ foreach (['wpmdb_saved_profiles', 'wpmdb_recent_migrations'] as $profile_type) {
+ $profiles = $profile_type === 'wpmdb_saved_profiles' ? $this->assets->get_saved_migration_profiles()
+ : $this->assets->get_recent_migrations(get_site_option($profile_type));
+
+ $updated_profiles = [];
+
+ foreach ($profiles as $profile) {
+ $profile_id = $profile['id'];
+
+ $profile = $this->profile_manager->get_profile_by_id($profile_type === 'wpmdb_recent_migrations' ? 'unsaved' : $profile_type, $profile_id);
+ $profile_data = json_decode($profile['value']);
+
+ foreach ($this->get_routines() as $routine) {
+ //Apply routine if stored schema version is lower than routine target schema version
+ if (version_compare($routine->get_target_schema_version(), $this->get_current_schema_version()) === 1) {
+ $last_routine_version = $routine->get_target_schema_version();
+ $routine->apply($profile_data);
+ }
+ }
+
+ $profile['value'] = json_encode($profile_data);
+ $updated_profiles[$profile_id] = $profile;
+ }
+
+ if ( ! empty($last_routine_version)) {
+ //Save the profiles
+ $updated_profiles = update_site_option($profile_type, $updated_profiles);
+
+ if (false === $updated_profiles) {
+ error_log(sprintf('WPMDB: Could not update %s to schema version: %s. It might already be up-to-date.',
+ $profile_type, $last_routine_version));
+ }
+ }
+ }
+
+ //Once all profiles have been updated, update the schema version in the database.
+ if (( ! empty($last_routine_version) && version_compare($last_routine_version,
+ $target_version) === 0) || "0" === $this->get_current_schema_version()) {
+ $this->update_schema_version($target_version);
+ }
+ }
+
+ /**
+ * Returns the current saved schema version.
+ *
+ * @return string
+ */
+ private function get_current_schema_version() {
+ return (string) get_site_option('wpmdb_schema_version', 0);
+ }
+
+ /**
+ * Updates the schema_version option in the database.
+ *
+ * @param string $version
+ *
+ * @return void
+ */
+ private function update_schema_version($version = "0")
+ {
+ update_site_option('wpmdb_schema_version', $version);
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Util/Singleton.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Util/Singleton.php
new file mode 100644
index 000000000..2d23e9d8c
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Util/Singleton.php
@@ -0,0 +1,14 @@
+props = $properties;
+ $this->filesystem = $filesystem;
+ }
+
+ public static function isPro()
+ {
+ return defined("WPMDB_PRO") && WPMDB_PRO;
+ }
+
+ /**
+ * Gets the global plugin meta info
+ *
+ * @return array
+ **/
+ public static function getPluginMeta()
+ {
+ if (self::isPro()) {
+ return $GLOBALS['wpmdb_meta']['wp-migrate-db-pro'];
+ }
+ return $GLOBALS['wpmdb_meta']['wp-migrate-db'];
+ }
+
+ /**
+ * Has a specific method been called in the stack trace.
+ *
+ * @param string $method
+ * @param null|array $stack
+ *
+ * @return bool
+ */
+ public static function has_method_been_called($method, $stack = null)
+ {
+ if (empty($stack)) {
+ // phpcs:ignore
+ $stack = debug_backtrace();
+ }
+
+ foreach ($stack as $caller) {
+ if ($method === $caller['function']) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the wpmdb_bottleneck value in bytes
+ *
+ * @param string $type
+ *
+ * @return int
+ */
+ function get_bottleneck($type = 'regular')
+ {
+ $suhosin_limit = false;
+ $suhosin_request_limit = false;
+ $suhosin_post_limit = false;
+
+ if (function_exists('ini_get')) {
+ $suhosin_request_limit = trim(ini_get('suhosin.request.max_value_length'));
+ $suhosin_post_limit = trim(ini_get('suhosin.post.max_value_length'));
+ }
+
+ if ($suhosin_request_limit && $suhosin_post_limit) {
+ $suhosin_limit = min(wp_convert_hr_to_bytes($suhosin_request_limit), wp_convert_hr_to_bytes($suhosin_post_limit));
+ }
+
+ $post_max_upper_size = apply_filters('wpmdb_post_max_upper_size', 26214400);
+
+ // we have to account for HTTP headers and other bloating, here we minus 1kb for bloat
+ $calculated_bottleneck = min(($this->get_post_max_size() - 1024), $post_max_upper_size);
+
+ if (0 >= $calculated_bottleneck) {
+ $calculated_bottleneck = $post_max_upper_size;
+ }
+
+ if ($suhosin_limit) {
+ $calculated_bottleneck = min($calculated_bottleneck, $suhosin_limit - 1024);
+ }
+
+ if ($type != 'max') {
+ $calculated_bottleneck = min($calculated_bottleneck, Settings::get_setting('max_request'));
+ }
+
+ return apply_filters('wpmdb_bottleneck', $calculated_bottleneck);
+ }
+
+ // Generates our secret key
+ public function generate_key($length = 40)
+ {
+ $keyset = 'abcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/';
+ $key = '';
+
+ for ($i = 0; $i < $length; $i++) {
+ $key .= substr($keyset, wp_rand(0, strlen($keyset) - 1), 1);
+ }
+
+ return $key;
+ }
+
+ /**
+ * Returns the php ini value for post_max_size in bytes
+ *
+ * @return int
+ */
+ public function get_post_max_size()
+ {
+ $bytes = max(wp_convert_hr_to_bytes(trim(ini_get('post_max_size'))), wp_convert_hr_to_bytes(trim(ini_get('hhvm.server.max_post_size'))));
+
+ return $bytes;
+ }
+
+ /**
+ * Get estimated usable memory limit.
+ *
+ * @return int
+ */
+ public function get_memory_limit()
+ {
+ if (function_exists('ini_get')) {
+ $memory_limit = ini_get('memory_limit');
+ } else {
+ $memory_limit = '256M';
+ }
+
+ if (!$memory_limit || -1 === $memory_limit) {
+ $memory_limit = '1000M';
+ }
+
+ return wp_convert_hr_to_bytes(trim($memory_limit)) * 0.8;
+ }
+
+ /**
+ * Test to see if executing an AJAX call specific to the WP Migrate DB family of plugins.
+ *
+ * @return bool
+ */
+ public static function is_ajax()
+ {
+ // must be doing AJAX the WordPress way
+ if (!defined('DOING_AJAX') || !DOING_AJAX) {
+ return false;
+ }
+
+ // must be one of our actions -- e.g. core plugin (wpmdb_*), media files (wpmdbmf_*)
+ if (!isset($_POST['action']) || 0 !== strpos($_POST['action'], 'wpmdb')) {
+ return false;
+ }
+
+ // must be on blog #1 (first site) if multisite
+ if (is_multisite() && 1 != get_current_site()->id) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks if another version of WPMDB(Pro) is active and deactivates it.
+ * To be hooked on `activated_plugin` so other plugin is deactivated when current plugin is activated.
+ *
+ * @param string $plugin
+ *
+ */
+ public static function deactivate_other_instances($plugin)
+ {
+ if (!in_array(basename($plugin), array('wp-migrate-db-pro.php', 'wp-migrate-db.php'))) {
+ return;
+ }
+
+ $plugin_to_deactivate = 'wp-migrate-db.php';
+ $deactivated_notice_id = '1';
+ if (basename($plugin) == $plugin_to_deactivate) {
+ $plugin_to_deactivate = 'wp-migrate-db-pro.php';
+ $deactivated_notice_id = '2';
+ }
+
+ if (is_multisite()) {
+ $active_plugins = (array)get_site_option('active_sitewide_plugins', array());
+ $active_plugins = array_keys($active_plugins);
+ } else {
+ $active_plugins = (array)get_option('active_plugins', array());
+ }
+
+ foreach ($active_plugins as $basename) {
+ if (false !== strpos($basename, $plugin_to_deactivate)) {
+ set_transient('wp_migrate_db_deactivated_notice_id', $deactivated_notice_id, 1 * HOUR_IN_SECONDS);
+ deactivate_plugins($basename);
+
+ return;
+ }
+ }
+ }
+
+ /**
+ * Return unserialized object or array
+ *
+ * @param string $serialized_string Serialized string.
+ * @param string $method The name of the caller method.
+ *
+ * @return mixed, false on failure
+ */
+ public static function unserialize($serialized_string, $method = '')
+ {
+ if ( ! is_serialized($serialized_string)) {
+ return false;
+ }
+
+ $serialized_string = trim($serialized_string);
+
+ // Because we support PHP versions less than 7.0 we need to use the polyfill.
+ $unserialized_string = @Unserialize::unserialize($serialized_string, array('allowed_classes' => false));
+
+ if (false === $unserialized_string && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
+ $scope = $method ? sprintf(__('Scope: %s().', 'wp-migrate-db'), $method) : false;
+ $error = sprintf(__('WPMDB Error: Data cannot be unserialized. %s', 'wp-migrate-db'), $scope);
+ error_log($error);
+ }
+
+ return $unserialized_string;
+ }
+
+ /**
+ * Use wp_unslash if available, otherwise fall back to stripslashes_deep
+ *
+ * @param string|array $arg
+ *
+ * @return string|array
+ */
+ public static function safe_wp_unslash($arg)
+ {
+ if (function_exists('wp_unslash')) {
+ return wp_unslash($arg);
+ } else {
+ return stripslashes_deep($arg);
+ }
+ }
+
+ /**
+ * Use gzdecode if available, otherwise fall back to gzinflate
+ *
+ * @param string $data
+ *
+ * @return string|bool
+ */
+ public static function gzdecode($data)
+ {
+ if (!function_exists('gzdecode')) {
+ return @gzinflate(substr($data, 10, -8));
+ }
+
+ return @gzdecode($data);
+ }
+
+ /**
+ * Require wpmdb-wpdb and create new instance
+ *
+ * @return MDBWPDB
+ */
+ public static function make_wpmdb_wpdb_instance()
+ {
+ return new MDBWPDB();
+ }
+
+ /**
+ * Wrapper for replacing first instance of string
+ *
+ * @return string
+ */
+ public static function str_replace_first($search, $replace, $string)
+ {
+ $pos = strpos($string, $search);
+
+ if (false !== $pos) {
+ $string = substr_replace($string, $replace, $pos, strlen($search));
+ }
+
+ return $string;
+ }
+
+ /**
+ * Runs WPs create nonce with all filters removed
+ *
+ * @param string|int $action Scalar value to add context to the nonce.
+ *
+ * @return string The Token
+ */
+ public static function create_nonce($action = -1)
+ {
+ global $wp_filter;
+ $filter_backup = $wp_filter;
+ self::filter_nonce_filters();
+ $return = wp_create_nonce($action);
+ $wp_filter = $filter_backup;
+
+ return $return;
+ }
+
+ /**
+ * Runs WPs check ajax_referer [sic] with all filters removed
+ *
+ * @param int|string $action Action nonce.
+ * @param false|string $query_arg Optional. Key to check for the nonce in `$_REQUEST` (since 2.5). If false,
+ * `$_REQUEST` values will be evaluated for '_ajax_nonce', and '_wpnonce'
+ * (in that order). Default false.
+ * @param bool $die Optional. Whether to die early when the nonce cannot be verified.
+ * Default true.
+ *
+ * @return false|int False if the nonce is invalid, 1 if the nonce is valid and generated between
+ * 0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
+ */
+ public static function check_ajax_referer($action = -1, $query_arg = false, $die = true)
+ {
+ global $wp_filter;
+ $filter_backup = $wp_filter;
+ self::filter_nonce_filters();
+ $return = check_ajax_referer($action, $query_arg, $die);
+ $wp_filter = $filter_backup;
+
+ return $return;
+ }
+
+ /**
+ * Removes filters from $wp_filter that might interfere with wpmdb nonce generation/checking
+ */
+ private static function filter_nonce_filters()
+ {
+ global $wp_filter;
+ $filtered_filters = apply_filters('wpmdb_filtered_filters', array(
+ 'nonce_life',
+ ));
+ foreach ($filtered_filters as $filter) {
+ unset($wp_filter[$filter]);
+ }
+ }
+
+ /**
+ *
+ * Checks if the current request is a WPMDB request
+ *
+ * @return bool
+ */
+ public static function is_wpmdb_ajax_call()
+ {
+ if ((defined('DOING_AJAX') && DOING_AJAX) && (isset($_POST['action']) && false !== strpos($_POST['action'], 'wpmdb'))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ *
+ * Sets 'Expect' header to an empty string which some server/host setups require
+ *
+ * Called from the `http_request_args` filter
+ *
+ * @param $r
+ * @param $url
+ *
+ * @return mixed
+ */
+ public static function preempt_expect_header($r, $url)
+ {
+ if (self::is_wpmdb_ajax_call()) {
+ $r['headers']['Expect'] = '';
+ }
+
+ return $r;
+ }
+
+ /*
+ * Patch wp_parse_url if it doesn't exist
+ * for compatibility with WP < 4.4
+ */
+ public static function parse_url($url)
+ {
+ if (function_exists('wp_parse_url')) {
+ return wp_parse_url($url);
+ }
+
+ $parts = @parse_url($url);
+ if (!$parts) {
+ // < PHP 5.4.7 compat, trouble with relative paths including a scheme break in the path
+ if ('/' == $url[0] && false !== strpos($url, '://')) {
+ // Since we know it's a relative path, prefix with a scheme/host placeholder and try again
+ if (!$parts = @parse_url('placeholder://placeholder' . $url)) {
+ return $parts;
+ }
+ // Remove the placeholder values
+ unset($parts['scheme'], $parts['host']);
+ } else {
+ return $parts;
+ }
+ }
+
+ // < PHP 5.4.7 compat, doesn't detect schemeless URL's host field
+ if ('//' == substr($url, 0, 2) && !isset($parts['host'])) {
+ $path_parts = explode('/', substr($parts['path'], 2), 2);
+ $parts['host'] = $path_parts[0];
+ if (isset($path_parts[1])) {
+ $parts['path'] = '/' . $path_parts[1];
+ } else {
+ unset($parts['path']);
+ }
+ }
+
+ return $parts;
+ }
+
+ /**
+ * Get the URL to MDB rest api base.
+ *
+ * WPML sometimes adds the language code in a subdirectory, this is to ensure
+ * compatibility with this plugin.
+ *
+ * @return string URL to rest_api_base, e.g. http://example.com/wp-json/mdb-api/vi
+ */
+ public function rest_url()
+ {
+ if ((is_plugin_active('sitepress-multilingual-cms/sitepress.php') || defined('ICL_SITEPRESS_VERSION')) && !empty(get_option('permalink_structure'))) {
+ return get_option('home') . '/' . rest_get_url_prefix() . '/' . $this->props->rest_api_base;
+ }
+ return get_rest_url(null, $this->props->rest_api_base);
+ }
+
+ /**
+ * Get the URL to wp-admin/admin-ajax.php for the intended WordPress site.
+ *
+ * The intended WordPress site URL is sent via Ajax, so to get a properly
+ * formatted URL to wp-admin/admin-ajax.php we can't count on the site
+ * URL being sent with a trailing slash.
+ *
+ * @return string URL to wp-admin/admin-ajax.php, e.g. http://example.com/wp-admin/admin-ajax.php
+ */
+ public function ajax_url()
+ {
+ $state_data = Persistence::getStateData();
+ $url = $state_data['url'];
+ static $ajax_url;
+
+ if (!empty($ajax_url)) {
+ return $ajax_url;
+ }
+
+ $ajax_url = trailingslashit($url) . 'wp-admin/admin-ajax.php';
+
+ return $ajax_url;
+ }
+
+ public function open_ssl_enabled()
+ {
+ if (defined('OPENSSL_VERSION_TEXT')) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function set_time_limit() {
+ if ( function_exists( 'set_time_limit' ) ) {
+ @\set_time_limit( 0 );
+ }
+ }
+
+ function display_errors()
+ {
+ $error_log = WPMDBDI::getInstance()->get(ErrorLog::class);
+ $curr_error = $error_log->getError();
+ if (!empty($curr_error)) {
+ echo $error_log->getError();
+ $error_log->setError('');
+
+ return true;
+ }
+
+ return false;
+ }
+
+ function diverse_array($vector)
+ {
+ $result = array();
+
+ foreach ($vector as $key1 => $value1) {
+ foreach ($value1 as $key2 => $value2) {
+ $result[$key2][$key1] = $value2;
+ }
+ }
+
+ return $result;
+ }
+
+ function set_time_limit_available()
+ {
+ if (!function_exists('set_time_limit') || !function_exists('ini_get')) {
+ return false;
+ }
+
+ $current_max_execution_time = ini_get('max_execution_time');
+ $proposed_max_execution_time = ($current_max_execution_time == 30) ? 31 : 30;
+ @set_time_limit($proposed_max_execution_time);
+ $current_max_execution_time = ini_get('max_execution_time');
+
+ return $proposed_max_execution_time == $current_max_execution_time;
+ }
+
+ /**
+ * Returns a url string given an associative array as per the output of parse_url.
+ *
+ * @param $parsed_url
+ *
+ * @return string
+ */
+ function unparse_url($parsed_url)
+ {
+ $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
+ $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
+ $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
+ $user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
+ $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
+ $pass = ($user || $pass) ? "$pass@" : '';
+ $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
+ $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
+ $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
+
+ return "$scheme$user$pass$host$port$path$query$fragment";
+ }
+
+ /**
+ * Get a simplified url for use as the referrer.
+ *
+ * @param $referer_url
+ *
+ * @return string
+ *
+ * NOTE: mis-spelling intentional to match usage.
+ */
+ function referer_from_url($referer_url)
+ {
+ $url_parts = self::parse_url($referer_url);
+
+ if (false !== $url_parts) {
+ $reduced_url_parts = array_intersect_key($url_parts, array_flip(array('scheme', 'host', 'port', 'path')));
+ if (!empty($reduced_url_parts)) {
+ $referer_url = $this->unparse_url($reduced_url_parts);
+ }
+ }
+
+ return $referer_url;
+ }
+
+ /**
+ * Get a simplified base url without scheme.
+ *
+ * @param string $url
+ *
+ * @return string
+ */
+ function scheme_less_url($url)
+ {
+ $url_parts = self::parse_url($url);
+
+ if (false !== $url_parts) {
+ $reduced_url_parts = array_intersect_key($url_parts, array_flip(array('host', 'port', 'path', 'user', 'pass')));
+ if (!empty($reduced_url_parts)) {
+ $url = $this->unparse_url($reduced_url_parts);
+ }
+ }
+
+ return $url;
+ }
+
+ /**
+ * Converts file paths that include mixed slashes to use the correct type of slash for the current operating system.
+ *
+ * @param $path string
+ *
+ * @return string
+ */
+ public static function slash_one_direction($path)
+ {
+ return str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
+ }
+
+ /**
+ * Returns the absolute path to the root of the website.
+ *
+ * @return string
+ */
+ public static function get_absolute_root_file_path()
+ {
+ static $absolute_path;
+
+ if (!empty($absolute_path)) {
+ return $absolute_path;
+ }
+
+ $absolute_path = rtrim(ABSPATH, '\\/');
+ $site_url = rtrim(site_url('', 'http'), '\\/');
+ $home_url = rtrim(home_url('', 'http'), '\\/');
+
+ if ($site_url != $home_url) {
+ $difference = str_replace($home_url, '', $site_url);
+ if (strpos($absolute_path, $difference) !== false) {
+ $absolute_path = rtrim(substr($absolute_path, 0, -strlen($difference)), '\\/');
+ }
+ }
+
+ return $absolute_path;
+ }
+
+ /**
+ * Returns the function name that called the function using this function.
+ *
+ * @return string
+ */
+ public function get_caller_function()
+ {
+ // phpcs:ignore
+ list(, , $caller) = debug_backtrace(false);
+
+ if (!empty($caller['function'])) {
+ $caller = $caller['function'];
+ } else {
+ $caller = '';
+ }
+
+ return $caller;
+ }
+
+ /**
+ * Returns uploads info for given subsite or primary site.
+ *
+ * @param int $blog_id Optional, defaults to primary.
+ *
+ * @return array
+ *
+ * NOTE: Must be run from primary site.
+ */
+ public function uploads_info($blog_id = 0)
+ {
+ static $primary_uploads = array();
+
+ if (!empty($blog_id) && is_multisite()) {
+ switch_to_blog($blog_id);
+ }
+
+ $uploads = wp_upload_dir();
+ $upload_dir = $uploads['basedir'];
+
+ if (!empty($blog_id) && is_multisite()) {
+ restore_current_blog();
+
+ if (empty($primary_uploads)) {
+ $primary_uploads = $this->uploads_info();
+ }
+
+ $main_uploads = $primary_uploads['basedir'];
+ $uploads['short_basedir'] = str_replace(trailingslashit($main_uploads), '', trailingslashit($upload_dir));
+
+ if (defined('UPLOADBLOGSDIR') && get_site_option('ms_files_rewriting')) {
+ // Get local upload path info from DB
+ switch_to_blog($blog_id);
+ $upload_path = get_option('upload_path');
+ if (!empty($upload_path)) {
+ $uploads['short_basedir'] = str_replace(trailingslashit(UPLOADBLOGSDIR), '', trailingslashit($upload_path));
+ }
+ restore_current_blog();
+ }
+ }
+
+ return $uploads;
+ }
+
+ /**
+ * Returns the profile value for a given key.
+ *
+ * @param string $key
+ *
+ * @param array $form_data
+ *
+ * @return mixed
+ */
+
+ /** @TODO replace with call to get from 'migration' settings directly */
+ function profile_value($key, $form_data = [])
+ {
+ if (empty($form_data)) {
+ $form_data = WPMDBDI::getInstance()->get(FormData::class)->getFormData();
+ }
+
+ if (!empty($key) && !empty($form_data) && isset($form_data[$key])) {
+ return $form_data[$key];
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a simplified site url (good for identifying subsites).
+ *
+ * @param string $site_url
+ *
+ * @return string
+ */
+ public function simple_site_url($site_url)
+ {
+ $site_url = untrailingslashit($this->scheme_less_url($site_url));
+
+ return $site_url;
+ }
+
+ /**
+ * Returns an associative array of html escaped useful information about the site.
+ * @param array $state_data
+ * @return array
+ */
+ public function site_details($state_data = [])
+ {
+ global $wpdb;
+ $table_prefix = $wpdb->base_prefix;
+ $uploads = wp_upload_dir();
+
+ $site_details = array(
+ 'is_multisite' => esc_html(is_multisite() ? 'true' : 'false'),
+ 'site_url' => esc_html(addslashes(site_url())),
+ 'home_url' => esc_html(addslashes(Util::home_url())),
+ 'prefix' => esc_html($table_prefix),
+ 'uploads_baseurl' => esc_html(addslashes(trailingslashit($uploads['baseurl']))),
+ 'uploads' => $this->uploads_info(),
+ 'uploads_dir' => esc_html(addslashes($this->get_short_uploads_dir())),
+ 'subsites' => $this->subsites_list(),
+ 'subsites_info' => $this->subsites_info(),
+ 'is_subdomain_install' => esc_html((is_multisite() && is_subdomain_install()) ? 'true' : 'false'),
+ 'high_performance_transfers' => (bool)Settings::get_setting('high_performance_transfers'),
+ 'theoreticalTransferBottleneck' => apply_filters('wpmdb_theoretical_transfer_bottleneck', 0),
+ 'firewall_plugins' => $this->get_active_firewall_plugins(),
+ 'platform' => apply_filters('wpmdb_hosting_platform', null),
+ );
+
+ $wpe_cookie = self::get_wpe_cookie();
+
+ if ( ! empty($wpe_cookie)) {
+ $site_details['wpe_cookie'] = $wpe_cookie;
+ }
+
+ $site_details = apply_filters('wpmdb_site_details', $site_details, $state_data);
+
+ return $site_details;
+ }
+
+ /**
+ * Returns an uploads dir without leading path to site.
+ *
+ * @return string
+ */
+ public function get_short_uploads_dir()
+ {
+ $short_path = str_replace(self::get_absolute_root_file_path(), '', $this->filesystem->get_upload_info('path'));
+
+ return trailingslashit(substr(str_replace('\\', '/', $short_path), 1));
+ }
+
+ /**
+ * Returns max upload size in bytes, defaults to 25M if no limits set.
+ *
+ * @return int
+ */
+ public function get_max_upload_size()
+ {
+ $bytes = wp_max_upload_size();
+
+ if (1 > (int)$bytes) {
+ $p_bytes = wp_convert_hr_to_bytes(ini_get('post_max_size'));
+ $u_bytes = wp_convert_hr_to_bytes(ini_get('upload_max_filesize'));
+
+ // If HHVM bug not returning either value, try its own settings.
+ // If HHVM not involved, will drop through to default value.
+ if (empty($p_bytes) && empty($u_bytes)) {
+ $p_bytes = wp_convert_hr_to_bytes(ini_get('hhvm.server.max_post_size'));
+ $u_bytes = wp_convert_hr_to_bytes(ini_get('hhvm.server.upload.upload_max_file_size'));
+
+ $bytes = min($p_bytes, $u_bytes);
+
+ if (0 < (int)$bytes) {
+ return $bytes;
+ }
+ }
+
+ if (0 < (int)$p_bytes) {
+ $bytes = $p_bytes;
+ } elseif (0 < (int)$u_bytes) {
+ $bytes = $u_bytes;
+ } else {
+ $bytes = wp_convert_hr_to_bytes('25M');
+ }
+ }
+
+ return $bytes;
+ }
+
+ /**
+ * Get active firewall plugins
+ *
+ * @return array
+ **/
+ protected function get_active_firewall_plugins()
+ {
+ $waf_plugins = [
+ 'wp-defender/wp-defender.php',
+ 'wordfence/wordfence.php'
+ ];
+ $local_plugins = $this->filesystem->get_local_plugins();
+ $active_waf = [];
+ foreach($local_plugins as $key=> $plugin) {
+ if(in_array($key, $waf_plugins) && true === $plugin[0]['active']) {
+ $active_waf[$key] = $plugin;
+ }
+ }
+ return $active_waf;
+ }
+
+
+ /**
+ * Extend Cache-Control header to include "no-store" so that Firefox doesn't override input selection after refresh.
+ *
+ * @param array $headers
+ *
+ * @return array
+ */
+ public function nocache_headers($headers)
+ {
+ if (is_array($headers) &&
+ key_exists('Cache-Control', $headers) &&
+ false === strpos($headers['Cache-Control'], 'no-store')
+ ) {
+ $headers['Cache-Control'] .= ', no-store';
+ }
+
+ return $headers;
+ }
+
+ public static function is_json($string, $strict = false)
+ {
+ if (!is_string($string)) {
+ return false;
+ }
+
+ $json = json_decode($string, true);
+ if ($strict === true && !is_array($json)) {
+ return false;
+ }
+
+ if ($json === null) {
+ return false;
+ }
+
+ if ($json === false) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks if the compatibility mu-plugin is installed
+ *
+ * @return bool $installed
+ */
+ public function is_muplugin_installed()
+ {
+ $plugins = wp_get_mu_plugins();
+ $muplugin_filename = basename($this->props->mu_plugin_dest);
+ $installed = false;
+
+ foreach ($plugins as $plugin) {
+ if (false !== strpos($plugin, $muplugin_filename)) {
+ $installed = true;
+ }
+ }
+
+ return $installed;
+ }
+
+
+ /**
+ *
+ * Utility function to check if the mu-plugin directory and compatibility plugin are both writable
+ *
+ *
+ * @return bool
+ */
+ public function is_muplugin_writable()
+ {
+ //Assumes by default we cannot create the mu-plugins folder and compatibility plugin if they don't exist
+ $mu_folder_writable = false;
+ $mu_plugin_writable = false;
+
+ //If the mu-plugins folder exists, make sure it's writable.
+ if (true === $this->filesystem->is_dir($this->props->mu_plugin_dir)) {
+ $mu_folder_writable = $this->filesystem->is_writable($this->props->mu_plugin_dir);
+ }
+
+ //If the mu-plugins/wp-migrate-db-pro-compatibility.php file exists, make sure it's writable.
+ if (true === $this->filesystem->file_exists($this->props->mu_plugin_dest)) {
+ $mu_plugin_writable = $this->filesystem->is_writable($this->props->mu_plugin_dest);
+ }
+
+ return true === $mu_folder_writable || true === $mu_plugin_writable;
+ }
+
+ function get_plugin_details($plugin_path, $prefix = '')
+ {
+ $plugin_data = get_plugin_data($plugin_path);
+ $plugin_name = strlen($plugin_data['Name']) ? $plugin_data['Name'] : basename($plugin_path);
+
+ if (empty($plugin_name)) {
+ return;
+ }
+
+ $version = '';
+ if ($plugin_data['Version']) {
+ $version = sprintf(" (v%s)", $plugin_data['Version']);
+ }
+
+ $author = '';
+ if ($plugin_data['AuthorName']) {
+ $author = sprintf(" by %s", $plugin_data['AuthorName']);
+ }
+
+ return sprintf("%s %s%s%s", $prefix, $plugin_name, $version, $author);
+ }
+
+ function print_plugin_details($plugin_path, $prefix = '')
+ {
+ echo $this->get_plugin_details($plugin_path, $prefix) . "\r\n";
+ }
+
+ function remove_wp_plugin_dir($name)
+ {
+ $plugin = str_replace(WP_PLUGIN_DIR, '', $name);
+
+ return substr($plugin, 1);
+ }
+
+ public static function gzip()
+ {
+ return function_exists('gzopen');
+ }
+
+ function get_path_from_url($url)
+ {
+ $parts = self::parse_url($url);
+
+ return (!empty($parts['path'])) ? trailingslashit($parts['path']) : '/';
+ }
+
+ function get_path_current_site()
+ {
+ if (!is_multisite()) {
+ return '';
+ }
+
+ $current_site = get_current_site();
+
+ return $current_site->path;
+ }
+
+ function get_short_home_address_from_url($url)
+ {
+ return untrailingslashit(str_replace(array('https://', 'http://', '//'), '', $url));
+ }
+
+
+ /**
+ * Get a plugin folder from the slug
+ *
+ * @param string $slug
+ *
+ * @return mixed
+ */
+ public function get_plugin_folder($slug)
+ {
+ if (isset($GLOBALS['wpmdb_meta'][$slug]['folder'])) {
+ return $GLOBALS['wpmdb_meta'][$slug]['folder'];
+ }
+
+ // If pre-1.1.2 version of Media Files addon, use the slug as folder name
+ return $slug;
+ }
+
+ /**
+ * Get array of subsite simple urls keyed by their ID.
+ *
+ * @return array
+ */
+ public function subsites_list()
+ {
+ $subsites = array();
+
+ if (!is_multisite()) {
+ return $subsites;
+ }
+
+
+ if (version_compare($GLOBALS['wp_version'], '4.6', '>=')) {
+ $sites = get_sites(array('number' => false));
+ } else {
+ $sites = wp_get_sites(array('limit' => 0));
+ }
+
+ if (!empty($sites)) {
+ foreach ((array)$sites as $subsite) {
+ $subsite = (array)$subsite;
+ $subsites[$subsite['blog_id']] = $this->simple_site_url(get_blogaddress_by_id($subsite['blog_id']));
+ }
+ }
+
+ return $subsites;
+ }
+
+ /**
+ * Get array of subsite info keyed by their ID.
+ *
+ * @return array
+ */
+ public function subsites_info()
+ {
+ $subsites = array();
+
+ if (!is_multisite()) {
+ return $subsites;
+ }
+
+ if (version_compare($GLOBALS['wp_version'], '4.6', '>=')) {
+ $sites = get_sites(array('number' => false));
+ } else {
+ $sites = wp_get_sites(array('limit' => 0));
+ }
+
+ if (!empty($sites)) {
+ // We to fix up the urls in uploads as they all use primary site's base!
+ $primary_url = site_url();
+
+ foreach ($sites as $subsite) {
+ $subsite = (array)$subsite;
+ $subsites[$subsite['blog_id']]['site_url'] = get_site_url($subsite['blog_id']);
+ $subsites[$subsite['blog_id']]['home_url'] = get_home_url($subsite['blog_id']);
+ $subsites[$subsite['blog_id']]['uploads'] = $this->uploads_info($subsite['blog_id']);
+
+ $subsites[$subsite['blog_id']]['uploads']['url'] = substr_replace($subsites[$subsite['blog_id']]['uploads']['url'], $subsites[$subsite['blog_id']]['site_url'], 0, strlen($primary_url));
+ $subsites[$subsite['blog_id']]['uploads']['baseurl'] = substr_replace($subsites[$subsite['blog_id']]['uploads']['baseurl'], $subsites[$subsite['blog_id']]['site_url'], 0, strlen($primary_url));
+ }
+ }
+
+ return $subsites;
+ }
+
+ // Ripped from WP Core to be used in `plugins_loaded` hook
+ public static function is_plugin_active($plugin)
+ {
+ return in_array($plugin, (array)get_option('active_plugins', array())) || self::is_plugin_active_for_network($plugin);
+ }
+
+ // Ripped from WP Core to be used in `plugins_loaded` hook
+ public static function is_plugin_active_for_network($plugin)
+ {
+ if (!is_multisite()) {
+ return false;
+ }
+
+ $plugins = get_site_option('active_sitewide_plugins');
+ if (isset($plugins[$plugin])) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function get_state_data()
+ {
+ return WPMDBDI::getInstance()->get(StateDataContainer::class)->state_data;
+ }
+
+ public static function throw_ajax_error($msg)
+ {
+ WPMDBDI::getInstance()->get(ErrorLog::class)->log_error($msg);
+
+ return wp_send_json_error($msg);
+ }
+
+ public static function validate_json($json, $assoc = true)
+ {
+ if (!is_string($json)) {
+ return false;
+ }
+ // set second parameter boolean TRUE for associative array output.
+ $result = json_decode($json, $assoc);
+
+ if (json_last_error() === JSON_ERROR_NONE) {
+ return $result;
+ }
+
+ return false;
+ }
+
+ function mask_licence($licence)
+ {
+ $licence_parts = explode('-', $licence);
+ $i = count($licence_parts) - 1;
+ $masked_licence = '';
+
+ foreach ($licence_parts as $licence_part) {
+ if ($i == 0) {
+ $masked_licence .= $licence_part;
+ continue;
+ }
+
+ $masked_licence .= str_repeat('•', strlen($licence_part)) . '–';
+ --$i;
+ }
+
+ return $masked_licence;
+ }
+
+ public static function uuidv4()
+ {
+ return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+
+ // 32 bits for "time_low"
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff),
+
+ // 16 bits for "time_mid"
+ mt_rand(0, 0xffff),
+
+ // 16 bits for "time_hi_and_version",
+ // four most significant bits holds version number 4
+ mt_rand(0, 0x0fff) | 0x4000,
+
+ // 16 bits, 8 bits for "clk_seq_hi_res",
+ // 8 bits for "clk_seq_low",
+ // two most significant bits holds zero and one for variant DCE1.1
+ mt_rand(0, 0x3fff) | 0x8000,
+
+ // 48 bits for "node"
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
+ );
+ }
+
+ public function isMDBPage()
+ {
+ $screen = get_current_screen();
+ $page_slugs = [
+ 'tools_page_wp-migrate-db',
+ 'tools_page_wp-migrate-db-pro',
+ 'settings_page_wp-migrate-db-network',
+ 'settings_page_wp-migrate-db-pro-network',
+ ];
+
+ if(is_multisite() && $screen->id === 'tools_page_wp-migrate-db-pro'){
+ return false;
+ }
+
+ if (in_array($screen->id, $page_slugs)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function formatBytes($bytes, $precision = 2)
+ {
+ $units = array('B', 'KB', 'MB', 'GB', 'TB');
+
+ $bytes = max($bytes, 0);
+ $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
+ $pow = min($pow, count($units) - 1);
+
+ $bytes /= pow(1024, $pow);
+
+ return round($bytes, $precision) . ' ' . $units[$pow];
+ }
+
+ public static function json_encode_trim($item)
+ {
+ return trim(json_encode($item), '"');
+ }
+
+ /**
+ * Checks that WordPress meets our version requirements
+ * and that React is registered.
+ *
+ * @return bool
+ */
+ public static function is_wp_compatible()
+ {
+ global $wp_version;
+
+ if (version_compare($wp_version, WPMDB_MINIMUM_WP_VERSION, '>=') && wp_script_is('react', 'registered')) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a WP admin link for MDB settings tab
+ *
+ * @return string
+ */
+ public static function settings_page_link()
+ {
+ $page = 'tools.php';
+
+ if (is_multisite()) {
+ $page = 'settings.php';
+ }
+
+ return add_query_arg(array(
+ 'page' => 'wp-migrate-db-pro#settings',
+ ), network_admin_url($page));
+ }
+
+ public static function is_regex_pattern_valid($pattern) {
+ return @preg_match($pattern, '') !== false;
+ }
+
+ /**
+ * Returns an array of table names with a new prefix.
+ *
+ * @param array $tables
+ *
+ * @param string $old_prefix
+ *
+ * @param string $new_prefix
+ *
+ * @return array
+ */
+ public static function change_tables_prefix($tables, $old_prefix, $new_prefix)
+ {
+ $new_tables = [];
+ foreach($tables as $table) {
+ $new_tables[] = self::prefix_updater($table, $old_prefix, $new_prefix);
+ }
+ return $new_tables;
+ }
+
+ /**
+ * Modifies of table name to have a new prefix.
+ *
+ * @param string $table
+ *
+ * @param string $old_prefix
+ *
+ * @param string $new_prefix
+ *
+ * @return array
+ */
+ public static function prefix_updater($prefixed, $old_prefix, $new_prefix)
+ {
+ if (substr($prefixed, 0, strlen($old_prefix)) == $old_prefix) {
+ $str = substr($prefixed, strlen($old_prefix));
+ return $new_prefix . $str;
+ }
+ return $prefixed;
+ }
+
+ /**
+ * Removes WPML home_url_filters if present.
+ *
+ * @return string
+ */
+ public static function home_url() {
+ global $wpml_url_filters;
+ if($wpml_url_filters) {
+ remove_filter('home_url', array($wpml_url_filters, 'home_url_filter'), -10, 4);
+ }
+ $home_url = home_url();
+ if($wpml_url_filters) {
+ add_filter('home_url', array($wpml_url_filters, 'home_url_filter'), -10, 4);
+ }
+ return $home_url;
+ }
+
+ public static function is_addon_registered($addon) {
+ return apply_filters('wpmdb_addon_registered_'.$addon, false);
+ }
+
+ /**
+ * Deactivates legacy addons on upgrade
+ *
+ * @return void
+ */
+ public static function disable_legacy_addons() {
+ deactivate_plugins([
+ 'wp-migrate-db-pro-media-files/wp-migrate-db-pro-media-files.php',
+ 'wp-migrate-db-pro-cli/wp-migrate-db-pro-cli.php',
+ 'wp-migrate-db-pro-multisite-tools/wp-migrate-db-pro-multisite-tools.php',
+ 'wp-migrate-db-pro-theme-plugin-files/wp-migrate-db-pro-theme-plugin-files.php',
+ ]);
+ }
+
+ /**
+ * Checks if a directory is empty
+ *
+ * @return bool
+ */
+ public static function is_empty_dir($dir)
+ {
+ $res = scandir($dir);
+ if ($res === false) {
+ return false;
+ }
+ //do not include directories with only '.' '..'
+ return count(array_diff($res, ['.', '..'])) === 0;
+ }
+
+ /**
+ * Checks if a request was initiated from a frontend page.
+ *
+ * @return bool
+ */
+ public static function is_frontend() {
+ return !(defined('WP_CLI') && WP_CLI) && !self::is_doing_mdb_rest() && !self::wpmdb_is_ajax() && !is_admin();
+ }
+
+ /**
+ * Checks if a REST request is being made to a migrate endpoint.
+ *
+ * @return bool
+ */
+ public static function is_doing_mdb_rest() {
+ $rest_endpoint = 'mdb-api';
+
+ return isset( $_SERVER['REQUEST_URI'] ) && false !== strpos( $_SERVER['REQUEST_URI'], $rest_endpoint );
+ }
+
+ /**
+ * Checks if an AJAX request is being made to a migrate endpoint.
+ *
+ * @return bool
+ */
+ public static function wpmdb_is_ajax() {
+ // must be doing AJAX the WordPress way
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
+ return false;
+ }
+
+ // must be one of our actions -- e.g. core plugin (wpmdb_*), media files (wpmdbmf_*)
+ if ( ! isset( $_POST['action'] ) || 0 !== strpos( $_POST['action'], 'wpmdb' ) ) {
+ return false;
+ }
+
+ // must be on blog #1 (first site) if multisite
+ if ( is_multisite() && 1 != get_current_site()->id ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Gets the directory for each stage
+ * Defaults to uploads dir if no match
+ *
+ * @param string $stage
+ * @return string
+ **/
+ public static function get_stage_base_dir($stage)
+ {
+ $wp_upload_dir = wp_upload_dir();
+ $dirs = [
+ 'media_files' => $wp_upload_dir['basedir'],
+ 'theme_files' => WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'themes',
+ 'themes' => WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'themes',
+ 'plugin_files' => WP_PLUGIN_DIR,
+ 'plugins' => WP_PLUGIN_DIR,
+ 'mu_plugin_files' => WPMU_PLUGIN_DIR,
+ 'muplugins' => WPMU_PLUGIN_DIR,
+ 'other_files' => WP_CONTENT_DIR,
+ 'others' => WP_CONTENT_DIR,
+ 'core_files' => ABSPATH ,
+ 'core' => ABSPATH
+ ];
+ $stage = in_array($stage, array_keys($dirs)) ? $stage : 'media_files';
+ return self::slash_one_direction($dirs[$stage]);
+ }
+
+ public static function get_wpe_cookie() {
+ if(method_exists('WpeCommon', 'get_wpe_auth_cookie_value')) {
+ return \WpeCommon::get_wpe_auth_cookie_value();
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks if the current environment is a development environment.
+ *
+ * @return bool
+ */
+ public static function is_dev_environment() {
+ return isset($_ENV['MDB_IS_DEV']) && (bool) $_ENV['MDB_IS_DEV'];
+ }
+
+ /**
+ * Create an external link for given URL.
+ *
+ * @param string $url
+ * @param string $text
+ *
+ * @return string
+ */
+ public static function external_link( $url, $text ) {
+ return sprintf( '%s', esc_url( $url ), esc_html( $text ) );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Common/Util/ZipAndEncode.php b/wp-content/plugins/wp-migrate-db-pro/class/Common/Util/ZipAndEncode.php
new file mode 100644
index 000000000..317582e5a
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Common/Util/ZipAndEncode.php
@@ -0,0 +1,41 @@
+classes[$key] = $instance;
+
+ return $instance;
+ }
+
+ //For back-compat
+ public function add($key, $instance)
+ {
+ return $this;
+ }
+
+ public function has($id)
+ {
+ if (!array_key_exists($id, $this->classes)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ //For back-compat
+ public function withArguments()
+ {
+ //NoOp
+ }
+
+ //For back-compat
+ public function register()
+ {
+ //NoOp
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Addon/Addon.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Addon/Addon.php
new file mode 100644
index 000000000..54f6908e9
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Addon/Addon.php
@@ -0,0 +1,95 @@
+download = $download;
+ }
+ public function register()
+ {
+ $api = WPMDBDI::getInstance()->get(Api::class);
+
+ $api->dbrains_api_url = $api->get_dbrains_api_base() . '/?wc-api=delicious-brains';
+
+ parent::register();
+ // Adds a custom error message to the plugin install page if required (licence expired / invalid)
+ add_filter('http_response', array($this->download, 'verify_download'), 10, 3);
+ add_filter('wpmdb_notification_strings', array($this, 'version_update_notice'));
+ }
+
+ public function version_update_notice($notifications)
+ {
+
+ $str = '';
+ // We don't want to show both the "Update Required" and "Update Available" messages at the same time
+ if (isset($this->addons[$this->props->plugin_basename]) && true === $this->is_addon_outdated($this->props->plugin_basename)) {
+ return;
+ }
+
+ // To reduce UI clutter we hide addon update notices if the core plugin has updates available
+ if (isset($this->addons[$this->props->plugin_basename])) {
+ $core_installed_version = $GLOBALS['wpmdb_meta'][$this->props->core_slug]['version'];
+ $core_latest_version = $this->get_latest_version($this->props->core_slug);
+ // Core update is available, don't show update notices for addons until core is updated
+ if (version_compare($core_installed_version, $core_latest_version, '<')) {
+ return;
+ }
+ }
+
+ $update_url = wp_nonce_url(network_admin_url('update.php?action=upgrade-plugin&plugin=' . urlencode($this->props->plugin_basename)), 'upgrade-plugin_' . $this->props->plugin_basename);
+
+ // If pre-1.1.2 version of Media Files addon, don't bother getting the versions
+ if (!isset($GLOBALS['wpmdb_meta'][$this->props->plugin_slug]['version'])) {
+ ?>
+
+
—
+ props->plugin_title, sprintf('
%s', $update_url, _x('Update Now', 'Download and install a new version of the plugin', 'wp-migrate-db'))); ?>
+
+ props->plugin_slug]['version'];
+ $latest_version = $this->get_latest_version($this->props->plugin_slug);
+
+ if (version_compare($installed_version, $latest_version, '<')) {
+ $str = \DeliciousBrains\WPMDB\Pro\Beta\BetaManager::is_beta_version($latest_version)
+ ? '' . __('Beta Update Available', 'A new version of the plugin is available', 'wp-migrate-db') . ' —'
+ : '' . __('Update Available', 'A new version of the plugin is available', 'wp-migrate-db') . ' —';
+
+ $str .= sprintf(__('%1$s %2$s is now available. You currently have %3$s installed. %5$s', 'wp-migrate-db'), $this->props->plugin_title, $latest_version, $installed_version, $update_url, __('Update Now', 'Download and install a new version of the plugin', 'wp-migrate-db'));
+ }
+ }
+
+ if (empty($str)) {
+ return $notifications;
+ }
+
+ $notifications['mdb_update-notice'] = [
+ 'message' => $str,
+ 'link' => false,
+ 'id' => 'mdb_update-notice',
+ ];
+
+ return $notifications;
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Addon/AddonsFacade.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Addon/AddonsFacade.php
new file mode 100644
index 000000000..7f531c820
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Addon/AddonsFacade.php
@@ -0,0 +1,110 @@
+license = $license;
+ parent::__construct($addons);
+ }
+
+ public function register()
+ {
+ if (false === self::$initialized) {
+ add_action('plugins_loaded', [$this, 'upgrade_routine'], PHP_INT_MAX);
+ }
+
+ parent::register();
+ }
+
+ /**
+ * Initializes registered addons
+ *
+ * @return void
+ */
+ public function initialize_addons()
+ {
+ $licensed_addons_list = $this->license->get_available_addons_list(get_current_user_id());
+ if (false === $licensed_addons_list) {
+ $this->license->check_license_status();
+ $licensed_addons_list = $this->license->get_available_addons_list(get_current_user_id());
+ }
+ $licensed_array = $licensed_addons_list ? array_keys($licensed_addons_list) : [];
+ $addons_list = array_unique(array_merge(self::GLOBAL_ADDONS, $licensed_array));
+ if (is_array($addons_list)) {
+ foreach ($this->addons as $addon) {
+ if (in_array($addon->get_license_response_key(), $addons_list)) {
+ $licensed = in_array($addon->get_license_response_key(), $licensed_array);
+ $addon->register($licensed);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Deactivates legacy addons on upgrade
+ *
+ * @return void
+ */
+ public static function disable_legacy_addons()
+ {
+ Util::disable_legacy_addons();
+ }
+
+ /**
+ * Prevents legacy addons from being activated
+ *
+ * @return void
+ */
+ public function prevent_legacy_addon_activation($plugin)
+ {
+ if (in_array($plugin, self::LEGACY_ADDONS)) {
+ $redirect = self_admin_url('plugins.php?legacyaddon=1');
+ wp_redirect($redirect);
+ exit;
+ }
+ }
+
+ /**
+ * Notice when trying to activate addon
+ *
+ * @return void
+ */
+ public function legacy_addon_notice()
+ {
+ if (isset($_GET['legacyaddon'])) {
+ $message = __('Legacy addons cannot be activated alongside WP Migrate version 2.3.0 or above. These features have been moved to WP Migrate.', 'wp-migrate-db');
+ echo '';
+ }
+ }
+
+ /**
+ * Executes upgrade routines for the addons
+ *
+ * @return void
+ */
+ public function upgrade_routine()
+ {
+ $addons_schema_version = get_option($this->addons_schema_option, 0);
+ if ((int)$addons_schema_version !== $this->current_schema_version) {
+ $this->license->check_licence($this->license->get_licence_key());
+ update_option($this->addons_schema_option, $this->current_schema_version);
+ }
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Api.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Api.php
new file mode 100644
index 000000000..fc867b044
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Api.php
@@ -0,0 +1,223 @@
+util = $util;
+ $this->props = $properties;
+ $this->settings = $settings->get_settings();
+ $this->error_log = $error_log;
+ $this->dbrains_api_url = self::$api_url = $this->get_dbrains_api_base() . '/?wc-api=delicious-brains';
+ $this->usage_tracking = $usage_tracking;
+ }
+
+ public static function get_api_url() {
+ return self::$api_url;
+ }
+
+ function get_dbrains_api_url( $request, $args = array() ) {
+ $url = $this->dbrains_api_url;
+ $args['request'] = $request;
+ $args['version'] = $GLOBALS['wpmdb_meta'][ $this->props->core_slug ]['version'];
+ $args['php_version'] = urlencode( PHP_VERSION );
+ $args['locale'] = urlencode( get_locale() );
+ $args['wordpress_version'] = urlencode( get_bloginfo( 'version' ) );
+
+ if ( 'check_support_access' === $request || 'activate_licence' === $request ) {
+ //@TODO refactor usage of Container here
+ $args['last_used'] = urlencode( $this->usage_tracking->get_last_usage_time() );
+ }
+
+ $url = add_query_arg( $args, $url );
+ if ( false !== get_site_transient( 'wpmdb_temporarily_disable_ssl' ) && 0 === strpos( $this->dbrains_api_url, 'https://' ) ) {
+ $url = substr_replace( $url, 'http', 0, 5 );
+ }
+
+ return $url;
+ }
+
+ /**
+ * Main function for communicating with the Delicious Brains API.
+ *
+ * @param string $request
+ * @param array $args
+ *
+ * @return mixed
+ */
+ function dbrains_api_request( $request, $args = array() ) {
+ $trans = get_site_transient( 'wpmdb_dbrains_api_down' );
+
+ if ( false !== $trans ) {
+ $api_down_message = sprintf( '%s
', $trans );
+
+ return json_encode( array( 'dbrains_api_down' => $api_down_message ) );
+ }
+
+ $sslverify = ( $this->settings['verify_ssl'] == 1 ? true : false );
+
+ $url = $this->get_dbrains_api_url( $request, $args );
+ $response = wp_remote_get(
+ $url,
+ array(
+ 'timeout' => 30,
+ 'blocking' => true,
+ 'sslverify' => $sslverify,
+ )
+ );
+
+ if ( is_wp_error( $response ) || (int) $response['response']['code'] < 200 || (int) $response['response']['code'] > 399 ) {
+ $this->error_log->log_error( print_r( $response, true ) );
+
+ if ( true === $this->dbrains_api_down() ) {
+ $trans = get_site_transient( 'wpmdb_dbrains_api_down' );
+
+ if ( false !== $trans ) {
+ $api_down_message = sprintf( '%s
', $trans );
+
+ return json_encode( array( 'dbrains_api_down' => $api_down_message ) );
+ }
+ }
+
+ $disable_ssl_url = network_admin_url( $this->props->plugin_base . '&nonce=' . Util::create_nonce( 'wpmdb-disable-ssl' ) . '&wpmdb-disable-ssl=1' );
+ $connection_failed_message = '';
+ $connection_failed_message .= sprintf( __( '
Could not connect to api.deliciousbrains.com — You will not receive update notifications or be able to activate your license until this is fixed. This issue is often caused by an improperly configured SSL server (https). We recommend
fixing the SSL configuration on your server, but if you need a quick fix you can:%2$s', 'wp-migrate-db' ), 'https://deliciousbrains.com/wp-migrate-db-pro/doc/could-not-connect-deliciousbrains-com/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin', sprintf( '
%2$s
', $disable_ssl_url, __( 'Temporarily disable SSL for connections to api.deliciousbrains.com', 'wp-migrate-db' ) ) );
+ $connection_failed_message .= '
';
+
+ if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL ) {
+ $url_parts = Util::parse_url( $url );
+ $host = $url_parts['host'];
+ if ( ! defined( 'WP_ACCESSIBLE_HOSTS' ) || strpos( WP_ACCESSIBLE_HOSTS, $host ) === false ) {
+ $connection_failed_message = '';
+ $connection_failed_message .= sprintf( __( 'We\'ve detected that
WP_HTTP_BLOCK_EXTERNAL
is enabled and the host
%1$s has not been added to
WP_ACCESSIBLE_HOSTS
. Please disable
WP_HTTP_BLOCK_EXTERNAL
or add
%1$s to
WP_ACCESSIBLE_HOSTS
to continue.
More information.', 'wp-migrate-db' ), esc_attr( $host ), 'https://deliciousbrains.com/wp-migrate-db-pro/doc/wp_http_block_external/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin' );
+ $connection_failed_message .= '
';
+ }
+ }
+
+ // Don't cache the license response so we can try again
+ delete_site_transient( Helpers::get_licence_response_transient_key() );
+
+ return json_encode( array( 'errors' => array( 'connection_failed' => $connection_failed_message ) ) );
+ }
+
+ return $response['body'];
+ }
+
+ /**
+ * Is the Delicious Brains API down?
+ *
+ * If not available then a 'wpmdb_dbrains_api_down' transient will be set with an appropriate message.
+ *
+ * @return bool
+ */
+ function dbrains_api_down() {
+ if ( false !== get_site_transient( 'wpmdb_dbrains_api_down' ) ) {
+ return true;
+ }
+
+ $response = wp_remote_get( $this->props->dbrains_api_status_url, array( 'timeout' => 30 ) );
+
+ // Can't get to api status url so fall back to normal failure handling.
+ if ( is_wp_error( $response ) || 200 !== (int) $response['response']['code'] || empty( $response['body'] ) ) {
+ return false;
+ }
+
+ $json = json_decode( $response['body'], true );
+
+ // Can't decode json so fall back to normal failure handling.
+ if ( ! $json ) {
+ return false;
+ }
+
+ // JSON doesn't seem to have the format we expect or is not down, so fall back to normal failure handling.
+ if ( ! isset( $json['api']['status'] ) || 'down' !== $json['api']['status'] ) {
+ return false;
+ }
+
+ $message = __( "Delicious Brains API is Down — Unfortunately we're experiencing some problems with our server.", 'wp-migrate-db' );
+
+ if ( ! empty( $json['api']['updated'] ) ) {
+ $updated = $json['api']['updated'];
+ $updated_ago = sprintf( _x( '%s ago', 'ex. 2 hours ago', 'wp-migrate-db' ), human_time_diff( strtotime( $updated ) ) );
+ }
+
+ if ( ! empty( $json['api']['message'] ) ) {
+ $message .= '
';
+ $message .= __( "Here's the most recent update on its status", 'wp-migrate-db' );
+ if ( ! empty( $updated_ago ) ) {
+ $message .= ' (' . $updated_ago . ')';
+ }
+ $message .= ': ' . $json['api']['message'] . '';
+ }
+
+ set_site_transient( 'wpmdb_dbrains_api_down', $message, $this->props->transient_retry_timeout );
+
+ return true;
+ }
+
+ public function get_dbrains_api_base() {
+ $dbrains_api_base = self::DBRAINS_API_BASE;
+
+ if ( defined( 'DBRAINS_API_BASE' ) ) {
+ $dbrains_api_base = DBRAINS_API_BASE;
+ }
+
+ if ( false === $this->util->open_ssl_enabled() ) {
+ $dbrains_api_base = str_replace( 'https://', 'http://', $dbrains_api_base );
+ }
+
+ return $dbrains_api_base;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Backups/BackupsManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Backups/BackupsManager.php
new file mode 100644
index 000000000..55bb186b1
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Backups/BackupsManager.php
@@ -0,0 +1,160 @@
+http_helper = $http_helper;
+ $this->filesystem = $filesystem;
+ $this->rest_API_server = $rest_API_server;
+ }
+
+ public function register()
+ {
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ add_action('admin_init', [$this, 'trigger_download']);
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute(
+ '/get-backups',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_get_backups'],
+ ]
+ );
+ $this->rest_API_server->registerRestRoute(
+ '/get-backup',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_get_backup'],
+ ]
+ );
+ $this->rest_API_server->registerRestRoute(
+ '/delete-backup',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_delete_backup'],
+ ]
+ );
+ }
+
+ public function trigger_download()
+ {
+ if (!isset($_GET['wpmdb-download-backup'])) {
+ return false;
+ }
+
+ $backup = filter_input(INPUT_GET, 'wpmdb-download-backup', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
+ $is_compressed = (bool) filter_input(INPUT_GET, 'wpmdb-compressed-backup', FILTER_VALIDATE_BOOLEAN);
+ if (empty($backup)) {
+ wp_die(__('Backup not found.', 'wp-migrate-db'));
+ }
+
+ $this->download_backup($backup, $is_compressed);
+ }
+
+ public function download_backup($backup, $is_compressed = false)
+ {
+ $backup_dir = $this->filesystem->get_upload_info('path') . DIRECTORY_SEPARATOR;
+ $ext = ($is_compressed ? '.sql.gz' : '.sql');
+ $diskfile = $backup_dir . $backup;
+ $diskfile .= $ext;
+
+ if (!file_exists($diskfile)) {
+ wp_die(__('Could not find backup file to download:', 'wp-migrate-db') . '
' . esc_html($diskfile));
+ }
+
+ header('Content-Description: File Transfer');
+ header('Content-Type: application/octet-stream');
+ header('Content-Length: ' . $this->filesystem->filesize($diskfile));
+ header('Content-Disposition: attachment; filename=' . $backup . $ext);
+ readfile($diskfile);
+ exit;
+ }
+
+ public function ajax_get_backups()
+ {
+ $this->http_helper->convert_json_body_to_post();
+ $backups = $this->filesystem->get_backups();
+ $backups = is_array($backups) ? $backups : array();
+ wp_send_json_success($backups);
+ }
+
+ public function ajax_get_backup()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $path = isset($_POST['path']) ? sanitize_file_name($_POST['path']) : '';
+ $is_compressed = isset($_POST['isCompressed']) ? (bool) $_POST['isCompressed'] : false;
+ $backup_dir = $this->filesystem->get_upload_info('path') . DIRECTORY_SEPARATOR;
+ $file_path = $backup_dir . $path . ($is_compressed ? '.sql.gz' : '.sql');
+
+ if (!file_exists($file_path)) {
+ $error = sprintf(__('File does not exist — %s', 'wp-migrate-db'), $file_path);
+ wp_send_json_error($error);
+ }
+
+ $redirect_query = [
+ 'page' => 'wp-migrate-db-pro',
+ 'wpmdb-download-backup' => $path,
+ 'wpmdb-compressed-backup' => $is_compressed
+ ];
+
+ $path = is_multisite() ? 'settings.php' : 'tools.php';
+ $redirect = add_query_arg($redirect_query, network_admin_url($path));
+
+ wp_send_json_success(['redirect' => $redirect]);
+ }
+
+ public function ajax_delete_backup()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $path = isset($_POST['path']) ? sanitize_file_name($_POST['path']) : '';
+ $is_compressed = isset($_POST['isCompressed']) ? (bool) $_POST['isCompressed'] : false;
+ $backup_dir = $this->filesystem->get_upload_info('path') . DIRECTORY_SEPARATOR;
+ $file_path = $backup_dir . $path . ($is_compressed ? '.sql.gz' : '.sql');
+ if (!file_exists($file_path)) {
+ $error = sprintf(__('File does not exist — %s', 'wp-migrate-db'), $file_path);
+ wp_send_json_error($error);
+ }
+
+ $deleted = $this->filesystem->unlink($file_path);
+
+ if (!$deleted) {
+ $error = sprintf(__('Unable to delete file — %s', 'wp-migrate-db'), $file_path);
+ wp_send_json_error($error);
+ }
+
+ wp_send_json_success();
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Beta/BetaManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Beta/BetaManager.php
new file mode 100644
index 000000000..2f61875f2
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Beta/BetaManager.php
@@ -0,0 +1,490 @@
+util = $util;
+ $this->props = $properties;
+ $this->addon = $addon;
+ $this->api = $api;
+ $this->settings = $settings->get_settings();
+ $this->template = $template;
+ $this->download = $download;
+
+ // Hack to access settings in static method
+ self::$static_settings = $this->settings;
+ }
+
+ /**
+ * Register action and filter hooks
+ */
+ public function register()
+ {
+ add_filter('wpmdb_js_strings', array($this, 'add_js_strings'));
+ add_filter('wpmdb_data', array($this, 'add_js_data'));
+ add_action('admin_init', array($this, 'handle_redirect_to_rollback_url'));
+ add_filter('admin_init', array($this, 'handle_maybe_set_rolling_back_flag'));
+ add_filter('site_transient_update_plugins', array($this, 'maybe_inject_stable_version_plugin_data'), 11);
+ add_action('shutdown', array($this, 'handle_maybe_clear_rolling_back_flag'));
+
+ if (self::is_beta_version($this->props->plugin_version) && !empty(get_option('permalink_structure'))) {
+ add_filter('wpmdb_notification_strings', array($this, 'template_beta_feedback_ask'));
+ }
+
+ add_action('wpmdb_before_schema_update', [$this, 'schema_update']);
+ }
+
+ /**
+ * Add to the strings passed to the JS.
+ *
+ * @param array $strings
+ *
+ * @return array
+ */
+ public function add_js_strings($strings)
+ {
+ $strings['rollback_beta_to_stable'] = __('Would you like to rollback WP Migrate to the latest stable release now?', 'wp-migrate-db');
+
+ return $strings;
+ }
+
+ /**
+ * Add JS object data for plugin rollback.
+ *
+ * @param array $data
+ *
+ * @return array
+ */
+ public function add_js_data($data)
+ {
+ $beta_plugins = $this->get_installed_beta_plugins();
+
+ $data['is_beta_plugins_installed'] = !empty($beta_plugins);
+ $data['rollback_to_stable_url'] = $this->get_redirect_to_rollback_url();
+
+ return $data;
+ }
+
+ /**
+ * Get an array of beta plugins installed
+ *
+ * @return array Associative array of string basenames.
+ */
+ protected function get_installed_beta_plugins()
+ {
+ $plugins = get_plugins();
+ $beta_plugins = array();
+ foreach ($plugins as $plugin => $data) {
+ if ($plugin !== $this->get_plugin_basename() && !in_array($plugin, $this->get_addon_basenames())) {
+ continue;
+ }
+
+ if (self::is_beta_version($data['Version'])) {
+ $beta_plugins[] = $plugin;
+ }
+ }
+
+ return $beta_plugins;
+ }
+
+ /**
+ * Is the rollback process in motion?
+ *
+ * @return bool
+ */
+ public static function is_rolling_back_plugins()
+ {
+ if (!isset($_GET['action']) || 'update-selected' !== $_GET['action']) {
+ return false;
+ }
+
+ return self::is_rolling_back_flag_set();
+ }
+
+ /**
+ * Is the rollback process flag set?
+ *
+ * @return bool
+ */
+ protected static function is_rolling_back_flag_set()
+ {
+ return (bool)get_user_meta(get_current_user_id(), 'wpmdb-rollback-beta-plugins', true);
+ }
+
+ /**
+ * Set a flag against the user so we know we are processing the rollback.
+ * We need this level of persistence as we can't send query args across the internal WP update URLs
+ * as the update mechanism uses an iframe and doesn't have friendly hooks.
+ */
+ public function handle_maybe_set_rolling_back_flag()
+ {
+ if (!isset($_GET['action']) || 'do-plugin-upgrade' !== $_GET['action']) {
+ return;
+ }
+
+ if (!isset($_GET['wpmdb-latest-version'])) {
+ return;
+ }
+
+ update_user_meta(get_current_user_id(), 'wpmdb-rollback-beta-plugins', true);
+ }
+
+ /**
+ * Clear the rolling back flag on shutdown for the WP core update request.
+ */
+ public function handle_maybe_clear_rolling_back_flag()
+ {
+ if (!isset($_GET['action']) || 'update-selected' !== $_GET['action']) {
+ return;
+ }
+
+ delete_user_meta(get_current_user_id(), 'wpmdb-rollback-beta-plugins');
+ }
+
+ /**
+ * Generate URL to kick off the process of rolling back plugins.
+ * This extra redirect is needed so the plugins in scope will always be current at the time of clicking the link.
+ *
+ * @return string
+ */
+ protected function get_redirect_to_rollback_url()
+ {
+ $page = 'tools.php';
+
+ if (is_multisite()) {
+ $page = 'settings.php';
+ }
+
+ $url = add_query_arg(array(
+ 'page' => 'wp-migrate-db-pro',
+ 'wpmdb-rollback' => 1,
+ ), network_admin_url($page));
+
+ return wp_nonce_url($url, 'wpmdb-beta-rollback-redirect');
+ }
+
+ /**
+ * Respond to the rollback redirection and pass off to the WordPress core plugin upgrade screen.
+ */
+ public function handle_redirect_to_rollback_url()
+ {
+ if (!isset($_GET['page']) || 'wp-migrate-db-pro' !== $_GET['page']) {
+ return;
+ }
+
+ if (!isset($_GET['wpmdb-rollback']) || 1 !== (int)$_GET['wpmdb-rollback']) {
+ return;
+ }
+
+ check_admin_referer('wpmdb-beta-rollback-redirect');
+
+ $url = $this->get_bulk_plugins_upgrade_url();
+
+ if (!$url) {
+ return;
+ }
+
+ wp_redirect($url);
+ exit;
+ }
+
+ /**
+ * Generate a URL to the WordPress updates page with the beta plugins needed to be rolled back.
+ *
+ * @return bool|string
+ */
+ protected function get_bulk_plugins_upgrade_url()
+ {
+ // get all beta plugins
+ $plugins = $this->get_installed_beta_plugins();
+
+ if (empty($plugins)) {
+ return false;
+ }
+
+ $url = add_query_arg(array(
+ 'action' => 'do-plugin-upgrade',
+ 'plugins' => htmlentities(implode(',', $plugins)),
+ 'wpmdb-latest-version' => 1,
+ ), network_admin_url('update-core.php'));
+
+ $url = add_query_arg('_wpnonce', wp_create_nonce('upgrade-core'), $url);
+
+ return $url;
+ }
+
+ /**
+ * Return stable versions of installed beta plugins
+ * if we are doing a bulk update of plugins for a rollback
+ *
+ * @param object $trans
+ *
+ * @return object
+ */
+ public function maybe_inject_stable_version_plugin_data($trans)
+ {
+ $is_mdb_plugin = false;
+
+ if ( isset( $trans->response ) ) {
+ $keys = array_keys( $trans->response );
+
+ foreach ( $keys as $key ) {
+ if ( stripos( $key, 'wp-migrate-db-pro' ) !== false ) {
+ $is_mdb_plugin = true;
+ break;
+ }
+ }
+ }
+
+ if ( !isset( $_GET['action'] )
+ || 'update-selected' !== $_GET['action']
+ || $is_mdb_plugin
+ ) {
+ return $trans;
+ }
+ $stack = debug_backtrace();
+
+ if (!Util::has_method_been_called('bulk_upgrade', $stack)) {
+ return $trans;
+ }
+
+ if ((Util::has_method_been_called('wp_update_plugins', $stack) || Util::has_method_been_called('wp_get_translation_updates', $stack)) && self::is_rolling_back_flag_set()) {
+ return $trans;
+ }
+
+ if (did_action('upgrader_process_complete')) {
+ return $trans;
+ }
+
+ $beta_plugins = $this->get_installed_beta_plugins();
+
+ return $this->inject_stable_version_plugin_data($trans, $beta_plugins);
+ }
+
+ /**
+ * Inject the stable versions of specific plugins to the 'update_plugins' transient
+ *
+ * @param object $trans
+ *
+ * @return object
+ */
+ protected function inject_stable_version_plugin_data($trans, $plugins)
+ {
+ $plugin_upgrade_data = $this->addon->get_upgrade_data();
+
+ if (false === $plugin_upgrade_data || !isset($plugin_upgrade_data['wp-migrate-db-pro'])) {
+ return $trans;
+ }
+
+ foreach ($plugin_upgrade_data as $slug => $upgrade_data) {
+ $plugin_folder = $this->util->get_plugin_folder($slug);
+ $plugin_basename = sprintf('%s/%s.php', $plugin_folder, $slug);
+
+ if (!in_array($plugin_basename, $plugins)) {
+ // We don't need to rollback this plugin
+ continue;
+ }
+
+ if (!isset($plugin_upgrade_data[$slug]['version']) || empty($plugin_upgrade_data[$slug]['version'])) {
+ // Plugin doesn't have a stable version to roll back to
+ continue;
+ }
+
+ $trans->response[$plugin_basename] = new \stdClass();
+ $trans->response[$plugin_basename]->url = $this->api->get_dbrains_api_base();
+ $trans->response[$plugin_basename]->slug = $slug;
+ $trans->response[$plugin_basename]->package = $this->download->get_plugin_update_download_url($slug);
+ $trans->response[$plugin_basename]->new_version = $plugin_upgrade_data[$slug]['version'];
+ $trans->response[$plugin_basename]->id = '0';
+ $trans->response[$plugin_basename]->plugin = $plugin_basename;
+ }
+
+ return $trans;
+ }
+
+ /**
+ * Register the beta feedback notice
+ */
+ public function template_beta_feedback_ask($templates)
+ {
+ global $current_user;
+
+ // allow rerun for each new beta version
+ $version_slug = str_replace('.', '', $this->props->plugin_version);
+
+ $welcome_notice_name = 'beta_welcome' . $version_slug;
+ $reminder_notice_name = 'beta_feedback_reminder_' . $version_slug;
+
+ $welcome_links = $this->template->notice->check_notice($welcome_notice_name, 'SHOW_ONCE');
+ // welcome notice
+ if ($welcome_links) {
+ $templates[$welcome_notice_name] = [
+ 'message' => $this->template->template_to_string('beta-welcome', 'pro', $welcome_links),
+ 'link' => $welcome_links,
+ 'id' => $welcome_notice_name,
+ ];
+
+ // Add 5-day sleep for reminder notification
+ $reminder_key = 'wpmdb_reminder_' . $reminder_notice_name;
+ if (!get_user_meta($current_user->ID, $reminder_key) && !get_user_meta($current_user->ID, 'wpmdb_dismiss_' . $reminder_notice_name)) {
+ update_user_meta($current_user->ID, $reminder_key, time() + (DAY_IN_SECONDS * 10));
+ }
+ }
+
+ $reminder_links = $this->template->notice->check_notice($reminder_notice_name, true, (DAY_IN_SECONDS * 10));
+
+ // reminder notice
+ if ($reminder_links) {
+ $templates[$reminder_notice_name] = [
+ 'message' => $this->template->template_to_string('beta-feedback-reminder', 'pro', $reminder_links),
+ 'link' => $reminder_links,
+ 'id' => $reminder_notice_name,
+ ];
+ }
+
+ return $templates;
+ }
+
+ /**
+ * Is the version a beta version?
+ *
+ * @param string $ver
+ *
+ * @return bool
+ */
+ public static function is_beta_version($ver)
+ {
+ if (preg_match('@b[0-9]+$@', $ver)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Has tbe beta optin been turned on?
+ *
+ * @return bool
+ */
+ public static function has_beta_optin($settings)
+ {
+ if (!isset($settings['beta_optin'])) {
+ return false;
+ }
+
+ return (bool)$settings['beta_optin'];
+ }
+
+ /**
+ * Sets the value of the beta optin setting
+ *
+ * @param bool $value
+ */
+ public static function set_beta_optin($value = true)
+ {
+ self::$static_settings['beta_optin'] = $value;
+ update_site_option('wpmdb_settings', self::$static_settings);
+ }
+
+ public function schema_update($schema_version)
+ {
+ if ($schema_version >= 2) {
+ return;
+ }
+
+ if (self::is_beta_version($this->props->plugin_version)) {
+ // If the current installed version is a beta version then turn on the beta optin
+ self::set_beta_optin();
+ // Dismiss the notice also, so it won't keep coming back
+ update_user_meta(get_current_user_id(), 'wpmdb_dismiss_beta_optin', true);
+ }
+ }
+
+ /**
+ * Get an array of the addon basenames
+ *
+ * @return array
+ */
+ public function get_addon_basenames()
+ {
+ return array_keys($this->addon->getAddons());
+ }
+
+ /**
+ * Get basename of the plugin
+ */
+ public function get_plugin_basename()
+ {
+ return $this->props->plugin_basename;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/ClassMap.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/ClassMap.php
new file mode 100644
index 000000000..3e4012f04
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/ClassMap.php
@@ -0,0 +1,643 @@
+import = new Import(
+ $this->http,
+ $this->migration_state_manager,
+ $this->error_log,
+ $this->filesystem,
+ $this->backup_export,
+ $this->table,
+ $this->form_data,
+ $this->properties,
+ $this->WPMDBRestAPIServer,
+ $this->http_helper
+ );
+
+ $this->download = new Download(
+ $this->properties,
+ $this->settings
+ );
+
+ $this->addon = new Addon(
+ $this->error_log,
+ $this->settings,
+ $this->properties,
+ $this->download
+ );
+
+ $this->common_flush = new \DeliciousBrains\WPMDB\Common\Migration\Flush($this->http_helper, $this->util, $this->remote_post, $this->http);
+ $this->flush = new Flush($this->http_helper, $this->util, $this->remote_post, $this->http);
+
+ $this->upgrade_routines_manager = new UpgradeRoutinesManager($this->assets, $this->profile_manager);
+
+ $this->pro_plugin_manager = new ProPluginManager(
+ $this->settings,
+ $this->assets,
+ $this->util,
+ $this->table,
+ $this->http,
+ $this->filesystem,
+ $this->multisite,
+ $this->addon,
+ $this->download,
+ $this->properties,
+ $this->migration_helper,
+ $this->WPMDBRestAPIServer,
+ $this->http_helper,
+ $this->template_base,
+ $this->notice,
+ $this->profile_manager,
+ $this->upgrade_routines_manager
+ );
+
+ $this->template = new Template(
+ $this->settings,
+ $this->util,
+ $this->profile_manager,
+ $this->filesystem,
+ $this->table,
+ $this->notice,
+ $this->form_data,
+ $this->addon,
+ $this->properties,
+ $this->pro_plugin_manager
+ );
+
+ $this->hosting_platform = new Platforms();
+
+ $this->usage_tracking = new UsageTracking(
+ $this->settings,
+ $this->filesystem,
+ $this->error_log,
+ $this->template,
+ $this->form_data,
+ $this->state_data_container,
+ $this->properties,
+ $this->migration_state_manager,
+ $this->http,
+ $this->http_helper,
+ $this->WPMDBRestAPIServer
+ );
+
+ $this->logger = new Logger();
+
+ $this->api = new Api(
+ $this->util,
+ $this->settings,
+ $this->error_log,
+ $this->properties,
+ $this->usage_tracking
+ );
+
+ $this->license = new License(
+ $this->api,
+ $this->settings,
+ $this->util,
+ $this->migration_state_manager,
+ $this->download,
+ $this->http,
+ $this->error_log,
+ $this->http_helper,
+ $this->scrambler,
+ $this->remote_post,
+ $this->properties,
+ $this->WPMDBRestAPIServer
+ );
+
+ $this->beta_manager = new BetaManager(
+ $this->util,
+ $this->addon,
+ $this->api,
+ $this->settings,
+ $this->template,
+ $this->download,
+ $this->properties
+ );
+ $this->local_connection = new Connection\Local(
+ $this->http,
+ $this->http_helper,
+ $this->properties,
+ $this->license,
+ $this->remote_post,
+ $this->util,
+ $this->WPMDBRestAPIServer
+ );
+
+ $this->remote_connection = new Connection\Remote(
+ $this->scrambler,
+ $this->http,
+ $this->http_helper,
+ $this->properties,
+ $this->error_log,
+ $this->license,
+ $this->remote_post,
+ $this->util,
+ $this->table,
+ $this->form_data,
+ $this->settings,
+ $this->filesystem,
+ $this->multisite,
+ $this->table_helper,
+ $this->backup_export
+ );
+
+ $this->local_tables = new Local();
+
+ $this->finalize_complete = new FinalizeComplete(
+ $this->scrambler,
+ $this->migration_state_manager,
+ $this->http,
+ $this->http_helper,
+ $this->properties,
+ $this->error_log,
+ $this->migration_manager,
+ $this->form_data,
+ $this->finalize_migration,
+ $this->settings,
+ $this->WPMDBRestAPIServer,
+ $this->flush
+ );
+
+ $this->remote_tables = new Remote(
+ $this->scrambler,
+ $this->settings,
+ $this->migration_state_manager,
+ $this->http,
+ $this->http_helper,
+ $this->table_helper,
+ $this->error_log,
+ $this->properties,
+ $this->form_data,
+ $this->migration_manager,
+ $this->table,
+ $this->backup_export,
+ $this->finalize_complete,
+ $this->WPMDBRestAPIServer
+ );
+
+ $this->cli_export = new Export(
+ $this->form_data,
+ $this->util,
+ $this->cli_manager,
+ $this->table,
+ $this->error_log,
+ $this->initiate_migration,
+ $this->finalize_migration,
+ $this->http_helper,
+ $this->migration_manager,
+ $this->migration_state_manager
+ );
+
+ $this->backups_manager = new BackupsManager(
+ $this->http_helper,
+ $this->filesystem,
+ $this->WPMDBRestAPIServer
+ );
+
+ $this->remote_updates_manager = new RemoteUpdatesManager(
+ $this->http_helper,
+ $this->http,
+ $this->remote_post,
+ $this->WPMDBRestAPIServer,
+ $this->migration_state_manager,
+ $this->properties,
+ $this->settings,
+ $this->util,
+ $this->license
+ );
+
+ // Transfers classes
+
+ $this->transfers_util = new Util(
+ $this->filesystem,
+ $this->http,
+ $this->error_log,
+ $this->http_helper,
+ $this->remote_post,
+ $this->settings,
+ $this->migration_state_manager,
+ $this->util
+ );
+
+ $this->transfers_chunker = new Chunker(
+ $this->transfers_util
+ );
+
+ $this->transfers_payload = new Payload(
+ $this->transfers_util,
+ $this->transfers_chunker,
+ $this->filesystem,
+ $this->http,
+ $this->util
+ );
+
+ $this->transfers_receiver = new Receiver(
+ $this->transfers_util,
+ $this->transfers_payload,
+ $this->settings,
+ $this->error_log,
+ $this->filesystem
+ );
+
+ $this->transfers_sender = new Sender(
+ $this->transfers_util,
+ $this->transfers_payload
+ );
+
+ $this->transfers_excludes = new Excludes();
+
+ $this->queue_manager = new Manager(
+ $this->properties,
+ $this->state_data_container,
+ $this->migration_state_manager,
+ $this->form_data
+ );
+
+ $this->incremental_size_controller = new IncrementalSizeController();
+
+ $this->transfers_manager = new TransferManager(
+ $this->queue_manager,
+ $this->transfers_payload,
+ $this->transfers_util,
+ $this->incremental_size_controller,
+ $this->http_helper,
+ $this->http,
+ $this->transfers_receiver,
+ $this->transfers_sender,
+ $this->full_site_export
+ );
+
+ $this->recursive_scanner = new RecursiveScanner($this->filesystem, $this->transfers_util);
+
+ $this->transfers_file_processor = new FileProcessor(
+ $this->filesystem,
+ $this->http,
+ $this->recursive_scanner
+ );
+
+ $this->transfers_plugin_helper = new PluginHelper(
+ $this->filesystem,
+ $this->properties,
+ $this->http,
+ $this->http_helper,
+ $this->settings,
+ $this->migration_state_manager,
+ $this->scrambler,
+ $this->transfers_file_processor,
+ $this->transfers_util,
+ $this->queue_manager,
+ $this->queue_manager,
+ $this->state_data_container,
+ $this->transfers_sender,
+ $this->transfers_receiver
+ );
+
+ $this->transfers_queue_helper = new QueueHelper(
+ $this->filesystem,
+ $this->http,
+ $this->http_helper,
+ $this->transfers_util,
+ $this->queue_manager,
+ $this->util
+ );
+
+ /* Start MF Section */
+
+
+ $this->media_files_addon_local = new MediaFilesLocal(
+ $this->form_data,
+ $this->http,
+ $this->util,
+ $this->http_helper,
+ $this->WPMDBRestAPIServer,
+ $this->transfers_manager,
+ $this->transfers_util,
+ $this->transfers_file_processor,
+ $this->transfers_queue_helper,
+ $this->queue_manager,
+ $this->transfers_plugin_helper,
+ $this->profile_manager
+ );
+
+ $this->media_files_addon_remote = new MediaFilesRemote(
+ $this->transfers_plugin_helper
+ );
+
+ $this->media_files_cli = new MediaFilesCli(
+ $this->addon,
+ $this->properties,
+ $this->cli,
+ $this->cli_manager,
+ $this->util,
+ $this->state_data_container,
+ $this->transfers_util,
+ $this->filesystem
+ );
+
+ $this->media_files_manager = new MF_Manager();
+ /* End MF Section */
+
+ /* Start TPF Section */
+
+ $this->tp_addon_transfer_check = new TransferCheck(
+ $this->form_data,
+ $this->http,
+ $this->error_log
+ );
+
+ $this->tp_addon_local = new ThemePluginFilesLocal(
+ $this->transfers_util,
+ $this->util,
+ $this->transfers_file_processor,
+ $this->queue_manager,
+ $this->transfers_manager,
+ $this->migration_state_manager,
+ $this->http,
+ $this->filesystem,
+ $this->tp_addon_transfer_check,
+ $this->WPMDBRestAPIServer,
+ $this->http_helper,
+ $this->transfers_queue_helper
+ );
+
+ $this->tp_addon_remote = new ThemePluginFilesRemote(
+ $this->transfers_util,
+ $this->transfers_file_processor,
+ $this->queue_manager,
+ $this->transfers_manager,
+ $this->transfers_receiver,
+ $this->http,
+ $this->http_helper,
+ $this->migration_state_manager,
+ $this->settings,
+ $this->properties,
+ $this->transfers_sender,
+ $this->filesystem,
+ $this->scrambler,
+ $this->transfers_plugin_helper
+ );
+
+ $this->tp_cli = new ThemePluginFilesCli(
+ $this->addon,
+ $this->properties,
+ $this->template,
+ $this->filesystem,
+ $this->profile_manager,
+ $this->util,
+ $this->transfers_util,
+ $this->transfers_receiver,
+ $this->tp_addon_finalize,
+ $this->transfers_plugin_helper,
+ $this->cli
+ );
+
+ $this->theme_plugin_manager = new TPF_Manager();
+ /* End TPF Section */
+
+ /* Start MST Section */
+ $this->media_files_compat = new MediaFilesCompat(
+ $this->util,
+ $this->filesystem
+ );
+
+ $this->mst_addon = new MultisiteToolsAddon(
+ $this->addon,
+ $this->properties,
+ $this->multisite,
+ $this->util,
+ $this->migration_state_manager,
+ $this->table,
+ $this->table_helper,
+ $this->form_data,
+ $this->template,
+ $this->profile_manager,
+ $this->dynamic_props,
+ $this->filesystem,
+ $this->media_files_compat
+ );
+
+ $this->mst_addon_cli = new MultisiteToolsAddonCli(
+ $this->addon,
+ $this->properties,
+ $this->multisite,
+ $this->util,
+ $this->migration_state_manager,
+ $this->table,
+ $this->table_helper,
+ $this->form_data,
+ $this->template,
+ $this->profile_manager,
+ $this->cli,
+ $this->dynamic_props,
+ $this->filesystem,
+ $this->media_files_compat
+ );
+
+ $this->multisite_tools_manager = new \DeliciousBrains\WPMDB\Pro\MST\Manager();
+
+ /* End MST Section*/
+
+ /* Start CLI Section */
+ $this->connection = new Connection();
+
+ $this->cli_addon = new CliAddon(
+ $this->addon,
+ $this->properties
+ );
+
+ $this->cli_addon_cli = new Cli(
+ $this->form_data,
+ $this->util,
+ $this->cli_manager,
+ $this->table,
+ $this->error_log,
+ $this->initiate_migration,
+ $this->finalize_migration,
+ $this->http_helper,
+ $this->migration_manager,
+ $this->migration_state_manager,
+ $this->connection,
+ $this->backup_export,
+ $this->properties,
+ $this->multisite,
+ $this->import
+ );
+
+ $this->cli_settings = new Setting(
+ $this->form_data,
+ $this->util,
+ $this->cli_manager,
+ $this->table,
+ $this->error_log,
+ $this->initiate_migration,
+ $this->finalize_migration,
+ $this->http_helper,
+ $this->migration_manager,
+ $this->migration_state_manager,
+ $this->license,
+ $this->settings
+ );
+
+ $this->extra_cli_manager = new \DeliciousBrains\WPMDB\Pro\Cli\Extra\Manager();
+ /* End CLI Section */
+
+ $this->addons_facade = new AddonsFacade($this->license, [
+ $this->media_files_manager,
+ $this->theme_plugin_manager,
+ $this->multisite_tools_manager,
+ $this->extra_cli_manager,
+ ]);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Command.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Command.php
new file mode 100644
index 000000000..94760499f
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Command.php
@@ -0,0 +1,228 @@
+
+ * : A file path to export to. Filename will be modified to end in .sql or
+ * .sql.gz if necessary.
+ *
+ * [--find=]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * Table names should be quoted as needed, i.e. when using a comma in the
+ * find/replace string.
+ *
+ * The --replace= argument should be used in conjunction to specify
+ * the replace values for the strings found using this argument. The number
+ * of strings specified in this argument should match the number passed into
+ * --replace= argument.
+ *
+ * [--replace=]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * Should be used in conjunction with the --find= argument, see it's
+ * documentation for further explanation of the find & replace functionality.
+ *
+ * [--regex-find]
+ * : A regex pattern to match against when performing a string find
+ * and replace across the database.
+ *
+ * [--regex-replace]
+ * : A replace string that may contain references of the form \n or $n, with the latter
+ * form being the preferred one. Every such reference will be replaced by the text captured by the n'th
+ * parenthesized pattern used in the --regex-find pattern.
+ *
+ * [--case-sensitive-find]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * [--case-sensitive-replace]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * [--include-tables=]
+ * : The comma separated list of tables to migrate. Excluding this parameter
+ * will migrate all tables in your database that begin with your
+ * installation's table prefix, e.g. wp_.
+ *
+ * [--exclude-post-types=]
+ * : A comma separated list of post types to exclude. Excluding this parameter
+ * will migrate all post types.
+ *
+ * [--skip-replace-guids]
+ * : Do not perform a find & replace on the guid column in the wp_posts table.
+ *
+ * [--exclude-spam]
+ * : Exclude spam comments.
+ *
+ * [--gzip-file]
+ * : GZip compress export file.
+ *
+ * [--include-transients]
+ * : Include transients (temporary cached data).
+ *
+ * [--subsite=]
+ * : Export the given subsite as a single site install. Requires the Multisite Tools addon.
+ *
+ * [--prefix=]
+ * : A new table prefix to be used for a subsite export.
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb export ./migratedb.sql \
+ * --find=http://dev.bradt.ca,/Users/bradt/home/bradt.ca
+ * --replace=http://bradt.ca,/home/bradt.ca
+ * --include-tables=wp_posts,wp_postmeta
+ *
+ * @param array $args
+ * @param array $assoc_args
+ */
+ public function export($args, $assoc_args)
+ {
+ parent::export($args, $assoc_args);
+ }
+
+ /**
+ * Run a find/replace on the database.
+ *
+ * ## OPTIONS
+ *
+ * [--find=]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * Table names should be quoted as needed, i.e. when using a comma in the
+ * find/replace string.
+ *
+ * The --replace= argument should be used in conjunction to specify
+ * the replace values for the strings found using this argument. The number
+ * of strings specified in this argument should match the number passed into
+ * --replace= argument.
+ *
+ * [--replace=]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * Should be used in conjunction with the --find= argument, see it's
+ * documentation for further explanation of the find & replace functionality.
+ *
+ * [--regex-find]
+ * : A regex pattern to match against when performing a string find
+ * and replace across the database.
+ *
+ * [--regex-replace]
+ * : A replace string that may contain references of the form \n or $n, with the latter
+ * form being the preferred one. Every such reference will be replaced by the text captured by the n'th
+ * parenthesized pattern used in the --regex-find pattern.
+ *
+ * [--case-sensitive-find]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * [--case-sensitive-replace]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * [--include-tables=]
+ * : The comma separated list of tables to migrate. Excluding this parameter
+ * will migrate all tables in your database that begin with your
+ * installation's table prefix, e.g. wp_.
+ *
+ * [--include-tables=]
+ * : The comma separated list of tables to search. Excluding this parameter
+ * will run a find & replace on all tables in your database that begin with your
+ * installation's table prefix, e.g. wp_.
+ *
+ * [--exclude-post-types=]
+ * : A comma separated list of post types to exclude from the find & replace.
+ * Excluding this parameter will run a find & replace on all post types.
+ *
+ * [--skip-replace-guids]
+ * : Do not perform a find & replace on the guid column in the wp_posts table.
+ *
+ * [--exclude-spam]
+ * : Exclude spam comments.
+ *
+ * [--include-transients]
+ * : Include transients (temporary cached data).
+ *
+ * [--subsite=]
+ * : Run a find & replace on the given subsite. Requires the Multisite Tools addon.
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb find-replace
+ * --find=http://dev.bradt.ca,/Users/bradt/home/bradt.ca
+ * --replace=http://bradt.ca,/home/bradt.ca
+ * --include-tables=wp_posts,wp_postmeta
+ *
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @subcommand find-replace
+ */
+ public function find_replace($args, $assoc_args)
+ {
+ parent::find_replace($args, $assoc_args);
+ }
+
+ /**
+ * Update settings for migratedb.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : Either get or update
+ *
+ *
+ * : Name of setting to update or get.
+ * Available settings: push | pull | connection-key | license
+ *
+ * []
+ * : Value of new setting
+ *
+ * [--user=]
+ * : Required for license
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb setting update license xxx-xxx-xxx-xxxxx --user=1
+ *
+ * wp migratedb setting get license --user=1
+ *
+ * wp migratedb setting update pull on
+ *
+ * wp migratedb setting get pull
+ *
+ * @param array $args
+ *
+ * @return bool
+ * @since 1.2.5
+ */
+ public function setting($args)
+ {
+ $wpmdb_cli_settings = WPMDBDI::getInstance()->get(Setting::class);
+
+ // Handle settings logic in dedicated class
+ $wpmdb_cli_settings->handle_setting($args);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Export.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Export.php
new file mode 100644
index 000000000..3296da622
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Export.php
@@ -0,0 +1,139 @@
+
+ if (!empty($assoc_args['include-tables'])) {
+ $table_migrate_option = 'migrate_select';
+ $select_tables = explode(',', $assoc_args['include-tables']);
+ } else {
+ $select_tables = array();
+ $table_migrate_option = 'migrate_only_with_prefix';
+ }
+
+ // --exclude-post-types=
+ $exclude_post_types = '0';
+ $select_post_types = array();
+ if (!empty($assoc_args['exclude-post-types'])) {
+ $exclude_post_types = '1';
+ $all_post_types = $this->table->get_post_types();
+ $select_post_types = explode(',', $assoc_args['exclude-post-types']);
+ }
+
+ $filtered_profile = compact(
+ 'table_migrate_option',
+ 'exclude_post_types',
+ 'select_post_types',
+ 'select_tables'
+ );
+
+ return array_merge($profile, $filtered_profile);
+ }
+
+ /**
+ * Use tables from --include-tables assoc arg if available
+ *
+ * @param array $tables_to_migrate
+ * @param array $profile
+ * @param array $migration
+ *
+ * @return array
+ */
+ function tables_to_migrate_include_select($tables_to_migrate, $profile, $migration)
+ {
+ if (isset($profile['table_migrate_option']) && in_array($profile['action'], array('find_replace', 'savefile'))) {
+ if ('migrate_select' === $profile['table_migrate_option'] && !empty($profile['select_tables'])) {
+ $tables_to_migrate = array_intersect($profile['select_tables'], $this->table->get_tables());
+ }
+ }
+
+ return $tables_to_migrate;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/ClassMap.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/ClassMap.php
new file mode 100644
index 000000000..90d7b2c5b
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/ClassMap.php
@@ -0,0 +1,69 @@
+connection = new Connection();
+
+ $this->cli_addon = new CliAddon(
+ $this->addon,
+ $this->properties
+ );
+
+ $this->cli_addon_cli = new Cli(
+ $this->form_data,
+ $this->util,
+ $this->cli_manager,
+ $this->table,
+ $this->error_log,
+ $this->initiate_migration,
+ $this->finalize_migration,
+ $this->http_helper,
+ $this->migration_manager,
+ $this->migration_state_manager,
+ $this->connection,
+ $this->backup_export,
+ $this->properties,
+ $this->multisite,
+ $this->import,
+ $this->flush
+ );
+
+ $this->cli_settings = new Setting(
+ $this->form_data,
+ $this->util,
+ $this->cli_manager,
+ $this->table,
+ $this->error_log,
+ $this->initiate_migration,
+ $this->finalize_migration,
+ $this->http_helper,
+ $this->migration_manager,
+ $this->migration_state_manager,
+ $this->license,
+ $this->settings
+ );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Cli.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Cli.php
new file mode 100644
index 000000000..dedec85dd
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Cli.php
@@ -0,0 +1,1086 @@
+backup_export = $backup_export;
+ $this->properties = $properties;
+ $this->multisite = $multisite;
+ $this->import = $import;
+ $this->dynamic_properties = DynamicProperties::getInstance();
+ }
+
+ public function register()
+ {
+ parent::register();
+
+ $container = WPMDBDI::getInstance();
+ $this->connection = $container->get(Local::class);
+ $this->flush = $container->get(Flush::class);
+
+ // extra profile fields
+ add_filter('wpmdb_accepted_profile_fields', [$this, 'accepted_profile_fields']);
+
+ // announce extra args
+ add_filter('wpmdb_cli_filter_get_extra_args', [$this, 'filter_extra_args'], 10, 1);
+
+ // process push/pull profile args
+ add_filter('wpmdb_cli_filter_get_profile_data_from_args', [$this, 'add_extra_args_for_addon_migrations'], 10, 3);
+
+ // add backup tables
+ add_filter('wpmdb_cli_filter_before_migrate_tables', [$this, 'backup_before_migrate_tables'], 10, 1);
+
+ // extend cli_migration with push/pull functionality
+ add_filter('wpmdb_cli_filter_before_cli_initiate_migration', [$this, 'extend_cli_migration'], 10, 2);
+
+ // extend get_tables to migrate with push/pull functionality
+ add_filter('wpmdb_cli_tables_to_migrate', [$this, 'extend_tables_to_migrate'], 10, 1);
+
+ //extend get_row_counts_from_table_list with remote tables if necessary
+ add_filter('wpmdb_cli_get_row_counts_from_table_list', [$this, 'get_push_pull_row_counts'], 10, 2);
+
+ // check for wpmdbpro version
+ add_filter('wpmdb_cli_profile_before_migration', [$this, 'check_wpmdbpro_version_before_migration'], 10, 1);
+
+ // enable profile migrations
+ add_filter('wpmdb_cli_profile_before_migration', [$this, 'get_wpmdbpro_profile_before_migration'], 10, 1);
+
+ // check for MF plugin locally
+ add_filter('wpmdb_cli_profile_before_migration', [$this, 'check_local_wpmdbpro_media_files_before_migration'], 20, 1);
+
+ // Add extra pull migration data
+ add_filter('wpmdb_cli_filter_before_cli_initiate_migration', [$this, 'handle_pull_post_type_exclusion'], 20, 2);
+
+ // check remote for MF plugin after remote connection has been made
+ add_filter('wpmdb_cli_filter_before_cli_initiate_migration', [$this, 'check_remote_wpmdbpro_media_files_before_migration'], 20, 2);
+
+ // check for MST plugin locally
+ add_filter('wpmdb_cli_filter_before_cli_initiate_migration', [$this, 'check_local_wpmdbpro_mst_before_migration'], 20, 1);
+
+ // check remote for MST plugin after remote connection has been made
+ add_filter('wpmdb_cli_filter_before_cli_initiate_migration', [$this, 'check_remote_wpmdbpro_mst_before_migration'], 20, 2);
+
+ // check remote for TPF plugin after remote connection has been made
+ add_filter('wpmdb_cli_filter_before_cli_initiate_migration', [$this, 'check_remote_wpmdbpro_tpf_before_migration'], 20, 2);
+
+ // flush rewrite rules
+ add_filter('wpmdb_cli_finalize_migration_response', [$this, 'finalize_flush'], 20, 2);
+
+ // add backup stage
+ add_filter('wpmdb_cli_initiate_migration_args', [$this, 'initate_migration_enable_backup'], 10, 2);
+
+ // use remote tables for pull migration
+ add_filter('wpmdb_cli_filter_source_tables', [$this, 'set_remote_source_tables_for_pull'], 10, 2);
+
+ // filter progress label for backup/migration
+ add_filter('wpmdb_cli_progress_label', [$this, 'modify_progress_label'], 10, 2);
+
+ // pass through pro filter including remote
+ add_filter('wpmdb_cli_finalize_migration', [$this, 'apply_pro_cli_finalize_migration_filter'], 10, 0);
+
+ // args for finalizing pro cli migrations
+ add_filter('wpmdb_cli_finalize_migration_args', [$this, 'apply_pro_cli_finalize_migration_args'], 10, 3);
+
+ // add delay between requests
+ add_action('wpmdb_before_remote_post', [$this, 'do_delay_between_requests'], 10, 0);
+ add_action('wpmdb_media_files_cli_before_migrate_media', [$this, 'do_delay_between_requests'], 10, 0);
+ add_action('wpmdb_theme_plugin_files_cli_before_migrate_files', [$this, 'do_delay_between_requests'], 10, 0);
+
+ // add import stage
+ add_action('wpmdb_cli_during_cli_migration', [$this, 'cli_import'], 10, 2);
+ }
+
+ /**
+ * Adds extra supported fields to the profile.
+ *
+ * @param $fields
+ *
+ * @return array
+ */
+ public function accepted_profile_fields($fields)
+ {
+ $fields[] = 'import_file';
+ $fields[] = 'media_files';
+
+ return $fields;
+ }
+
+ /**
+ * Get profile by key.
+ *
+ * @since 1.1
+ *
+ * @param int $key Profile key
+ *
+ * @return array|WP_Error If profile exists return array, otherwise WP_Error.
+ */
+ public function get_profile_by_key($key)
+ {
+ $profiles = get_site_option('wpmdb_saved_profiles');
+
+ if (!isset($profiles[$key])) {
+ return $this->cli_error(__('Profile ID not found.', 'wp-migrate-db'));
+ }
+
+ $this->profileID = $key;
+
+ return $profiles[$this->profileID];
+ }
+
+ /**
+ * Get profile by name.
+ *
+ * @param string $name
+ *
+ * @return array|WP_Error
+ */
+ public function get_profile_by_name($name)
+ {
+ $profiles = get_site_option('wpmdb_saved_profiles', []);
+ $names = array_column($profiles, 'name');
+ $counts = array_count_values($names);
+
+ if (1 < $counts[$name]) {
+ return $this->cli_error(__('There is more than one profile with that name, please use the profile ID instead. See wp migratedb profiles for help.', 'wp-migrate-db'));
+ }
+
+ foreach($profiles as $key => $profile) {
+ if ($profile['name'] === $name) {
+ $this->profileID = $key;
+ return $profiles[$this->profileID];
+ }
+ }
+
+ return $this->cli_error(__('Profile not found.', 'wp-migrate-db'));
+ }
+
+ /**
+ * Retrieve information from the remote machine, e.g. tables, prefix, bottleneck, gzip, etc
+ *
+ * @return array
+ */
+ public function verify_remote_connection($profile)
+ {
+ do_action('wpmdb_cli_before_verify_connection_to_remote_site', $profile);
+
+ \WP_CLI::log(__('Verifying connection...', 'wp-migrate-db'));
+
+ $connection_info = preg_split('/\s+/', $profile['connection_info']);
+ $remote_site_args = $this->post_data;
+ $remote_site_args['intent'] = $profile['action'];
+ $remote_site_args['url'] = trim($connection_info[0]);
+ $remote_site_args['key'] = trim($connection_info[1]);
+ $this->post_data = apply_filters('wpmdb_cli_verify_connection_to_remote_site_args', $remote_site_args, $profile);
+
+ $response = $this->verify_connection_to_remote_site($this->post_data);
+
+ $verified_response = $this->verify_cli_response($response, 'ajax_verify_connection_to_remote_site()');
+ if (!is_wp_error($verified_response)) {
+ $verified_response = apply_filters('wpmdbpro_cli_verify_connection_response', $verified_response, $profile);
+ }
+
+ return $verified_response;
+ }
+
+ /**
+ * Stub for ajax_verify_connection_to_remote_site()
+ *
+ * @param array|bool $args
+ *
+ * @return array
+ */
+ public function verify_connection_to_remote_site($args = false)
+ {
+ $_POST = $args;
+ $response = $this->connection->ajax_verify_connection_to_remote_site();
+
+ return $response;
+ }
+
+ /**
+ * Stub for ajax_flush()
+ *
+ * @param array|bool $args
+ *
+ * @return bool|null
+ */
+ public function flush($args = false)
+ {
+ $_POST = $args;
+ $response = $this->flush->ajax_flush();
+
+ return $response;
+ }
+
+ /**
+ * Add extra CLI args used by this plugin.
+ *
+ * @param array $args
+ *
+ * @return array
+ */
+ public function filter_extra_args($args = [])
+ {
+ $args[] = 'preserve-active-plugins';
+ $args[] = 'include-transients';
+ $args[] = 'backup';
+ $args[] = 'import-file';
+
+ return $args;
+ }
+
+ /**
+ * Extend get_profile_data_from_args with options for push/pull
+ * hooks on: wpmdb_cli_filter_get_profile_data_from_args
+ *
+ * @param array $profile
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @return array|WP_Error
+ */
+ public function add_extra_args_for_addon_migrations($profile, $args, $assoc_args)
+ {
+ if (!is_array($profile)) {
+ return $profile;
+ }
+
+ $import_file = null;
+ $connection_info = null;
+
+ if (in_array($assoc_args['action'], ['push', 'pull'])) {
+ if (empty($args[0]) || empty($args[1])) {
+ return $this->cli_error(__('URL and secret-key are required', 'wp-migrate-db'));
+ }
+ $connection_info = sprintf('%s %s', $args[0], $args[1]);
+ }
+
+ if ('import' === $assoc_args['action']) {
+ $import_file = $assoc_args['import-file'];
+ }
+
+ // --preserve-active-plugins
+ $keep_active_plugins = intval(isset($assoc_args['preserve-active-plugins']));
+
+ // --include-transients.
+ $exclude_transients = intval(!isset($assoc_args['include-transients']));
+
+ // --backup.
+ $create_backup = 0;
+ $backup_option = 'backup_only_with_prefix';
+ $select_backup = [];
+ if (!empty($assoc_args['backup'])) {
+ $create_backup = 1;
+ if (!in_array($assoc_args['backup'], ['prefix', 'selected'])) {
+ $backup_option = 'backup_manual_select';
+ $select_backup = explode(',', $assoc_args['backup']);
+ } elseif ('selected' === $assoc_args['backup']) {
+ $backup_option = 'backup_selected';
+ }
+ }
+
+ $filtered_profile = compact(
+ 'connection_info',
+ 'exclude_transients',
+ 'keep_active_plugins',
+ 'create_backup',
+ 'backup_option',
+ 'select_backup',
+ 'import_file'
+ );
+
+ return array_merge($profile, $filtered_profile);
+ }
+
+ /**
+ * Add backup stage when selected
+ * hooks on: wpmdb_cli_filter_before_migrate_tables
+ *
+ * @param array $filter_vars
+ *
+ * @return array|WP_Error
+ */
+ public function backup_before_migrate_tables($filter_vars)
+ {
+ $this->post_data = $this->dynamic_properties->post_data;
+
+ // No good reason this should happen, but lets not risk an undefined index warning
+ if (!array_key_exists('tables', $filter_vars)) {
+ return $filter_vars;
+ }
+
+ $tables = $filter_vars['tables'];
+
+ if ('push' === $this->profile['action']) {
+ $all_tables = $this->remote['tables'];
+ $prefixed_tables = $this->remote['prefixed_tables'];
+ } else {
+ $all_tables = $this->table->get_tables();
+ $prefixed_tables = $this->table->get_tables('prefix');
+ }
+
+ $tables_to_backup = $this->backup_export->get_tables_to_backup($this->profile, $prefixed_tables, $all_tables);
+ if (
+ 'backup' == $this->post_data['stage'] &&
+ 'backup_manual_select' == $this->profile['backup_option'] &&
+ array_diff($this->profile['select_backup'], $tables_to_backup)
+ ) {
+ return $this->cli_error(__('Invalid backup option or non-existent table selected for backup.', 'wp-migrate-db'));
+ }
+
+ $tables = ('backup' == $this->post_data['stage']) ? $tables_to_backup : $tables;
+ $stage_iterator = ('backup' == $this->post_data['stage']) ? 1 : 2;
+
+ return compact('tables', 'stage_iterator');
+ }
+
+ /**
+ * Extend cli_migration with push/pull
+ * hooks on: wpmdb_cli_filter_before_cli_initiate_migration
+ *
+ * @param array $profile
+ *
+ * @return array
+ */
+ public function extend_cli_migration($profile, $post_data = [])
+ {
+ if (in_array($profile['action'], ['push', 'pull'])) {
+ $this->remote = $this->verify_remote_connection($profile);
+ if (is_wp_error($this->remote)) {
+ return $this->remote;
+ }
+
+ if (!empty($post_data)) {
+ $this->post_data = array_merge($this->post_data, $post_data);
+ }
+
+ $this->post_data['gzip'] = ('1' == $this->remote['gzip']) ? 1 : 0;
+ $this->post_data['bottleneck'] = $this->remote['bottleneck'];
+ $this->post_data['prefix'] = $this->remote['prefix'];
+
+ $this->post_data['site_details']['remote'] = $this->remote['site_details'];
+
+ // set delay between requests if remote has a delay
+ if (isset($this->remote['delay_between_requests'])) {
+ $this->delay_between_requests = $this->remote['delay_between_requests'];
+ }
+
+ if (!empty($this->remote['temp_prefix'])) {
+ $this->post_data['temp_prefix'] = $this->remote['temp_prefix'];
+ }
+
+ // Default the find/replace pairs if nothing specified so that we don't break the target.
+ $search_replace = $profile['search_replace'];
+ if (
+ !isset($search_replace['custom_search_replace']) ||
+ empty($search_replace['custom_search_replace'])
+ ) {
+ $search_replace['standard_search_replace'] = $this->get_standard_search_replace_pairs($profile['action']);
+ $search_replace['standard_search_visible'] = true;
+ $search_replace['standard_options_enabled'] = ['domain', 'path'];
+ $profile['search_replace'] = $search_replace;
+
+ $profile = apply_filters('wpmdb_cli_default_find_and_replace', $profile, $this->post_data);
+ }
+ }
+
+ $this->dynamic_properties->post_data = $this->post_data;
+
+ return $profile;
+ }
+
+ /**
+ * Gets the default search/replace pairs in an array.
+ *
+ * @param string $action Whether we're pushing or pulling.
+ *
+ * @return array
+ */
+ public function get_standard_search_replace_pairs($action = 'push')
+ {
+ $local_url = preg_replace('#^https?:#', '', Util::home_url());
+ $local_path = Util::get_absolute_root_file_path();
+ $remote_url = preg_replace('#^https?:#', '', $this->remote['url']);
+ $remote_path = $this->remote['path'];
+ $push = 'push' === $action;
+
+ return [
+ 'domain' => [
+ 'search' => $push ? $local_url : $remote_url,
+ 'replace' => $push ? $remote_url : $local_url,
+ 'enabled' => true,
+ ],
+ 'path' => [
+ 'search' => $push ? $local_path : $remote_path,
+ 'replace' => $push ? $remote_path : $local_path,
+ 'enabled' => true,
+ ],
+ ];
+ }
+
+ /**
+ * Return correct set of tables to migrate on push/pull migrations
+ * hooks on: wpmdb_cli_tables_to_migrate
+ *
+ * @param array $tables_to_migrate
+ *
+ * @return array
+ */
+ public function extend_tables_to_migrate($tables_to_migrate)
+ {
+ if (null === $this->profile && !empty($this->dynamic_properties->profile)) {
+ $this->profile = $this->dynamic_properties->profile;
+ }
+
+ if (empty($this->post_data) && !empty($this->dynamic_properties->post_data)) {
+ $this->post_data = $this->dynamic_properties->post_data;
+ }
+
+ if ('push' == $this->profile['action']) {
+ if ('migrate_only_with_prefix' == $this->profile['table_migrate_option']) {
+ $tables_to_migrate = $this->table->get_tables('prefix');
+ } elseif ('migrate_select' == $this->profile['table_migrate_option']) {
+ $tables_to_migrate = array_intersect($this->profile['select_tables'], $this->table->get_tables());
+ }
+ } elseif ('pull' == $this->profile['action']) {
+ if ('migrate_only_with_prefix' == $this->profile['table_migrate_option']) {
+ $tables_to_migrate = $this->remote['prefixed_tables'];
+ } elseif ('migrate_select' == $this->profile['table_migrate_option']) {
+ $tables_to_migrate = array_intersect($this->profile['select_tables'], $this->remote['tables']);
+ } else {
+ $tables_to_migrate = $this->remote['prefixed_tables'];
+ }
+ } elseif ('import' === $this->profile['action'] && 'find_replace' === $this->post_data['stage']) {
+ $temp_tables = $this->table->get_tables('temp');
+ $tables_to_migrate = [];
+
+ if (isset($this->profile['select_tables']) && !empty($this->profile['select_tables'])) {
+ $selected_tables = $this->profile['select_tables'];
+
+ foreach ($selected_tables as $table) {
+ if (in_array($this->properties->temp_prefix . $table, $temp_tables)) {
+ $tables_to_migrate[] = $this->properties->temp_prefix . $table;
+ }
+ }
+ } else {
+ $tables_to_migrate = $temp_tables;
+ }
+ }
+
+ return $tables_to_migrate;
+ }
+
+ /**
+ * Return correct row counts for stage/migration type
+ * hooks on: wpmdb_cli_get_row_counts_from_table_list
+ *
+ * @param array $cached_stage_results
+ * @param int $stage
+ *
+ * @return array
+ */
+ public function get_push_pull_row_counts($cached_stage_results, $stage)
+ {
+ $migration_type = $this->profile['action'];
+ $local_table_rows = $cached_stage_results;
+ $remote_table_rows = isset($this->remote['table_rows']) ? $this->remote['table_rows'] : 0;
+
+ if (1 === $stage) { // 1 = backup stage, 2 = migration stage
+ $cached_stage_results = ('push' === $migration_type) ? $remote_table_rows : $local_table_rows;
+ } else {
+ $cached_stage_results = ('pull' === $migration_type) ? $remote_table_rows : $local_table_rows;
+ }
+
+ return $cached_stage_results;
+ }
+
+ /**
+ * Error if WPMDBPro version is not compatible
+ * hooks on: wpmdb_cli_profile_before_migration
+ *
+ * @param array $profile
+ *
+ * @return array|WP_Error
+ */
+ public function check_wpmdbpro_version_before_migration($profile)
+ {
+ // TODO: maybe instantiate WPMDBPro_CLI_Addon to make WPMDBPro_Addon::meets_version_requirements() available here
+ $wpmdb_pro_version = $GLOBALS['wpmdb_meta']['wp-migrate-db-pro']['version'];
+ if (!version_compare($wpmdb_pro_version, '1.8.3', '>=')) {
+ return $this->cli_error(__('Please update WP Migrate.', 'wp-migrate-db'));
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Get profile by key
+ * hooks on: wpmdb_cli_profile_before_migration
+ *
+ * @param array $profile
+ *
+ * @return array|WP_Error
+ */
+ public function get_wpmdbpro_profile_before_migration($profile)
+ {
+ if (is_wp_error($profile) || is_array($profile)) {
+ return $profile;
+ }
+
+ if (empty($profile)) {
+ return $this->cli_error(__('Profile ID missing.', 'wp-migrate-db'));
+ }
+
+ if (is_numeric($profile)) {
+ $profile = $this->get_profile_by_key(absint($profile));
+ } else {
+ $profile = $this->get_profile_by_name($profile);
+ }
+
+ if (is_wp_error($profile)) {
+ return $profile;
+ }
+
+ $imported = isset($profile['imported']);
+ $profile = json_decode($profile['value'], true);
+
+ if ($imported) {
+ $outdated = $this->maybe_show_outdated_profile_error($profile);
+ if (is_wp_error($outdated)) {
+ return $outdated;
+ }
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Checks if the provided profile has outdated settings,
+ * and returns an error if so.
+ *
+ * @param array $profile The profile to check.
+ *
+ * @return boolean|WP_Error
+ */
+ public function maybe_show_outdated_profile_error($profile)
+ {
+ $outdated = false;
+ $current_migration = isset($profile['current_migration']) ? $profile['current_migration'] : [];
+ $intent = $current_migration['intent'];
+
+ if (isset($current_migration['post_types_option']) && 'all' !== $current_migration['post_types_option'] && in_array($intent, ['pull', 'import'])) {
+ $outdated = true;
+ }
+
+ if (isset($profile['media_files'])) {
+ if (isset($profile['media_files']['enabled']) && true === $profile['media_files']['enabled']) {
+ $outdated = true;
+ }
+ }
+
+ if ($outdated) {
+ $path = is_multisite() ? 'settings.php' : 'tools.php';
+ $url = add_query_arg(['page' => 'wp-migrate-db-pro'], network_admin_url($path));
+
+ $message = __('This profile is from an older version of WP Migrate and some settings have changed.', 'wp-migrate-db');
+ $message .= "
";
+ $message .= sprintf(
+ __('Please visit %s to update the profile.', 'wp-migrate-db'),
+ $url
+ );
+
+ $outdated = new \WP_Error(
+ 'wpmdb-outdated-profile',
+ $message
+ );
+ }
+
+ return $outdated;
+ }
+
+ /**
+ * Check if MF option enabled in profile but plugin not active locally.
+ * hooks on: wpmdb_cli_profile_before_migration
+ *
+ * @param array $profile
+ *
+ * @return array|WP_Error
+ */
+ public function check_local_wpmdbpro_media_files_before_migration($profile)
+ {
+ if (is_wp_error($profile)) {
+ return $profile;
+ }
+
+ if (isset($profile['media_files']) && true === $profile['media_files']['enabled']) {
+ if (false === class_exists('\DeliciousBrains\WPMDB\Pro\MF\MediaFilesRemote')) {
+ return $this->cli_error(__('The profile is set to migrate media files, however migrating media files is not supported with the current license.', 'wp-migrate-db'));
+ }
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Check if MF option enabled in profile but plugin not active on remote and that selected subsites make sense if being used.
+ * hooks on: wpmdb_cli_filter_before_cli_initiate_migration
+ *
+ * @param array $profile
+ *
+ * @return array|WP_Error
+ */
+ public function check_remote_wpmdbpro_media_files_before_migration($profile)
+ {
+ if (is_wp_error($profile)) {
+ return $profile;
+ }
+
+ if (isset($profile['media_files']) && true === $profile['media_files']['enabled']) {
+ if (!isset($this->remote['media_files_max_file_uploads'])) {
+ return $this->cli_error(__('The profile is set to migrate media files, however migrating media files is not supported with the current license of the remote site.', 'wp-migrate-db'));
+ }
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Check this is migration needs MST but it isn't installed locally.
+ * hooks on: wpmdb_cli_profile_before_migration
+ *
+ * @param array $profile
+ *
+ * @return array|WP_Error
+ */
+ public function check_local_wpmdbpro_mst_before_migration($profile)
+ {
+ if (is_wp_error($profile) || in_array($profile['action'], [
+ 'savefile',
+ 'find_replace',
+ 'import',
+ ])) {
+ return $profile;
+ }
+
+ $local_is_multisite = is_multisite();
+ $remote_is_multisite = 'true' === $this->remote['site_details']['is_multisite'];
+
+ if ($local_is_multisite === $remote_is_multisite) {
+ return $profile;
+ }
+
+ if (false === class_exists('\DeliciousBrains\WPMDB\Pro\MST\MultisiteToolsAddon')) {
+ return $this->cli_error(__('The profile is set to migrate between a single site and a multisite, however this type of multisite migration is not supported with the current license.', 'wp-migrate-db'));
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Check if --subsite param is passed in the profile but the MST plugin is not installed/active on remote
+ * hooks on: wpmdb_cli_filter_before_cli_initiate_migration
+ *
+ * @param $profile
+ *
+ * @return array|WP_Error
+ */
+ public function check_remote_wpmdbpro_mst_before_migration($profile)
+ {
+
+ if (is_wp_error($profile) || in_array($profile['action'], [
+ 'savefile',
+ 'find_replace',
+ 'import',
+ ])) {
+ return $profile;
+ }
+
+ $local_is_multisite = is_multisite();
+ $remote_is_multisite = 'true' === $this->remote['site_details']['is_multisite'];
+ $is_subsite_migration = isset($profile['mst_select_subsite']) && true == $profile['mst_select_subsite'];
+ $is_two_multisites = $local_is_multisite && $remote_is_multisite;
+ // Two single site installs, nothing for us to do here.
+ if (!$local_is_multisite && !$remote_is_multisite) {
+ return $profile;
+ }
+
+ if ($is_two_multisites && !$is_subsite_migration) {
+ return $profile;
+ }
+
+ // At this point we know that MST is needed.
+ if (!isset($this->remote['mst_available'])) {
+ return $this->cli_error(__('The profile is set to migrate a subsite, however subsite migrations are not supported with the current license of the remote site.', 'wp-migrate-db'));
+ }
+
+ // Validate remote subsite ID.
+ if (!$local_is_multisite && $remote_is_multisite) {
+ $profile['mst_selected_subsite'] = $this->multisite->get_subsite_id($profile['mst_selected_subsite'], $this->remote['site_details']['subsites']);
+ }
+
+ if (false === $profile['mst_selected_subsite']) {
+ return $this->cli_error(__('A valid Blog ID or Subsite URL must be supplied to make use of the subsite option', 'wp-migrate-db'));
+ }
+
+ if ($is_two_multisites) {
+ if(!$remote_is_multisite) {
+ return $this->cli_error(__('The profile is set to perform a subsite to subsite migration, however the remote website is a single site installation.', 'wp-migrate-db-pro-cli'));
+ }
+
+ $remote_subsite = 'push' === $profile['action'] ? $profile['mst_destination_subsite'] : $profile['mst_selected_subsite'];
+ if (!$this->multisite->get_subsite_id($remote_subsite, $this->remote['site_details']['subsites'])) {
+ return $this->cli_error(__('A valid Blog ID or Subsite URL must be supplied to subsite-destination to make use of the subsite option', 'wp-migrate-db-pro-cli'));
+ }
+ }
+
+ if (
+ 1 < $profile['mst_selected_subsite']
+ && !preg_match('/\w+_\d_/', $profile['new_prefix']) // Prefix already set in saved profiles
+ && (
+ ('pull' === $profile['action'] && $local_is_multisite) ||
+ ('push' === $profile['action'] && $remote_is_multisite))
+ ) {
+ // @TODO this changes a correct prefix when a pull profile is run
+ $profile['new_prefix'] .= $profile['mst_selected_subsite'] . '_';
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Check if TPF options enabled in profile but plugin not active on remote and that selected themes/plugins make sense if being used.
+ * hooks on: wpmdb_cli_filter_before_cli_initiate_migration
+ *
+ * @param array $profile
+ *
+ * @return array|WP_Error
+ */
+ public function check_remote_wpmdbpro_tpf_before_migration($profile)
+ {
+ if (is_wp_error($profile) || !in_array($profile['action'], ['push', 'pull'])) {
+ return $profile;
+ }
+
+ if (!isset($profile['theme_plugin_files'])) {
+ return $profile;
+ }
+
+ $theme_plugin_files = $profile['theme_plugin_files'];
+ $tpf_enabled = false;
+
+ if (isset($theme_plugin_files['plugin_files']) && true === $theme_plugin_files['plugin_files']['enabled']) {
+ $tpf_enabled = true;
+ }
+
+ if (isset($theme_plugin_files['theme_files']) && true === $theme_plugin_files['theme_files']['enabled']) {
+ $tpf_enabled = true;
+ }
+
+ if (isset($theme_plugin_files['muplugin_files']) && true === $theme_plugin_files['muplugin_files']['enabled']) {
+ $tpf_enabled = true;
+ }
+
+ if (isset($theme_plugin_files['other_files']) && true === $theme_plugin_files['other_files']['enabled']) {
+ $tpf_enabled = true;
+ }
+
+ if ($tpf_enabled) {
+ if (!isset($this->remote['theme_plugin_files_available'])) {
+ return $this->cli_error(__('The profile is set to migrate theme or plugin files, however migrating theme and plugin files is not supported with the current license.', 'wp-migrate-db'));
+ }
+
+ $profile = apply_filters('wpmdb_cli_filter_tpf_profile_args', $profile, $this->remote);
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Flush rewrite rules
+ * hooks on: wpmdb_cli_finalize_migration_response
+ *
+ * @param string $response
+ *
+ * @return string
+ */
+ public function finalize_flush($response, $post_data)
+ {
+ if (is_wp_error($response)) {
+ return $response;
+ }
+
+ \WP_CLI::log(_x('Flushing caches and rewrite rules...', 'The caches and rewrite rules for the target are being flushed', 'wp-migrate-db'));
+
+ $response = $this->flush($post_data);
+
+ return $this->verify_cli_response($response, 'finalize_flush()');
+ }
+
+ /**
+ * Check profile for backup option and set stage appropriately
+ * hooks on: wpmdb_cli_initiate_migration_args
+ *
+ * @param array $migration_args
+ * @param array $profile
+ *
+ * @return array
+ */
+ public function initate_migration_enable_backup($migration_args, $profile)
+ {
+ if ('0' != $profile['create_backup']) {
+ $migration_args['stage'] = 'backup';
+ }
+
+ return $migration_args;
+ }
+
+ /**
+ * Use remote tables for pull migration
+ * hooks on: wpmdb_cli_filter_source_tables
+ *
+ * @param $source_tables
+ *
+ * @return array
+ */
+ public function set_remote_source_tables_for_pull($source_tables, $profile)
+ {
+ if ('pull' == $profile['action']) {
+ $source_tables = $this->remote['tables'];
+ }
+
+ return $source_tables;
+ }
+
+ /**
+ * Update progress label for migrations / backups
+ * hooks on: 'wpmdb_cli_progress_label
+ *
+ * @param string $progress_label
+ * @param int $stage
+ *
+ * @return string
+ */
+ public function modify_progress_label($progress_label, $stage)
+ {
+ if (!in_array($this->profile['action'], ['savefile', 'backup_local', 'find_replace'])) {
+ if (1 === $stage) { // 1 = backup stage, 2 = migration stage
+ $progress_label = __('Performing backup', 'wp-migrate-db');
+ } else {
+ $progress_label = __('Migrating tables', 'wp-migrate-db');
+
+ if ('import' === $this->profile['action']) {
+ $progress_label = __('Running find & replace', 'wp-migrate-db');
+ }
+ }
+ }
+
+ return $progress_label;
+ }
+
+ /**
+ * Apply pro only finalize migration filter
+ * hooks on: wpmdb_cli_finalize_migration
+ *
+ * @return mixed
+ */
+ public function apply_pro_cli_finalize_migration_filter()
+ {
+ return apply_filters('wpmdb_pro_cli_finalize_migration', true, $this->profile, $this->remote, $this->post_data);
+ }
+
+ /**
+ * Apply args for pro cli finalize migration
+ * hooks on: wpmdb_cli_finalize_migration_args
+ */
+ public function apply_pro_cli_finalize_migration_args($post_data, $profile, $migration)
+ {
+ if (0 !== $this->profileID) {
+ $post_data['profileID'] = $this->profileID;
+ $post_data['profileType'] = 'saved';
+ }
+
+ return $post_data;
+ }
+
+ public function do_delay_between_requests()
+ {
+ if (0 < $this->delay_between_requests) {
+ sleep($this->delay_between_requests);
+ }
+ }
+
+ /**
+ * Imports a file to the database.
+ *
+ * @param $post_data
+ * @param $profile
+ */
+ public function cli_import($post_data, $profile)
+ {
+ if ('import' !== $this->profile['action']) {
+ return;
+ }
+
+ $file = $this->profile['import_file'];
+ $importer = $this->import;
+
+ if ($importer->file_is_gzipped($file)) {
+ $file = $importer->decompress_file($this->profile['import_file']);
+ }
+
+ $chunk = 0;
+ $num_chunks = $importer->get_num_chunks_in_file($file);
+
+ if (is_wp_error($num_chunks)) {
+ $error = $num_chunks->get_error_message();
+ $this->error_log->log_error($error);
+ \WP_CLI::error($error);
+ }
+
+ $file_object = $importer->get_file_object($file);
+ $file_header = $file_object->fread(2000);
+ $this->post_data['import_info'] = $importer->parse_file_header($file_header);
+
+ $current_query = '';
+ $progress = \WP_CLI\Utils\make_progress_bar(__('Importing file', 'wp-migrate-db'), $num_chunks);
+
+ while ($num_chunks > $chunk) {
+ $import = $importer->import_chunk($file, $chunk, $current_query);
+
+ if (is_wp_error($import)) {
+ $error = $import->get_error_message();
+ $this->error_log->log_error($error);
+ \WP_CLI::error($error);
+ }
+
+ $current_query = $import['current_query'];
+
+ $chunk++;
+
+ $progress->tick();
+ }
+
+ $progress->finish();
+
+ $search_replace = $this->profile['search_replace'];
+
+ if (isset($search_replace['custom_search_replace']) && !empty($search_replace['custom_search_replace'])) {
+ $this->post_data['stage'] = 'find_replace';
+ $this->migrate_tables();
+ }
+ }
+
+ public function handle_pull_post_type_exclusion($profile, $post_data)
+ {
+ if ($profile['action'] === 'pull' && !empty($profile['current_migration']['cli_exclude_post_types'])) {
+ $profile['current_migration']['post_types_selected'] = array_merge($profile['current_migration']['post_types_selected'], array_values(array_diff($this->remote['post_types'], $profile['current_migration']['cli_exclude_post_types'])));
+ }
+
+ return $profile;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/CliAddon.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/CliAddon.php
new file mode 100644
index 000000000..feda3ee6b
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/CliAddon.php
@@ -0,0 +1,15 @@
+plugin_slug = $this->properties->plugin_slug;
+ $this->plugin_version = $this->properties->plugin_version;
+ $this->addon_name = 'WP Migrate CLI';
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Command.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Command.php
new file mode 100644
index 000000000..f4faa15d2
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Command.php
@@ -0,0 +1,731 @@
+]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * Table names should be quoted as needed, i.e. when using a comma in the
+ * find/replace string.
+ *
+ * The --replace= argument should be used in conjunction to specify
+ * the replace values for the strings found using this argument. The number
+ * of strings specified in this argument should match the number passed into
+ * --replace= argument.
+ *
+ * [--replace=]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * Should be used in conjunction with the --find= argument, see it's
+ * documentation for further explanation of the find & replace functionality.
+ *
+ * [--regex-find]
+ * : A regex pattern to match against when performing a string find
+ * and replace across the database.
+ *
+ * [--regex-replace]
+ * : A replace string that may contain references of the form \n or $n, with the latter
+ * form being the preferred one. Every such reference will be replaced by the text captured by the n'th
+ * parenthesized pattern used in the --regex-find pattern.
+ *
+ * [--case-sensitive-find]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * [--case-sensitive-replace]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * [--include-tables=]
+ * : The comma separated list of tables to search. Excluding this parameter
+ * will run a find & replace on all tables in your database that begin with your
+ * installation's table prefix, e.g. wp_.
+ *
+ * [--backup=]
+ * : Perform a backup of the destination site's database tables before replacing it.
+ *
+ * Accepted values:
+ *
+ * * prefix - Backup only tables that begin with your installation's
+ * table prefix (e.g. wp_)
+ * * selected - Backup only tables selected for migration (as in --include-tables)
+ * * A comma separated list of the tables to backup.
+ *
+ * [--exclude-post-types=]
+ * : A comma separated list of post types to exclude from the find & replace.
+ * Excluding this parameter will run a find & replace on all post types.
+ *
+ * [--skip-replace-guids]
+ * : Do not perform a find & replace on the guid column in the wp_posts table.
+ *
+ * [--exclude-spam]
+ * : Exclude spam comments.
+ *
+ * [--include-transients]
+ * : Include transients (temporary cached data).
+ *
+ * [--subsite=]
+ * : Run a find & replace on the given subsite. Requires the Multisite Tools addon.
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb find-replace
+ * --find=http://dev.bradt.ca,/Users/bradt/home/bradt.ca
+ * --replace=http://bradt.ca,/home/bradt.ca
+ * --include-tables=wp_posts,wp_postmeta
+ * --backup=selected
+ *
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @subcommand find-replace
+ */
+ public function find_replace($args, $assoc_args)
+ {
+ parent::find_replace($args, $assoc_args);
+ }
+
+ /**
+ * Import an SQL file into the database.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : The path of the SQL file to import.
+ *
+ * [--find=]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * Table names should be quoted as needed, i.e. when using a comma in the
+ * find/replace string.
+ *
+ * The --replace= argument should be used in conjunction to specify
+ * the replace values for the strings found using this argument. The number
+ * of strings specified in this argument should match the number passed into
+ * --replace= argument.
+ *
+ * [--replace=]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * Should be used in conjunction with the --find= argument, see it's
+ * documentation for further explanation of the find & replace functionality.
+ *
+ * [--regex-find]
+ * : A regex pattern to match against when performing a string find
+ * and replace across the database.
+ *
+ * [--regex-replace]
+ * : A replace string that may contain references of the form \n or $n, with the latter
+ * form being the preferred one. Every such reference will be replaced by the text captured by the n'th
+ * parenthesized pattern used in the --regex-find pattern.
+ *
+ * [--case-sensitive-find]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * [--case-sensitive-replace]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * [--backup=]
+ * : Perform a backup of the destination site's database tables before replacing it.
+ *
+ * Accepted values:
+ *
+ * * prefix - Backup only tables that begin with your installation's
+ * table prefix (e.g. wp_)
+ * * selected - Backup only tables selected for migration (as in --include-tables)
+ * * A comma separated list of the tables to backup.
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb import ./migratedb.sql \
+ * --find=http://dev.bradt.ca,/Users/bradt/home/bradt.ca
+ * --replace=http://bradt.ca,/home/bradt.ca
+ *
+ * @param array $args
+ * @param array $assoc_args
+ */
+ public function import($args, $assoc_args)
+ {
+ $assoc_args['action'] = 'import';
+ $assoc_args['import-file'] = trim($args[0]);
+
+ if (empty($assoc_args['import-file'])) {
+ \WP_CLI::error(__('You must provide an import file.', 'wp-migrate-db-cli'));
+ }
+
+ $profile = $this->_get_profile_data_from_args($args, $assoc_args);
+
+ if (is_wp_error($profile)) {
+ \WP_CLI::error($profile);
+ }
+
+ $this->_perform_cli_migration($profile);
+ }
+
+ /**
+ * Push local DB up to remote.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : The URL of the remote site. Should include the URL encoded basic
+ * authentication credentials (if required). e.g. http://user:password@example.com
+ *
+ * Must include the WordPress directory if WordPress is stored in a subdirectory.
+ * e.g. http://example.com/wp
+ *
+ *
+ * : The remote site's secret key.
+ *
+ * [--find=]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * Table names should be quoted as needed, i.e. when using a comma in the
+ * find/replace string.
+ *
+ * The --replace= argument should be used in conjunction to specify
+ * the replace values for the strings found using this argument. The number
+ * of strings specified in this argument should match the number passed into
+ * --replace= argument.
+ *
+ * If omitted, a set of 2 find and replace pairs will be performed by default:
+ *
+ * 1. Strings containing URLs referencing the source site will be replace
+ * by the destination URL.
+ *
+ * 2. Strings containing root file paths referencing the source site will
+ * be replaced by the destination root file path.
+ *
+ * [--replace=]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * Should be used in conjunction with the --find= argument, see it's
+ * documentation for further explanation of the find & replace functionality.
+ *
+ * [--regex-find]
+ * : A regex pattern to match against when performing a string find
+ * and replace across the database.
+ *
+ * [--regex-replace]
+ * : A replace string that may contain references of the form \n or $n, with the latter
+ * form being the preferred one. Every such reference will be replaced by the text captured by the n'th
+ * parenthesized pattern used in the --regex-find pattern.
+ *
+ * [--case-sensitive-find]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * [--case-sensitive-replace]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * [--include-tables=]
+ * : The comma separated list of tables to migrate. Excluding this parameter
+ * will migrate all tables in your database that begin with your
+ * installation's table prefix, e.g. wp_.
+ *
+ * [--exclude-database]
+ * : Will not perform any table/database migration.
+ *
+ * [--exclude-post-types=]
+ * : A comma separated list of post types to exclude. Excluding this parameter
+ * will migrate all post types.
+ *
+ * [--skip-replace-guids]
+ * : Do not perform a find & replace on the guid column in the wp_posts table.
+ *
+ * [--exclude-spam]
+ * : Exclude spam comments.
+ *
+ * [--preserve-active-plugins]
+ * : Preserves the active_plugins option (which plugins are activated/deactivated).
+ *
+ * [--include-transients]
+ * : Include transients (temporary cached data).
+ *
+ * [--backup=]
+ * : Perform a backup of the destination site's database tables before replacing it.
+ *
+ * Accepted values:
+ *
+ * * prefix - Backup only tables that begin with your installation's
+ * table prefix (e.g. wp_)
+ * * selected - Backup only tables selected for migration (as in --include-tables)
+ * * A comma separated list of the tables to backup.
+ *
+ * [--media=]
+ * : Perform a migration of the media files. Requires the Media Files addon.
+ *
+ * Accepted values:
+ *
+ * * all - Uploads all media files to the remote site.
+ * * since-date - Uploads all media files that have been added to the
+ * local site since the provided. Use --media-date to pass a date.
+ *
+ * [--media-date=]
+ * : The date to use for media migrations.
+ *
+ * * Date must be in Y-m-d H:i:s format (i.e. '2020-01-28 09:10:00').
+ * * Time is optional.
+ * * Server timezone will be used.
+ *
+ * [--exclude-media=]
+ * : A comma-separated list of media files and folders that should be excluded
+ * from the migration.
+ *
+ * [--subsite=]
+ * : Push the given subsite to the remote single site install.
+ * Requires the Multisite Tools addon.
+ *
+ * [--subsite-source=]
+ * : Push the given subsite to another subsite, used in conjunction with subsite-destination. Requires the Multisite Tools addon.
+ *
+ * [--subsite-destination=]
+ * : Push the given subsite to another subsite, used in conjunction with subsite-source. Requires the Multisite Tools addon.
+ *
+ * [--theme-files=]
+ * : Perform a migration of the theme files. Requires the Theme & Plugin files addon.
+ *
+ * Accepted values:
+ *
+ * * all - Uploads all themes to the remote site.
+ * * A comma separated list of themes to migrate. See `wp theme list` for a list
+ * of theme slugs.
+ *
+ * [--plugin-files=]
+ * : Perform a migration of the plugin files. Requires the Theme & Plugin files addon.
+ *
+ * Accepted values:
+ *
+ * * all - Uploads all plugins to the remote site.
+ * * A comma separated list of plugins to migrate. See `wp plugin list` for a list
+ * of plugin slugs.
+ *
+ * [--mu-plugin-files=]
+ * : Perform a migration of the must-use plugin files. Requires the Theme & Plugin files addon.
+ *
+ * Accepted values:
+ *
+ * * all - Uploads all must-use plugins to the remote site.
+ * * A comma separated list of must-use plugin files and directories to migrate.
+ *
+ * [--other-files=]
+ * : Perform a migration of the other files in the wp-content directory not covered by. Requires the Theme & Plugin files addon.
+ *
+ * Accepted values:
+ *
+ * * all - Uploads all other plugins to the remote site.
+ * * A comma separated list of other plugin files and directories to migrate.
+ *
+ * [--exclude-theme-plugin-files=]
+ * : A comma-separated list of theme or plugin files and folders that should be excluded
+ * from the migration.
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb push http://bradt.ca LJPmq3t8h6uuN7aqQ3YSnt7C88Wzzv5BVPlgLbYE \
+ * --find=http://dev.bradt.ca,/Users/bradt/home/bradt.ca
+ * --replace=http://bradt.ca,/home/bradt.ca
+ * --include-tables=wp_posts,wp_postmeta
+ * --media=all
+ * --theme-files=twentytwenty
+ * --plugin-files=hello-dolly,akismet
+ *
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @since 1.1
+ */
+ public function push($args, $assoc_args)
+ {
+ $assoc_args['action'] = 'push';
+ $profile = $this->_get_profile_data_from_args($args, $assoc_args);
+ if (is_wp_error($profile)) {
+ \WP_CLI::error(Cli::cleanup_message($profile->get_error_message()));
+ }
+
+ $this->_perform_cli_migration($profile);
+ }
+
+ /**
+ * Pull remote DB down to local.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : The URL of the remote site. Should include the URL encoded basic
+ * authentication credentials (if required). e.g. http://user:password@example.com
+ *
+ * Must include the WordPress directory if WordPress is stored in a subdirectory.
+ * e.g. http://example.com/wp
+ *
+ *
+ * : The remote site's secret key.
+ *
+ * [--find=]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * Table names should be quoted as needed, i.e. when using a comma in the
+ * find/replace string.
+ *
+ * The --replace= argument should be used in conjunction to specify
+ * the replace values for the strings found using this argument. The number
+ * of strings specified in this argument should match the number passed into
+ * --replace= argument.
+ *
+ * If omitted, a set of 2 find and replace pairs will be performed by default:
+ *
+ * 1. Strings containing URLs referencing the source site will be replace
+ * by the destination URL.
+ *
+ * 2. Strings containing root file paths referencing the source site will
+ * be replaced by the destination root file path.
+ *
+ * [--replace=]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * Should be used in conjunction with the --find= argument, see it's
+ * documentation for further explanation of the find & replace functionality.
+ *
+ * [--regex-find]
+ * : A regex pattern to match against when performing a string find
+ * and replace across the database.
+ *
+ * [--regex-replace]
+ * : A replace string that may contain references of the form \n or $n, with the latter
+ * form being the preferred one. Every such reference will be replaced by the text captured by the n'th
+ * parenthesized pattern used in the --regex-find pattern.
+ *
+ * [--case-sensitive-find]
+ * : A comma separated list of strings to find when performing a string find
+ * and replace across the database.
+ *
+ * [--case-sensitive-replace]
+ * : A comma separated list of replace value strings to implement when
+ * performing a string find & replace across the database.
+ *
+ * [--include-tables=]
+ * : The comma separated list of tables to migrate. Excluding this parameter
+ * will migrate all tables in your database that begin with your
+ * installation's table prefix, e.g. wp_.
+ *
+ * [--exclude-database]
+ * : Will not perform any table/database migration.
+ *
+ * [--exclude-post-types=]
+ * : A comma separated list of post types to exclude. Excluding this parameter
+ * will migrate all post types.
+ *
+ * [--skip-replace-guids]
+ * : Do not perform a find & replace on the guid column in the wp_posts table.
+ *
+ * [--exclude-spam]
+ * : Exclude spam comments.
+ *
+ * [--preserve-active-plugins]
+ * : Preserves the active_plugins option (which plugins are activated/deactivated).
+ *
+ * [--include-transients]
+ * : Include transients (temporary cached data).
+ *
+ * [--backup=]
+ * : Perform a backup of the destination site's database tables before replacing it.
+ *
+ * Accepted values:
+ *
+ * * prefix - Backup only tables that begin with your installation's
+ * table prefix (e.g. wp_)
+ * * selected - Backup only tables selected for migration (as in --include-tables)
+ * * A comma separated list of the tables to backup.
+ *
+ * [--media=]
+ * : Perform a migration of the media files. Requires the Media Files addon.
+ *
+ * Accepted values:
+ *
+ * * all - Downloads all media files from the remote site.
+ * * since-date - Downloads all media files that have been added to the
+ * remote site since the provided date. Use --media-date to pass a date.
+ *
+ * [--media-date=]
+ * : The date to use for media migrations.
+ *
+ * * Date must be in Y-m-d H:i:s format (i.e. '2020-01-28 09:10:00').
+ * * Time is optional.
+ * * Server timezone will be used.
+ *
+ * [--exclude-media=]
+ * : A comma-separated list of media files and folders that should be excluded
+ * from the migration.
+ *
+ * [--subsite=]
+ * : Pull the remote single site install into the given subsite.
+ * Requires the Multisite Tools addon.
+ *
+ * [--subsite-source=]
+ * : Pull the given subsite to another subsite, used in conjunction with subsite-destination. Requires the Multisite Tools addon.
+ *
+ * [--subsite-destination=]
+ * : Pull the given subsite to another subsite, used in conjunction with subsite-source. Requires the Multisite Tools addon.
+ *
+ * [--theme-files=]
+ * : Perform a migration of the theme files. Requires the Theme & Plugin files addon.
+ *
+ * Accepted values:
+ *
+ * * all - Downloads all themes from the remote site.
+ * * A comma separated list of themes to migrate. See `wp theme list` for a list
+ * of theme slugs.
+ *
+ * [--plugin-files=]
+ * : Perform a migration of the plugin files. Requires the Theme & Plugin files addon.
+ *
+ * Accepted values:
+ *
+ * * all - Downloads all plugins from the remote site.
+ * * A comma separated list of plugins to migrate. See `wp plugin list` for a list
+ * of plugin slugs.
+ *
+ * [--mu-plugin-files=]
+ * : Perform a migration of the must-use plugin files. Requires the Theme & Plugin files addon.
+ *
+ * Accepted values:
+ *
+ * * all - Uploads all must-use plugins to the remote site.
+ * * A comma separated list of must-use plugin files and directories to migrate.
+ *
+ * [--other-files=]
+ * : Perform a migration of the other files in the wp-content directory not covered by. Requires the Theme & Plugin files addon.
+ *
+ * Accepted values:
+ *
+ * * all - Uploads all must-use plugins to the remote site.
+ * * A comma separated list of must-use plugin files and directories to migrate.
+ *
+ * [--exclude-theme-plugin-files=]
+ * : A comma-separated list of theme or plugin files and folders that should be excluded
+ * from the migration.
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb pull http://bradt.ca LJPmq3t8h6uuN7aqQ3YSnt7C88Wzzv5BVPlgLbYE \
+ * --find=http://dev.bradt.ca,/Users/bradt/home/bradt.ca
+ * --replace=http://bradt.ca,/home/bradt.ca
+ * --include-tables=wp_posts,wp_postmeta
+ * --media=all
+ * --theme-files=twentytwenty
+ * --plugin-files=hello-dolly,akismet
+ * --mu-plugin-files=hello-dolly,akismet
+ * --other-files=langauges
+ *
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @since 1.1
+ */
+ public function pull($args, $assoc_args)
+ {
+ $assoc_args['action'] = 'pull';
+
+ $profile = $this->_get_profile_data_from_args($args, $assoc_args);
+ if (is_wp_error($profile)) {
+ \WP_CLI::error(Cli::cleanup_message($profile->get_error_message()));
+ }
+
+ $this->_perform_cli_migration($profile);
+ }
+
+ /**
+ * Run a migration.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : ID of the profile to use for the migration.
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb migrate 1
+ *
+ * @synopsis
+ *
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @since 1.0
+ */
+ public function migrate($args, $assoc_args)
+ {
+ $profile = $args[0];
+
+ $this->_perform_cli_migration($profile, $assoc_args);
+ }
+
+ /**
+ * Returns a list of profiles.
+ *
+ * @since 1.2.4
+ */
+ public function profiles()
+ {
+ $profiles = get_site_option('wpmdb_saved_profiles');
+
+ // Display error if no profiles are present
+ if (!is_array($profiles) || empty($profiles)) {
+ \WP_CLI::error(__('There are no saved profiles for this site.', 'wp-migrate-db'));
+
+ return;
+ }
+
+ // Get profile information in CLI format
+ $cli_items = [];
+ foreach ($profiles as $key => $profile) {
+ $data = json_decode($profile['value'], true);
+
+ if (!is_array($data)) {
+ continue;
+ }
+
+ // Allow actions to be translated for output
+ $action_strings = [
+ 'push' => _x('push', 'Export data to remote database', 'wp-migrate-db'),
+ 'pull' => _x('pull', 'Import data from remote database', 'wp-migrate-db'),
+ 'savefile' => _x('export', 'Export file from local database', 'wp-migrate-db'),
+ 'find_replace' => _x('find & replace', 'Run a find & replace on local database', 'wp-migrate-db'),
+ 'import' => _x('import', 'Import data from SQL file', 'wp-migrate-db'),
+ 'backup_local' => _x('backup', 'Backup the local database', 'wp-migrate-db'),
+ ];
+
+ $action = $data['current_migration']['intent'];
+
+ if (isset($action_strings[$action])) {
+ $profile['action'] = strtoupper($action_strings[$action]);
+ } else {
+ $profile['action'] = '---';
+ }
+
+ $remote = '---';
+ if (in_array($action, ['push', 'pull'])) {
+ $remote = isset($data['connection_info']) && isset($data['connection_info']['connection_state']) ? $data['connection_info']['connection_state']['url'] : '---';
+ }
+
+ //Populate CLI items with saved profile information
+ $cli_items[] = [
+ _x('ID', 'Profile list column heading', 'wp-migrate-db') => $key,
+ _x('NAME', 'Profile list column heading', 'wp-migrate-db') => $profile['name'],
+ _x('ACTION', 'Profile list column heading', 'wp-migrate-db') => $profile['action'],
+ _x('REMOTE', 'Profile list column heading', 'wp-migrate-db') => $remote,
+ ];
+ }
+
+ // Set CLI column headers. Must match `cli_items` keys
+ $cli_keys = array_keys(reset($cli_items));
+ \WP_CLI\Utils\format_items('table', $cli_items, $cli_keys);
+ }
+
+ /**
+ * Run a migration.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : ID or name of the profile to use for the migration.
+ *
+ * [--import-file=]
+ * : The SQL file to import.
+ *
+ * ## EXAMPLES
+ *
+ * wp migratedb profile 1
+ * wp migratedb profile 'Push to live'
+ *
+ * @synopsis [--import-file=]
+ *
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @since 1.1
+ */
+ public function profile($args, $assoc_args)
+ {
+ // uses migrate method
+ $this->migrate($args, $assoc_args);
+ }
+
+ // overrides _perform_cli_migration from WPMDB_Command
+ protected function _perform_cli_migration($profile, $assoc_args = [])
+ {
+ global $wpmdbpro_cli;
+
+ if (empty($wpmdbpro_cli)) {
+ \WP_CLI::error(__('WP Migrate CLI class not available.', 'wp-migrate-db'));
+
+ return;
+ }
+
+ $result = $wpmdbpro_cli->cli_migration($profile, $assoc_args);
+
+ if (true === $result) {
+ \WP_CLI::success(__('Migration successful.', 'wp-migrate-db'));
+ } elseif (!is_wp_error($result)) {
+ \WP_CLI::success(sprintf(__('Export saved to: %s', 'wp-migrate-db'), $result));
+ } elseif (is_wp_error($result)) {
+ \WP_CLI::error(Cli::cleanup_message($result->get_error_message()));
+ }
+ }
+}
+
+/**
+ * Deprecated WP Migrate DB Pro command. Use migratedb instead.
+ */
+class WPMDBCLI_Deprecated extends Command
+{
+ /**
+ * Run a migration.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : ID or name of the profile to use for the migration.
+ *
+ * ## EXAMPLES
+ *
+ * wp wpmdb migrate 1
+ * wp wpmdb migrate 'Push to live'
+ *
+ * @synopsis
+ *
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @since 1.0
+ */
+ public function migrate($args, $assoc_args)
+ {
+ parent::migrate($args, $assoc_args);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Initialize.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Initialize.php
new file mode 100644
index 000000000..b2c09773e
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Initialize.php
@@ -0,0 +1,14 @@
+get(CliAddon::class)->register();
+ $container->get(CliAddon::class)->set_licensed($licensed);
+
+ $wpmdbpro_cli = $container->get(Cli::class);
+ $wpmdbpro_cli->register();
+
+ if (defined('WP_CLI') && WP_CLI) {
+ \DeliciousBrains\WPMDB\Pro\Cli\Extra\Command::register();
+ }
+
+ add_filter('wpmdb_addon_registered_cli', '__return_true');
+ }
+
+
+ public function get_license_response_key()
+ {
+ return 'wp-migrate-db-pro-cli';
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Setting.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Setting.php
new file mode 100644
index 000000000..94c46cf6e
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Cli/Extra/Setting.php
@@ -0,0 +1,297 @@
+allowed_actions = array('get', 'update');
+ $this->allowed_settings = array('license', 'push', 'pull', 'connection-key');
+ $this->allowed_push_pull_values = array('off', 'on');
+
+ //Map command line args to option keys
+ $this->options_map = array(
+ 'push' => 'allow_push',
+ 'pull' => 'allow_pull',
+ 'connection-key' => 'key',
+ 'license' => 'licence',
+ );
+
+ $this->license = $license;
+ $this->settings = $settings->get_settings();
+ }
+
+ /**
+ *
+ * Main method for handling the getting and updating of settings
+ *
+ * @param $args
+ *
+ * @return bool|void
+ */
+ public function handle_setting($args)
+ {
+ /**
+ *
+ * $arg[0] = update | get
+ * $arg[1] =
+ * $arg[2] = - optional if 'update' is action
+ *
+ */
+ $current_settings = $this->settings;
+ $allowed_actions = $this->allowed_actions;
+ $allowed_settings = $this->allowed_settings;
+ $allowed_push_pull_values = $this->allowed_push_pull_values;
+ $options_map = $this->options_map;
+
+ // Either the action or setting name aren't passed
+ if (!isset($args[0]) || !isset($args[1])) {
+ return false;
+ }
+
+ if (!in_array($args[0], $allowed_actions)) {
+ \WP_CLI::error(sprintf(__('Invalid action parameter - `%s`', 'wp-migrate-db'), $args[0]));
+
+ return;
+ }
+
+ if (!in_array($args[1], $allowed_settings)) {
+ \WP_CLI::error(sprintf(__('Invalid setting parameter - `%s`', 'wp-migrate-db'), $args[1]));
+
+ return;
+ }
+
+ // Handle updating of settings
+ if ('update' == $args[0]) {
+ // $args[2] is the value to update the settings object with. If it's not set, stop.
+ if (!isset($args[2])) {
+ \WP_CLI::error(__('Please pass a value to update.', 'wp-migrate-db'));
+ }
+
+ if ('push' == $args[1] || 'pull' == $args[1]) {
+ // Only allow valid push/pull values
+ if (!in_array($args[2], $allowed_push_pull_values)) {
+ \WP_CLI::error(sprintf(__('Invalid parameter for push/push settings. Value must be `on` or `off`.', 'wp-migrate-db'), $args[1]));
+
+ return;
+ }
+
+ $option_name = $options_map[$args[1]];
+ $update = $this->_cli_save_setting($option_name, $args[2]);
+
+ if ($update) {
+ \WP_CLI::success(sprintf(__('%s setting updated.', 'wp-migrate-db'), $args[1]));
+ } else {
+ \WP_CLI::warning(sprintf(__('Setting unchanged.', 'wp-migrate-db'), $args[1]));
+ }
+ } elseif ('connection-key' == $args[1]) {
+ \WP_CLI::error(__('The connection-key cannot be set via the CLI.', 'wp-migrate-db'));
+ } elseif ('license' == $args[1]) {
+ $this->_cli_update_licence($args);
+ }
+ // Handle getting of settings
+ } elseif ('get' == $args[0]) {
+ // Because the options arguments are different format than the options keys, use the array map to get the option key
+ $key = $options_map[$args[1]];
+
+ // No need to pass the 3rd positional argument to a get command.
+ if (isset($args[2])) {
+ \WP_CLI::error(sprintf(__('Too many positional arguments: %s', 'wp-migrate-db'), $args[2]));
+ }
+
+ // If there is a value stored for the given key...
+ $val = '';
+ if ('license' == $args[1]) {
+ $val = $this->_cli_get_licence($args);
+ } elseif (isset($current_settings[$key]) && '' !== $current_settings[$key]) {
+ $setting = $current_settings[$key];
+ if (is_bool($setting)) {
+ $val = $allowed_push_pull_values[$setting];
+ } else {
+ $val = $setting;
+ }
+ }
+ if ($val) {
+ \WP_CLI::log($val);
+ } else {
+ \WP_CLI::warning(sprintf(__('No setting `%s` currently saved in the database.', 'wp-migrate-db'), $key));
+ }
+ }
+ }
+
+ /**
+ *
+ * Update WP Migrate DB Pro Licence.
+ * If passing in a license, be sure to pass the value through _handle_license() first to verify the license.
+ *
+ * @param $args
+ *
+ * @return void
+ */
+ protected function _cli_update_licence($args)
+ {
+ $key = $args[2];
+ $this->_has_valid_admin();
+ // Validates licence against dbrains api
+ $licence_response = $this->_handle_licence($key);
+
+ if (true === $licence_response) {
+ $this->license->set_licence_key($key);
+ $this->license->check_licence($key);
+ \WP_CLI::success(__('License updated.', 'wp-migrate-db'));
+ } else if (is_array($licence_response)) {
+ foreach ($licence_response as $error) {
+ //Strip HTML, convert HTML entities to ASCII...
+ \WP_CLI::error(self::cleanup_message($error));
+ }
+ }
+ }
+
+ /**
+ *
+ * Get WP Migrate DB Pro Licence.
+ *
+ * @param $args
+ *
+ * @return void|string
+ */
+ protected function _cli_get_licence($args)
+ {
+ $this->_has_valid_admin();
+ return $this->license->get_licence_key();
+ }
+
+ /**
+ *
+ * Check if a valid admin user has been passed.
+ *
+ * @param $args
+ *
+ * @return bool|void
+ */
+ protected function _has_valid_admin()
+ {
+ $config = \WP_CLI::get_config();
+ $user = $config['user'];
+ if (!$user) {
+ \WP_CLI::error(__('License requires specifying a user via --user=', 'wp-migrate-db') );
+ }
+ if (!current_user_can('administrator')) {
+ \WP_CLI::error(__('User must be an Admin', 'wp-migrate-db') );
+ }
+
+ return true;
+ }
+
+ /**
+ *
+ * Save a WP Migrate DB Pro setting.
+ * If passing in a license, be sure to pass the value through _handle_license() first to verify the license.
+ *
+ * @param $setting_name
+ * @param $value
+ *
+ * @return mixed
+ */
+ protected function _cli_save_setting($setting_name, $value)
+ {
+ $settings = $this->settings;
+ $new_setting = $value;
+
+ if ('allow_push' === $setting_name || 'allow_pull' === $setting_name) {
+ $new_setting = ('on' == $value) ? true : false;
+ } else {
+ $new_setting = sanitize_text_field($new_setting); //Sanitize value as update_site_option() doesn't sanitize non-core options.
+ }
+
+ $settings[$setting_name] = $new_setting;
+ $update = update_site_option('wpmdb_settings', $settings);
+
+ return $update;
+ }
+
+ /**
+ *
+ * Validates licence against dbrains api
+ *
+ * @param $licence
+ *
+ * @return bool
+ */
+ protected function _handle_licence($licence)
+ {
+
+ $this->cli_manager->set_cli_migration();
+
+ \WP_CLI::log(__('Checking license key...', 'wp-migrate-db'));
+
+ $_POST['action'] = 'wpmdb_activate_licence';
+ $_POST['licence_key'] = $licence;
+ $_POST['context'] = 'licence';
+
+ //ajax_activate_licence() validates the license against the Delicious Brains API and sets the option in the database if valid. Returns an error otherwise.
+ $licence_response = $this->license->ajax_activate_licence();
+
+ $decoded_response = json_decode($licence_response, true);
+
+ if ((!isset($decoded_response['masked_licence']) && isset($decoded_response['errors'])) || (isset($decoded_response['masked_licence']) && isset($decoded_response['errors']))) {
+ return $decoded_response['errors'];
+ }
+
+ return true;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/Addons.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/Addons.php
new file mode 100644
index 000000000..cbecbfc76
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Addons/Addons.php
@@ -0,0 +1,25 @@
+supported_platform as $platform) {
+ $platform = new $platform;
+ $platform->register();
+ }
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Platforms/WPEngine.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Platforms/WPEngine.php
new file mode 100644
index 000000000..fcd7b6112
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Compatibility/Layers/Platforms/WPEngine.php
@@ -0,0 +1,19 @@
+props = $props;
+ $this->settings = $settings;
+ }
+
+ /**
+ * @param $plugin_slug
+ * @param bool $is_beta
+ *
+ * @return string
+ */
+ function get_plugin_update_download_url( $plugin_slug, $is_beta = false ) {
+ $licence = License::get_license();
+
+ if(!$licence) {
+ return '#';
+ }
+
+ $query_args = array(
+ 'request' => 'download',
+ 'licence_key' => $licence,
+ 'slug' => $plugin_slug,
+ 'site_url' => home_url( '', 'http' ),
+ );
+
+ if ( $is_beta ) {
+ $query_args['beta'] = '1';
+ }
+
+ return add_query_arg( $query_args, Api::get_api_url() );
+ }
+
+ /**
+ * @param $response
+ * @param $args
+ * @param $url
+ *
+ * @return \WP_Error
+ */
+ function verify_download( $response, $args, $url ) {
+ if ( is_wp_error( $response ) ) {
+ return $response;
+ }
+
+ $download_url = $this->get_plugin_update_download_url( $this->props->plugin_slug );
+
+ if ( false === strpos( $url, $download_url ) || 402 != $response['response']['code'] ) {
+ return $response;
+ }
+
+ // The $response['body'] is blank but output is actually saved to a file in this case
+ $data = @file_get_contents( $response['filename'] );
+
+ if ( ! $data ) {
+ return new \WP_Error( 'wpmdbpro_download_error_empty', sprintf( __( 'Error retrieving download from deliciousbrains.com. Please try again or download manually from %2$s.', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin', _x( 'My Account', 'Delicious Brains account', 'wp-migrate-db' ) ) );
+ }
+
+ $decoded_data = json_decode( $data, true );
+
+ // Can't decode the JSON errors, so just barf it all out
+ if ( ! isset( $decoded_data['errors'] ) || ! $decoded_data['errors'] ) {
+ return new \WP_Error( 'wpmdbpro_download_error_raw', $data );
+ }
+
+ foreach ( $decoded_data['errors'] as $key => $msg ) {
+ return new \WP_Error( 'wpmdbpro_' . $key, $msg );
+ }
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Import.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Import.php
new file mode 100644
index 000000000..4cbd349e5
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Import.php
@@ -0,0 +1,731 @@
+http = $http;
+ $this->error_log = $error_log;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->form_data = $form_data;
+ $this->filesystem = $filesystem;
+ $this->table = $table;
+ $this->backup_export = $backup_export;
+ $this->props = $properties;
+ $this->rest_API_server = $rest_API_server;
+ $this->http_helper = $http_helper;
+ }
+
+ /**
+ * Stores the chunk size used for imports
+ *
+ * @var int $chunk_size
+ */
+ protected $chunk_size = 10000;
+
+ /**
+ * State data for the migration
+ *
+ * @var array $state_data
+ */
+ protected $state_data;
+
+ /**
+ *
+ */
+ public function register()
+ {
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ add_filter('wpmdb_preserved_options', array($this, 'filter_preserved_options'), 10, 2);
+ add_filter('wpmdb_preserved_options_data', array($this, 'filter_preserved_options_data'), 10, 2);
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute(
+ '/get-import-info',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_get_import_info'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/upload-file',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_upload_file'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/prepare-upload',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_prepare_import_file'],
+ ]
+ );
+
+ $this->rest_API_server->registerRestRoute(
+ '/import-file',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_import_file'],
+ ]
+ );
+ }
+
+
+ /**
+ * Returns info about the import file.
+ *
+ * @return array|bool
+ */
+ public function ajax_get_import_info()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $data = $this->decode_chunk($_POST['file_data']);
+ $is_gzipped = false;
+
+ if (false !== $data && $this->str_is_gzipped($data)) {
+ if (!Util::gzip()) {
+ $error_msg = __('The server is not compatible with gzip, please decompress the import file and try again.', 'wp-migrate-db');
+ $return = array('wpmdb_error' => 1, 'body' => $error_msg);
+ $this->error_log->log_error($error_msg);
+
+ return $this->http->end_ajax(json_encode($return));
+ }
+
+ $data = Util::gzdecode($data);
+ $is_gzipped = true;
+ }
+
+ if (!$data && !$is_gzipped) {
+ $error_msg = __('Unable to read data from the import file', 'wp-migrate-db');
+ $return = array('wpmdb_error' => 1, 'body' => $error_msg);
+ $this->error_log->log_error($error_msg);
+ $result = $this->http->end_ajax(json_encode($return));
+
+ return $result;
+ }
+
+ $return = $this->parse_file_header($data);
+ $return['import_gzipped'] = $is_gzipped;
+
+ return $this->http->end_ajax($return);
+ }
+
+ /**
+ * Parses info from the export file header.
+ *
+ * @param $data
+ *
+ * @return array
+ */
+ public function parse_file_header($data)
+ {
+ $lines = preg_split('/\n|\r\n?/', $data);
+ $return = array();
+
+ if (is_array($lines) && 10 <= count($lines)) {
+ if ('# URL:' === substr($lines[5], 0, 6)) {
+ $return['URL'] = substr($lines[5], 7);
+ }
+
+ if ('# Path:' === substr($lines[6], 0, 7)) {
+ $return['path'] = substr($lines[6], 8);
+ }
+
+ if ('# Tables:' === substr($lines[7], 0, 9)) {
+ $return['tables'] = explode(', ', substr($lines[7], 10));
+ }
+
+ if ('# Table Prefix:' === substr($lines[8], 0, 15)) {
+ $return['prefix'] = substr($lines[8], 16);
+ }
+
+ if ('# Post Types:' === substr($lines[9], 0, 13)) {
+ $return['post_types'] = explode(', ', substr($lines[9], 14));
+ }
+
+ if ('# Protocol:' === substr($lines[10], 0, 11)) {
+ $return['protocol'] = substr($lines[10], 12);
+ }
+
+ if ('# Multisite:' === substr($lines[11], 0, 12)) {
+ $return['multisite'] = substr($lines[11], 13);
+ }
+
+ if ('# Subsite Export:' === substr($lines[12], 0, 17)) {
+ $return['subsite_export'] = substr($lines[12], 18);
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Uploads the import file to the server.
+ *
+ * @return null
+ */
+ public function ajax_upload_file()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = [
+ 'form_data' => 'json',
+ 'file_data' => 'string',
+ 'import_path' => 'string',
+ ];
+
+ $this->state_data = Persistence::setPostData($key_rules, __METHOD__);
+ $this->form_data->parse_and_save_migration_form_data($this->state_data['form_data']);
+
+ $file_data = $this->decode_chunk($this->state_data['file_data']);
+
+ if (false === $file_data) {
+ $error_msg = __('An error occurred while uploading the file.', 'wp-migrate-db');
+ $return = array('wpmdb_error' => 1, 'body' => $error_msg);
+ $this->error_log->log_error($error_msg);
+
+ return $this->http->end_ajax($return);
+ }
+
+ // Store the data in the file.
+ $fp = fopen($this->state_data['import_path'], 'a');
+ fwrite($fp, $file_data);
+ fclose($fp);
+
+ return $this->http->end_ajax('success');
+ }
+
+ /**
+ * Prepares for import of a SQL file.
+ *
+ * @return mixed
+ */
+ public function ajax_prepare_import_file()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $this->state_data = $this->migration_state_manager->set_post_data();
+
+ $file = $this->state_data['import_path'];
+
+ if ($this->file_is_gzipped($file)) {
+ $file = $this->decompress_file($this->state_data['import_path']);
+
+ if (false === $file) {
+ $error_msg = __('An error occurred while decompressing the import file.', 'wp-migrate-db');
+
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-import-decompress-error',
+ $error_msg
+ )
+ );
+ }
+ }
+
+ $return = array(
+ 'num_chunks' => $this->get_num_chunks_in_file($file),
+ 'import_file' => $file,
+ 'import_size' => $this->filesystem->filesize($file),
+ );
+
+ return $this->http->end_ajax($return);
+ }
+
+ /**
+ * Handles AJAX requests to import a SQL file.
+ *
+ * @return mixed
+ */
+ public function ajax_import_file()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $key_rules = [
+ 'chunk' => 'int',
+ 'current_query' => 'string',
+ 'import_file' => 'string',
+ 'import_info' => 'json_array',
+ ];
+
+ $this->state_data = Persistence::setPostData($key_rules, __METHOD__);
+
+ $file = $this->state_data['import_file'];
+ $chunk = isset($this->state_data['chunk']) ? $this->state_data['chunk'] : 0;
+ $num_chunks = isset($this->state_data['num_chunks']) ? $this->state_data['num_chunks'] : $this->get_num_chunks_in_file($file);
+ $current_query = isset($this->state_data['current_query']) ? base64_decode($this->state_data['current_query']) : '';
+
+ $import = $this->import_chunk($file, $chunk, $current_query);
+
+ if (is_wp_error($import)) {
+ $error_msg = $import->get_error_message();
+ $return = array('wpmdb_error' => 1, 'body' => $error_msg);
+ $this->error_log->log_error($error_msg);
+
+ return $this->http->end_ajax(json_encode($return));
+ }
+
+ $encoded_query = base64_encode($import['current_query']);
+ $return = array(
+ 'chunk' => ++$chunk,
+ 'num_chunks' => $num_chunks,
+ 'current_query' => $encoded_query,
+ 'chunk_size' => mb_strlen($import['current_query']),
+ );
+
+ // Return updated table sizes
+ if ($chunk >= $num_chunks) {
+ $is_backup = $this->state_data['import_info']['import_gzipped'] === true;
+
+ $this->backup_export->delete_export_file($this->state_data['import_filename'], $is_backup);
+
+ $return['table_sizes'] = $this->table->get_table_sizes();
+ $return['table_rows'] = $this->table->get_table_row_count();
+ $table_names = array_keys($return['table_rows']);
+ $filtered = [];
+ foreach ($table_names as $name) {
+ if (0 === strpos($name, $this->props->temp_prefix)) {
+ $filtered[] = str_replace($this->props->temp_prefix, '', $name);
+ }
+ }
+
+ $return['tables'] = $filtered;
+ }
+
+ return $this->http->end_ajax($return);
+ }
+
+ /**
+ * Gets the file data from the base64 encoded chunk
+ *
+ * @param string $data
+ *
+ * @return string|bool
+ */
+ public function decode_chunk($data)
+ {
+ $data = explode(';base64,', $data);
+
+ if (!is_array($data) || !isset($data[1])) {
+ return false;
+ }
+
+ $data = base64_decode($data[1]);
+ if (!$data) {
+ return false;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Gets the SplFileObject for the provided file
+ *
+ * @param string $file
+ * @param int $line
+ *
+ * @return object SplFileObject|WP_Error
+ */
+ public function get_file_object($file, $line = 0)
+ {
+ if (!$this->filesystem->file_exists($file) || !$this->filesystem->is_readable($file)) {
+ return new \WP_Error('invalid_import_file', __('The import file could not be read.', 'wp-migrate-db'));
+ }
+
+ $file = new \SplFileObject($file);
+ $file->seek($line);
+
+ return $file;
+ }
+
+ /**
+ * Check that SplFileObject key and fgets are aligned
+ *
+ * Some versions of PHP $file->key returns 0 twice
+ *
+ * @return bool
+ **/
+ protected function has_aligned_keys()
+ {
+ $file = new \SplTempFileObject();
+
+ for ($i = 0; $i < 3; $i++) {
+ $file->fwrite($i . PHP_EOL);
+ }
+ $file->rewind();
+ while (!$file->eof()) {
+ if ($file->key() !== intval($file->fgets())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the number of chunks in a SQL file
+ *
+ * @param $file
+ *
+ * @return int|object WP_Error
+ */
+ public function get_num_chunks_in_file($file)
+ {
+ $file = $this->get_file_object($file, PHP_INT_MAX);
+
+ if (is_wp_error($file)) {
+ return $file;
+ }
+
+ $lines = $file->key();
+
+ return ceil($lines / $this->chunk_size);
+ }
+
+ /**
+ * Imports a chunk of a provided SQL file into the database
+ *
+ * @param string $file
+ * @param int $chunk
+ * @param string $current_query
+ *
+ * @return array|object WP_Error
+ */
+ public function import_chunk($file, $chunk = 0, $current_query = '')
+ {
+ global $wpdb;
+
+ $start = $chunk * $this->chunk_size;
+ if (false === $this->has_aligned_keys() && $start > 0 ) {
+ $start = $start - 1;
+ }
+
+ $lines = 0;
+ $file = $this->get_file_object($file, $start);
+
+ if (is_wp_error($file)) {
+ return $file;
+ }
+
+ while (!$file->eof()) {
+ $line = trim($file->fgets());
+ $lines++;
+
+ if ($lines > $this->chunk_size) {
+ // Bail if we've exceeded the chunk size
+ return array(
+ 'import_complete' => false,
+ 'current_query' => $current_query,
+ );
+ }
+
+ if (empty($line) || '' === $line) {
+ // Skip empty/new lines
+ continue;
+ }
+
+ if ('--' === substr($line, 0, 2) ||
+ '/* ' === substr($line, 0, 3) ||
+ '#' === substr($line, 0, 1)
+ ) {
+ // Skip if it's a comment
+ continue;
+ }
+
+ if (preg_match('/\/\*![0-9]{5} SET (.*)\*\/;/', $line, $matches)) {
+ // Skip user and system defined MySQL variables
+ continue;
+ }
+
+ $current_query .= $line;
+
+ if (';' !== substr($line, -1, 1)) {
+ // Doesn't have a semicolon at the end, not the end of the query
+ continue;
+ }
+
+ // Run the query
+ ob_start();
+ $wpdb->show_errors();
+
+ $current_query = $this->convert_to_temp_query($current_query);
+ if (false === $wpdb->query($current_query)) {
+ $error = ob_get_clean();
+ $error_msg = sprintf(__('Failed to import the SQL query: %s', 'wp-migrate-db'), esc_html($error));
+ $return = new \WP_Error('import_sql_execution_failed', $error_msg);
+
+ $invalid_text = $this->table->maybe_strip_invalid_text_and_retry($current_query, 'import');
+ if (false !== $invalid_text) {
+ $return = $invalid_text;
+ }
+
+ if (is_wp_error($return)) {
+ return $return;
+ }
+ }
+
+ ob_end_clean();
+
+ // Reset the temp variable
+ $current_query = '';
+ }
+
+ return array('import_complete' => true, 'current_query' => $current_query);
+ }
+
+ /**
+ * Decompress a file
+ *
+ * @param string $file The file to decompress
+ * @param string $dest The destination of the decompressed file
+ *
+ * @return string|boolean
+ */
+ public function decompress_file($file, $dest = '')
+ {
+ if (!function_exists('wp_tempnam')) {
+ require_once(ABSPATH . 'wp-admin/includes/file.php');
+ }
+
+ $error = false;
+
+ if (!$this->filesystem->file_exists($file) || !$this->filesystem->is_readable($file)) {
+ return $error;
+ }
+
+ $tmp_file = wp_tempnam();
+
+ if ('' === $dest) {
+ $dest = ('.gz' === substr($file, -3)) ? substr($file, 0, -3) : $file;
+ }
+
+ if ($fp_in = gzopen($file, 'rb')) {
+ if ($fp_out = fopen($tmp_file, 'w')) {
+ while (!gzeof($fp_in)) {
+ $string = gzread($fp_in, '4096');
+ fwrite($fp_out, $string, strlen($string));
+ }
+
+ fclose($fp_out);
+
+ $this->filesystem->move($tmp_file, $dest);
+ } else {
+ $error = true;
+ }
+
+ gzclose($fp_in);
+ } else {
+ $error = true;
+ }
+
+ if ($error) {
+ return false;
+ }
+
+ return $dest;
+ }
+
+ /**
+ * Converts a query to run on temporary tables
+ *
+ * @param $query
+ *
+ * @return string
+ */
+ public function convert_to_temp_query($query)
+ {
+ $temp_prefix = $this->props->temp_prefix;
+
+ //Look for ansi quotes and replace them with back ticks
+ if ( substr( $query, 0, 14 ) === 'CREATE TABLE "' ) {
+ $query = $this->table->remove_ansi_quotes( $query );
+ }
+
+ if ( substr( $query, 0, 13 ) === 'INSERT INTO `' ) {
+ $query = Util::str_replace_first( 'INSERT INTO `', 'INSERT INTO `' . $temp_prefix, $query );
+ } elseif ( substr( $query, 0, 14 ) === 'CREATE TABLE `' ) {
+ $query = Util::str_replace_first( 'CREATE TABLE `', 'CREATE TABLE `' . $temp_prefix, $query );
+ } elseif ( substr( $query, 0, 22 ) === 'DROP TABLE IF EXISTS `' ) {
+ $query = Util::str_replace_first( 'DROP TABLE IF EXISTS `', 'DROP TABLE IF EXISTS `' . $temp_prefix, $query );
+ } elseif ( substr( $query, 0, 13 ) === 'LOCK TABLES `' ) {
+ $query = Util::str_replace_first( 'LOCK TABLES `', 'LOCK TABLES `' . $temp_prefix, $query );
+ } elseif ( substr( $query, 0, 13 ) === 'ALTER TABLE `' || substr( $query, 9, 13 ) === 'ALTER TABLE `' ) {
+ $query = Util::str_replace_first( 'ALTER TABLE `', 'ALTER TABLE `' . $temp_prefix, $query );
+ }
+
+ return $query;
+ }
+
+ /**
+ * Checks if a string is compressed via gzip
+ *
+ * @param string $string
+ *
+ * @return bool
+ */
+ public function str_is_gzipped($string)
+ {
+ if (!function_exists('wp_tempnam')) {
+ require_once(ABSPATH . 'wp-admin/includes/file.php');
+ }
+ $is_gzipped = false;
+ $tmp_file = \wp_tempnam();
+
+ $fh = fopen($tmp_file, 'a');
+ fwrite($fh, $string);
+
+
+ if ($this->file_is_gzipped($tmp_file)) {
+ $is_gzipped = true;
+ }
+
+ $this->filesystem->unlink($tmp_file);
+
+ return $is_gzipped;
+ }
+
+ /**
+ * Checks if the provided file is gzipped
+ *
+ * @param string $file
+ *
+ * @return bool
+ */
+ public function file_is_gzipped($file)
+ {
+ $is_gzipped = false;
+
+ if (!$this->filesystem->is_file($file)) {
+ return $is_gzipped;
+ }
+
+ $content_type = mime_content_type($file);
+
+ if (in_array($content_type, array('application/x-gzip', 'application/gzip'))) {
+ $is_gzipped = true;
+ }
+
+ return $is_gzipped;
+ }
+
+ /**
+ * Maybe change options keys to be preserved.
+ *
+ * @param array $preserved_options
+ * @param string $intent
+ *
+ * @return array
+ */
+ public function filter_preserved_options($preserved_options, $intent = '')
+ {
+ if ('import' === $intent) {
+ $preserved_options = $this->table->preserve_active_plugins_option($preserved_options);
+ }
+
+ return $preserved_options;
+ }
+
+ /**
+ * Maybe preserve the WPMDB plugins if they aren't already preserved.
+ *
+ * @param array $preserved_options_data
+ * @param string $intent
+ *
+ * @return array
+ */
+ public function filter_preserved_options_data($preserved_options_data, $intent = '')
+ {
+ if ('import' === $intent) {
+ $preserved_options_data = $this->table->preserve_wpmdb_plugins($preserved_options_data);
+ }
+
+ return $preserved_options_data;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/License.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/License.php
new file mode 100644
index 000000000..b2c3850ec
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/License.php
@@ -0,0 +1,1045 @@
+props = $properties;
+ $this->api = $api;
+ $this->settings = $settings->get_settings();
+ $this->util = $util;
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->migration_state_manager = $migration_state_manager;
+ $this->download = $download;
+ $this->http = $http;
+ $this->error_log = $error_log;
+ $this->http_helper = $http_helper;
+ $this->scrambler = $scrambler;
+ $this->remote_post = $remote_post;
+
+ self::$license_key = $this->get_licence_key();
+ self::$static_settings = $this->settings;
+ $this->rest_API_server = $rest_API_server;
+ }
+
+ public function register()
+ {
+ $this->http_remove_license();
+ $this->http_disable_ssl();
+ $this->http_refresh_licence();
+
+ // Required for Pull if user tables being updated.
+ add_action( 'wp_ajax_wpmdb_check_licence', array( $this, 'ajax_check_licence' ) );
+ add_action( 'wp_ajax_nopriv_wpmdb_copy_licence_to_remote_site', array( $this, 'respond_to_copy_licence_to_remote_site' ) );
+
+ // REST endpoints
+ add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
+
+ // Init license response messages
+ add_action('admin_init', [$this, 'init_license_response_messages']);
+ }
+
+ /**
+ * Initializes license response messages.
+ * Hooked to admin_init
+ */
+ public function init_license_response_messages()
+ {
+ $this->license_response_messages = $this->setup_license_responses( $this->props->plugin_base );
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute( '/copy-license-to-remote', [
+ 'methods' => 'POST',
+ 'callback' => [ $this, 'ajax_copy_licence_to_remote_site' ],
+ ] );
+
+ $this->rest_API_server->registerRestRoute( '/activate-license', [
+ 'methods' => 'POST',
+ 'callback' => [ $this, 'ajax_activate_licence' ],
+ ] );
+
+ $this->rest_API_server->registerRestRoute( '/remove-license', [
+ 'methods' => 'POST',
+ 'callback' => [ $this, 'ajax_remove_license' ],
+ ] );
+
+ $this->rest_API_server->registerRestRoute( '/disable-ssl', [
+ 'methods' => 'POST',
+ 'callback' => [ $this, 'ajax_disable_ssl' ],
+ ] );
+
+ $this->rest_API_server->registerRestRoute( '/check-license', [
+ 'methods' => 'POST',
+ 'callback' => [ $this, 'ajax_check_licence' ],
+ ] );
+
+ $this->rest_API_server->registerRestRoute( '/reactivate-license', [
+ 'methods' => 'POST',
+ 'callback' => [ $this, 'ajax_reactivate_licence' ],
+ ] );
+ }
+
+ public function ajax_disable_ssl()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ set_site_transient( 'wpmdb_temporarily_disable_ssl', '1', 60 * 60 * 24 * 30 ); // 30 days
+ // delete the licence transient as we want to attempt to fetch the licence details again
+ delete_site_transient( Helpers::get_licence_response_transient_key() );
+
+ // @TODO we're not checking if this fails
+ return $this->http->end_ajax( 'ssl disabled' );
+ }
+
+ public function ajax_remove_license()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'remove_license' => 'bool',
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data( $key_rules );
+
+ if ( $state_data['remove_license'] !== true ) {
+ $this->http->end_ajax( 'license not removed' );
+ }
+
+ $this->set_licence_key( '' );
+ // delete these transients as they contain information only valid for authenticated licence holders
+ delete_site_transient( 'update_plugins' );
+ delete_site_transient( 'wpmdb_upgrade_data' );
+ delete_site_transient( Helpers::get_licence_response_transient_key() );
+ delete_site_transient($this->get_available_addons_list_transient_key(get_current_user_id()));
+
+ $this->http->end_ajax( 'license removed' );
+ }
+
+ /**
+ * AJAX handler for checking a licence.
+ *
+ * @return string (JSON)
+ */
+ //@TODO this needs a major cleanup/refactor
+ function ajax_check_licence()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'licence' => 'string',
+ 'context' => 'key',
+ 'message_context' => 'string',
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data( $key_rules );
+
+ $message_context = isset( $state_data['message_context'] ) ? $state_data['message_context'] : 'ui';
+
+ $licence = ( empty( $state_data['licence'] ) ? $this->get_licence_key() : $state_data['licence'] );
+ $response = $this->check_licence( $licence );
+ $decoded_response = json_decode( $response, ARRAY_A );
+ $context = ( empty( $state_data['context'] ) ? null : $state_data['context'] );
+
+ if (
+ isset( $decoded_response['errors'] )
+ && !empty( $decoded_response['errors'] )
+ ) {
+ $keys = array_keys( $decoded_response['errors'] );
+
+ if ( isset( $keys[0] ) ) {
+ $decoded_response['licence_status'] = $keys[0];
+ }
+ }
+
+ if ( false == $licence ) {
+ $decoded_response = array( 'errors' => array() );
+ $decoded_response['errors'] = array( sprintf( '%s
', $this->get_licence_status_message( false, null, $message_context ) ) );
+ } elseif ( !empty( $decoded_response['dbrains_api_down'] ) ) {
+ $help_message = get_site_transient( 'wpmdb_help_message' );
+
+ if ( !$help_message ) {
+ ob_start();
+ ?>
+ active license, you may send an email to the following address.', 'wp-migrate-db' ); ?>
+
+
+
+ wpmdb@deliciousbrains.com
+ get_licence_status_message( $decoded_response, $context, $message_context );
+ foreach ( $licence_status_messages as $frontend_context => $status_message ) {
+ $decoded_response['errors']['subscription_expired'][$frontend_context] = sprintf( '%s
', $status_message );
+ }
+ } else {
+ $error_key = array_keys( $decoded_response['errors'] )[0];
+
+ $decoded_response['errors'][$error_key] = [ 'default' => sprintf( '%s
', $this->get_licence_status_message( $decoded_response, $context, $message_context ) ) ];
+ }
+ } elseif ( !empty( $decoded_response['message'] ) && !get_site_transient( 'wpmdb_help_message' ) ) {
+ set_site_transient( 'wpmdb_help_message', $decoded_response['message'], $this->props->transient_timeout );
+ }
+
+ if ( isset( $decoded_response['addon_list'] ) ) {
+
+ if ( empty( $decoded_response['errors'] ) ) {
+ $addons_available = ( $decoded_response['addons_available'] == '1' );
+ $addon_content = array();
+
+ if ( ! $addons_available ) {
+ $addon_content['error'] = sprintf(
+ __( 'Addons Unavailable — Addons are not included with the Personal license. Visit My Account to upgrade in just a few clicks.', 'wp-migrate-db'),
+ 'https://deliciousbrains.com/my-account/?utm_campaign=support%2Bdocs&utm_source=MDB%2BPaid&utm_medium=insideplugin'
+ );
+ }
+ }
+
+ // Save the addons list for use when installing
+ // Don't really need to expire it ever, but let's clean it up after 60 days
+ set_site_transient( 'wpmdb_addons', $decoded_response['addon_list'], HOUR_IN_SECONDS * 24 * 60 );
+
+ if ( isset( $decoded_response['addons_available_list'] ) ) {
+ $this->set_available_addons_list_transient(
+ $decoded_response['addons_available_list'],
+ get_current_user_id()
+ );
+ }
+
+ foreach ( $decoded_response['addon_list'] as $key => $addon ) {
+ $plugin_file = sprintf( '%1$s/%1$s.php', $key );
+ $plugin_ids = array_keys( get_plugins() );
+
+ if ( in_array( $plugin_file, $plugin_ids ) ) {
+ if ( ! is_plugin_active( $plugin_file ) ) {
+ $addon_content[$key]['activate_url'] = add_query_arg(
+ array(
+ 'action' => 'activate',
+ 'plugin' => $plugin_file,
+ '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $plugin_file ),
+ ),
+ network_admin_url( 'plugins.php' )
+ );
+ }
+ } else {
+ $addon_content[$key]['install_url'] = add_query_arg(
+ array(
+ 'action' => 'install-plugin',
+ 'plugin' => $key,
+ '_wpnonce' => wp_create_nonce( 'install-plugin_' . $key ),
+ ),
+ network_admin_url( 'update.php' )
+ );
+ }
+
+ $is_beta = !empty( $addon['beta_version'] ) && BetaManager::has_beta_optin( $this->settings );
+ $addon_content[$key]['download_url'] = $this->download->get_plugin_update_download_url( $key, $is_beta );
+ }
+ $decoded_response['addon_content'] = $addon_content;
+ }
+
+ return $this->http->end_ajax( $decoded_response );
+ }
+
+ /**
+ * AJAX handler for activating a licence.
+ *
+ * @return string (JSON)
+ */
+ function ajax_activate_licence()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'licence_key' => 'string',
+ 'context' => 'key',
+ 'message_context' => 'string',
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data( $key_rules );
+ $message_context = isset( $state_data['message_context'] ) ? $state_data['message_context'] : 'ui';
+
+ $args = array(
+ 'licence_key' => urlencode( $state_data['licence_key'] ),
+ 'site_url' => urlencode( untrailingslashit( network_home_url( '', 'http' ) ) ),
+ );
+
+ $response = $this->api->dbrains_api_request( 'activate_licence', $args );
+ $decoded_response = json_decode( $response, true );
+
+ if ( empty( $decoded_response['errors'] ) && empty( $decoded_response['dbrains_api_down'] ) ) {
+ $this->set_licence_key( $state_data['licence_key'] );
+ $decoded_response['masked_licence'] = $this->util->mask_licence( $state_data['licence_key'] );
+ } else { // License check errors
+
+ if ( isset( $decoded_response['errors']['activation_deactivated'] ) ) {
+ $this->set_licence_key( $state_data['licence_key'] );
+ } elseif ( isset( $decoded_response['errors']['subscription_expired'] ) || isset( $decoded_response['dbrains_api_down'] ) ) {
+ $this->set_licence_key( $state_data['licence_key'] );
+ $decoded_response['masked_licence'] = $this->util->mask_licence( $state_data['licence_key'] );
+ }
+
+ set_site_transient( Helpers::get_licence_response_transient_key(), $response, $this->props->transient_timeout );
+
+ if (isset($decoded_response['available_addons_list'])) {
+ $this->set_available_addons_list_transient($decoded_response['available_addons_list'], get_current_user_id());
+ }
+
+ if ( true === $this->dynamic_props->doing_cli_migration ) {
+ $decoded_response['errors'] = array(
+ $this->get_licence_status_message( $decoded_response, $state_data['context'], $message_context ),
+ );
+ } else {
+ list( $error_key ) = array_keys( $decoded_response['errors'] );
+ $decoded_response['error_type'] = $error_key;
+
+ $decoded_response['errors'][$error_key] =
+ $this->get_licence_status_message( $decoded_response, $state_data['context'], $message_context );
+ }
+
+ if ( isset( $decoded_response['dbrains_api_down'] ) ) {
+ $decoded_response['errors'][] = $decoded_response['dbrains_api_down'];
+ }
+ }
+ $result = $this->http->end_ajax( $decoded_response );
+
+ return $result;
+ }
+
+
+ /**
+ * Sends the local WP Migrate DB Pro licence to the remote machine and activates it, returns errors if applicable.
+ *
+ * @return array Empty array or an array containing an error message.
+ */
+ function ajax_copy_licence_to_remote_site()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'url' => 'url',
+ 'key' => 'string',
+ 'nonce' => 'key',
+ );
+ $state_data = $this->migration_state_manager->set_post_data( $key_rules );
+
+ $current_user = wp_get_current_user();
+
+ $data = array(
+ 'action' => 'wpmdb_copy_licence_to_remote_site',
+ 'licence' => $this->get_licence_key(),
+ 'user_id' => $current_user->ID,
+ 'user_email' => $current_user->user_email,
+ );
+
+ $data['sig'] = $this->http_helper->create_signature( $data, $state_data['key'] );
+ $ajax_url = $this->util->ajax_url();
+ $response = $this->remote_post->post( $ajax_url, $data, __FUNCTION__, array() );
+
+ if (is_wp_error($response)) {
+ return $this->http->end_ajax($response);
+ }
+
+ return $this->http->end_ajax(true);
+ }
+
+ /**
+ * Stores and attempts to activate the licence key received via a remote machine, returns errors if applicable.
+ *
+ * @return array Empty array or an array containing an error message.
+ */
+ function respond_to_copy_licence_to_remote_site()
+ {
+ add_filter( 'wpmdb_before_response', array( $this->scrambler, 'scramble' ) );
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'licence' => 'string',
+ 'sig' => 'string',
+ 'user_id' => 'numeric',
+ 'user_email' => 'string',
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data( $key_rules );
+ $filtered_post = $this->http_helper->filter_post_elements( $state_data, array( 'action', 'licence', 'user_id', 'user_email' ) );
+
+ if ( ! $this->http_helper->verify_signature( $filtered_post, $this->settings['key'] ) ) {
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb_invalid_content_verification_error',
+ $this->props->invalid_content_verification_error . ' (#142)'
+ )
+ );
+ }
+
+ $user = get_user_by( 'id', $state_data['user_id'] );
+ if ( $user && $user->user_email === $state_data['user_email'] ) {
+ update_user_meta( $user->ID, Helpers::USER_LICENCE_META_KEY, trim( $state_data['licence'] ) );
+ } else {
+ $this->set_global_licence_key( trim( $state_data['licence'] ) );
+ }
+
+ $licence_status = json_decode( $this->check_licence( trim( $state_data['licence'] ), $state_data['user_id'] ), true );
+
+ if ( isset( $licence_status['errors'] ) && !isset( $licence_status['errors']['subscription_expired'] ) ) {
+ $message = reset( $licence_status['errors'] );
+ $this->error_log->log_error( $message, $licence_status );
+
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb_invalid_license',
+ $message
+ )
+ );
+ }
+
+ return $this->http->end_ajax(true);
+ }
+
+
+ public static function get_license()
+ {
+ return self::$license_key;
+ }
+
+ public function setup_license_responses( $plugin_base )
+ {
+ $disable_ssl_url = network_admin_url( $plugin_base . '&nonce=' . Util::create_nonce( 'wpmdb-disable-ssl' ) . '&wpmdb-disable-ssl=1' );
+ $check_licence_again_url = network_admin_url( $plugin_base . '&nonce=' . Util::create_nonce( 'wpmdb-check-licence' ) . '&wpmdb-check-licence=1' );
+
+ // List of potential license responses. Keys must must exist in both arrays, otherwise the default error message will be shown.
+ $this->license_response_messages = array(
+ 'connection_failed' => array(
+ 'ui' => sprintf( __( 'Could not connect to api.deliciousbrains.com — You will not receive update notifications or be able to activate your license until this is fixed. This issue is often caused by an improperly configured SSL server (https). We recommend fixing the SSL configuration on your server, but if you need a quick fix you can:%2$s', 'wp-migrate-db' ),
+ 'https://deliciousbrains.com/wp-migrate-db-pro/doc/could-not-connect-deliciousbrains-com/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin', sprintf( '%2$s', $disable_ssl_url, __( 'Temporarily disable SSL for connections to api.deliciousbrains.com', 'wp-migrate-db' ) ) ),
+ 'settings' => sprintf( __( 'Could not connect to api.deliciousbrains.com — You will not receive update notifications or be able to activate your license until this is fixed. This issue is often caused by an improperly configured SSL server (https). We recommend fixing the SSL configuration on your server.', 'wp-migrate-db' ),
+ 'https://deliciousbrains.com/wp-migrate-db-pro/doc/could-not-connect-deliciousbrains-com/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ 'cli' => __( 'Could not connect to api.deliciousbrains.com - You will not receive update notifications or be able to activate your license until this is fixed. This issue is often caused by an improperly configured SSL server (https). We recommend fixing the SSL configuration on your server, but if you need a quick fix you can temporarily disable SSL for connections to api.deliciousbrains.com by adding `define( \'DBRAINS_API_BASE\', \'http://api.deliciousbrains.com\' );` to your wp-config.php file.',
+ 'wp-migrate-db' ),
+ ),
+ 'http_block_external' => array(
+ 'ui' => __( 'We\'ve detected that WP_HTTP_BLOCK_EXTERNAL
is enabled and the host %1$s has not been added to WP_ACCESSIBLE_HOSTS
. Please disable WP_HTTP_BLOCK_EXTERNAL
or add %1$s to WP_ACCESSIBLE_HOSTS
to continue. More information.', 'wp-migrate-db' ),
+ 'cli' => __( 'We\'ve detected that WP_HTTP_BLOCK_EXTERNAL is enabled and the host %1$s has not been added to WP_ACCESSIBLE_HOSTS. Please disable WP_HTTP_BLOCK_EXTERNAL or add %1$s to WP_ACCESSIBLE_HOSTS to continue.', 'wp-migrate-db' ),
+ ),
+ 'subscription_cancelled' => array(
+ 'ui' => sprintf( __( 'License Cancelled — The license key has been cancelled. Please remove it and enter a valid license key.
Your license key can be found in My Account. If you don\'t have an account yet, purchase a new license.', 'wp-migrate-db' ), network_admin_url( $this->props->plugin_base ) . '#settings/enter', 'https://deliciousbrains.com/my-account/?utm_campaign=support%2Bdocs&utm_source=MDB%2BPaid&utm_medium=insideplugin',
+ 'https://deliciousbrains.com/wp-migrate-db-pro/pricing/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ 'settings' => sprintf( __( 'License Cancelled — The license key below has been cancelled. Please remove it and enter a valid license key.
Your license key can be found in My Account. If you don\'t have an account yet, purchase a new license.', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account/?utm_campaign=support%2Bdocs&utm_source=MDB%2BPaid&utm_medium=insideplugin',
+ 'https://deliciousbrains.com/wp-migrate-db-pro/pricing/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ 'cli' => sprintf( __( 'License Cancelled - Please login to your account (%s) to renew or upgrade your license and enable push and pull.', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account/?utm_campaign=support%2Bdocs&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ ),
+ 'subscription_expired_base' => array(
+ 'ui' => sprintf( '%s — ', __( 'Your License Has Expired', 'wp-migrate-db' ) ),
+ 'cli' => sprintf( '%s - ', __( 'Your License Has Expired', 'wp-migrate-db' ) ),
+ ),
+ 'subscription_expired_end' => array(
+ 'ui' => sprintf( __( 'Login to My Account to renew.', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account/?utm_campaign=support%2Bdocs&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ 'settings' => sprintf( __( 'Login to My Account to renew.', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account/?utm_campaign=support%2Bdocs&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ 'cli' => sprintf( __( 'Login to your account to renew (%s)', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account/' ),
+ ),
+ 'no_activations_left' => array(
+ 'ui' => sprintf( __( 'No Activations Left — Please visit My Account to upgrade your license and enable push and pull.', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account/?utm_campaign=support%2Bdocs&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ 'settings' => sprintf( __( 'No Activations Left — Please visit My Account to upgrade your license and enable push and pull.', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account/?utm_campaign=support%2Bdocs&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ 'cli' => sprintf( __( 'No Activations Left - Please visit your account (%s) to upgrade your license and enable push and pull.', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account/?utm_campaign=support%2Bdocs&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ ),
+ 'licence_not_found_api_failed' => array(
+ 'ui' => sprintf( __( 'License Not Found — The license key below cannot be found in our database. Please remove it and enter a valid license key.
Your license key can be found in My Account . If you don\'t have an account yet, purchase a new license.', 'wp-migrate-db' ),
+ 'https://deliciousbrains.com/my-account/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin', 'https://deliciousbrains.com/wp-migrate-db-pro/pricing/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ 'settings' => sprintf( __( 'License Not Found — The license key below cannot be found in our database. Please remove it and enter a valid license key.
Your license key can be found in My Account . If you don\'t have an account yet, purchase a new license.', 'wp-migrate-db' ),
+ 'https://deliciousbrains.com/my-account/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin', 'https://deliciousbrains.com/wp-migrate-db-pro/pricing/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin' ),
+ 'cli' => sprintf( __( 'Your License Was Not Found - The license key below cannot be found in our database. Please remove it and enter a valid license key. Please visit your account (%s) to double check your license key.', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account/' ),
+ ),
+ 'licence_not_found_api' => array(
+ 'ui' => __( 'License Not Found — %s', 'wp-migrate-db' ),
+ 'cli' => __( 'License Not Found - %s', 'wp-migrate-db' ),
+ ),
+ 'activation_deactivated' => array(
+ 'ui' => sprintf( '%1$s — %2$s', __( 'License Inactive', 'wp-migrate-db' ), __( 'The license was deactivated after 30 days of not using WP Migrate. Reactivate to access plugin updates, support, and premium features.', 'wp-migrate-db' ) ),
+ 'cli' => sprintf( '%s - %s %s at %s', __( 'License Inactive', 'wp-migrate-db' ), __( 'The license was deactivated after 30 days of not using WP Migrate. Reactivate to access plugin updates, support, and premium features.', 'wp-migrate-db' ), __( 'Reactivate license', 'wp-migrate-db' ), 'https://deliciousbrains.com/my-account' ),
+ ),
+ 'default' => array(
+ 'ui' => __( 'An Unexpected Error Occurred — Please contact us at %2$s and quote the following: %3$s
', 'wp-migrate-db' ),
+ 'cli' => __( 'An Unexpected Error Occurred - Please contact us at %2$s and quote the following: %3$s', 'wp-migrate-db' ),
+ ),
+ );
+
+ return $this->license_response_messages;
+ }
+
+
+ function is_licence_constant()
+ {
+ return defined( 'WPMDB_LICENCE' );
+ }
+
+ public function get_licence_key()
+ {
+ if ( $this->is_licence_constant() ) {
+ return WPMDB_LICENCE;
+ }
+
+ $user_id = Helpers::get_current_or_first_user_id_with_licence_key();
+ if ( $user_id ) {
+ $licence = Helpers::get_user_licence_key( $user_id );
+ if ( $licence ) {
+ return $licence;
+ }
+ }
+
+ if ( isset( $this->settings['licence'] ) && '' !== $this->settings['licence'] ) {
+ return $this->settings['licence'];
+ }
+
+ return false;
+ }
+
+ /**
+ * Sets the licence index in the $settings array class property and updates the wpmdb_settings option.
+ *
+ * @param string $key
+ */
+ function set_licence_key( $key )
+ {
+ if ( isset( $this->settings['licence'] ) ) {
+ unset( $this->settings['licence'] );
+ update_site_option( 'wpmdb_settings', $this->settings );
+ }
+
+ update_user_meta( get_current_user_id(), Helpers::USER_LICENCE_META_KEY, $key );
+ }
+
+ /**
+ * Set Global licence key, stored in Options table.
+ *
+ * @param string $key License key.
+ */
+ public function set_global_licence_key( $key ) {
+ $this->settings['licence'] = $key;
+ update_site_option( 'wpmdb_settings', $this->settings );
+ }
+
+ public function check_license_status()
+ {
+ $response = $this->get_license_status();
+
+ if ( isset( $response['errors']['subscription_expired'] ) && 1 === count( $response['errors'] ) ) {
+ return 'subscription_expired';
+ }
+
+ if ( isset( $response['errors']['subscription_cancelled'] ) && 1 === count( $response['errors'] ) ) {
+ return 'subscription_cancelled';
+ }
+
+ if ( isset( $response['errors']['licence_not_found'] ) && 1 === count( $response['errors'] ) ) {
+ return 'licence_not_found';
+ }
+
+ if ( isset( $response['errors']['activation_deactivated'] ) && 1 === count( $response['errors'] ) ) {
+ return 'activation_deactivated';
+ }
+
+ if ( isset( $response['errors']['no_licence'] ) ) {
+ return null;
+ }
+
+ if ( !isset( $response['errors'] ) ) {
+ return 'active_licence';
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks whether the saved licence has expired or not.
+ *
+ * @param bool $skip_transient_check
+ *
+ * @return bool
+ */
+ function is_valid_licence( $skip_transient_check = false )
+ {
+ if (empty($this->get_available_addons_list(get_current_user_id()))) {
+ $skip_transient_check = true;
+ }
+
+ $response = $this->get_license_status( $skip_transient_check );
+
+ if ( isset( $response['dbrains_api_down'] ) ) {
+ return true;
+ }
+
+ // Don't cripple the plugin's functionality if the user's licence is expired
+ if ( isset( $response['errors']['subscription_expired'] ) && 1 === count( $response['errors'] ) ) {
+ return true;
+ }
+
+ return ( isset( $response['errors'] ) ) ? false : true;
+ }
+
+ function get_license_status( $skip_transient_check = false )
+ {
+ $licence = $this->get_licence_key();
+
+ if ( empty( $licence ) ) {
+ $settings_link = sprintf( '%s', network_admin_url( $this->props->plugin_base ) . '#settings/enter', _x( 'Settings', 'Plugin configuration and preferences', 'wp-migrate-db' ) );
+ $message = sprintf( __( 'To finish activating WP Migrate, please go to %1$s and enter your license key. If you don\'t have a license key, you may purchase one.', 'wp-migrate-db' ), $settings_link, 'http://deliciousbrains.com/wp-migrate-db-pro/pricing/' );
+
+ return array( 'errors' => array( 'no_licence' => $message ) );
+ }
+
+ if ( !$skip_transient_check
+ && ( !defined( 'WPMDB_SKIP_LICENSE_TRANSIENT' ) ) ) {
+ $trans = get_site_transient( Helpers::get_licence_response_transient_key() );
+
+ if ( false !== $trans ) {
+ $decoded_transient = json_decode( $trans, true );
+ $user_id = get_current_user_id();
+ if (false === $this->get_available_addons_list($user_id) && isset($decoded_transient['addons_available_list'])) {
+ $this->set_available_addons_list_transient($decoded_transient['addons_available_list'], $user_id);
+ }
+ return $decoded_transient;
+ }
+ }
+
+ return json_decode( $this->check_licence( $licence, get_current_user_id() ), true );
+ }
+
+ /**
+ * @TODO this needs to be refactored to actually check API response - take a look when refactoring ajax_check_licence() above
+ *
+ * @return array|bool|mixed|object
+ */
+ public function get_api_data()
+ {
+ $api_data = get_site_transient( Helpers::get_licence_response_transient_key() );
+ if ( !empty( $api_data ) ) {
+ return json_decode( $api_data, true );
+ }
+
+ $response = $this->check_licence( $this->get_licence_key(), get_current_user_id() );
+ if ( ! empty( $response ) ) {
+ return json_decode( $response, true );
+ }
+
+ return false;
+ }
+
+ function check_licence( $licence_key, $user_id = false )
+ {
+ if ( empty( $licence_key ) ) {
+ return false;
+ }
+
+ if ( empty( $user_id ) ) {
+ $user_id = get_current_user_id();
+ }
+
+ $args = array(
+ 'licence_key' => urlencode( $licence_key ),
+ 'site_url' => urlencode( untrailingslashit( network_home_url( '', 'http' ) ) ),
+ );
+
+ $response = $this->api->dbrains_api_request( 'check_support_access', $args );
+
+ set_site_transient( Helpers::get_licence_response_transient_key( $user_id, false ), $response, $this->props->transient_timeout );
+
+ //Store available addons list
+ $decoded_response = json_decode($response, true);
+ if (isset($decoded_response['addons_available_list'])) {
+ $this->set_available_addons_list_transient(
+ $decoded_response['addons_available_list'],
+ $user_id
+ );
+ }
+
+ return $response;
+ }
+
+
+ /**
+ *
+ * Get a message from the $messages array parameter based on a context
+ *
+ * Assumes the $messages array exists in the format of a nested array.
+ *
+ * Also assumes the nested array of strings has a key of 'default'
+ *
+ * Ex:
+ *
+ * array(
+ * 'key1' => array(
+ * 'ui' => 'Some message',
+ * 'cli' => 'Another message',
+ * ...
+ * ),
+ *
+ * 'key2' => array(
+ * 'ui' => 'Some message',
+ * 'cli' => 'Another message',
+ * ...
+ * ),
+ *
+ * 'default' => array(
+ * 'ui' => 'Some message',
+ * 'cli' => 'Another message',
+ * ...
+ * ),
+ * )
+ *
+ * @param array $messages
+ * @param $key
+ * @param string $context
+ *
+ * @return mixed
+ */
+ function get_contextual_message_string( $messages, $key, $context = 'ui' )
+ {
+ $message = $messages[$key];
+
+ if ( isset( $message[$context] ) ) {
+ return $message[$context];
+ }
+
+ if ( isset( $message['ui'] ) ) {
+ return $message['ui'];
+ }
+
+ if ( isset( $message['default'] ) ) {
+ return $message['default'];
+ }
+
+ return '';
+ }
+
+ /**
+ * Returns a formatted message dependant on the status of the licence.
+ *
+ * @param bool $trans
+ * @param string $context
+ * @param string $message_context
+ *
+ * @return array|mixed|string
+ */
+ function get_licence_status_message( $trans = false, $context = null, $message_context = 'ui' )
+ {
+ $this->setup_license_responses( $this->props->plugin_base );
+
+ $licence = $this->get_licence_key();
+ $api_response_provided = true;
+ $messages = $this->license_response_messages;
+ $message = '';
+
+ if ( $this->dynamic_props->doing_cli_migration ) {
+ $message_context = 'cli';
+ }
+
+ if ( empty( $licence ) && !$trans ) {
+ $message = [];
+ $message['default'] = sprintf( __( 'Activate Your License — Please enter your license key to enable push and pull functionality, priority support and plugin updates.', 'wp-migrate-db' ), network_admin_url( $this->props->plugin_base . '#settings/enter' ), 'js-action-link enter-licence' );
+ $message['addons'] = sprintf( __( 'Activate Your License — Please enter your license key to activate any upgrades associated with your license.', 'wp-migrate-db' ), network_admin_url( $this->props->plugin_base . '#settings/enter' ), 'js-action-link enter-licence' );
+
+ if ('update' === $context) {
+ return $message['default'];
+ }
+
+ return $message;
+ }
+
+ if ( !$trans ) {
+ $trans = get_site_transient( Helpers::get_licence_response_transient_key() );
+
+ if ( false === $trans ) {
+ $trans = $this->check_licence( $licence );
+ }
+
+ $trans = json_decode( $trans, true );
+ $api_response_provided = false;
+ }
+
+ if ( isset( $trans['dbrains_api_down'] ) ) {
+ return __( "We've temporarily activated your license and will complete the activation once the Delicious Brains API is available again.", 'wp-migrate-db' );
+ }
+
+ $errors = empty( $trans['errors'] ) || !is_array( $trans['errors'] ) ? [] : $trans['errors'];
+
+ if ( isset( $errors['connection_failed'] ) ) {
+ $message = $this->get_contextual_message_string( $messages, 'connection_failed', $message_context );
+
+ if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL ) {
+ $url_parts = Util::parse_url( $this->api->get_dbrains_api_base() );
+ $host = $url_parts['host'];
+ if ( !defined( 'WP_ACCESSIBLE_HOSTS' ) || strpos( WP_ACCESSIBLE_HOSTS, $host ) === false ) {
+ $message = sprintf( $this->get_contextual_message_string( $messages, 'http_block_external', $message_context ), esc_attr( $host ), 'https://deliciousbrains.com/wp-migrate-db-pro/doc/wp_http_block_external/?utm_campaign=error%2Bmessages&utm_source=MDB%2BPaid&utm_medium=insideplugin' );
+ }
+ }
+
+ // Don't cache the license response so we can try again
+ delete_site_transient( Helpers::get_licence_response_transient_key() );
+ } elseif ( isset( $errors['subscription_cancelled'] ) ) {
+ $message = $this->get_contextual_message_string( $messages, 'subscription_cancelled', $message_context );
+ } elseif ( isset( $errors['subscription_expired'] ) ) {
+ $message_base = $this->get_contextual_message_string( $messages, 'subscription_expired_base', $message_context );
+ $message_end = $this->get_contextual_message_string( $messages, 'subscription_expired_end', $message_context );
+
+ $contextual_messages = array(
+ 'default' => $message_base . $message_end,
+ 'update' => $message_base . __( 'Updates are only available to those with an active license. ', 'wp-migrate-db' ) . $message_end,
+ 'addons' => $message_base . __( 'Only active licenses can download and install addons. ', 'wp-migrate-db' ) . $message_end,
+ 'support' => $message_base . __( 'Only active licenses can submit support requests. ', 'wp-migrate-db' ) . $message_end,
+ 'licence' => $message_base . __( "All features will continue to work, but you won't be able to receive updates or email support. ", 'wp-migrate-db' ) . $message_end,
+ );
+
+ if ( empty( $context ) ) {
+ $context = 'default';
+ }
+ if ( !empty( $contextual_messages[$context] ) ) {
+ $message = $contextual_messages[$context];
+ } elseif ( 'all' === $context ) {
+ $message = $contextual_messages;
+ }
+ } elseif ( isset( $errors['no_activations_left'] ) ) {
+ $message = $this->get_contextual_message_string( $messages, 'no_activations_left', $message_context );
+ } elseif ( isset( $errors['licence_not_found'] ) ) {
+ if ( !$api_response_provided ) {
+ $message = $this->get_contextual_message_string( $messages, 'licence_not_found_api_failed', $message_context );
+ } else {
+ $error = reset( $errors );
+ $message = sprintf( $this->get_contextual_message_string( $messages, 'licence_not_found_api', $message_context ), $error );
+ }
+ } elseif ( isset( $errors['activation_deactivated'] ) ) {
+ $message = $this->get_contextual_message_string( $messages, 'activation_deactivated', $message_context );
+ } else {
+ $error = reset( $errors );
+ $message = sprintf( $this->get_contextual_message_string( $messages, 'default', $message_context ), 'mailto:nom@deliciousbrains.com', 'nom@deliciousbrains.com', $error );
+ }
+
+ return $message;
+ }
+
+ /**
+ * Check for wpmdb-remove-licence and related nonce
+ * if found cleanup routines related to licenced product
+ *
+ * @return void
+ */
+ function http_remove_license()
+ {
+ if ( isset( $_GET['wpmdb-remove-licence'] ) && wp_verify_nonce( $_GET['nonce'], 'wpmdb-remove-licence' ) ) {
+ $this->set_licence_key( '' );
+ // delete these transients as they contain information only valid for authenticated licence holders
+ delete_site_transient( 'update_plugins' );
+ delete_site_transient( 'wpmdb_upgrade_data' );
+ delete_site_transient( Helpers::get_licence_response_transient_key() );
+ // redirecting here because we don't want to keep the query string in the web browsers address bar
+ wp_redirect( network_admin_url( $this->props->plugin_base . '#settings' ) );
+ exit;
+ }
+ }
+
+ /**
+ * Check for wpmdb-disable-ssl and related nonce
+ * if found temporaily disable ssl via transient
+ *
+ * @return void
+ */
+ function http_disable_ssl()
+ {
+ if ( isset( $_GET['wpmdb-disable-ssl'] ) && wp_verify_nonce( $_GET['nonce'], 'wpmdb-disable-ssl' ) ) {
+ set_site_transient( 'wpmdb_temporarily_disable_ssl', '1', 60 * 60 * 24 * 30 ); // 30 days
+ $hash = ( isset( $_GET['hash'] ) ) ? '#' . sanitize_title( $_GET['hash'] ) : '';
+ // delete the licence transient as we want to attempt to fetch the licence details again
+ delete_site_transient( Helpers::get_licence_response_transient_key() );
+ // redirecting here because we don't want to keep the query string in the web browsers address bar
+ wp_redirect( network_admin_url( $this->props->plugin_base . $hash ) );
+ exit;
+ }
+ }
+
+ /**
+ * Check for wpmdb-check-licence and related nonce
+ * if found refresh licence details
+ *
+ * @return void
+ */
+ function http_refresh_licence()
+ {
+ if ( isset( $_GET['wpmdb-check-licence'] ) && wp_verify_nonce( $_GET['nonce'], 'wpmdb-check-licence' ) ) {
+ $hash = ( isset( $_GET['hash'] ) ) ? '#' . sanitize_title( $_GET['hash'] ) : '';
+ // delete the licence transient as we want to attempt to fetch the licence details again
+ delete_site_transient( Helpers::get_licence_response_transient_key() );
+ // redirecting here because we don't want to keep the query string in the web browsers address bar
+ wp_redirect( network_admin_url( $this->props->plugin_base . $hash ) );
+ exit;
+ }
+ }
+
+ function get_formatted_masked_licence()
+ {
+ return sprintf(
+ '%s %s
',
+ $this->util->mask_licence( $this->get_licence_key() ),
+ network_admin_url( $this->props->plugin_base . '&nonce=' . Util::create_nonce( 'wpmdb-remove-licence' ) . '&wpmdb-remove-licence=1#settings' ),
+ _x( 'Remove', 'Delete license', 'wp-migrate-db' )
+ );
+ }
+
+ /**
+ * Attempts to reactivate this instance via the Delicious Brains API.
+ *
+ * @return string (JSON)
+ */
+ public function ajax_reactivate_licence()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = array(
+ 'context' => 'key',
+ 'message_context' => 'string',
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data( $key_rules );
+ $message_context = isset( $state_data['message_context'] ) ? $state_data['message_context'] : 'ui';
+ $licence_key = $this->get_licence_key();
+
+ $args = array(
+ 'licence_key' => urlencode( $licence_key ),
+ 'site_url' => urlencode( untrailingslashit( network_home_url( '', 'http' ) ) ),
+ );
+
+ $response = $this->api->dbrains_api_request( 'reactivate_licence', $args );
+ $decoded_response = json_decode( $response, true );
+
+ if ( empty( $decoded_response['errors'] ) && empty( $decoded_response['dbrains_api_down'] ) ) {
+ // Successfully reactivating a license does not return license info,
+ // so ensure license info is refreshed on next check.
+ delete_site_transient( 'wpmdb_upgrade_data' );
+ delete_site_transient( Helpers::get_licence_response_transient_key() );
+ } else {
+ // There was some sort of error.
+ set_site_transient( Helpers::get_licence_response_transient_key(), $response, $this->props->transient_timeout );
+
+ if ( ! empty( $decoded_response['errors'] ) ) {
+ list( $error_key ) = array_keys( $decoded_response['errors'] );
+ $decoded_response['error_type'] = $error_key;
+
+ $decoded_response['errors'][$error_key] =
+ $this->get_licence_status_message( $decoded_response, $state_data['context'], $message_context );
+ }
+
+ if ( ! empty( $decoded_response['dbrains_api_down'] ) ) {
+ $decoded_response['errors'][] = $decoded_response['dbrains_api_down'];
+ }
+ }
+
+ // Error or not, ensure masked license is returned.
+ $decoded_response['masked_licence'] = $this->util->mask_licence( $licence_key );
+
+ return $this->http->end_ajax( $decoded_response );
+ }
+
+
+ private function get_available_addons_list_transient_key($user_id = null)
+ {
+ $transient_key = 'wpmdb_available_addons';
+ if ( !empty($user_id) && 0 !== $user_id ) {
+ $transient_key = 'wpmdb_available_addons_per_user_' . $user_id;
+ }
+
+ return $transient_key;
+ }
+
+
+ private function set_available_addons_list_transient($list, $user_id = null)
+ {
+ set_site_transient($this->get_available_addons_list_transient_key($user_id), $list);
+ }
+
+
+ public function get_available_addons_list($user_id = null)
+ {
+ return get_site_transient($this->get_available_addons_list_transient_key($user_id));
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCli.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCli.php
new file mode 100644
index 000000000..bf49ec101
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCli.php
@@ -0,0 +1,371 @@
+cli = $cli;
+ $this->cli_manager = $cli_manager;
+ $this->util = $util;
+ $this->state_data_container = $state_data_container;
+ $this->filesystem = $filesystem;
+ }
+
+ public function register()
+ {
+ // Accepted profile fields exclusive to Media Files.
+ add_filter('wpmdb_accepted_profile_fields', [$this, 'accepted_profile_fields']);
+
+ // Announce extra args for Media Files.
+ add_filter('wpmdb_cli_filter_get_extra_args', [$this, 'filter_extra_args'], 10, 1);
+
+ // Add extra args for Media Files migrations.
+ add_filter('wpmdb_cli_filter_get_profile_data_from_args', [$this, 'add_mf_profile_args'], 11, 3);
+
+ // Add the Media Files stage.
+ add_filter('wpmdb_cli_profile_before_migration', [$this, 'add_mf_stage'], PHP_INT_MAX);
+
+ // Initialize the CLI Migration.
+ add_filter('wpmdb_pro_cli_finalize_migration', [$this, 'cli_migration'], 10, 4);
+
+ $this->media_files_local = WPMDBDI::getInstance()->get(MediaFilesLocal::class);
+ }
+
+ /**
+ * Checks if the current migration includes a Media Files migration.
+ *
+ * @param array $profile
+ *
+ * @return bool
+ */
+ public function is_mf_migration($profile)
+ {
+ if (!isset($profile['media_files']) || true !== $profile['media_files']['enabled']) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Adds extra profile fields used by the Media Files addon.
+ *
+ * @param array $fields
+ *
+ * @return array
+ */
+ public function accepted_profile_fields($fields)
+ {
+ $fields[] = 'exclude_media';
+ $fields[] = 'media_date';
+
+ return $fields;
+ }
+
+ /**
+ * Add extra CLI args used by the Media Files addon.
+ * Hooks on: wpmdb_cli_filter_get_extra_args
+ *
+ * @param array $args
+ *
+ * @return array
+ */
+ public function filter_extra_args($args)
+ {
+ $args[] = 'media';
+ $args[] = 'exclude-media';
+ $args[] = 'media-date';
+
+ return $args;
+ }
+
+ /**
+ * Adds extra args for Media Files migrations.
+ * Hooks on: wpmdb_cli_filter_get_profile_data_from_args
+ *
+ * @param array $profile
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @return array|WP_Error
+ */
+ public function add_mf_profile_args($profile, $args, $assoc_args)
+ {
+ if (!isset($assoc_args['media'])) {
+ return $profile; // Not a media files migration.
+ }
+
+ if (!in_array($assoc_args['media'], ['all', 'since-date'])) {
+ return $this->cli->cli_error(__('--media must be set to an acceptable value, see: wp help migratedb ' . $assoc_args['action'], 'wp-migrate-db'));
+ }
+
+ $media_files = [
+ 'enabled' => true,
+ 'option' => $assoc_args['media'],
+ 'available' => true,
+ ];
+
+ if ('since-date' === $assoc_args['media']) {
+ if (!isset($assoc_args['media-date'])) {
+ return $this->cli->cli_error(__('--media-date required when using --media=since-date, see: wp help migratedb ' . $assoc_args['action'], 'wp-migrate-db'));
+ }
+
+ $media_date = $assoc_args['media-date'];
+ $valid_date = false;
+
+ if (preg_match('/^\d\d\d\d-\d\d-\d\d( \d\d:\d\d:\d\d)?$/', $media_date)) {
+ $mm = substr($media_date, 5, 2);
+ $jj = substr($media_date, 8, 2);
+ $aa = substr($media_date, 0, 4);
+ $valid_date = wp_checkdate($mm, $jj, $aa, $media_date);
+ }
+
+ if (!$valid_date) {
+ return $this->cli->cli_error(__('--media-date parameter received an invalid date format, see wp help migratedb ' . $assoc_args['action'], 'wp-migrate-db'));
+ }
+
+ $media_files['date'] = $media_date;
+ }
+
+ if (!empty($assoc_args['exclude-media'])) {
+ $media_files['excludes'] = str_replace(',', "\n", $assoc_args['exclude-media']);
+ }
+
+ $profile['media_files'] = $media_files;
+
+ return $profile;
+ }
+
+ /**
+ * Adds the Media Files stage to the current migration.
+ *
+ * @param array $profile
+ *
+ * @return array
+ */
+ public function add_mf_stage($profile)
+ {
+ if (is_wp_error($profile)) {
+ return $profile;
+ }
+
+ if ($this->is_mf_migration($profile)) {
+ $profile['current_migration']['stages'][] = 'media_files';
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Gets the correct folder based on the migration type.
+ *
+ * @param array $profile
+ * @param array $post_data
+ *
+ * @return string
+ */
+ public function get_folder($profile, $post_data)
+ {
+ $site_details = json_decode($post_data['site_details'], true);
+
+ if ('push' === $profile['action']) {
+ $folder = $site_details['local']['uploads']['basedir'];
+ } else {
+ $folder = $site_details['remote']['uploads']['basedir'];
+ }
+
+ return apply_filters('wpmdb_cli_media_files_folder', $folder);
+ }
+
+ /**
+ * Initialize the MF stage.
+ *
+ * @param array $profile
+ * @param array $post_data
+ *
+ * @return array|WP_Error
+ */
+ public function initialize_mf_migration($profile, $post_data)
+ {
+ \WP_CLI::log(__('Initiating media migration...', 'wp-migrate-db'));
+
+ $date = new \DateTime();
+ $tz = $date->getTimezone();
+ $mf_options = $profile['media_files'];
+
+ $_POST = [
+ 'action' => $profile['action'],
+ 'migration_state_id' => $profile['current_migration']['migration_id'],
+ 'folder' => $this->get_folder($profile, $post_data),
+ 'date' => null,
+ 'timezone' => $tz->getName(),
+ 'stage' => 'media_files',
+ 'is_cli_migration' => 1,
+ ];
+
+ if (!empty($mf_options['excludes'])) {
+ $_POST['excludes'] = json_encode($mf_options['excludes']);
+ }
+
+ if ('new' === $mf_options['option'] && ! empty($mf_options['date'])) {
+ $_POST['date'] = $mf_options['date'];
+ }
+
+ if ('new_subsequent' === $mf_options['option'] && ! empty($mf_options['last_migration'])) {
+ $_POST['date'] = $mf_options['last_migration'];
+ }
+
+ $response = $this->media_files_local->ajax_initiate_media_file_migration();
+
+ return $this->cli->verify_cli_response($response, 'initialize_mf_migration()');
+ }
+
+ /**
+ * Transfers files during the MF stage.
+ *
+ * @param array $profile
+ * @param array $post_data
+ *
+ * @return array|WP_Error
+ */
+ public function mf_transfer_files($profile, $post_data)
+ {
+ $_POST = [
+ 'action' => $profile['action'],
+ 'stage' => 'media_files',
+ 'migration_state_id' => $profile['current_migration']['migration_id'],
+ ];
+
+ $response = $this->media_files_local->ajax_mf_transfer_files();
+
+ return $this->cli->verify_cli_response($response, 'tansfer_mf_files()');
+ }
+
+ /**
+ * Run the media migration from the CLI.
+ *
+ * @param bool $outcome
+ * @param array $profile
+ * @param array $verify_connection_response
+ * @param array $post_data
+ *
+ * @return bool|WP_Error
+ */
+ public function cli_migration($outcome, $profile, $verify_connection_response, $post_data)
+ {
+ if (true !== $outcome || !$this->is_mf_migration($profile)) {
+ return $outcome;
+ }
+
+ if (!isset($verify_connection_response['media_files_max_file_uploads'])) {
+ return $this->cli->cli_error(__('WP Migrate Media Files does not seem to be installed/active on the remote website.', 'wp-migrate-db'));
+ }
+
+ $intent = $profile['action'];
+
+ // Kick off the Media Files stage.
+ $mf_initialize_response = $this->initialize_mf_migration($profile, $post_data);
+ if (is_wp_error($mf_initialize_response)) {
+ return $mf_initialize_response;
+ }
+
+ $queue_status = $mf_initialize_response['queue_status'];
+ $total_size = isset($queue_status['size']) ? (int) $queue_status['size'] : 0;
+
+ $migrate_bar = $this->make_progress_bar($this->get_string('migrate_media_files_' . $intent), 0);
+ $migrate_bar->setTotal($total_size);
+
+ $result = ['status' => 0];
+ while (!is_wp_error($result) && $result['status'] !== 'complete') {
+ // Delay between requests
+ do_action('wpmdb_media_files_cli_before_migrate_media');
+
+ // Migrate the files.
+ $result = $this->mf_transfer_files($profile, $post_data);
+
+ if (isset($result['status']['error'])) {
+ return new \WP_Error('wpmdb_cli_mf_migration_failed', $result['status']['message']);
+ }
+
+ $batch_size = is_array($result['status']) ? array_sum(array_column($result['status'], 'batch_size')) : 0;
+
+ // Update progress.
+ $migrate_bar->tick($batch_size);
+ }
+
+ if (is_wp_error($result)) {
+ return $result;
+ }
+
+ // Finish things up.
+ $migrate_bar->finish();
+
+ return true;
+ }
+
+ /**
+ * Like WP_CLI\Utils\make_progress_bar, but uses our own wrapper classes
+ *
+ * @param $message
+ * @param $count
+ *
+ * @return MediaFilesCliBar|MediaFilesCliBarNoOp
+ */
+ public function make_progress_bar($message, $count)
+ {
+ if (method_exists('cli\Shell', 'isPiped') && \cli\Shell::isPiped()) {
+ return new MediaFilesCliBarNoOp();
+ }
+
+ return new MediaFilesCliBar($message, $count);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCliBar.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCliBar.php
new file mode 100644
index 000000000..361dbe11c
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCliBar.php
@@ -0,0 +1,15 @@
+_message = $message;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCliBarNoOp.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCliBarNoOp.php
new file mode 100644
index 000000000..82eb89887
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/CliCommand/MediaFilesCliBarNoOp.php
@@ -0,0 +1,30 @@
+_message = $message;
+ }
+
+ public function tick() {
+ }
+
+ public function finish() {
+ // log last _message to show count of files migrated
+ \WP_CLI::log( $this->_message );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/Manager.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/Manager.php
new file mode 100644
index 000000000..aea4810d2
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/Manager.php
@@ -0,0 +1,33 @@
+get(MediaFilesAddon::class);
+ $media_files->register();
+ $media_files->set_licensed($licensed);
+
+ $container->get(MediaFilesLocal::class)->register();
+ $container->get(MediaFilesRemote::class)->register();
+ $container->get(MediaFilesCli::class)->register();
+
+ add_filter('wpmdb_addon_registered_mf', '__return_true');
+
+ return $media_files;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/MediaFilesRemote.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/MediaFilesRemote.php
new file mode 100644
index 000000000..ee7d2720d
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/MediaFilesRemote.php
@@ -0,0 +1,51 @@
+plugin_helper = $plugin_helper;
+ }
+
+ public function register() {
+ // Remote AJAX handlers
+ add_action( 'wp_ajax_nopriv_wpmdbmf_respond_to_get_remote_media', array( $this, 'respond_to_get_remote_media' ) );
+
+ add_action('wp_ajax_nopriv_wpmdbmf_respond_to_save_queue_status', array($this, 'ajax_mf_respond_to_save_queue_status'));
+ add_action('wp_ajax_nopriv_wpmdbmf_transfers_send_file', array($this, 'ajax_mf_respond_to_request_files',));
+ add_action('wp_ajax_nopriv_wpmdbmf_transfers_receive_file', array($this, 'ajax_mf_respond_to_post_file'));
+ }
+
+ /**
+ * @param $stage
+ *
+ * @return mixed|null
+ */
+ public function respond_to_get_remote_media()
+ {
+ return $this->plugin_helper->respond_to_get_remote_folders('media_files');
+ }
+
+ public function ajax_mf_respond_to_save_queue_status(){
+ return $this->plugin_helper->respond_to_save_queue_status();
+ }
+
+ public function ajax_mf_respond_to_request_files(){
+ return $this->plugin_helper->respond_to_request_files();
+ }
+
+ public function ajax_mf_respond_to_post_file(){
+ return $this->plugin_helper->respond_to_post_file();
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/ServiceProvider.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/ServiceProvider.php
new file mode 100644
index 000000000..35da632a8
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MF/ServiceProvider.php
@@ -0,0 +1,68 @@
+media_files_addon = new MediaFilesAddon(
+ $this->addon,
+ $this->properties,
+ $this->template
+ );
+
+ $this->media_files_addon_base = new MediaFilesBase(
+ $this->filesystem,
+ $this->migration_state_manager,
+ $this->form_data
+ );
+
+ $this->media_files_addon_local = new MediaFilesLocal(
+ $this->filesystem,
+ $this->migration_state_manager,
+ $this->form_data,
+ $this->http,
+ $this->settings,
+ $this->util,
+ $this->http_helper,
+ $this->remote_post,
+ $this->error_log,
+ $this->state_data_container
+ );
+
+ $this->media_files_addon_remote = new MediaFilesRemote(
+ $this->filesystem,
+ $this->migration_state_manager,
+ $this->form_data,
+ $this->http,
+ $this->settings,
+ $this->util,
+ $this->http_helper,
+ $this->error_log,
+ $this->properties,
+ $this->scrambler
+ );
+
+ $this->media_files_cli = new MediaFilesCli(
+ $this->addon,
+ $this->properties,
+ $this->template,
+ $this->cli,
+ $this->cli_manager,
+ $this->util,
+ $this->state_data_container
+ );
+
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/CliCommand/MultisiteToolsAddonCli.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/CliCommand/MultisiteToolsAddonCli.php
new file mode 100644
index 000000000..ea1daec18
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/CliCommand/MultisiteToolsAddonCli.php
@@ -0,0 +1,380 @@
+cli = $cli;
+ }
+
+ public function register()
+ {
+ // Add support for extra CLI args with a lower priority so that it can check media options.
+ add_filter('wpmdb_cli_filter_get_extra_args', array($this, 'filter_extra_args'), 10, 1);
+ add_filter('wpmdb_cli_filter_get_profile_data_from_args', array($this, 'mst_add_extra_cli_args'), 14, 3);
+
+ // Only runs on push or pull
+ add_filter('wpmdb_cli_default_find_and_replace', array($this, 'filter_cli_default_find_and_replace'), 10, 2);
+
+ add_filter('wpmdb_cli_initiate_migration_args', [$this, 'filter_initiate_post_data_cli'], 10, 2);
+ add_filter('wpmdb_cli_filter_before_cli_initiate_migration', array($this, 'extend_mst_cli_migration'), 10, 2);
+
+ add_filter('wpmdb_cli_filter_before_migration', [$this, 'update_prefix'], 10, 2);
+
+ add_filter('wpmdbpro_cli_verify_connection_response', array($this, 'filter_pull_migration_tables'), 10, 2);
+ }
+
+ /**
+ * Add prefix to profile.
+ *
+ * @param array $post_data
+ * @param array $profile
+ * @return array
+ */
+ public function update_prefix($profile, $post_data){
+ $destination_site = 'push' === $profile['action'] ? $post_data['site_details']['remote'] : $post_data['site_details']['local'];
+ $destination_prefix = $destination_site['prefix'];
+ $site_id = 0;
+
+ if (isset($profile['multisite_tools']['enabled']) && $profile['multisite_tools']['enabled']) {
+ $site_id = $profile['mst_subsite_to_subsite'] ? $profile['mst_destination_subsite'] : $profile['mst_selected_subsite'];
+ }
+
+ if ($destination_site['is_multisite'] === 'true' && 1 < $site_id) {
+ $profile['new_prefix'] = $destination_prefix . $site_id . '_';
+ } else {
+ $profile['new_prefix'] = $destination_prefix;
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Add MST to profile.
+ *
+ * @param array $post_data
+ * @param array $profile
+ * @return array
+ */
+ public function extend_mst_cli_migration($profile, $post_data)
+ {
+ if (!isset($profile['multisite_tools'])) {
+ return $profile;
+ }
+ if (isset($profile['mst_select_subsite']) && ! $profile['mst_select_subsite']) {
+ $profile['multisite_tools']['enabled'] = false;
+ }
+
+ $mst = $profile['multisite_tools'];
+ if (!isset($mst['enabled']) || !$mst['enabled']) {
+ return $profile;
+ }
+
+ if (!isset($mst['selected_subsite'], $mst['new_prefix'])) {
+ return $profile;
+ }
+ $mst_select_subsite = $mst['enabled'] ? '1' : '0';
+ $mst_subsite_to_subsite = isset($profile['mst_subsite_to_subsite']) ? $profile['mst_subsite_to_subsite'] : $profile['current_migration']['twoMultisites'];
+ $mst_args = [
+ 'mst_select_subsite' => $mst_select_subsite,
+ 'mst_selected_subsite' => (int)$mst['selected_subsite'],
+ 'mst_subsite_to_subsite' => $mst_subsite_to_subsite,
+ 'new_prefix' => $mst['new_prefix'],
+ ];
+
+ if ($mst_subsite_to_subsite === true) {
+ $mst_args['mst_destination_subsite'] = isset($mst['destination_subsite']) ? $mst['destination_subsite'] : '0';
+ }
+
+ $profile = array_merge($profile, $mst_args);
+
+ return $profile;
+ }
+
+ public function filter_initiate_post_data_cli($args, $profile)
+ {
+ if (!isset($args['form_data'])) {
+ return $args;
+ }
+
+ $form_data_parsed = json_decode($args['form_data'], true);
+
+ if (isset($form_data_parsed['mst_select_subsite'], $form_data_parsed['mst_selected_subsite'])
+ && $form_data_parsed['mst_select_subsite']) {
+ $args['mst_select_subsite'] = '1';
+ $args['mst_selected_subsite'] = $form_data_parsed['mst_selected_subsite'];
+
+ }
+ if (isset($form_data_parsed['mst_destination_subsite'])) {
+ $args['mst_destination_subsite'] = $form_data_parsed['mst_destination_subsite'];
+ }
+
+ if (isset($form_data_parsed['new_prefix'])) {
+ $args['new_prefix'] = $form_data_parsed['new_prefix'];
+ }
+
+ return $args;
+ }
+
+ /**
+ * Add extra CLI args used by this plugin.
+ *
+ * @param array $args
+ *
+ * @return array
+ */
+ public function filter_extra_args($args = array())
+ {
+ $args[] = 'subsite';
+ $args[] = 'subsite-source';
+ $args[] = 'subsite-destination';
+ $args[] = 'prefix';
+
+ return $args;
+ }
+
+ /**
+ * Add support for extra CLI args.
+ *
+ * @param array $profile
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @return array
+ */
+ function mst_add_extra_cli_args($profile, $args, $assoc_args)
+ {
+ if (!is_array($profile)) {
+ return $profile;
+ }
+
+ // --subsite=
+ $mst_select_subsite = false;
+ $mst_selected_subsite = 0;
+ $mst_destination_subsite = 0;
+ $mst_subsite_to_subsite = false;
+ if (isset($assoc_args['subsite'])) {
+ if (!is_multisite() && 'savefile' === $profile['action']) {
+ return $this->cli->cli_error(__('The installation must be a Multisite network to make use of the export subsite option', 'wp-migrate-db'));
+ }
+ if (empty($assoc_args['subsite'])) {
+ return $this->cli->cli_error(__('A valid Blog ID or Subsite URL must be supplied to make use of the subsite option', 'wp-migrate-db'));
+ }
+ if (is_multisite()) {
+ if (isset($assoc_args['subsite-source']) || isset($assoc_args['subsite-destination'])) {
+ return $this->cli->cli_error(__('For subsite to subsite migrations subsite-source and subsite-destination are both required', 'wp-migrate-db-pro-multisite-tools'));
+ }
+ $mst_selected_subsite = $this->multisite->get_subsite_id($assoc_args['subsite']);
+
+ if (false === $mst_selected_subsite) {
+ return $this->cli->cli_error(__('A valid Blog ID or Subsite URL must be supplied to make use of the subsite option', 'wp-migrate-db'));
+ }
+ } else {
+ $mst_selected_subsite = $assoc_args['subsite'];
+ }
+
+ $mst_select_subsite = true;
+ }
+
+ if (isset($assoc_args['subsite-source']) || isset($assoc_args['subsite-destination'])) {
+
+ if (!isset($assoc_args['subsite-source'])) {
+ return $this->cli->cli_error(__('subsite-source must also be used to make use of the subsite to subsite option', 'wp-migrate-db-pro-multisite-tools'));
+ }
+
+ if (!isset($assoc_args['subsite-destination'])) {
+ return $this->cli->cli_error(__('subsite-destination must also be used to make use of the subsite to subsite option', 'wp-migrate-db-pro-multisite-tools'));
+ }
+
+ if (empty($assoc_args['subsite-source']) || empty($assoc_args['subsite-destination']) ) {
+ return $this->cli->cli_error(__('A valid Blog ID or Subsite URL must be supplied for both networks to make use of the subsite to subsite option', 'wp-migrate-db-pro-multisite-tools'));
+ }
+
+ if (!is_multisite()) {
+ return $this->cli->cli_error(__('Both source and destination must be networks to make use of the subsite to subsite option', 'wp-migrate-db-pro-multisite-tools'));
+ }
+ $subsite_id = 'push' === $profile['action'] ? $assoc_args['subsite-source'] : $assoc_args['subsite-destination'];
+
+ if (!$this->multisite->get_subsite_id($subsite_id)) {
+ return $this->cli->cli_error(__('A valid Blog ID or Subsite URL must be supplied to make use of the subsite option', 'wp-migrate-db-pro-multisite-tools'));
+ }
+ $mst_selected_subsite = $assoc_args['subsite-source'];
+ $mst_destination_subsite = $assoc_args['subsite-destination'];
+ $mst_select_subsite = true;
+ $mst_subsite_to_subsite = true;
+ }
+
+ // --prefix=
+ global $wpdb;
+ $new_prefix = $wpdb->base_prefix;
+ if (isset($assoc_args['prefix'])) {
+ if (false === $mst_select_subsite) {
+ return $this->cli->cli_error(__('A new table name prefix may only be specified for subsite exports.', 'wp-migrate-db'));
+ }
+ if (empty($assoc_args['prefix'])) {
+ return $this->cli->cli_error(__('A valid prefix must be supplied to make use of the prefix option', 'wp-migrate-db'));
+ }
+ $new_prefix = trim($assoc_args['prefix']);
+
+ if (sanitize_key($new_prefix) !== $new_prefix) {
+ return $this->cli->cli_error($this->get_string('new_prefix_contents'));
+ }
+ }
+
+ // Disable Media Files Select Subsites if using Subsite Migration.
+ if ($mst_select_subsite && !empty($profile['mf_select_subsites']) && !empty($profile['mf_selected_subsites'])) {
+ unset($profile['mf_select_subsites'], $profile['mf_selected_subsites']);
+ }
+
+ $filtered_profile = compact(
+ 'mst_select_subsite',
+ 'mst_subsite_to_subsite',
+ 'mst_selected_subsite',
+ 'new_prefix'
+ );
+ if ($mst_subsite_to_subsite === true) {
+ $filtered_profile['mst_destination_subsite'] = $mst_destination_subsite;
+ }
+ return array_merge($profile, $filtered_profile);
+ }
+
+ /**
+ * Ensure CLI has appropriate default find and replace values when doing MST.
+ *
+ * @param array $profile
+ * @param array $post_data
+ *
+ * @return array
+ *
+ * TODO: Update for multisite <=> multisite (blog_ids)
+ */
+ public function filter_cli_default_find_and_replace($profile, $post_data)
+ {
+ if (is_wp_error($profile)) {
+ return $profile;
+ }
+
+ $state_data = $this->migration_state_manager->set_post_data();
+
+ if (!empty($state_data)) {
+ $post_data = array_merge($post_data, $state_data);
+ }
+
+ if (empty($profile['mst_select_subsite']) || empty($profile['mst_selected_subsite'])) {
+ return $profile;
+ }
+
+ $source = ('pull' === $post_data['intent']) ? $post_data['site_details']['remote'] : $post_data['site_details']['local'];
+ $target = ('pull' === $post_data['intent']) ? $post_data['site_details']['local'] : $post_data['site_details']['remote'];
+
+ $blog_id = false;
+
+ if ('true' === $source['is_multisite'] && !empty($source['subsites'])) {
+ $blog_id = $this->multisite->get_subsite_id($profile['mst_selected_subsite'], $source['subsites']);
+ } elseif ('true' === $target['is_multisite'] && !empty($target['subsites'])) {
+ $blog_id = $this->multisite->get_subsite_id($profile['mst_selected_subsite'], $target['subsites']);
+ }
+
+ if (false === $blog_id) {
+ return $profile;
+ }
+
+ if ('true' === $source['is_multisite'] && !empty($source['subsites_info'][$blog_id]['site_url'])) {
+ $profile['search_replace']['standard_search_replace']['domain']['search'] = '//' . untrailingslashit($this->util->scheme_less_url($source['subsites_info'][$blog_id]['site_url']));
+ }
+
+ if ('true' === $target['is_multisite'] && !empty($target['subsites_info'][$blog_id]['site_url'])) {
+ $profile['search_replace']['standard_search_replace']['domain']['replace'] = '//' . untrailingslashit($this->util->scheme_less_url($target['subsites_info'][$blog_id]['site_url']));
+ }
+
+ return $profile;
+ }
+
+ /**
+ * When pulling from a multisite subsite into a single site install,
+ * ensure correct tables are selected if --include-tables option not in use.
+ *
+ * @param array $remote_response
+ * @param array $profile
+ *
+ * @return array
+ */
+ public function filter_pull_migration_tables($remote_response, $profile)
+ {
+ if (
+ ! empty($profile['action']) &&
+ 'pull' === $profile['action'] &&
+ ! is_multisite() &&
+ ! empty($profile['mst_select_subsite']) &&
+ ! empty($profile['mst_selected_subsite']) &&
+ (empty($profile['table_migrate_option']) || 'migrate_select' !== $profile['table_migrate_option']) &&
+ ! empty($remote_response['prefix']) &&
+ ! empty($remote_response['prefixed_tables'])
+ ) {
+ $filtered_tables = $this->filter_tables_for_subsite_id(
+ $profile['mst_selected_subsite'],
+ $remote_response['prefixed_tables'],
+ $remote_response['prefix']
+ );
+
+ if ( ! empty($filtered_tables)) {
+ $remote_response['prefixed_tables'] = $filtered_tables;
+ }
+ }
+
+ return $remote_response;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/Initialize.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/Initialize.php
new file mode 100644
index 000000000..78edc428c
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/Initialize.php
@@ -0,0 +1,9 @@
+cli = true;
+ return $this->init($licensed);
+ });
+
+ return $this->init($licensed);
+ }
+
+ private function init($licensed) {
+ global $wpmdbpro_multisite_tools;
+
+ if ( ! is_null($wpmdbpro_multisite_tools) ) {
+ return $wpmdbpro_multisite_tools;
+ }
+
+ $container = WPMDBDI::getInstance();
+ $mst = $container->get(MultisiteToolsAddon::class);
+
+ $mst->register();
+ $mst->set_licensed($licensed);
+
+ $mst_cli = $container->get(MultisiteToolsAddonCli::class);
+ $mst_cli->register();
+
+
+ if ($this->cli) {
+ $wpmdbpro_multisite_tools = $mst_cli;
+ } else {
+ $wpmdbpro_multisite_tools = $mst;
+ }
+
+ add_filter('wpmdb_addon_registered_mst', '__return_true');
+
+ // Allows hooks to bypass the regular admin / ajax checks to force load the addon (required for the CLI addon).
+ $force_load = apply_filters('wp_migrate_db_pro_multisite_tools_force_load', false);
+
+ if (false === $force_load && ! is_null($wpmdbpro_multisite_tools)) {
+ return $wpmdbpro_multisite_tools;
+ }
+
+ if (false === $force_load
+ && ((is_multisite() && wp_is_large_network()))) {
+ return false;
+ }
+
+ return $wpmdbpro_multisite_tools;
+ }
+
+ public function get_license_response_key()
+ {
+ return 'wp-migrate-db-pro-multisite-tools';
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/MediaFilesCompat.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/MediaFilesCompat.php
new file mode 100644
index 000000000..5c1209ab0
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/MediaFilesCompat.php
@@ -0,0 +1,253 @@
+util = $util;
+ $this->filesystem = $filesystem;
+ }
+
+ /**
+ * Given the $state_data array, check if 'mst_selected_subsite' is 1
+ *
+ * @param $state_data
+ *
+ * @return int
+ */
+ public function get_subsite_from_state_data($state_data, $target = 'source')
+ {
+ $local_multisite = 'true' === $state_data['site_details']['local']['is_multisite'];
+ $remote_multisite = 'true' === $state_data['site_details']['remote']['is_multisite'];
+ if ($local_multisite && $remote_multisite) {
+ if ('destination' === $target) {
+ return (int)isset($state_data['mst_destination_subsite']) ? $state_data['mst_destination_subsite'] : 0;
+ }
+ return (int)isset($state_data['mst_selected_subsite']) ? $state_data['mst_selected_subsite'] : 0;
+ }
+ if ($local_multisite){
+ if ('push' === $state_data['intent']) {
+ return 'source' === $target ? $state_data['mst_selected_subsite'] : 0;
+ }
+ return 'source' === $target ? 0 : $state_data['mst_selected_subsite'];
+ }
+ //if remote is multisite
+ if ('push' === $state_data['intent']) {
+ return 'source' === $target ? 0 : $state_data['mst_selected_subsite'];
+ }
+ return isset($state_data['mst_selected_subsite']) && 'source' === $target ? $state_data['mst_selected_subsite']: 0;
+ }
+
+ /**
+ *
+ * Called from the 'wpmdb_mf_destination_uploads' hook
+ *
+ * @param $uploads_dir
+ * @param $state_data
+ *
+ * @return mixed
+ */
+ public function filter_media_uploads($uploads_dir, $state_data)
+ {
+ if (!is_multisite() || !$this->is_subsite_migration($state_data)) {
+ return $uploads_dir;
+ }
+
+ $site_id = $this->get_subsite_from_state_data($state_data, 'destination');
+
+ if ($site_id === 0) {
+ return $uploads_dir;
+ }
+
+ $uploads_info = $this->util->uploads_info($site_id);
+
+ if (isset($uploads_info['basedir'])) {
+ return $uploads_info['basedir'];
+ }
+
+ return $uploads_dir;
+ }
+
+ /**
+ *
+ * Called from the 'wpmdb_mf_destination_file' hook
+ *
+ * @param string $file
+ * @param array $state_data
+ *
+ * @return string
+ */
+ public function filter_media_destination($file, $state_data)
+ {
+ $site_id = $this->get_subsite_from_state_data($state_data, 'source');
+ if ($site_id === 0 || !$this->is_subsite_migration($state_data)) {
+ return $file;
+ }
+
+ $slashed_file = $this->filesystem->slash_one_direction($file);
+
+ $pattern = '/^\\' . DIRECTORY_SEPARATOR . 'sites\\' . DIRECTORY_SEPARATOR . $site_id . '/';
+
+ if (false !== strpos($slashed_file, 'blogs.dir')) {
+ $pattern = '/^blogs.dir\\' . DIRECTORY_SEPARATOR . $site_id.'\\' . DIRECTORY_SEPARATOR . 'files/';
+ }
+
+ $file = preg_replace($pattern, '', $slashed_file);
+
+ return $file;
+ }
+
+ /**
+ * Filter relative paths for media exports
+ *
+ * Hooked to filter_media_export_destination
+ *
+ * @param string $relative_path
+ * @param string $state_data
+ * @return string
+ **/
+ public function filter_media_export_destination($relative_path, $state_data)
+ {
+
+ if ( ! isset($state_data['mst_select_subsite']) || $state_data['mst_select_subsite'] !== '1' || $state_data['mst_selected_subsite'] < 2) {
+ return $relative_path;
+ }
+
+ return str_replace('sites/' . $state_data['mst_selected_subsite'], '', $relative_path);
+
+ }
+
+ /**
+ *
+ * Called from the 'wpmdb_mf_local_uploads_folder' hook
+ *
+ * @param $path
+ * @param $state_data
+ *
+ * @return array|mixed
+ */
+ public function filter_uploads_path_local($path, $state_data)
+ {
+ return $this->filter_uploads_path($path, $state_data, 'local');
+ }
+
+ /**
+ *
+ * Called from the 'wpmdb_mf_remote_uploads_folder' hook
+ *
+ * @param $path
+ * @param $state_data
+ *
+ * @return array|mixed
+ */
+ public function filter_uploads_path_remote($path, $state_data)
+ {
+ return $this->filter_uploads_path($path, $state_data, 'remote');
+ }
+
+ /**
+ *
+ * Given $state_data and an uploads file path, determine new uploads path
+ *
+ * @param $path
+ * @param $state_data
+ * @param string $location
+ *
+ * @return array|mixed
+ */
+ public function filter_uploads_path($path, $state_data, $location = 'local')
+ {
+ $target = 'local' === $location ? 'source' : 'destination';
+ if ('pull' === $state_data['intent']) {
+ $target = 'local' === $location ? 'destination' : 'source';
+ }
+ $blog_id = $this->get_subsite_from_state_data($state_data, $target);
+
+ if ($blog_id <= 1 || !$this->is_subsite_migration($state_data)) {
+ return $path;
+ }
+
+ $uploads = 'remote' === $location ? $this->get_remote_uploads_dir($blog_id, $state_data) : $this->util->uploads_info($blog_id);
+
+ if (isset($uploads['basedir']) && 'remote' !== $location) {
+ $path = $uploads['basedir'];
+ } else {
+ $path = $uploads;
+ }
+
+ $path = $location === 'remote' ? (array)$path : $path;
+
+ return $path;
+ }
+
+ /**
+ *
+ * Get path of remote uploads directory
+ *
+ * @param int $blog_id
+ * @param array $state_data
+ * @return string
+ **/
+ public function get_remote_uploads_dir($blog_id, $state_data) {
+ $path = $state_data['site_details']['remote']['uploads']['basedir'];
+ if ($blog_id > 1) {
+ $path .= '/sites/' . $blog_id;
+ }
+ return $path;
+ }
+
+ /**
+ *
+ * Filter excludes if subsite ID is 1, we don't want to migrate all the other subsites as well
+ *
+ * Call from the 'wpmdb_mf_excludes' hook
+ *
+ * @param $excludes
+ * @param $state_data
+ *
+ * @return array
+ */
+ public function filter_media_excludes($excludes, $state_data)
+ {
+ if (!$this->is_subsite_migration($state_data)) {
+ return $excludes;
+ }
+ $blog_id = $this->get_subsite_from_state_data($state_data, 'source');
+
+ if ($blog_id !== 1) {
+ return $excludes;
+ }
+
+ $excludes[] = '**/sites/*';
+
+ return $excludes;
+ }
+
+ /**
+ *
+ * @param $state_data
+ *
+ * @return bool
+ */
+ private function is_subsite_migration($state_data) {
+ return isset($state_data['mst_select_subsite']) && '1' === $state_data['mst_select_subsite'];
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/MultisiteToolsAddon.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/MultisiteToolsAddon.php
new file mode 100644
index 000000000..edc6441a1
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/MST/MultisiteToolsAddon.php
@@ -0,0 +1,1282 @@
+plugin_slug = $properties->plugin_slug;
+ $this->plugin_version = $properties->plugin_version;
+ $this->plugin_dir_path = __DIR__;
+ $this->plugin_folder_name = $properties->plugin_folder_name;
+ $this->template_path = $this->plugin_dir_path . 'template/';
+
+ $this->accepted_fields = array(
+ 'multisite_subsite_export', // TODO: Remove backwards compatibility for CLI once Core/MST/CLI dependencies updated.
+ 'select_subsite', // TODO: Remove backwards compatibility for CLI once Core/MST/CLI dependencies updated.
+ 'mst_select_subsite',
+ 'mst_selected_subsite',
+ 'mst_destination_subsite',
+ 'mst_subsite_to_subsite',
+ 'multisite_tools',
+ 'new_prefix',
+ 'keep_active_plugins',
+ );
+
+ $this->multisite = $multisite;
+ $this->util = $util;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->table = $table;
+ $this->table_helper = $table_helper;
+ $this->form_data_class = $form_data_class;
+ $this->template = $template;
+ $this->profile_manager = $profile_manager;
+ $this->dynamic_properties = $dynamic_properties;
+ $this->filesystem = $filesystem;
+ $this->media_files_compat = $media_files_compat;
+ $this->selected_subsite_id = null;
+ }
+
+ public function register()
+ {
+
+ $this->addon_name = $this->addon->get_plugin_name('wp-migrate-db-pro-multisite-tools/wp-migrate-db-pro-multisite-tools.php');
+
+ add_action('wpmdb_load_assets', array($this, 'load_assets'));
+ add_filter('wpmdb_accepted_profile_fields', array($this, 'accepted_profile_fields'));
+ add_filter('wpmdb_establish_remote_connection_data', array($this, 'establish_remote_connection_data'));
+ add_filter('wpmdb_data', array($this, 'js_variables'));
+ add_filter('wpmdb_initiate_key_rules', array($this, 'filter_key_rules'), 10, 2);
+ add_filter('wpmdb_verify_connection_key_rules', array($this, 'filter_key_rules'), 10, 2);
+ add_filter('wpmdb_respond_to_verify_connection_key_rules', array($this, 'filter_key_rules'), 10, 2);
+ add_filter('wpmdb_initiate_push_pull_post', array($this, 'filter_initiate_post_data'), 10, 2);
+
+ add_filter('wpmdb_diagnostic_info', array($this, 'diagnostic_info'));
+ add_filter('wpmdb_tables', array($this, 'filter_tables_for_subsite'), 10, 2);
+ add_filter('wpmdb_table_sizes', array($this, 'filter_table_sizes_for_subsite'), 10, 2);
+ add_filter('wpmdb_target_table_name', array($this, 'filter_target_table_name'), 10, 4);
+ add_filter('wpmdb_table_row', array($this, 'filter_table_row'), 10, 5);
+ add_filter('wpmdb_find_and_replace', array($this, 'filter_find_and_replace'), 10, 3);
+ add_filter('wpmdb_finalize_target_table_name', array($this, 'filter_finalize_target_table_name'), 10, 2);
+ add_filter('wpmdb_preserved_options', array($this, 'filter_preserved_options'), 10, 2);
+ add_filter('wpmdb_preserved_options_data', array($this, 'filter_preserved_options_data'), 10, 2);
+ add_filter('wpmdb_get_alter_queries', array($this, 'filter_get_alter_queries'), 10, 2);
+ add_filter('wpmdb_replace_site_urls', array($this, 'filter_replace_site_urls'));
+ add_filter('wpmdb_backup_header_url', array($this, 'filter_backup_header_url'));
+ add_filter('wpmdb_backup_header_included_tables', array($this, 'filter_backup_header_tables'));
+ add_filter('wpmdb_backup_header_is_subsite_export', array($this, 'filter_backup_header_is_subsite_export'));
+
+ /**
+ * Media Files Hooks
+ */
+ add_filter('wpmdb_mf_local_uploads_folder', [$this->media_files_compat, 'filter_uploads_path_local'], 10, 2);
+ add_filter('wpmdb_mf_remote_uploads_source_folder', [$this->media_files_compat, 'filter_uploads_path_remote'], 10, 2);
+ add_filter('wpmdb_mf_remote_uploads_folder', [$this->media_files_compat, 'filter_uploads_path_remote'], 10, 2);
+ add_filter('wpmdb_mf_destination_file', [$this->media_files_compat, 'filter_media_destination'], 10, 2);
+ add_filter('wpmdb_mf_destination_uploads', [$this->media_files_compat, 'filter_media_uploads'], 10, 2);
+ add_filter('wpmdb_mf_media_upload_path', [$this->media_files_compat, 'filter_media_uploads'], 10, 2);
+ add_filter('wpmdb_mf_excludes', [$this->media_files_compat, 'filter_media_excludes'], 10, 2);
+ add_filter('wpmdb_export_relative_path', [$this->media_files_compat, 'filter_media_export_destination'], 10, 2);
+
+ $this->container = WPMDBDI::getInstance();
+ }
+
+ /**
+ * Allow MST params to be passed to certain ajax endpoints.
+ *
+ * @param array $rules
+ * @param string $context
+ *
+ * @return mixed
+ */
+ public function filter_key_rules($rules, $context)
+ {
+ switch ($context) {
+ case 'ajax_verify_connection_to_remote_site':
+ case 'wpmdb_verify_connection_key_rules':
+ case 'ajax_initiate_migration':
+ case 'wpmdb_remote_initiate_migration':
+ case 'respond_to_verify_connection_to_remote_site':
+ case 'respond_to_remote_initiate_migration':
+ $rules['mst_select_subsite'] = 'string';
+ $rules['mst_selected_subsite'] = 'int';
+ $rules['mst_destination_subsite'] = 'int';
+ $rules['new_prefix'] = 'string';
+ }
+
+ return $rules;
+ }
+
+ public function filter_initiate_post_data($postData, $state_data)
+ {
+ if (empty($postData) || $postData['action'] !== 'wpmdb_remote_initiate_migration') {
+ return $postData;
+ }
+
+ if (isset($state_data['mst_select_subsite'])) {
+ $postData['mst_select_subsite'] = (string)$state_data['mst_select_subsite'];
+ }
+
+ if (isset($state_data['mst_selected_subsite'])) {
+ $postData['mst_selected_subsite'] = $state_data['mst_selected_subsite'];
+ }
+
+ if (isset($state_data['mst_destination_subsite'])) {
+ $postData['mst_destination_subsite'] = $state_data['mst_destination_subsite'];
+ }
+
+ if (isset($state_data['new_prefix'])) {
+ $postData['new_prefix'] = $state_data['new_prefix'];
+ }
+
+ return $postData;
+ }
+
+ /**
+ * Does the given user need to be migrated?
+ *
+ * @param int $user_id
+ * @param int $blog_id Optional.
+ *
+ * @return bool
+ */
+ protected function is_user_required_for_blog($user_id, $blog_id = 0)
+ {
+ static $users = array();
+
+ if (empty($user_id)) {
+ $user_id = 0;
+ }
+
+ if (empty($blog_id)) {
+ $blog_id = 0;
+ }
+
+ if (isset($users[$blog_id][$user_id])) {
+ return $users[$blog_id][$user_id];
+ }
+
+ if (!is_multisite()) {
+ $users[$blog_id][$user_id] = true;
+
+ return $users[$blog_id][$user_id];
+ }
+
+ $subsites = $this->util->subsites_list();
+
+ if (empty($subsites) || !array_key_exists($blog_id, $subsites)) {
+ $users[$blog_id][$user_id] = false;
+
+ return $users[$blog_id][$user_id];
+ }
+
+ if (is_user_member_of_blog($user_id, $blog_id)) {
+ $users[$blog_id][$user_id] = true;
+
+ return $users[$blog_id][$user_id];
+ }
+
+ // If the user has any posts that are going to be migrated, we need the user regardless of whether they still have access.
+ switch_to_blog($blog_id);
+ $user_posts = count_user_posts($user_id);
+ restore_current_blog();
+
+ if (0 < $user_posts) {
+ $users[$blog_id][$user_id] = true;
+
+ return $users[$blog_id][$user_id];
+ }
+
+ // If here, user not required.
+ $users[$blog_id][$user_id] = false;
+
+ return $users[$blog_id][$user_id];
+ }
+
+ /**
+ * @param array|false $state_data
+ *
+ * @return int Will return 0 if not doing MST migration.
+ *
+ * Will return 0 if not doing MST migration.
+ * @return int
+ */
+ public function selected_subsite($state_data = false)
+ {
+ $blog_id = 0;
+
+ if ($this->selected_subsite_id) {
+ return $this->selected_subsite_id;
+ }
+
+ if (!$state_data) {
+ $state_data = filter_var_array($_POST, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
+ }
+
+ if (empty($state_data)) {
+ return 0;
+ }
+
+ $select_subsite = isset($state_data['mst_select_subsite']) ? $state_data['mst_select_subsite'] : false;
+ $selected_subsite = isset($state_data['mst_selected_subsite']) ? (int)$state_data['mst_selected_subsite'] : false;
+
+ // During a migration, this is where the subsite's id will be derived.
+ if (empty($blog_id) &&
+ !empty($select_subsite) &&
+ !empty($selected_subsite) &&
+ is_numeric($selected_subsite)
+ ) {
+ $blog_id = $selected_subsite;
+ }
+
+ // When loading a saved migration profile, this is where the subsite's id will be derived.
+ // @TODO refactor this, we're not using $loaded_profile any more
+ global $loaded_profile;
+
+ if (empty($blog_id) &&
+ !empty($loaded_profile['mst_select_subsite']) &&
+ !empty($loaded_profile['mst_selected_subsite']) &&
+ is_numeric($loaded_profile['mst_selected_subsite'])
+ ) {
+ $blog_id = $loaded_profile['mst_selected_subsite'];
+ }
+
+ // Early in a push or pull migration selected subsite might just be injected in ajax params.
+ if (empty($blog_id) &&
+ !empty($this->state_data['mst_select_subsite']) &&
+ !empty($this->state_data['mst_selected_subsite'])
+ ) {
+ $blog_id = $this->multisite->get_subsite_id($this->state_data['mst_selected_subsite']);
+ }
+
+ // If on multisite we can check that selected blog exists as all scenarios would require it.
+ if (1 < $blog_id && is_multisite() && !$this->subsite_exists($blog_id)) {
+ $blog_id = 0;
+ }
+
+ if ($blog_id === 0) {
+ return 0;
+ }
+
+ $this->selected_subsite_id = $blog_id;
+
+ return $blog_id;
+ }
+
+ /**
+ * Whitelist multisite tools setting fields for use in AJAX save in core
+ *
+ * @param array $profile_fields
+ *
+ * @return array
+ */
+ public function accepted_profile_fields($profile_fields)
+ {
+ return array_merge($profile_fields, $this->accepted_fields);
+ }
+
+ /**
+ * Check the remote site has the multisite tools addon setup
+ *
+ * @param array $data Connection data
+ *
+ * @return array Updated connection data
+ */
+ public function establish_remote_connection_data($data)
+ {
+ $data['mst_available'] = '1';
+ $data['mst_version'] = $this->plugin_version;
+
+ return $data;
+ }
+
+ /**
+ * Add multisite tools related javascript variables to the page
+ *
+ * @param array $data
+ *
+ * @return array
+ */
+ public function js_variables($data)
+ {
+ global $loaded_profile;
+
+ $data['mst_version'] = $this->plugin_version;
+ $data['mst_is_licensed'] = $this->licensed ? '1' : '0';
+
+ // Track originally selected subsite.
+ if (empty($loaded_profile) && !empty($data['profile']) && is_numeric($data['profile'])) {
+ $loaded_profile = $this->profile_manager->get_profile($data['profile']);
+ }
+
+ if (!empty($loaded_profile['mst_select_subsite']) &&
+ !empty($loaded_profile['mst_selected_subsite']) &&
+ is_numeric($loaded_profile['mst_selected_subsite'])
+ ) {
+ $data['mst_selected_subsite'] = (int)$loaded_profile['mst_selected_subsite'];
+ }
+
+ return $data;
+ }
+
+ /**
+ * Get translated strings for javascript and other functions.
+ *
+ * @return array
+ */
+ public function get_strings()
+ {
+ static $strings;
+
+ if (!empty($strings)) {
+ return $strings;
+ }
+
+ $strings = array(
+ 'new_prefix_contents' => __('Please only enter letters, numbers or underscores for the new table prefix.', 'wp-migrate-db'),
+ );
+
+ return $strings;
+ }
+
+ /**
+ * Retrieve a specific translated string.
+ *
+ * @param string $key
+ *
+ * @return string
+ */
+ public function get_string($key)
+ {
+ $strings = $this->get_strings();
+
+ return (isset($strings[$key])) ? $strings[$key] : '';
+ }
+
+ /**
+ * Load multisite tools related assets in core plugin.
+ */
+ public function load_assets()
+ {
+ $plugins_url = trailingslashit(plugins_url()) . trailingslashit($this->plugin_folder_name);
+
+ $version = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? time() : $this->plugin_version;
+
+
+ $src = $plugins_url . "frontend/public/noop.js";
+ wp_enqueue_script(
+ 'wp-migrate-db-pro-multisite-tools-script',
+ $src,
+ array(
+ 'jquery',
+ 'wp-migrate-db-pro-script-v2',
+ ),
+ $version,
+ true
+ );
+
+ wp_localize_script('wp-migrate-db-pro-multisite-tools-script', 'wpmdbmst_strings', $this->get_strings());
+ wp_localize_script(
+ 'wp-migrate-db-pro-multisite-tools-script',
+ 'wpmdbmst',
+ [
+ 'enabled' => true,
+ ]
+ );
+ }
+
+ /**
+ * Adds extra information to the core plugin's diagnostic info
+ */
+ public function diagnostic_info($diagnostic_info)
+ {
+ if (is_multisite()) {
+ $diagnostic_info['multisite-tools'] = array('Sites' => number_format(get_blog_count()));
+ }
+
+ return $diagnostic_info;
+ }
+
+ /**
+ * Should the given table be excluded from a subsite migration.
+ *
+ * @param bool $exclude Filtered value passed through for non MS globals.
+ * @param int $blog_id Subsite ID.
+ * @param string $table_name Table name to check.
+ * @param string $base_prefix Optional, base prefix override, e.g. when checking remote's tables.
+ *
+ * @return bool
+ */
+ public function filter_table_for_subsite($exclude, $blog_id, $table_name, $base_prefix = '')
+ {
+ if (0 < $blog_id) {
+ // wp_users and wp_usermeta are relevant to all sites, shortcut out.
+ if ($this->table_helper->table_is('', $table_name, 'non_ms_global')) {
+ return $exclude;
+ }
+
+ // Following tables are Multisite setup tables and can be excluded from migration.
+ if ($this->table_helper->table_is('', $table_name, 'ms_global')) {
+ return true;
+ }
+
+ global $wpdb;
+ $prefix = empty($base_prefix) ? $wpdb->base_prefix : $base_prefix;
+
+ if (1 == $blog_id) {
+ $prefix_escaped = preg_quote($prefix);
+
+ // Exclude tables from non-primary subsites.
+ if (preg_match('/^' . $prefix_escaped . '([0-9]+)_/', $table_name)) {
+ $exclude = true;
+ }
+ } else {
+ $prefix .= $blog_id . '_';
+ if (0 !== stripos($table_name, $prefix)) {
+ $exclude = true;
+ }
+ }
+ }
+
+ return $exclude;
+ }
+
+ /**
+ * Filter the given tables if doing a subsite migration.
+ *
+ * @param array $tables
+ * @param string $scope
+ *
+ * @return array
+ */
+ public function filter_tables_for_subsite($tables, $scope = 'regular')
+ {
+ if ( ! is_multisite() || empty($tables)) {
+ return $tables;
+ }
+
+ // We will not alter backup or temp tables list.
+ if (in_array($scope, array('backup', 'temp'))) {
+ return $tables;
+ }
+
+ $blog_id = $this->selected_subsite();
+
+ return $this->filter_tables_for_subsite_id($blog_id, $tables);
+ }
+
+ /**
+ * If doing a subsite migration, reduces tables to those relevant for subsite.
+ *
+ * @param int $blog_id Subsite ID.
+ * @param array $tables Tables to be checked whether belonging to subsite being migrated.
+ * @param string $base_prefix Optional, base prefix override, e.g. when checking remote's tables.
+ *
+ * @return array
+ */
+ public function filter_tables_for_subsite_id($blog_id, $tables, $base_prefix = '')
+ {
+ if (0 < $blog_id) {
+ $filtered_tables = array();
+
+ foreach ($tables as $key => $value) {
+ if (false === $this->filter_table_for_subsite(false, $blog_id, $value, $base_prefix)) {
+ $filtered_tables[$key] = $value;
+ }
+ }
+
+ return $filtered_tables;
+ }
+
+ return $tables;
+ }
+
+ /**
+ * Filter the given tables with sizes if doing a subsite migration.
+ *
+ * @param array $table_sizes
+ * @param string $scope
+ *
+ * @return array
+ */
+ public function filter_table_sizes_for_subsite($table_sizes, $scope = 'regular')
+ {
+ if (!is_multisite() || empty($table_sizes)) {
+ return $table_sizes;
+ }
+
+ $tables = $this->filter_tables_for_subsite(array_keys($table_sizes), $scope);
+
+ return array_intersect_key($table_sizes, array_flip($tables));
+ }
+
+ /**
+ * Change the name of the given table if subsite selected and migration profile has new prefix.
+ *
+ * @param string $table_name
+ * @param string $action
+ * @param string $stage
+ * @param array $site_details
+ *
+ * @return string
+ */
+ public function filter_target_table_name($table_name, $state_data, $site_details = array(), $subsite_migration = false)
+ {
+ if( !$subsite_migration ){
+ return $table_name;
+ }
+ $stage = $state_data['stage'];
+ $action = $state_data['intent'];
+
+ $blog_id = $this->selected_subsite($state_data);
+
+ $destination_subsite = isset($state_data['mst_destination_subsite']) ? $state_data['mst_destination_subsite'] : 0;
+ if (0 === $destination_subsite && 1 > $blog_id || 'backup' === $stage) {
+ return $table_name;
+ }
+
+ $new_prefix = isset($state_data['new_prefix']) ? $state_data['new_prefix'] : Persistence::getFromStateData('new_prefix');
+
+ if (empty($new_prefix)) {
+ return $table_name;
+ }
+
+ global $wpdb;
+ $old_prefix = $wpdb->base_prefix;
+ if (is_multisite() && 1 < $blog_id && !$this->table_helper->table_is('', $table_name, 'global', '', $blog_id)) {
+ $old_prefix .= $blog_id . '_';
+ }
+
+ // We do not want to overwrite the global tables unless exporting or target is a single site install.
+ if ('savefile' !== $action &&
+ (
+ ('pull' === $action && 'true' === $site_details['local']['is_multisite']) ||
+ ('push' === $action && 'true' === $site_details['remote']['is_multisite'])
+ ) &&
+ $this->table_helper->table_is('', $table_name, 'global')
+ ) {
+ $new_prefix .= 'wpmdbglobal_';
+ }
+
+ if (0 === stripos($table_name, $old_prefix)) {
+ $table_name = substr_replace($table_name, $new_prefix, 0, strlen($old_prefix));
+ }
+
+ return $table_name;
+ }
+
+ /**
+ * Handler for the wpmdb_table_row filter.
+ * The given $row can be modified, but if we return false the row will not be used.
+ *
+ * @param stdClass $row
+ * @param string $table_name
+ * @param string $action
+ * @param string $stage
+ *
+ * @return bool
+ */
+ public function filter_table_row($row, $table_name, $state_data)
+ {
+ $use = true;
+ $stage = $state_data['stage'];
+ $form_data = $state_data['form_data'];
+
+ if ($this->blog_id) {
+ $blog_id = $this->blog_id;
+ } else {
+ $blog_id = $this->selected_subsite($state_data);
+ }
+
+ if (1 > $blog_id || 'backup' == $stage) {
+ return $use;
+ }
+ $new_prefix = $state_data['new_prefix'];
+
+ if (empty($new_prefix)) {
+ return $row;
+ }
+
+ global $wpdb;
+
+ $old_prefix = $state_data['source_prefix'];
+ if (is_multisite() && 1 < $blog_id) {
+ $old_prefix .= $blog_id . '_';
+ }
+
+ if ($this->table_helper->table_is('options', $table_name)) {
+ // Rename options records like wp_X_user_roles to wp_Y_user_roles otherwise no users can do anything in the migrated site.
+ if (0 === stripos($row->option_name, $old_prefix)) {
+ $row->option_name = substr_replace($row->option_name, $new_prefix, 0, strlen($old_prefix));
+ }
+ }
+ if ($this->table_helper->table_is('usermeta', $table_name)) {
+ if (!$this->is_user_required_for_blog($row->user_id, $blog_id)) {
+ $use = false;
+ } elseif (1 == $blog_id) {
+ $prefix_escaped = preg_quote($state_data['source_prefix']);
+ if (1 === preg_match('/^' . $prefix_escaped . '([0-9]+)_/', $row->meta_key, $matches)) {
+ // Remove non-primary subsite records from usermeta when migrating primary subsite.
+ $use = false;
+ } elseif (0 === stripos($row->meta_key, $old_prefix)) {
+ // Rename prefixed keys.
+ $row->meta_key = substr_replace($row->meta_key, $new_prefix, 0, strlen($old_prefix));
+ }
+ } else {
+ if (0 === stripos($row->meta_key, $old_prefix)) {
+ // Rename prefixed keys.
+ $row->meta_key = substr_replace($row->meta_key, $new_prefix, 0, strlen($old_prefix));
+ } elseif (0 === stripos($row->meta_key, $state_data['source_prefix'])) {
+ // Remove wp_* records from usermeta not for extracted subsite.
+ $use = false;
+ }
+ }
+ }
+
+ if ($this->table_helper->table_is('users', $table_name)) {
+ if (!$this->is_user_required_for_blog($row->ID, $blog_id)) {
+ $use = false;
+ }
+ }
+
+ return $use;
+ }
+
+ /**
+ * Handler for the wpmdb_find_and_replace filter.
+ *
+ * @param array $tmp_find_replace_pairs
+ * @param string $intent
+ * @param string $site_url
+ *
+ * @return array
+ */
+ public function filter_find_and_replace($tmp_find_replace_pairs, $intent, $site_url)
+ {
+ $blog_id = $this->selected_subsite();
+
+ $this->state_data = $this->migration_state_manager->set_post_data();
+
+ if (1 > $blog_id) {
+ return $tmp_find_replace_pairs;
+ }
+
+ $source = ('pull' === $intent) ? 'remote' : 'local';
+ $target = ('pull' === $intent) ? 'local' : 'remote';
+
+ $is_source_multi = 'true' === $this->state_data['site_details'][$source]['is_multisite'];
+ $is_destination_multi = 'true' === $this->state_data['site_details'][$target]['is_multisite'];
+ $destination_id = $is_source_multi && $is_destination_multi && isset($this->state_data['mst_destination_subsite']) ? $this->state_data['mst_destination_subsite'] : $blog_id;
+ if ('true' === $this->state_data['site_details'][$source]['is_multisite']) {
+ $source_site_url = $this->state_data['site_details'][$source]['subsites_info'][$blog_id]['site_url'];
+ $source_uploads_baseurl = $this->state_data['site_details'][$source]['subsites_info'][$blog_id]['uploads']['baseurl'];
+ $source_short_basedir = $this->state_data['site_details'][$source]['subsites_info'][$blog_id]['uploads']['short_basedir'];
+ } else {
+ $source_site_url = $this->state_data['site_details'][$source]['site_url'];
+ $source_uploads_baseurl = $this->state_data['site_details'][$source]['uploads']['baseurl'];
+ $source_short_basedir = '';
+ }
+ $source_site_url = '//' . untrailingslashit($this->util->scheme_less_url($source_site_url));
+ $source_uploads_baseurl = '//' . untrailingslashit($this->util->scheme_less_url($source_uploads_baseurl));
+
+ if (in_array($intent, array('savefile', 'find_replace'))) {
+ $target_site_url = '';
+ $target_uploads_baseurl = '';
+ $target_short_basedir = '';
+
+ foreach ($tmp_find_replace_pairs as $find => $replace) {
+ if ($find == $source_site_url) {
+ $target_site_url = $replace;
+ break;
+ }
+ }
+
+ // Append extra path elements from uploads url, removing unneeded subsite specific elements.
+ if (!empty($target_site_url)) {
+ $target_uploads_baseurl = $target_site_url . substr($source_uploads_baseurl, strlen($source_site_url));
+
+ if (!empty($source_short_basedir) && 'savefile' === $intent) {
+ $target_uploads_baseurl = substr(untrailingslashit($target_uploads_baseurl), 0, -strlen(untrailingslashit($source_short_basedir)));
+ }
+ }
+ } elseif ('true' === $this->state_data['site_details'][$target]['is_multisite']) {
+ $target_site_url = $this->state_data['site_details'][$target]['subsites_info'][$destination_id]['site_url'];
+ $target_uploads_baseurl = $this->state_data['site_details'][$target]['subsites_info'][$destination_id]['uploads']['baseurl'];
+ $target_short_basedir = $this->state_data['site_details'][$target]['subsites_info'][$destination_id]['uploads']['short_basedir'];
+ } else {
+ $target_site_url = $this->state_data['site_details'][$target]['site_url'];
+ $target_uploads_baseurl = $this->state_data['site_details'][$target]['uploads']['baseurl'];
+ $target_short_basedir = '';
+ }
+
+ // If we have a target uploads url, we can add in the find/replace we need.
+ if (!empty($target_uploads_baseurl)) {
+ $target_site_url = '//' . untrailingslashit($this->util->scheme_less_url($target_site_url));
+ $target_uploads_baseurl = '//' . untrailingslashit($this->util->scheme_less_url($target_uploads_baseurl));
+
+ $target_site_url = apply_filters('wpmdb_mst_target_site_url', $target_site_url);
+ $target_uploads_baseurl = apply_filters('wpmdb_mst_target_uploads_baseurl', $target_uploads_baseurl);
+
+ // As we're appending to the find/replace rows, we need to use the already replaced values for altering uploads url.
+ $old_uploads_url = substr_replace($source_uploads_baseurl, $target_site_url, 0, strlen($source_site_url));
+ $tmp_find_replace_pairs[$old_uploads_url] = $target_uploads_baseurl;
+ }
+
+ return $tmp_find_replace_pairs;
+ }
+
+ /**
+ * Change the name of the given table depending on migration profile settings and source and target site setup.
+ *
+ * @param string $table_name
+ * @param string $intent
+ * @param array $site_details
+ *
+ * @return string
+ *
+ * This is run in response to the wpmdb_finalize_target_table_name filter on the target site.
+ */
+ public function filter_finalize_target_table_name($table_name, $state_data)
+ {
+ $intent = $state_data['intent'];
+ $site_details = $state_data['site_details'];
+ if ('find_replace' === $intent) {
+ return $table_name;
+ }
+
+ $blog_id = isset($state_data['mst_selected_subsite']) ? $state_data['mst_selected_subsite'] : 0;
+
+ if (1 > $blog_id) {
+ return $table_name;
+ }
+
+ $new_prefix = $state_data['new_prefix'];
+ if (empty($new_prefix)) {
+ return $table_name;
+ }
+
+ // During a MST migration we add a custom prefix to the global tables so that we can manipulate their data before use.
+ $type = isset($state_data['type']) ? $state_data['type'] : $intent;
+ $old_prefix = ('push' === $type) ? $state_data['site_details']['local']['prefix'] : $state_data['site_details']['remote']['prefix'];
+ if (is_multisite() && $this->table_helper->table_is('', $table_name, 'global', $new_prefix, $blog_id, $old_prefix)) {
+ $new_prefix .= 'wpmdbglobal_';
+ }
+ $destination_subsite = isset($state_data['mst_destination_subsite']) ? $state_data['mst_destination_subsite'] : 0;
+ if ((0 < $destination_subsite || !is_multisite()) && 1 < $blog_id && !$this->table_helper->table_is('', $table_name, 'global', $new_prefix, $blog_id, $old_prefix)) {
+ $old_prefix .= $blog_id . '_';
+ }
+
+ if (0 === stripos($table_name, $old_prefix)) {
+ $table_name = substr_replace($table_name, $new_prefix, 0, strlen($old_prefix));
+ }
+
+ return $table_name;
+ }
+
+ /**
+ * Returns validated and sanitized form data.
+ *
+ * @param array|string $data
+ *
+ * @return array|string
+ */
+ public function parse_migration_form_data($data)
+ {
+ // ***+=== @TODO - revisit usage of parse_migration_form_data
+ $form_data = $this->form_data_class->parse_and_save_migration_form_data($data);
+
+ $form_data = array_intersect_key($form_data, array_flip($this->accepted_fields));
+
+ return $form_data;
+ }
+
+ /**
+ * Alter file path pulled from remote to local equivalent.
+ *
+ * @param string $file
+ * @param integer $blog_id
+ *
+ * @return string TODO: Update for multisite <=> multisite (blog_ids)
+ *
+ * @throws Exception
+ *
+ * TODO: Update for multisite <=> multisite (blog_ids)
+ */
+ protected function alter_pulled_file_path($file, $blog_id)
+ {
+ if (1 > $blog_id) {
+ return $file;
+ }
+
+ if ($this->state_data['site_details']['local']['is_multisite'] !== $this->state_data['site_details']['remote']['is_multisite']) {
+ if (is_multisite()) {
+ if (isset($this->state_data['site_details']['local']['subsites_info'][$blog_id]['uploads']['short_basedir'])) {
+ $file = ltrim(trailingslashit($this->state_data['site_details']['local']['subsites_info'][$blog_id]['uploads']['short_basedir']) . $file, '/');
+ } else {
+ throw new \Exception(__('Expected local subsite "short_basedir" missing from `state_data`.', 'wp-migrate-db'));
+ }
+ } else {
+ if (isset($this->state_data['site_details']['remote']['subsites_info'][$blog_id]['uploads']['short_basedir'])) {
+ $file = substr($file, strlen($this->state_data['site_details']['remote']['subsites_info'][$blog_id]['uploads']['short_basedir']));
+ } else {
+ throw new \Exception(__('Expected remote subsite "short_basedir" missing from `state_data`.', 'wp-migrate-db'));
+ }
+ }
+ }
+
+ return $file;
+ }
+
+ /**
+ * Maybe change options keys to be preserved.
+ *
+ * @param array $preserved_options
+ * @param string $intent
+ *
+ * @return array
+ */
+ public function filter_preserved_options($preserved_options, $intent = '')
+ {
+ $state_data = $intent === 'push' ? Persistence::getRemoteStateData() : Persistence::getStateData();
+ $blog_id = $this->selected_subsite($state_data);
+
+ if (0 < $blog_id && in_array($intent, array('push', 'pull'))) {
+ $preserved_options = $this->table->preserve_active_plugins_option($preserved_options);
+ }
+
+ return $preserved_options;
+ }
+
+ /**
+ * Maybe preserve the WPMDB plugins if they aren't already preserved.
+ *
+ * @param array $preserved_options_data
+ * @param string $intent
+ *
+ * @return array
+ */
+ public function filter_preserved_options_data($preserved_options_data, $intent = '')
+ {
+ $state_data = $intent === 'push' ? Persistence::getRemoteStateData() : Persistence::getStateData();
+ $blog_id = $this->selected_subsite($state_data);
+
+ if (0 < $blog_id && in_array($intent, array('push', 'pull'))) {
+ $preserved_options_data = $this->table->preserve_wpmdb_plugins($preserved_options_data);
+ }
+
+ return $preserved_options_data;
+ }
+
+ /**
+ * Append more queries to be run at finalize_migration.
+ *
+ * @param array $queries
+ *
+ * @return array
+ */
+ public function filter_get_alter_queries($queries, $state_data)
+ {
+ if(empty($state_data)) {
+ return $queries;
+ }
+
+ $blog_id = isset($state_data['mst_destination_subsite']) ? $state_data['mst_destination_subsite'] : $this->selected_subsite($state_data);
+
+ if (1 > $blog_id) {
+ return $queries;
+ }
+
+ if (!is_multisite() || 'pull' !== $state_data['intent'] || empty($state_data['tables'])) {
+ return $queries;
+ }
+
+ global $wpdb;
+
+ $tables = explode(',', $state_data['tables']);
+
+ $target_users_table = null;
+ $source_users_table = null;
+ $target_usermeta_table = null;
+ $source_usermeta_table = null;
+ $posts_imported = false;
+ $target_posts_table = null;
+ $target_postmeta_table = null;
+ $comments_imported = false;
+ $target_comments_table = null;
+ $intent = isset($state_data['type']) ? $state_data['type'] : $state_data['intent'];
+ $source_prefix = ('pull' === $intent) ? $state_data['site_details']['remote']['prefix'] : $state_data['site_details']['local']['prefix'];
+ foreach ($tables as $table) {
+ if (empty($source_users_table) && $this->table_helper->table_is('users', $table, 'table', $source_prefix, 0, $source_prefix)) {
+ $target_users_table = Util::prefix_updater($table, $state_data['source_prefix'], $state_data['destination_prefix']);
+ $source_users_table = $this->filter_finalize_target_table_name($table, $state_data);
+ continue;
+ }
+ if (empty($source_usermeta_table) && $this->table_helper->table_is('usermeta', $table, 'table', $source_prefix, 0, $source_prefix)) {
+ $target_usermeta_table = Util::prefix_updater($table, $state_data['source_prefix'], $state_data['destination_prefix']);
+ $source_usermeta_table = $this->filter_finalize_target_table_name($table, $state_data);
+ continue;
+ }
+ if (!$posts_imported && $this->table_helper->table_is('posts', $table, 'table', $source_prefix)) {
+ $posts_imported = true;
+ $target_posts_table = $this->filter_finalize_target_table_name($table, $state_data);
+ continue;
+ }
+ if ($this->table_helper->table_is('postmeta', $table, 'table', $source_prefix)) {
+ $target_postmeta_table = $this->filter_finalize_target_table_name($table, $state_data);
+ continue;
+ }
+ if (!$comments_imported && $this->table_helper->table_is('comments', $table, 'table', $source_prefix)) {
+ $comments_imported = true;
+ $target_comments_table = $this->filter_finalize_target_table_name($table, $state_data);
+ continue;
+ }
+ }
+
+ // Find users that already exist and update their content to adopt existing user id and remove from import.
+ if (!empty($source_users_table)) {
+ $updated_user_ids = array();
+ $temp_prefix = $state_data['temp_prefix'];
+ $temp_source_users_table = $temp_prefix . $source_users_table;
+ $temp_source_usermeta_table = $temp_prefix . $source_usermeta_table;
+
+ $sql = "
+ SELECT source.id AS source_id, target.id AS target_id FROM `{$temp_source_users_table}` AS source, `{$target_users_table}` AS target
+ WHERE target.user_login = source.user_login
+ AND target.user_email = source.user_email
+ ";
+
+ $user_ids_to_update = $wpdb->get_results($sql, ARRAY_A);
+
+ //If users match from both sites
+ if (!empty($user_ids_to_update)) {
+ foreach ($user_ids_to_update as $user_ids) {
+ $blogs_of_user = get_blogs_of_user($user_ids['target_id']);
+
+ // Log user for exclusion from import.
+ $updated_user_ids[] = $user_ids['source_id'];
+
+ //Add new blog capabilities to imported users
+ if (null !== $source_usermeta_table && !array_key_exists($blog_id, $blogs_of_user)) {
+ $queries = $this->update_usermeta_for_imported_users($queries, $user_ids, $temp_source_usermeta_table, $target_usermeta_table, $blog_id);
+ }
+
+ if (empty($blogs_of_user) || array_key_exists($blog_id, $blogs_of_user)) {
+ // Only update content ownership if user id has changed.
+ if ($user_ids['source_id'] !== $user_ids['target_id']) {
+ if ($posts_imported) {
+ $queries[]['query'] = "
+ UPDATE `{$target_posts_table}`
+ SET post_author = {$user_ids['target_id']}
+ WHERE post_author = {$user_ids['source_id']}
+ ;\n
+ ";
+ }
+
+ if ($comments_imported) {
+ $queries[]['query'] = "
+ UPDATE `{$target_comments_table}`
+ SET user_id = {$user_ids['target_id']}
+ WHERE user_id = {$user_ids['source_id']}
+ ;\n
+ ";
+ }
+ }
+ }
+ }
+ }
+
+ $queries[]['query'] = "ALTER TABLE `{$target_users_table}` ADD COLUMN wpmdb_user_id BIGINT(20) UNSIGNED;\n";
+
+ $where = '';
+ if (!empty($updated_user_ids)) {
+ $where = 'WHERE u2.id NOT IN (' . implode(',', $updated_user_ids) . ')';
+ }
+ $queries[]['query'] = "INSERT INTO `{$target_users_table}` (user_login, user_pass, user_nicename, user_email, user_url, user_registered, user_activation_key, user_status, display_name, wpmdb_user_id)
+ SELECT u2.user_login, u2.user_pass, u2.user_nicename, u2.user_email, u2.user_url, u2.user_registered, u2.user_activation_key, u2.user_status, u2.display_name, u2.id
+ FROM `{$source_users_table}` AS u2
+ {$where};\n";
+
+ if (!empty($source_usermeta_table)) {
+ $queries[]['query'] = "INSERT INTO `{$target_usermeta_table}` (user_id, meta_key, meta_value)
+ SELECT u.id, m2.meta_key, m2.meta_value
+ FROM `{$source_usermeta_table}` AS m2
+ JOIN `{$target_users_table}` AS u ON m2.user_id = u.wpmdb_user_id;\n";
+ }
+
+ if ($posts_imported) {
+ $queries[]['query'] = "
+ UPDATE `{$target_posts_table}` AS p, `{$target_users_table}` AS u
+ SET p.post_author = u.id
+ WHERE p.post_author = u.wpmdb_user_id
+ ;\n";
+ }
+
+ if (!is_null($target_postmeta_table) && !empty($user_ids)) {
+ $queries[]['query'] = "
+ UPDATE `{$target_postmeta_table}`
+ SET {$target_postmeta_table}.meta_value = {$user_ids['target_id']}
+ WHERE {$target_postmeta_table}.meta_key = '_edit_last'
+ AND {$target_postmeta_table}.meta_value = {$user_ids['source_id']};
+ ";
+ }
+
+ if ($comments_imported) {
+ $queries[]['query'] = "
+ UPDATE `{$target_comments_table}` AS c, `{$target_users_table}` AS u
+ SET c.user_id = u.id
+ WHERE c.user_id = u.wpmdb_user_id
+ ;\n";
+ }
+ $queries[]['query'] = "DROP TABLE `{$source_users_table}`;\n";
+
+ $queries[]['query'] = "ALTER TABLE `{$target_users_table}` DROP COLUMN wpmdb_user_id;\n";
+ }
+
+ // Cleanup imported usermeta table, whether used by above user related queries or not.
+ // TODO: Maybe support updating usermeta without imported users table?
+ if (!empty($source_usermeta_table)) {
+ $queries[]['query'] = "DROP TABLE `{$source_usermeta_table}`;\n";
+ }
+
+ return $queries;
+ }
+
+
+ /**
+ * Filters the URLs used by WPMDB_Replace.
+ *
+ * @param array $site_urls
+ *
+ * @TODO add unit tests for this method
+ * @return array
+ */
+ function filter_replace_site_urls($site_urls)
+ {
+ if (isset($this->form_data['mst_select_subsite']) && '1' === $this->form_data['mst_select_subsite']) {
+ $selected_subsite_id = $this->form_data['mst_selected_subsite'];
+
+ foreach (array('local', 'remote') as $which) {
+ if (isset($this->state_data['site_details'][$which]['subsites_info'][$selected_subsite_id])) {
+ $subsite_base = $this->state_data['site_details'][$which]['subsites_info'][$selected_subsite_id];
+ $subsite_url = $subsite_base['site_url'];
+
+ // Use home_url if it's set.
+ if (isset($subsite_base['home_url'])) {
+ $subsite_url = $subsite_base['home_url'];
+ }
+
+ $site_urls[$which] = $subsite_url;
+ }
+ }
+ }
+
+ return $site_urls;
+ }
+
+ /**
+ * Updates the URL in the export header for MST exports
+ *
+ * @param string $url
+ *
+ * @return string
+ */
+ function filter_backup_header_url($url)
+ {
+ $selected_subsite = $this->selected_subsite();
+
+ if (0 !== $selected_subsite &&
+ 'backup' !== $this->state_data['stage'] &&
+ isset($this->state_data['site_details']['local']['subsites_info'][$selected_subsite])) {
+ $subsite_base = $this->state_data['site_details']['local']['subsites_info'][$selected_subsite];
+ $url = $subsite_base['home_url'];
+ }
+
+ return $url;
+ }
+
+ /**
+ * @param array $tables
+ *
+ * @return array
+ */
+ function filter_backup_header_tables($tables)
+ {
+ if (!is_multisite() || 'backup' === $this->state_data['stage']) {
+ return $tables;
+ }
+
+ foreach ($tables as $key => $table) {
+ $tables[$key] = $this->filter_target_table_name($table, Persistence::getStateData());
+ }
+
+ return $tables;
+ }
+
+ function filter_backup_header_is_subsite_export()
+ {
+ return 0 === $this->selected_subsite() ? 'false' : 'true';
+ }
+
+ /**
+ * Does the passed subsite (ID) exist?
+ *
+ * @param int $blog_id
+ *
+ * @return bool
+ */
+ public function subsite_exists($blog_id)
+ {
+ if (!is_multisite()) {
+ return false;
+ }
+
+ if (version_compare($GLOBALS['wp_version'], '4.6', '>=')) {
+ $blogs = get_sites(array('number' => false));
+ } else {
+ $blogs = wp_get_sites(array('limit' => 0));
+ }
+
+ if (empty($blogs)) {
+ return false;
+ }
+
+ foreach ($blogs as $blog) {
+ $blog = (array)$blog;
+ if (!empty($blog['blog_id']) && $blog_id == $blog['blog_id']) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param array $queries
+ * @param array $user_ids
+ * @param string $temp_source_usermeta_table
+ * @param string $target_usermeta_table
+ * @param int $blog_id
+ *
+ * @return array
+ */
+ protected function update_usermeta_for_imported_users($queries, $user_ids, $temp_source_usermeta_table, $target_usermeta_table, $blog_id)
+ {
+ global $wpdb;
+ $prefix = $wpdb->prefix;
+
+ $blog_id_string = 1 === (int)$blog_id ? $prefix : "{$prefix}{$blog_id}_";
+
+ $sql = "SELECT meta_value as value
+ FROM `{$temp_source_usermeta_table}`
+ WHERE ( meta_key = '{$blog_id_string}user_level' OR meta_key = '{$blog_id_string}capabilities' )
+ AND user_id={$user_ids['source_id']}
+ ORDER BY meta_key";
+
+ $result = $wpdb->get_results($sql, OBJECT);
+
+ if ($result) {
+ //User level
+ $queries[]['query'] = "
+ INSERT INTO `{$target_usermeta_table}` (user_id, meta_key, meta_value)
+ VALUES ({$user_ids['target_id']}, '{$blog_id_string}user_level', {$result[1]->value} );
+ \n";
+
+ //Capabilities
+ $queries[]['query'] = "
+ INSERT INTO `{$target_usermeta_table}`(user_id, meta_key, meta_value)
+ VALUES({$user_ids['target_id']}, '{$blog_id_string}capabilities', '{$result[0]->value}')
+ ;\n";
+ }
+
+ return $queries;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Connection.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Connection.php
new file mode 100644
index 000000000..922333004
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Connection.php
@@ -0,0 +1,10 @@
+http = $http;
+ $this->http_helper = $http_helper;
+ $this->props = $props;
+ $this->license = $license;
+ $this->remote_post = $remote_post;
+ $this->util = $util;
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->rest_API_server = $rest_API_server;
+ }
+
+ public function register()
+ {
+ // @TODO probably need to force flush rewrite rules
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute('/verify-connection', [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_verify_connection_to_remote_site'],
+ ]);
+ }
+
+ /**
+ * WP REST API endpoint call to `/verify-connection`
+ * Verifies that the local site has a valid licence.
+ * Sends a request to the remote site to collect additional information required to complete the migration.
+ *
+ * @return mixed
+ */
+ public function ajax_verify_connection_to_remote_site()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+
+ $key_rules = apply_filters(
+ 'wpmdb_verify_connection_key_rules',
+ array(
+ 'action' => 'key',
+ 'url' => 'url',
+ 'key' => 'string',
+ 'intent' => 'key',
+ 'nonce' => 'key',
+ 'convert_post_type_selection' => 'numeric',
+ 'profile' => 'numeric',
+ ),
+ __FUNCTION__
+ );
+
+ Persistence::cleanupStateOptions(); // Wipe old migration options
+ $state_data = Persistence::setPostData( $key_rules, __METHOD__ );
+
+ if ( !$this->license->is_valid_licence() ) {
+ $message = __( 'Please activate your license before attempting a pull or push migration.', 'wp-migrate-db' );
+
+ return $this->http->end_ajax( new \WP_Error( 'invalid-license', $message ) );
+ }
+
+ do_action('wpmdb_before_verify_connection_to_remote_site', $state_data);
+
+ $data = array(
+ 'action' => 'wpmdb_verify_connection_to_remote_site',
+ 'intent' => $state_data['intent'],
+ 'referer' => $this->util->get_short_home_address_from_url( Util::home_url() ),
+ 'version' => $this->props->plugin_version,
+ );
+
+ $data = apply_filters( 'wpmdb_verify_connection_to_remote_site_args', $data, $state_data );
+
+ $data['sig'] = $this->http_helper->create_signature( $data, $state_data['key'] );
+ $ajax_url = $this->util->ajax_url();
+ $timeout = apply_filters( 'wpmdb_prepare_remote_connection_timeout', 30 );
+ $remote_response = $this->remote_post->post( $ajax_url, $data, __FUNCTION__, compact( 'timeout' ), true );
+
+ $url_bits = Util::parse_url( $this->dynamic_props->attempting_to_connect_to );
+
+ // WP_Error is thrown manually by remote_post() to tell us something went wrong
+ if ( is_wp_error( $remote_response ) ) {
+ return $this->http->end_ajax(
+ $remote_response
+ );
+ }
+
+ $response = false;
+ if ( Util::is_json( $remote_response ) ) {
+ $response = json_decode( $remote_response, true );
+ }
+
+ if ( !$response ) {
+ return $this->http->end_ajax(
+ new \WP_Error( 'json-decode-failure', __( 'Failed attempting to decode the response from the remote server. Please contact support.', 'wp-migrate-db' ) ),
+ $remote_response
+ );
+ }
+
+ if (isset($response['site_details']['wpe_cookie'])) {
+ Persistence::storeRemoteWPECookie($response['site_details']['wpe_cookie']);
+ }
+
+ $data['scheme'] = $url_bits['scheme'];
+ $data += $response;
+
+ // Store response in DB
+ Persistence::storeRemoteResponse($data);
+
+ // Check tables exist on remote/local and update profile
+
+ return $this->http->end_ajax($data);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Connection/Remote.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Connection/Remote.php
new file mode 100644
index 000000000..3c0353148
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Connection/Remote.php
@@ -0,0 +1,462 @@
+scrambler = $scrambler;
+ $this->http = $http;
+ $this->http_helper = $http_helper;
+ $this->props = $props;
+ $this->error_log = $error_log;
+ $this->form_data = $form_data;
+ $this->settings = $settings->get_settings();
+ $this->license = $license;
+ $this->remote_post = $remote_post;
+ $this->util = $util;
+ $this->table = $table;
+ $this->filesystem = $filesystem;
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->multisite = $multisite;
+ $this->table_helper = $table_helper;
+ $this->backup_export = $backup_export;
+ }
+
+ public function register()
+ {
+ // external AJAX handlers
+ add_action('wp_ajax_nopriv_wpmdb_verify_connection_to_remote_site', array($this, 'respond_to_verify_connection_to_remote_site'));
+ add_action('wp_ajax_nopriv_wpmdb_remote_initiate_migration', array($this, 'respond_to_remote_initiate_migration'));
+
+ // Report outdated addon versions to the other site.
+ add_filter('wpmdb_establish_remote_connection_data', array($this, 'report_outdated_addon_versions'), 100);
+ }
+
+
+ /**
+ * No privileges AJAX endpoint for the wpmdb_verify_connection_to_remote_site action.
+ * Verifies that the connecting site is using the same version of WP Migrate DB as the local site. * Verifies that the request is originating from a trusted source by verifying the request signature.
+ * Verifies that the local site has a valid licence.
+ * Verifies that the local site is allowed to perform a pull / push migration.
+ * If all is successful, returns an array of local site information used to complete the migration.
+ *
+ * @return mixed
+ */
+ public function respond_to_verify_connection_to_remote_site()
+ {
+ $key_rules = apply_filters(
+ 'wpmdb_respond_to_verify_connection_key_rules',
+ array(
+ 'action' => 'key',
+ 'intent' => 'key',
+ 'referer' => 'string',
+ 'version' => 'string',
+ 'sig' => 'string',
+ ),
+ __FUNCTION__
+ );
+
+ Persistence::cleanupStateOptions('wpmdb_remote_migration_state'); // Wipe old migration options
+ $state_data = Persistence::setRemotePostData($key_rules, __METHOD__);
+
+ if (is_wp_error($state_data)) {
+ return wp_send_json_error($state_data->get_error_message());
+ }
+
+ $return = array();
+
+ unset($key_rules['sig']);
+ $filtered_post = $this->http_helper->filter_post_elements($state_data, array_keys($key_rules));
+
+ // Only scramble response once we know it can be handled.
+ add_filter('wpmdb_before_response', array($this->scrambler, 'scramble'));
+
+ if (!$this->http_helper->verify_signature($filtered_post, $this->settings['key'])) {
+ $err = $this->props->invalid_content_verification_error . ' (#120)';
+
+ return $this->http->end_ajax(
+ new \WP_Error('invalid-content-verification', $err),
+ $filtered_post
+ );
+ }
+
+ /**
+ * Check remote version against local version
+ */
+ if (!isset($filtered_post['version']) || version_compare($filtered_post['version'], $this->props->plugin_version, '!=')) {
+ if (!isset($filtered_post['version'])) {
+ $return['message'] = sprintf(__('Version Mismatch — We\'ve detected you have version %1$s of WP Migrate at %2$s but are using an outdated version here. Please go to the Plugins page on both installs and check for updates.', 'wp-migrate-db'), $GLOBALS['wpmdb_meta'][$this->props->plugin_slug]['version'], $this->util->get_short_home_address_from_url(Util::home_url()));
+ } else {
+ if (version_compare($filtered_post['version'], $this->props->plugin_version, '>')) {
+ $return['message'] = sprintf(__('Version Mismatch — We\'ve detected you have version %1$s of WP Migrate at %2$s but are using %3$s here. (#196)', 'wp-migrate-db'), $GLOBALS['wpmdb_meta'][$this->props->plugin_slug]['version'], $this->util->get_short_home_address_from_url(Util::home_url()), $filtered_post['version']);
+ } else {
+ $return['message'] = sprintf(__('Version Mismatch — We\'ve detected you have version %1$s of WP Migrate at %2$s but are using %3$s here. Please go to the Plugins page on both installs and check for updates.', 'wp-migrate-db'), $GLOBALS['wpmdb_meta'][$this->props->plugin_slug]['version'], $this->util->get_short_home_address_from_url(Util::home_url()), $filtered_post['version']);
+
+ // If the other site is pre-2.0, we need to serialize the response.
+ if (version_compare($filtered_post['version'], '2.0b1', '<')) {
+ $return['error'] = 1;
+ $return['error_id'] = 'version_mismatch';
+ $return = json_encode($return);
+ return $this->http->end_ajax($return, false, true);
+ }
+ }
+ }
+
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-version-mismatch',
+ $return['message']
+ )
+ );
+ }
+
+ /**
+ * Do license check
+ */
+ if (!$this->license->is_valid_licence()) {
+ $local_host = $this->util->get_short_home_address_from_url(Util::home_url());
+
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-license-not-valid',
+ sprintf(__("Activate Remote License — Looks like you don't have a WP Migrate license active at %s (#195).", 'wp-migrate-db'), $local_host)
+ )
+ );
+ }
+
+ /**
+ * Check allow push/pull settings
+ */
+ $key = 'allow_' . $state_data['intent'];
+ if (!isset($this->settings[$key]) || (bool)$this->settings[$key] !== true) {
+ if ($state_data['intent'] === 'pull') {
+ $message = __('The connection succeeded but the remote site is configured to reject pull connections. You can change this in the "settings" tab on the remote site. (#122)', 'wp-migrate-db');
+ } else {
+ $message = __('The connection succeeded but the remote site is configured to reject push connections. You can change this in the "settings" tab on the remote site. (#122)', 'wp-migrate-db');
+ }
+
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-license-push-pull-disabled',
+ $message
+ )
+ );
+ }
+
+ $site_details = $this->util->site_details();
+
+ $return['tables'] = $this->table->get_tables();
+ $return['prefixed_tables'] = $this->table->get_tables('prefix');
+ $return['table_sizes'] = $this->table->get_table_sizes();
+ $return['table_rows'] = $this->table->get_table_row_count();
+ $return['table_sizes_hr'] = array_map(array($this->table, 'format_table_sizes'), $this->table->get_table_sizes());
+ $return['path'] = Util::get_absolute_root_file_path();
+ $return['url'] = Util::home_url();
+ $return['prefix'] = $site_details['prefix']; // TODO: Remove backwards compatibility.
+ $return['bottleneck'] = $this->util->get_bottleneck();
+ $return['delay_between_requests'] = $this->settings['delay_between_requests'];
+ $return['error'] = 0;
+ $return['plugin_version'] = $this->props->plugin_version;
+ $return['domain'] = $this->multisite->get_domain_current_site();
+ $return['path_current_site'] = $this->util->get_path_current_site();
+ $return['uploads_dir'] = $site_details['uploads_dir']; // TODO: Remove backwards compatibility.
+ $return['gzip'] = (Util::gzip() ? '1' : '0');
+ $return['post_types'] = $this->table->get_post_types();
+ // TODO: Use WP_Filesystem API.
+ $return['write_permissions'] = (is_writeable($this->filesystem->get_upload_info('path')) ? 'true' : 'false');
+ $return['themes_permissions'] = is_writeable(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'themes') ? 'true' : 'false';
+ $return['plugins_permissions'] = is_writeable(WP_PLUGIN_DIR) ? 'true' : 'false';
+ $return['muplugins_permissions'] = is_writeable(WPMU_PLUGIN_DIR) ? 'true' : 'false';
+ $return['others_permissions'] = is_writeable(WP_CONTENT_DIR) ? 'true' : 'false';
+ $return['upload_dir_long'] = $this->filesystem->get_upload_info('path');
+ $return['wp_upload_dir'] = $this->filesystem->get_wp_upload_dir();
+ $return['temp_prefix'] = $this->props->temp_prefix;
+ $return['lower_case_table_names'] = $this->table->get_lower_case_table_names_setting();
+ $return['subsites'] = $site_details['subsites']; // TODO: Remove backwards compatibility.
+ $return['site_details'] = $site_details;
+ $return['beta_optin'] = BetaManager::has_beta_optin($this->settings);
+ $return['firewall_plugins'] = $site_details['firewall_plugins'];
+ $return = apply_filters('wpmdb_establish_remote_connection_data', $return);
+ $result = $this->http->end_ajax(json_encode($return), false, true);
+
+ return $result;
+ }
+
+ /**
+ * Reports outdated addon versions (pre-2.0) to the site initiating the migration.
+ * TODO: Remove when enough people have moved on from 1.9.x and earlier.
+ *
+ * Hooks on: wpmdb_establish_remote_connection_data
+ *
+ * @param array $return The existing site data.
+ *
+ * @return array
+ */
+ public function report_outdated_addon_versions($return)
+ {
+ if (!isset($return['media_files_version']) && isset($GLOBALS['wpmdb_meta']['wp-migrate-db-pro-media-files'])) {
+ $return['media_files_version'] = $GLOBALS['wpmdb_meta']['wp-migrate-db-pro-media-files']['version'];
+ $return['media_files_available'] = '1';
+ }
+
+ if (!isset($return['theme_plugin_files_version']) && isset($GLOBALS['wpmdb_meta']['wp-migrate-db-pro-theme-plugin-files'])) {
+ $return['theme_plugin_files_version'] = $GLOBALS['wpmdb_meta']['wp-migrate-db-pro-theme-plugin-files']['version'];
+ $return['theme_plugin_files_available'] = '1';
+ }
+
+ if (!isset($return['mst_version']) && isset($GLOBALS['wpmdb_meta']['wp-migrate-db-pro-multisite-tools'])) {
+ $return['mst_version'] = $GLOBALS['wpmdb_meta']['wp-migrate-db-pro-multisite-tools']['version'];
+ $return['mst_available'] = '1';
+ }
+
+ return $return;
+ }
+
+ /**
+ * Validates migration request as the remote site and sets up anything that may be needed before the migration starts.
+ *
+ * @return array
+ */
+ public function respond_to_remote_initiate_migration()
+ {
+ add_filter('wpmdb_before_response', array($this->scrambler, 'scramble'));
+
+ $key_rules = apply_filters(
+ 'wpmdb_initiate_key_rules',
+ array(
+ 'action' => 'key',
+ 'intent' => 'key',
+ 'form_data' => 'string',
+ 'sig' => 'string',
+ 'site_details' => 'string',
+ ),
+ __FUNCTION__
+ );
+
+ Persistence::cleanupStateOptions();
+ Persistence::cleanupStateOptions('wpmdb_remote_migration_state');
+
+ $state_data = Persistence::setRemotePostData($key_rules, __METHOD__);
+
+ if (is_wp_error($state_data)) {
+ return wp_send_json_error($state_data->get_error_message());
+ }
+
+ $return = array();
+ $filtered_post = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'action',
+ 'intent',
+ 'form_data',
+ 'site_details',
+ )
+ );
+
+ $signature = $this->http_helper->verify_signature($filtered_post, $this->settings['key']);
+
+ if (!$signature) {
+ $error_msg = $this->props->invalid_content_verification_error . ' (#111)';
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-invalid-content-verification-error',
+ $error_msg
+ ),
+ $filtered_post
+ );
+ }
+
+
+ $state_data['form_data'] = base64_decode($state_data['form_data']);
+ $settings_key = 'allow_' . $state_data['intent'];
+
+ if (isset($this->settings[$settings_key]) && true !== (bool)$this->settings[$settings_key]) {
+ if ('pull' === $state_data['intent']) {
+ $message = __('The connection succeeded but the remote site is configured to reject pull connections. You can change this in the "settings" tab on the remote site. (#110)', 'wp-migrate-db');
+ } else {
+ $message = __('The connection succeeded but the remote site is configured to reject push connections. You can change this in the "settings" tab on the remote site. (#110)', 'wp-migrate-db');
+ }
+
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-push-pull-disabled',
+ $message
+ )
+ );
+ }
+
+ UsageTracking::log_usage($state_data['intent'] . '-remote');
+
+ $state_data['site_details'] = json_decode(
+ base64_decode($filtered_post['site_details']),
+ true
+ );
+
+ // ***+=== @TODO - revisit usage of parse_migration_form_data
+ $this->migration_options = $this->form_data->parse_and_save_migration_form_data($state_data['form_data']);
+
+ $return = $this->maybe_setup_backups($state_data, $return);
+
+ // Store current migration state and return its id.
+ $state = array_merge($state_data, $return);
+
+ $key_rules += [
+ 'db_version',
+ 'site_url',
+ 'dump_filename',
+ 'dump_url',
+ ];
+
+ $result = Persistence::setRemotePostData(
+ $key_rules,
+ __METHOD__,
+ 'wpmdb_remote_migration_state',
+ $state,
+ false
+ );
+ do_action('wpmdb_respond_remote_initiate', $state_data);
+
+ return wp_send_json_success($result);
+ }
+
+ /**
+ * @param $state_data
+ * @param array $return
+ *
+ * @return array
+ */
+ protected function maybe_setup_backups($state_data, array $return)
+ {
+ global $wpdb;
+ if ('push' === $state_data['intent']) {
+ if ('none' !== $this->migration_options['current_migration']['backup_option']) {
+ list($dump_filename, $dump_url) = $this->backup_export->setup_backups();
+ $return['dump_filename'] = $dump_filename;
+ $return['dump_url'] = $dump_url;
+ }
+
+ // sets up our table to store 'ALTER' queries
+ $create_alter_table_query = $this->table->get_create_alter_table_query();
+ $process_chunk_result = $this->table->process_chunk($create_alter_table_query);
+
+ if (true !== $process_chunk_result) {
+ return $this->http->end_ajax($process_chunk_result);
+ }
+
+ $return['db_version'] = $wpdb->db_version();
+ $return['site_url'] = site_url();
+ }
+
+ return $return;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/FinalizeComplete.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/FinalizeComplete.php
new file mode 100644
index 000000000..df18c54e1
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/FinalizeComplete.php
@@ -0,0 +1,227 @@
+scrambler = $scrambler;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->http = $http;
+ $this->http_helper = $http_helper;
+ $this->props = $props;
+ $this->error_log = $error_log;
+ $this->migration_manager = $migration_manager;
+ $this->form_data = $form_data;
+ $this->finalize = $finalize;
+ $this->settings = $settings->get_settings();
+ $this->rest_API_server = $rest_API_server;
+ $this->flush = $flush;
+ }
+
+ public function register()
+ {
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+
+ add_action('wp_ajax_nopriv_wpmdb_remote_finalize_migration', [$this, 'respond_to_remote_finalize_migration']);
+ add_action('wp_ajax_nopriv_wpmdb_remote_flush', [$this, 'respond_to_remote_flush']);
+ add_action('wp_ajax_nopriv_wpmdb_fire_migration_complete', [$this, 'fire_migration_complete']);
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute('/migration-complete', [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'fire_migration_complete'],
+ ]);
+ }
+
+ public function respond_to_remote_flush()
+ {
+ add_filter('wpmdb_before_response', array($this->scrambler, 'scramble'));
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'sig' => 'string',
+ );
+ $state_data = $this->migration_state_manager->set_post_data($key_rules);
+
+ $filtered_post = $this->http_helper->filter_post_elements($state_data, array('action'));
+
+ if (!$this->http_helper->verify_signature($filtered_post, $this->settings['key'])) {
+ $error_msg = $this->props->invalid_content_verification_error . ' (#123)';
+ $result = $this->http->end_ajax(new \WP_Error('wpmdb_invalid_content_verification_error', $error_msg));
+
+ return $result;
+ }
+
+ $return = $this->flush->flush();
+ $result = $this->http->end_ajax($return);
+
+ return $result;
+ }
+
+ /**
+ * The remote's handler for a request to finalize a migration.
+ *
+ * @return bool|null
+ */
+ function respond_to_remote_finalize_migration()
+ {
+ add_filter('wpmdb_before_response', array($this->scrambler, 'scramble'));
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'intent' => 'key',
+ 'url' => 'url',
+ 'form_data' => 'string',
+ 'tables' => 'string',
+ 'temp_prefix' => 'string',
+ 'site_details' => 'string',
+ 'prefix' => 'string',
+ 'stage' => 'string',
+ 'type' => 'key',
+ 'location' => 'url',
+ 'sig' => 'string',
+ );
+
+ $state_data = Persistence::setRemotePostData($key_rules, __METHOD__);
+
+ $filtered_post = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'action',
+ 'intent',
+ 'url',
+ 'form_data',
+ 'site_details',
+ 'tables',
+ 'temp_prefix',
+ 'prefix',
+ 'type',
+ 'location',
+ )
+ );
+
+ if (!$this->http_helper->verify_signature($filtered_post, $this->settings['key'])) {
+ $error_msg = $this->props->invalid_content_verification_error . ' (#123)';
+ $result = $this->http->end_ajax(new \WP_Error('wpmdb_invalid_content_verification_error', $error_msg));
+
+ return $result;
+ }
+
+ $this->form_data = base64_decode($filtered_post['form_data']);
+ $state_data['site_details'] = json_decode(base64_decode($state_data['site_details']), true);
+ do_action('wpmdb_remote_finalize', $state_data);
+ $return = $this->finalize->finalize_migration($state_data);
+ $result = $this->http->end_ajax($return);
+
+ return $result;
+ }
+
+ /**
+ * Triggers the wpmdb_migration_complete action once the migration is complete.
+ *
+ * @return bool|null
+ */
+ public function fire_migration_complete()
+ {
+ $state_data = Persistence::setPostData(
+ [
+ 'action' => 'string',
+ 'url' => 'string',
+ 'sig' => 'string',
+ ],
+ __METHOD__
+ );
+ $filtered_post = $this->http_helper->filter_post_elements($state_data, array('action', 'url'));
+
+ if (!$this->http_helper->verify_signature($filtered_post, $this->settings['key'])) {
+ $error_msg = $this->props->invalid_content_verification_error . ' (#138)';
+ $result = $this->http->end_ajax(new \WP_Error('wpmdb_invalid_content_verification_error', $error_msg));
+
+ return $result;
+ }
+
+ do_action('wpmdb_migration_complete', 'pull', $state_data['url'], '');
+ $result = $this->http->end_ajax(true);
+
+ return $result;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Flush.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Flush.php
new file mode 100644
index 000000000..930bf28ab
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Migration/Flush.php
@@ -0,0 +1,43 @@
+scrambler = $scrambler;
+ $this->settings = $settings->get_settings();
+ $this->migration_state_manager = $migration_state_manager;
+ $this->http = $http;
+ $this->http_helper = $http_helper;
+ $this->error_log = $error_log;
+ $this->props = $props;
+ $this->form_data = $form_data;
+ $this->migration_manager = $migration_manager;
+ $this->table = $table;
+ $this->backup_export = $backup_export;
+ $this->finalize_complete = $finalize_complete;
+ $this->rest_API_server = $rest_API_server;
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->table_helper = $table_helper;
+ }
+
+
+ public function register()
+ {
+ add_action('wp_ajax_nopriv_wpmdb_process_pull_request', array($this, 'respond_to_process_pull_request'));
+ add_action('wp_ajax_nopriv_wpmdb_process_chunk', array($this, 'respond_to_process_chunk'));
+ add_action('wp_ajax_nopriv_wpmdb_backup_remote_table', array($this, 'respond_to_backup_remote_table'));
+ add_action('wp_ajax_nopriv_wpmdb_process_push_migration_cancellation', array($this, 'respond_to_process_push_migration_cancellation'));
+ }
+
+ /**
+ * Exports table data from remote site during a Pull migration.
+ *
+ * @return string
+ */
+ function respond_to_process_pull_request()
+ {
+ add_filter('wpmdb_before_response', array($this->scrambler, 'scramble'));
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'remote_state_id' => 'key',
+ 'intent' => 'key',
+ 'url' => 'url',
+ 'table' => 'string',
+ 'form_data' => 'string',
+ 'stage' => 'key',
+ 'bottleneck' => 'positive_int',
+ 'current_row' => 'int',
+ 'last_table' => 'positive_int',
+ 'gzip' => 'positive_int',
+ 'primary_keys' => 'string',
+ 'site_url' => 'url',
+ 'site_details' => 'string',
+ 'find_replace_pairs' => 'string',
+ 'pull_limit' => 'positive_int',
+ 'db_version' => 'string',
+ 'path_current_site' => 'string',
+ 'domain_current_site' => 'text',
+ 'prefix' => 'string',
+ 'sig' => 'string',
+ 'source_prefix' => 'string',
+ 'destination_prefix' => 'string',
+ );
+
+ $state_data = Persistence::setRemotePostData($key_rules, __METHOD__);
+
+ if (is_wp_error($state_data)) {
+ return wp_send_json_error($state_data->get_error_message());
+ }
+
+ $state_data['find_replace_pairs'] = json_decode(base64_decode($state_data['find_replace_pairs']), true);
+ $state_data['form_data'] = base64_decode($state_data['form_data']);
+ $state_data['site_details'] = json_decode(base64_decode($state_data['site_details']), true);
+ $state_data['primary_keys'] = base64_decode($state_data['primary_keys']);
+ $state_data['source_prefix'] = base64_decode($state_data['source_prefix']);
+ $state_data['destination_prefix'] = base64_decode($state_data['destination_prefix']);
+
+ $this->form_data->parse_and_save_migration_form_data($state_data['form_data']);
+
+ // Save decoded state_data
+ Persistence::saveStateData($state_data, 'wpmdb_remote_migration_state');
+
+ $filtered_post = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'action',
+ 'remote_state_id',
+ 'intent',
+ 'url',
+ 'table',
+ 'form_data',
+ 'stage',
+ 'bottleneck',
+ 'current_row',
+ 'last_table',
+ 'gzip',
+ 'primary_keys',
+ 'site_url',
+ 'find_replace_pairs',
+ 'pull_limit',
+ 'db_version',
+ 'path_current_site',
+ 'domain_current_site',
+ 'prefix',
+ 'source_prefix',
+ 'destination_prefix',
+ )
+ );
+
+ $sig_data = $filtered_post;
+ // find_replace_pairs and form_data weren't used to create the migration signature
+ unset ($sig_data['find_replace_pairs'], $sig_data['form_data'], $sig_data['source_prefix'], $sig_data['destination_prefix']);
+
+
+ if (!$this->http_helper->verify_signature($sig_data, $this->settings['key'])) {
+ $error_msg = $this->props->invalid_content_verification_error . ' (#124)';
+
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-invalid-content-verification',
+ $error_msg
+ )
+ );
+ }
+
+ if ($this->settings['allow_pull'] != true) {
+ $message = __('The connection succeeded but the remote site is configured to reject pull connections. You can change this in the "settings" tab on the remote site. (#141)', 'wp-migrate-db');
+
+ return $this->http->end_ajax(new \WP_Error(
+ 'wpmdb-allow-pull-disabled',
+ $message
+ ));
+ }
+
+ if (!empty($filtered_post['db_version'])) {
+ $this->dynamic_props->target_db_version = $filtered_post['db_version'];
+ add_filter('wpmdb_create_table_query', array($this->table_helper, 'mysql_compat_filter'), 10, 5);
+ }
+
+ $this->dynamic_props->find_replace_pairs = $filtered_post['find_replace_pairs'];
+
+ // @TODO move to better place
+ $this->dynamic_props->maximum_chunk_size = $state_data['pull_limit'];
+ $this->table->process_table($state_data['table'], null, $state_data);
+ ob_start();
+ $return = ob_get_clean();
+
+ return $this->http->end_ajax($return, '', true);
+ }
+
+ /**
+ * Handler for the ajax request to process a chunk of data (e.g. SQL inserts).
+ *
+ * @return bool|null
+ */
+ public function respond_to_process_chunk()
+ {
+ add_filter('wpmdb_before_response', array($this->scrambler, 'scramble'));
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'table' => 'string',
+ 'chunk_gzipped' => 'positive_int',
+ 'sig' => 'string',
+ );
+
+ $state_data = Persistence::setPostData($key_rules, __METHOD__);
+
+ $filtered_post = $this->http_helper->filter_post_elements($state_data, array(
+ 'action',
+ 'remote_state_id',
+ 'table',
+ 'chunk_gzipped',
+ )
+ );
+
+ $gzip = (isset($state_data['chunk_gzipped']) && $state_data['chunk_gzipped']);
+
+ $tmp_file_name = 'chunk.txt';
+
+ if ($gzip) {
+ $tmp_file_name .= '.gz';
+ }
+
+ $tmp_file_path = wp_tempnam($tmp_file_name);
+
+ if (!isset($_FILES['chunk']['tmp_name']) || !move_uploaded_file($_FILES['chunk']['tmp_name'], $tmp_file_path)) {
+ $result = $this->http->end_ajax(new \WP_Error('wpmdb_could_not_upload_sql', __('Could not upload the SQL to the server. (#135)', 'wp-migrate-db')));
+
+ return $result;
+ }
+
+ if (false === ($chunk = file_get_contents($tmp_file_path))) {
+ $result = $this->http->end_ajax(new \WP_Error('wpmdb_could_not_read_sql', __('Could not read the SQL file we uploaded to the server. (#136)', 'wp-migrate-db')));
+
+ return $result;
+ }
+
+ // TODO: Use WP_Filesystem API.
+ @unlink($tmp_file_path);
+
+ $filtered_post['chunk'] = $chunk;
+
+ if (!$this->http_helper->verify_signature($filtered_post, $this->settings['key'])) {
+ $error_msg = $this->props->invalid_content_verification_error . ' (#130)';
+ $result = $this->http->end_ajax(new \WP_Error('wpmdb_invalid_content_verification_error', $error_msg));
+
+ return $result;
+ }
+
+ if ($this->settings['allow_push'] != true) {
+ $result = $this->http->end_ajax(new \WP_Error('wpmdb_reject_push', __('The connection succeeded but the remote site is configured to reject push connections. You can change this in the "settings" tab on the remote site. (#139)', 'wp-migrate-db')));
+
+ return $result;
+ }
+
+ if ($gzip) {
+ $filtered_post['chunk'] = gzuncompress($filtered_post['chunk']);
+ }
+
+ $process_chunk_result = $this->table->process_chunk($filtered_post['chunk']);
+ $result = $this->http->end_ajax($process_chunk_result);
+
+ return $result;
+ }
+
+ /**
+ * The remote's handler for requests to backup a table.
+ *
+ * @return bool|mixed|null
+ */
+ public function respond_to_backup_remote_table()
+ {
+ add_filter('wpmdb_before_response', array($this->scrambler, 'scramble'));
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'intent' => 'key',
+ 'url' => 'url',
+ 'table' => 'string',
+ 'form_data' => 'string',
+ 'stage' => 'key',
+ 'prefix' => 'string',
+ 'current_row' => 'string',
+ 'last_table' => 'string',
+ 'gzip' => 'string',
+ 'primary_keys' => 'string',
+ 'path_current_site' => 'string',
+ 'domain_current_site' => 'text',
+ 'sig' => 'string',
+ );
+
+ $state_data = Persistence::setRemotePostData($key_rules, __METHOD__);
+
+ $filtered_post = $this->http_helper->filter_post_elements(
+ $state_data,
+ array_keys($key_rules)
+ );
+
+ $filtered_post['primary_keys'] = base64_decode($filtered_post['primary_keys']);
+
+ if (!$this->http_helper->verify_signature($filtered_post, $this->settings['key'])) {
+ $error_msg = $this->props->invalid_content_verification_error . ' (#137)';
+
+ return $this->http->end_ajax(new \WP_Error('wpmdb-respond-to-remote-backup-error', $error_msg));
+ }
+
+
+ return $this->migration_manager->handle_table_backup('wpmdb_remote_migration_state');
+ }
+
+ /**
+ * Handler for a request to the remote to cancel a migration.
+ *
+ * @return bool|string
+ */
+ function respond_to_process_push_migration_cancellation()
+ {
+ add_filter('wpmdb_before_response', array($this->scrambler, 'scramble'));
+
+ $key_rules = array(
+ 'action' => 'key',
+ 'remote_state_id' => 'key',
+ 'intent' => 'key',
+ 'url' => 'url',
+ 'form_data' => 'string',
+ 'temp_prefix' => 'string',
+ 'stage' => 'key',
+ 'sig' => 'string',
+ );
+
+ $state_data = Persistence::setRemotePostData($key_rules, __METHOD__);
+
+ $filtered_post = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'action',
+ 'intent',
+ 'url',
+ 'form_data',
+ 'temp_prefix',
+ 'stage',
+ )
+ );
+
+ if (!$this->http_helper->verify_signature($filtered_post, $this->settings['key'])) {
+ $result = $this->http->end_ajax(new \WP_Error('wpmdb_invalid_content_verification_error', $this->props->invalid_content_verification_error));
+
+ return $result;
+ }
+
+ // ***+=== @TODO - revisit usage of parse_migration_form_data
+ $this->form_data = $this->form_data->parse_and_save_migration_form_data(base64_decode($filtered_post['form_data']));
+
+ if ($filtered_post['stage'] == 'backup' && !empty($state_data['dumpfile_created'])) {
+ $this->backup_export->delete_export_file($state_data['dump_filename'], true);
+ } else {
+ $this->table->delete_temporary_tables($filtered_post['temp_prefix']);
+ }
+
+ do_action('wpmdb_respond_to_push_cancellation');
+
+ $result = $this->http->end_ajax(true);
+
+ return $result;
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Plugin/ProPluginManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Plugin/ProPluginManager.php
new file mode 100644
index 000000000..e90d7069e
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Plugin/ProPluginManager.php
@@ -0,0 +1,773 @@
+addon = $addon;
+ $this->download = $download;
+ $this->rest_API_server = $rest_API_server;
+ $this->migration_helper = $migration_helper;
+ }
+
+ public function register()
+ {
+ parent::register();
+
+ $this->license = WPMDBDI::getInstance()->get(License::class);
+ $this->api = WPMDBDI::getInstance()->get(Api::class);
+ add_filter('wpmdb_data', [$this, 'filter_wpmdb_data']);
+
+ add_filter(
+ 'wpmdb_settings_js',
+ function ($settings) {
+ if ($this->license->is_licence_constant()) {
+ $settings['license_constant'] = true;
+ }
+
+ return $settings;
+ }
+ );
+
+
+ // Remove licence from the database if constant is set
+ if (defined('WPMDB_LICENCE') && !empty($this->settings['licence'])) {
+ $this->settings['licence'] = '';
+ update_site_option('wpmdb_settings', $this->settings);
+ }
+
+ // Add after_plugin_row... action for pro plugin and all addons
+ add_action('after_plugin_row_wp-migrate-db-pro/wp-migrate-db-pro.php', array($this, 'plugin_row'), 11, 2);
+ add_action('after_plugin_row_wp-migrate-db-pro-cli/wp-migrate-db-pro-cli.php', array($this, 'legacy_addon_row'), 11);
+ add_action('after_plugin_row_wp-migrate-db-pro-media-files/wp-migrate-db-pro-media-files.php', array($this, 'legacy_addon_row'), 11);
+ add_action('after_plugin_row_wp-migrate-db-pro-multisite-tools/wp-migrate-db-pro-multisite-tools.php', array($this, 'legacy_addon_row'), 11);
+ add_action('after_plugin_row_wp-migrate-db-pro-theme-plugin-files/wp-migrate-db-pro-theme-plugin-files.php', array($this, 'legacy_addon_row'), 11);
+
+ // Seen when the user clicks "view details" on the plugin listing page
+ add_action('install_plugins_pre_plugin-information', array($this, 'plugin_update_popup'));
+
+ add_filter('plugin_action_links_' . $this->props->plugin_basename, array($this, 'plugin_action_links'));
+ add_filter('network_admin_plugin_action_links_' . $this->props->plugin_basename, array($this, 'plugin_action_links'));
+
+ // Short circuit the HTTP request to WordPress.org for plugin information
+ add_filter('plugins_api', array($this, 'short_circuit_wordpress_org_plugin_info_request'), 10, 3);
+
+ // Take over the update check
+ add_filter('site_transient_update_plugins', array($this, 'site_transient_update_plugins'));
+
+ //Add some custom JS into the WP admin pages
+ add_action('admin_enqueue_scripts', array($this, 'enqueue_plugin_update_script'));
+
+ // Add some custom CSS into the WP admin pages
+ add_action('admin_head-plugins.php', array($this, 'add_plugin_update_styles'));
+
+ // Hook into the plugin install process, inject addon download url
+ add_filter('plugins_api', array($this, 'inject_addon_install_resource'), 10, 3);
+
+ // Clear update transients when the user clicks the "Check Again" button from the update screen
+ add_action('current_screen', array($this, 'check_again_clear_transients'));
+
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+
+ add_filter('admin_footer_text', [$this, 'admin_footer_text']);
+
+ add_filter( 'update_footer', [$this, 'update_footer'], 20 );
+ }
+
+ public function filter_wpmdb_data($data)
+ {
+ $valid_license = $this->license->is_valid_licence();
+ $data['valid_licence'] = $valid_license ? 1 : 0;
+ $data['has_licence'] = $this->license->get_licence_key() === '' ? 0 : 1;
+ $data['licence_status'] = $this->license->check_license_status();
+ $data['license_errors'] = [];
+ $data['api_data'] = [];
+
+ if ($data['has_licence']) {
+ $api_data = $this->license->get_api_data();
+ if (!empty($api_data)) {
+ $data['api_data'] = $api_data;
+ //Get expired license notification messages
+ if ($data['licence_status'] === 'subscription_expired') {
+ $data['api_data']['errors']['subscription_expired'] = [];
+ $licence_status_messages = $this->license->get_licence_status_message(
+ null,
+ 'all'
+ );
+ foreach ($licence_status_messages as $frontend_context => $status_message) {
+ $data['api_data']['errors']['subscription_expired'][$frontend_context] = sprintf(
+ '%s
',
+ $status_message
+ );
+ }
+ } elseif (!empty($api_data['errors'][$data['licence_status']])) {
+ $data['api_data']['errors'][$data['licence_status']] = [];
+ $data['api_data']['errors'][$data['licence_status']]['default'] = $this->license->get_licence_status_message();
+ $data['license_errors'][$data['licence_status']] = $data['api_data']['errors'][$data['licence_status']]['default'];
+ }
+ }
+ }
+ if (!$valid_license) {
+ add_filter('wpmdb_notification_strings', array($this, 'template_invalid_license'));
+ }
+
+ return $data;
+ }
+
+ public function template_invalid_license($templates)
+ {
+ $notice_name = 'wpmdb_invalid_license';
+ $templates[$notice_name] = [
+ 'message' => $this->license->get_licence_status_message(),
+ 'id' => $notice_name,
+ 'error' => true,
+ ];
+
+ return $templates;
+ }
+
+ /**
+ * Shows a message below the plugin on the plugins page when:
+ * 1. the license hasn't been activated
+ * 2. when there's an update available but the license is expired
+ *
+ * @param string $plugin_path Path of current plugin listing relative to plugins directory
+ *
+ * @return void
+ */
+ function plugin_row($plugin_path, $plugin_data)
+ {
+ $plugin_title = $plugin_data['Name'];
+ $plugin_slug = sanitize_title($plugin_title);
+ $licence = $this->license->get_licence_key();
+ $licence_response = $this->license->get_license_status();
+ $licence_problem = isset($licence_response['errors']);
+ $active = is_plugin_active($plugin_path) ? 'active' : '';
+ $shiny_updates = version_compare(get_bloginfo('version'), '4.6-beta1-37926', '>=');
+ $update_msg_classes = $shiny_updates ? 'notice inline notice-warning notice-alt post-shiny-updates' : 'pre-shiny-updates';
+ $colspan = function_exists('wp_is_auto_update_enabled_for_type') && wp_is_auto_update_enabled_for_type('plugin') ? 4 : 3;
+
+ if (!isset($GLOBALS['wpmdb_meta'][$plugin_slug]['version'])) {
+ $installed_version = '0';
+ } else {
+ $installed_version = $GLOBALS['wpmdb_meta'][$plugin_slug]['version'];
+ }
+
+ $latest_version = $this->addon->get_latest_version($plugin_slug);
+
+ $new_version = '';
+ if (version_compare($installed_version, $latest_version, '<')) {
+ $new_version = sprintf(__('There is a new version of %s available.', 'wp-migrate-db'), $plugin_title);
+ $new_version .= ' ';
+ $new_version .= sprintf(__('View version %s details', 'wp-migrate-db'), $latest_version) . '.';
+ }
+
+ if (!$new_version && !empty($licence)) {
+ return;
+ }
+
+ if (empty($licence)) {
+ $settings_link = sprintf('%s', network_admin_url($this->props->plugin_base) . '#settings', _x('Settings', 'Plugin configuration and preferences', 'wp-migrate-db'));
+ if ($new_version) {
+ $message = sprintf(__('To update, go to %1$s and enter your license key. If you don\'t have a license key, you may purchase one.', 'wp-migrate-db'), $settings_link, 'http://deliciousbrains.com/wp-migrate-db-pro/pricing/');
+ } else {
+ $message = sprintf(__('To finish activating %1$s, please go to %2$s and enter your license key. If you don\'t have a license key, you may purchase one.', 'wp-migrate-db'), $this->props->plugin_title, $settings_link, 'http://deliciousbrains.com/wp-migrate-db-pro/pricing/');
+ }
+ } elseif ($licence_problem) {
+ $message = array_shift($licence_response['errors']) . sprintf(' %s', __('Check my license again', 'wp-migrate-db'));
+ } else {
+ return;
+ } ?>
+
+
+
+
+
+
+ license->get_licence_status_message(null, 'update'); ?>
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+ Addon No Longer Required - As of WP Migrate version 2.3.0, this addon is no longer required and can be safely deleted.', 'wp-mgrate-db'),
+ 'https://deliciousbrains.com/wp-migrate-db-pro-2-3-released/#no-more-installing-addons'
+ );
+ ?>
+
+
+
+ |
+
+
+ %s
', __('Could not retrieve version details. Please try again.', 'wp-migrate-db'));
+ $latest_version = $this->addon->get_latest_version($plugin_slug);
+
+ if (false === $latest_version) {
+ echo $error_msg;
+ exit;
+ }
+
+ $data = $this->get_changelog($plugin_slug, BetaManager::is_beta_version($latest_version));
+
+ if (is_wp_error($data) || empty($data)) {
+ echo '' . __('Could not retrieve version details. Please try again.', 'wp-migrate-db') . '
';
+ } else {
+ echo $data;
+ }
+
+ exit;
+ }
+
+ //@TODO Move to Pro/PluginManager class
+ function inject_addon_install_resource($res, $action, $args)
+ {
+ if ('plugin_information' != $action || empty($args->slug)) {
+ return $res;
+ }
+
+ $addons = get_site_transient('wpmdb_addons');
+
+ if (!isset($addons[$args->slug])) {
+ return $res;
+ }
+
+ $addon = $addons[$args->slug];
+ $is_beta = !empty($addon['beta_version']) && BetaManager::has_beta_optin($this->settings);
+
+ $res = new \stdClass();
+ $res->name = 'WP Migrate ' . $addon['name'];
+ $res->version = $is_beta ? $addon['beta_version'] : $addon['version'];
+ $res->download_link = $this->download->get_plugin_update_download_url($args->slug, $is_beta);
+ $res->tested = isset($addon['tested']) ? $addon['tested'] : false;
+
+ return $res;
+ }
+
+ /**
+ * Was the core plugin literally JUST updated?
+ *
+ * @return bool
+ */
+ function core_plugin_just_updated()
+ {
+ if (!isset($_REQUEST['action']) || !in_array($_REQUEST['action'], ['update-plugin', 'update-selected'])) {
+ return false;
+ }
+
+ // Updates through the WP "Plugins" page.
+ if (isset($_REQUEST['slug']) && 'wp-migrate-db-pro' !== $_REQUEST['slug']) {
+ return false;
+ }
+
+ // Updates through the WP "Updates" page.
+ if (isset($_REQUEST['plugins']) && is_string($_REQUEST['plugins'])) {
+ $plugins = explode(',', $_REQUEST['plugins']);
+
+ if (!in_array('wp-migrate-db-pro/wp-migrate-db-pro.php', $plugins)) {
+ return false;
+ }
+ }
+
+ if (!did_action('upgrader_process_complete')) {
+ return false;
+ }
+
+ return true;
+ }
+
+ function site_transient_update_plugins($trans)
+ {
+ if ($this->core_plugin_just_updated() || BetaManager::is_rolling_back_plugins()) {
+ return $trans;
+ }
+
+ $plugin_upgrade_data = $this->addon->get_upgrade_data();
+
+ if (false === $plugin_upgrade_data || !isset($plugin_upgrade_data['wp-migrate-db-pro'])) {
+ return $trans;
+ }
+
+ foreach ($plugin_upgrade_data as $plugin_slug => $upgrade_data) {
+ $plugin_folder = $this->util->get_plugin_folder($plugin_slug);
+
+ $plugin_basename = sprintf('%s/%s.php', $plugin_folder, $plugin_slug);
+ $latest_version = $this->addon->get_latest_version($plugin_slug);
+
+ if (!isset($GLOBALS['wpmdb_meta'][$plugin_slug]['version'])) {
+ $version_file = sprintf('%s%s/version.php', $this->plugins_dir(), $plugin_folder);
+
+ if (file_exists($version_file)) {
+ include_once($version_file);
+ $installed_version = $GLOBALS['wpmdb_meta'][$plugin_slug]['version'];
+ } else {
+ $addon_file = sprintf('%s%s/%s.php', $this->plugins_dir(), $plugin_folder, $plugin_slug);
+ // No addon plugin file or version.php file, bail and move on to the next addon
+ if (!file_exists($addon_file)) {
+ continue;
+ }
+ /*
+ * The addon's plugin file exists but a version.php file doesn't
+ * We're now assuming that the addon is outdated and provide an arbitrary out-of-date version number
+ * This will trigger a update notice
+ */
+ $installed_version = $GLOBALS['wpmdb_meta'][$plugin_slug]['version'] = '0.1';
+ }
+ } else {
+ $installed_version = $GLOBALS['wpmdb_meta'][$plugin_slug]['version'];
+ }
+
+ if (isset($installed_version)) {
+ $is_beta = BetaManager::is_beta_version($latest_version);
+ $update_available = version_compare($installed_version, $latest_version, '<') ? true : false;
+
+ if (!$trans) {
+ $trans = new \stdClass();
+ $trans->response = [];
+ }
+
+ $plugin_response = new \stdClass();
+ $plugin_response->url = $this->api->get_dbrains_api_base();
+ $plugin_response->slug = $plugin_slug;
+ $plugin_response->package = $this->download->get_plugin_update_download_url($plugin_slug, $is_beta);
+ $plugin_response->new_version = $latest_version;
+ $plugin_response->id = '0';
+ $plugin_response->plugin = $plugin_basename;
+
+ if (isset($upgrade_data['icon_url'])) {
+ $plugin_response->icons['svg'] = $upgrade_data['icon_url'];
+ }
+
+ if (isset($upgrade_data['requires_php'])) {
+ $plugin_response->requires_php = $upgrade_data['requires_php'];
+ }
+
+ if ($update_available) {
+ $trans->response[$plugin_basename] = $plugin_response;
+ } else {
+ $trans->no_update[$plugin_basename] = $plugin_response;
+ }
+ }
+ }
+
+ return $trans;
+ }
+
+ /**
+ * Short circuits the HTTP request to WordPress.org servers to retrieve plugin information.
+ * Will only fire on the update-core.php admin page.
+ *
+ * @param object|bool $res Plugin resource object or boolean false.
+ * @param string $action The API call being performed.
+ * @param object $args Arguments for the API call being performed.
+ *
+ * @return object|bool Plugin resource object or boolean false.
+ */
+ function short_circuit_wordpress_org_plugin_info_request($res, $action, $args)
+ {
+ if ('plugin_information' != $action || empty($args->slug) || 'wp-migrate-db-pro' != $args->slug) {
+ return $res;
+ }
+
+ $screen = get_current_screen();
+
+ // Only fire on the update-core.php admin page
+ if (empty($screen->id) || ('update-core' !== $screen->id && 'update-core-network' !== $screen->id)) {
+ return $res;
+ }
+
+ $res = new \stdClass();
+ $plugin_info = $this->addon->get_upgrade_data();
+
+ if (isset($plugin_info['wp-migrate-db-pro']['tested'])) {
+ $res->tested = $plugin_info['wp-migrate-db-pro']['tested'];
+ } else {
+ $res->tested = false;
+ }
+
+ return $res;
+ }
+
+ /**
+ * Adds profiles and settings links to plugin page
+ *
+ * @param array $links
+ *
+ * @return array $links
+ */
+ function plugin_action_links($links)
+ {
+ $start_links = array(
+ 'profiles' => sprintf('%s', network_admin_url($this->props->plugin_base) , __('Migrate', 'wp-migrate-db')),
+ 'settings' => sprintf('%s', network_admin_url($this->props->plugin_base) . '#settings', _x('Settings', 'Plugin configuration and preferences', 'wp-migrate-db'))
+ );
+
+ return $start_links + $links;
+ }
+
+ /**
+ * Get changelog contents for the given plugin slug.
+ *
+ * @param string $slug
+ * @param bool $beta
+ *
+ * @return bool|string
+ */
+ function get_changelog($slug, $beta = false)
+ {
+ if (true === $beta) {
+ $slug .= '-beta';
+ }
+
+ $args = array(
+ 'slug' => $slug,
+ );
+
+ $response = $this->api->dbrains_api_request('changelog', $args);
+
+ return $response;
+ }
+
+ function enqueue_plugin_update_script($hook)
+ {
+ if ('plugins.php' != $hook) {
+ return;
+ }
+
+ $ver_string = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? time() : $this->props->plugin_version;
+
+ $src = plugins_url("frontend/plugin-update/plugin-update.js", $GLOBALS['wpmdb_meta']['wp-migrate-db-pro']['abspath'] . '/wp-migrate-db-pro');
+ wp_enqueue_script('wp-migrate-db-pro-plugin-update-script', $src, array('jquery'), $ver_string, true);
+
+ wp_localize_script('wp-migrate-db-pro-plugin-update-script', 'wpmdb_nonces', array('check_licence' => Util::create_nonce('check-licence'), 'process_notice_link' => Util::create_nonce('process-notice-link'), 'wp_rest' => Util::create_nonce('wp_rest')));
+ wp_localize_script('wp-migrate-db-pro-plugin-update-script', 'wpmdb_update_strings', array('check_license_again' => __('Check my license again', 'wp-migrate-db'), 'license_check_problem' => __('A problem occurred when trying to check the license, please try again.', 'wp-migrate-db'),));
+
+ wp_add_inline_script(
+ 'wp-migrate-db-pro-plugin-update-script',
+ sprintf('var wpmdbAPIBase = %s;', wp_json_encode($this->util->rest_url())),
+ 'before'
+ );
+ }
+
+ function add_plugin_update_styles()
+ {
+ $version = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? time() : $this->props->plugin_version;
+ $plugins_url = trailingslashit(plugins_url()) . trailingslashit($this->props->plugin_folder_name);
+ $src = $plugins_url . 'frontend/plugin-update/plugin-update-styles.css';
+ wp_enqueue_style('plugin-update-styles', $src, array(), $version);
+ }
+
+
+ /**
+ * Clear update transients when the user clicks the "Check Again" button from the update screen.
+ *
+ * @param object $current_screen
+ */
+ function check_again_clear_transients($current_screen)
+ {
+ if (!isset($current_screen->id) || strpos($current_screen->id, 'update-core') === false || !isset($_GET['force-check'])) {
+ return;
+ }
+
+ delete_site_transient('wpmdb_upgrade_data');
+ delete_site_transient('update_plugins');
+ delete_site_transient( Helpers::get_licence_response_transient_key() );
+ delete_site_transient('wpmdb_dbrains_api_down');
+ }
+
+ /**
+ * Get the plugin title
+ *
+ * @return string
+ **/
+ public function get_plugin_title()
+ {
+ return __('WP Migrate', 'wp-migrate-db');
+ }
+
+ /**
+ * Get the plugin version
+ *
+ * @return string
+ **/
+ public function get_plugin_version()
+ {
+ if (!isset($GLOBALS['wpmdb_meta']['wp-migrate-db-pro']['version'])) {
+ return '0';
+ }
+ return $GLOBALS['wpmdb_meta']['wp-migrate-db-pro']['version'];
+ }
+
+ /**
+ * Get the plugin page url
+ *
+ * @return string
+ **/
+ public static function plugin_page_url()
+ {
+ if (is_multisite()) {
+ return menu_page_url('tools_page_wp-migrate-db-pro');
+ }
+ return menu_page_url('settings_page_wp-migrate-db-pro-network');
+ }
+
+ public function register_rest_routes()
+ {
+ parent::register_rest_routes(); // TODO: Change the autogenerated stub
+
+ $this->rest_API_server->registerRestRoute('/local-site-details',
+ ['methods' => 'POST', 'callback' => [$this, 'ajax_get_local_site_details']]);
+ }
+
+ public function ajax_get_local_site_details() {
+ return $this->http->end_ajax([
+ 'mst_available' => (string)((int)(Util::isPro() && Util::is_addon_registered('mst'))),
+ 'tpf_available' => (string)Util::is_addon_registered('tpf'),
+ 'mf_available' => (string)Util::is_addon_registered('mf'),
+ ]);
+ }
+
+ /**
+ * Filter admin footer text for Migrate pages
+ *
+ * @param string $text
+ * @return string
+ * @handles admin_footer_text
+ **/
+ public function admin_footer_text($text)
+ {
+ if (!$this->util->isMDBPage()) {
+ return $text;
+ }
+ $product_link = Util::external_link(
+ static::delicious_brains_url(
+ '/wp-migrate-db-pro/',
+ [
+ 'utm_source' => 'MDB%2BPaid',
+ 'utm_medium' => 'insideplugin',
+ 'utm_campaign' => 'plugin_footer',
+ 'utm_content' => 'footer_colophon'
+ ]
+ ),
+ $this->get_plugin_title()
+ );
+ $wpe_link = Util::external_link(
+ static::wpe_url(
+ '',
+ [
+ 'utm_source' => 'migrate_plugin',
+ 'utm_content' => 'migrate_pro_plugin_footer_text'
+ ]
+ ),
+ 'WP Engine'
+ );
+ return $this->generate_admin_footer_text($text, $product_link, $wpe_link);
+ }
+
+ /**
+ * Filter update footer text for Migrate pages
+ *
+ * @param string $content
+ * @return string
+ * @handles update_footer
+ **/
+ public function update_footer($content)
+ {
+ if (!$this->util->isMDBPage()) {
+ return $content;
+ }
+ $utm_params = [
+ 'utm_source' => 'migrate_pro',
+ 'utm_campaign' => 'plugin_footer',
+ 'utm_content' => 'footer_navigation'
+ ];
+
+ $links[] = Util::external_link(
+ static::delicious_brains_url(
+ '/wp-migrate-db-pro/docs/getting-started/',
+ $utm_params
+ ),
+ __('Documentation', 'wp-migrate-db')
+ );
+
+ $links[] = '' . __( 'Support', 'wp-migrate-db' ) . '';
+
+ $links[] = Util::external_link(
+ static::delicious_brains_url(
+ '/wp-migrate-db-pro/feedback/',
+ $utm_params
+ ),
+ __('Feedback', 'wp-migrate-db')
+ );
+
+ $links[] = Util::external_link(
+ static::delicious_brains_url(
+ '/wp-migrate-db-pro/whats-new/',
+ $utm_params
+ ),
+ $this->get_plugin_title() . ' ' . $this->get_plugin_version(),
+ 'whats-new'
+ );
+ return join( ' ∙ ', $links );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/RegisterPro.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/RegisterPro.php
new file mode 100644
index 000000000..2e6efca3b
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/RegisterPro.php
@@ -0,0 +1,176 @@
+get(Filesystem::class);
+ $filesystem->register();
+
+ // $this->pro_migration_manager = $container->get(RespondToMigrationAction::class);
+ $container->set(
+ Menu::class,
+ new Menu(
+ $container->get(Util::class),
+ $container->get(Properties::class),
+ $container->get(PluginManagerBase::class),
+ $container->get(Assets::class),
+ $container->get(CompatibilityManager::class)
+ )
+ );
+
+ $this->remote_table = $container->get(Migration\Tables\Remote::class);
+
+ $this->local_connection = $container->get(Local::class);
+ $this->remote_connection = $container->get(Remote::class);
+ $this->finalize_complete = $container->get(FinalizeComplete::class);
+ $this->migration_manager = $container->get(MigrationManager::class);
+ $this->template = $container->get(Template::class);
+ $this->license = $container->get(License::class);
+ $this->import = $container->get(Import::class);
+ $this->addon = $container->get(Addon::class);
+ $this->addons_facade = $container->get(AddonsFacade::class);
+ $this->beta_manager = $container->get(BetaManager::class);
+ $this->pro_plugin_manager = $container->get(ProPluginManager::class);
+ $this->menu = $container->get(Menu::class);
+ $this->usage_tracking = $container->get(UsageTracking::class);
+ $this->logger = $container->get(Logger::class);
+ $this->backups_manager = $container->get(BackupsManager::class);
+ $this->cli_export = $container->get(Export::class);
+ $this->remote_updates_manager = $container->get(RemoteUpdatesManager::class);
+
+ // Register other class actions and filters
+ $this->addons_facade->register();
+ $this->local_connection->register();
+ $this->remote_connection->register();
+ $this->remote_table->register();
+ $this->finalize_complete->register();
+ $this->migration_manager->register();
+ $this->template->register();
+ $this->license->register();
+ $this->import->register();
+ $this->addon->register();
+ $this->beta_manager->register();
+ $this->pro_plugin_manager->register();
+ $this->menu->register();
+ $this->usage_tracking->register();
+ $this->logger->register();
+ $this->backups_manager->register();
+ $this->remote_updates_manager->register();
+
+ if (!class_exists('\DeliciousBrains\WPMDB\Pro\Cli\Extra\Cli')) {
+ $this->cli_export->register();
+ }
+ }
+
+ // @TODO remove once enough users off of 1.9.* branch
+ public function loadContainer() { }
+
+ public function loadTransfersContainer() { }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/RemoteUpdates/RemoteUpdatesManager.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/RemoteUpdates/RemoteUpdatesManager.php
new file mode 100644
index 000000000..c02358dd4
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/RemoteUpdates/RemoteUpdatesManager.php
@@ -0,0 +1,226 @@
+http_helper = $http_helper;
+ $this->http = $http;
+ $this->remote_post = $remote_post;
+ $this->rest_API_server = $rest_API_server;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->props = $props;
+ $this->settings = $settings->get_settings();
+ $this->util = $util;
+ $this->license = $license;
+ }
+
+ public function register()
+ {
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ add_action('wp_ajax_nopriv_wpmdb_remote_update_plugin', array($this, 'respond_to_remote_update_plugin'));
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute('/update-plugin-on-remote', [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'ajax_update_plugin_on_remote']
+ ]);
+ }
+
+ /**
+ * WP REST API endpoint for `/update-plugin-on-remote`
+ * Verifies that the local site has a valid license and that the remote site has a valid connection string.
+ *
+ * @return mixed
+ */
+ public function ajax_update_plugin_on_remote()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $key_rules = apply_filters(
+ 'wpmdb_key_rules',
+ array(
+ 'action' => 'key',
+ 'url' => 'url',
+ 'key' => 'string',
+ 'slug' => 'string',
+ 'nonce' => 'key'
+ )
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data($key_rules);
+
+ if (!$this->license->is_valid_licence()) {
+ $message = __('Please activate your license before updating.', 'wp-migrate-db');
+ return $this->http->end_ajax(new \WP_Error('invalid-license', $message));
+ }
+
+ $data = array(
+ 'action' => 'wpmdb_remote_update_plugin',
+ 'referer' => $this->util->get_short_home_address_from_url(Util::home_url()),
+ 'version' => $this->props->plugin_version,
+ 'slug' => $state_data['slug']
+ );
+
+ $data['sig'] = $this->http_helper->create_signature($data, $state_data['key']);
+ $ajax_url = $this->util->ajax_url();
+ $timeout = apply_filters('wpmdb_prepare_remote_connection_timeout', 30);
+ $remote_response = $this->remote_post->post($ajax_url, $data, __FUNCTION__, compact('timeout'));
+
+ if (is_wp_error($remote_response)) {
+ return $this->http->end_ajax($remote_response);
+ }
+
+ return $this->http->end_ajax(true);
+ }
+
+ /**
+ * Responds to remote request to update WP Migrate DB Pro.
+ *
+ * @return mixed
+ */
+ public function respond_to_remote_update_plugin()
+ {
+ $key_rules = apply_filters(
+ 'wpmdb_key_rules',
+ array(
+ 'action' => 'key',
+ 'intent' => 'key',
+ 'referer' => 'string',
+ 'version' => 'string',
+ 'slug' => 'string',
+ 'sig' => 'string'
+ ),
+ __FUNCTION__
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data($key_rules);
+
+ if (is_wp_error($state_data)) {
+ return wp_send_json_error($state_data->get_error_message());
+ }
+
+ unset($key_rules['sig']);
+ $filtered_post = $this->http_helper->filter_post_elements($state_data, array_keys($key_rules));
+
+ if (!$this->http_helper->verify_signature($filtered_post, $this->settings['key'])) {
+ $err = $this->props->invalid_content_verification_error . ' (#120)';
+
+ return $this->http->end_ajax(
+ new \WP_Error('invalid-content-verification', $err),
+ $filtered_post
+ );
+ }
+
+ define('DOING_CRON', true); // Keeps the plugin active.
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; // Required for upgrade classes.
+
+ $skin = new \WP_AJAX_Upgrader_Skin();
+ $upgrader = new \Plugin_Upgrader($skin);
+ $plugin_basename = $this->props->plugin_basename;
+ $plugin_data = null;
+
+ // Check if beta updates is disabled and enable it
+ if (BetaManager::is_beta_version($state_data['version']) && !BetaManager::has_beta_optin($this->settings)) {
+ BetaManager::set_beta_optin(true);
+
+ //Update plugins updates transient
+ $current = get_site_transient( 'update_plugins' );
+ $current = apply_filters( 'site_transient_update_plugins', $current );
+ set_site_transient( 'update_plugins', $current );
+ }
+
+ //if the post data contains a slug that means we're updating an addon, look for the plugin information and get the plugin basename
+ if (!empty($state_data['slug'])) {
+ $plugin_data = get_plugins(DIRECTORY_SEPARATOR . $state_data['slug']);
+ if (!empty($plugin_data)) {
+ $plugin_basename = $state_data['slug'] . DIRECTORY_SEPARATOR . key($plugin_data);
+ }
+ }
+ //if plugin data still empty, get the plugin data for the default base name
+ if (empty($plugin_data)) {
+ $plugin_data = get_plugins();
+ $plugin_data = isset($plugin_data[$plugin_basename]) ? $plugin_data[$plugin_basename] : [];
+ }
+
+ //set the plugin name
+ $plugin_name = isset($plugin_data['Name']) ? $plugin_data['Name'] : reset($plugin_data)['Name'];
+
+ $result = false;
+ //make sure there's a plugin to update
+ if ($plugin_basename !== null) {
+ $result = $upgrader->upgrade($plugin_basename);
+ }
+
+ if (!$result || is_wp_error($result)) {
+ return $this->http->end_ajax(
+ new \WP_Error(
+ 'wpmdb-remote-update-failed',
+ __(sprintf('There was an error updating %s on the remote server. Please try again or update the plugin manually.', $plugin_name), 'wp-migrate-db')
+ )
+ );
+ }
+
+ return $this->http->end_ajax(true);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCli.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCli.php
new file mode 100644
index 000000000..01cc71c65
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCli.php
@@ -0,0 +1,807 @@
+cli = $cli;
+ }
+
+ public function register()
+ {
+ // Accepted profile fields exclusive to Theme & Plugin Files.
+ add_filter('wpmdb_accepted_profile_fields', [$this, 'accepted_profile_fields']);
+
+ // Announce extra args for Theme & Plugin Files.
+ add_filter('wpmdb_cli_filter_get_extra_args', [$this, 'filter_extra_args'], 10, 1);
+
+ // Add extra args for Theme & Plugin Files migrations.
+ add_filter('wpmdb_cli_filter_get_profile_data_from_args', [$this, 'add_tpf_profile_args'], 11, 3);
+
+ // Add the Theme & Plugin Files stage.
+ add_filter('wpmdb_cli_profile_before_migration', [$this, 'add_tpf_stages'], PHP_INT_MAX);
+
+ // Check for remote themes and plugins after the connection has been made.
+ add_filter('wpmdb_cli_filter_tpf_profile_args', [$this, 'extend_tpf_profile_args'], 10, 2);
+
+ // Initialize the CLI Migration.
+ add_filter('wpmdb_pro_cli_finalize_migration', [$this, 'cli_migration'], 10, 4);
+
+ $this->tpf_local = WPMDBDI::getInstance()->get(ThemePluginFilesLocal::class);
+ }
+
+ /**
+ * Gets the TPF stages, if any.
+ *
+ * @param array $profile
+ *
+ * @return array
+ */
+ public function get_tpf_stages($profile)
+ {
+ $stages = [];
+
+ if (!isset($profile['theme_plugin_files'], $profile['theme_plugin_files']['available'])) {
+ return $stages;
+ }
+
+ $tpf = $profile['theme_plugin_files'];
+
+ if (true !== $tpf['available']) {
+ return $stages;
+ }
+
+ if (isset($tpf['theme_files'], $tpf['theme_files']['enabled']) && true === $tpf['theme_files']['enabled']) {
+ $stages[] = 'themes';
+ }
+
+ if (isset($tpf['plugin_files'], $tpf['plugin_files']['enabled']) && true === $tpf['plugin_files']['enabled']) {
+ $stages[] = 'plugins';
+ }
+
+ if (isset($tpf['muplugin_files'], $tpf['muplugin_files']['enabled']) && true === $tpf['muplugin_files']['enabled']) {
+ $stages[] = 'muplugins';
+ }
+
+ if (isset($tpf['other_files'], $tpf['other_files']['enabled']) && true === $tpf['other_files']['enabled']) {
+ $stages[] = 'others';
+ }
+
+ return $stages;
+ }
+
+ /**
+ * Adds extra profile fields used by the Theme & Plugin Files addon.
+ * Hooks on: wpmdb_accepted_profile_fields
+ *
+ * @param array $fields
+ *
+ * @return array
+ */
+ public function accepted_profile_fields($fields)
+ {
+ $fields[] = 'theme_plugin_files';
+
+ return $fields;
+ }
+
+ /**
+ * Add extra CLI args used by the Theme & Plugin Files addon.
+ * Hooks on: wpmdb_cli_filter_get_extra_args
+ *
+ * @param array $args
+ *
+ * @return array
+ */
+ public function filter_extra_args($args)
+ {
+ $args[] = 'theme-files';
+ $args[] = 'plugin-files';
+ $args[] = 'mu-plugin-files';
+ $args[] = 'other-files';
+ $args[] = 'exclude-theme-plugin-files';
+
+ return $args;
+ }
+
+ /**
+ * Adds extra args for Theme & Plugin Files migrations.
+ * Hooks on: wpmdb_cli_filter_get_profile_data_from_args
+ *
+ * @param array $profile
+ * @param array $args
+ * @param array $assoc_args
+ *
+ * @return array|WP_Error
+ */
+ public function add_tpf_profile_args($profile, $args, $assoc_args)
+ {
+ if (!isset($assoc_args['theme-files'])
+ && !isset($assoc_args['plugin-files'])
+ && !isset($assoc_args['mu-plugin-files'])
+ && !isset($assoc_args['other-files'])
+ ) {
+ return $profile;
+ }
+
+ $theme_plugin_files = [
+ 'available' => true,
+ 'theme_files' => ['enabled' => false],
+ 'themes_selected' => [],
+ 'themes_excludes' => [],
+ 'plugin_files' => ['enabled' => false],
+ 'plugins_selected' => [],
+ 'plugins_excludes' => [],
+ 'muplugin_files' => ['enabled' => false],
+ 'muplugins_selected' => [],
+ 'muplugins_excludes' => [],
+ 'other_files' => ['enabled' => false],
+ 'others_selected' => [],
+ 'others_excludes' => [],
+ ];
+
+ if (isset($assoc_args['theme-files']) && $assoc_args['theme-files']) {
+ $theme_plugin_files['theme_files'] = ['enabled' => true];
+ $theme_plugin_files['themes_selected'] = $assoc_args['theme-files'];
+ }
+
+ if (isset($assoc_args['plugin-files']) && $assoc_args['plugin-files']) {
+ $theme_plugin_files['plugin_files'] = ['enabled' => true];
+ $theme_plugin_files['plugins_selected'] = $assoc_args['plugin-files'];
+ }
+
+ if (isset($assoc_args['mu-plugin-files']) && $assoc_args['mu-plugin-files']) {
+ $theme_plugin_files['muplugin_files'] = ['enabled' => true];
+ $theme_plugin_files['muplugins_selected'] = $assoc_args['mu-plugin-files'];
+ }
+
+ if (isset($assoc_args['other-files']) && $assoc_args['other-files']) {
+ $theme_plugin_files['other_files'] = ['enabled' => true];
+ $theme_plugin_files['others_selected'] = $assoc_args['other-files'];
+ }
+
+ if (isset($assoc_args['exclude-theme-plugin-files'])) {
+ $theme_plugin_files['themes_excludes'] = str_replace(',', "\n", $assoc_args['exclude-theme-plugin-files']);
+ $theme_plugin_files['plugins_excludes'] = str_replace(',', "\n", $assoc_args['exclude-theme-plugin-files']);
+ $theme_plugin_files['muplugins_excludes'] = str_replace(',', "\n", $assoc_args['exclude-theme-plugin-files']);
+ $theme_plugin_files['others_excludes'] = str_replace(',', "\n", $assoc_args['exclude-theme-plugin-files']);
+ }
+
+ $profile['theme_plugin_files'] = $theme_plugin_files;
+
+ return $profile;
+ }
+
+ /**
+ * Extends the T&P profile args after a connection has been made.
+ * Hooks on: wpmdb_cli_filter_tpf_profile_args
+ *
+ * @param array $profile
+ * @param array $remote
+ *
+ * @return array $profile
+ */
+ public function extend_tpf_profile_args($profile, $remote)
+ {
+ $stages = $this->get_tpf_stages($profile);
+ $intent = $profile['action'];
+
+ if ('pull' === $intent) {
+ $themes = isset($remote['site_details'], $remote['site_details']['themes']) ? $remote['site_details']['themes'] : [];
+ $plugins = isset($remote['site_details'], $remote['site_details']['plugins']) ? $remote['site_details']['plugins'] : [];
+ $muplugins = isset($remote['site_details'], $remote['site_details']['muplugins']) ? $remote['site_details']['muplugins'] : [];
+ $others = isset($remote['site_details'], $remote['site_details']['others']) ? $remote['site_details']['others'] : [];
+ } else {
+ $themes = $this->get_local_themes();
+ $plugins = $this->filesystem->get_local_plugins(true);
+ $muplugins = $this->get_local_muplugin_files();
+ $others = $this->get_local_other_files();
+ }
+
+ if (in_array('themes', $stages)) {
+ $themes = $this->get_themes_to_migrate($profile, $themes);
+
+ if (is_wp_error($themes)) {
+ return $themes;
+ }
+
+ $profile['theme_plugin_files']['themes_selected'] = $themes;
+ }
+
+ if (in_array('plugins', $stages)) {
+ $plugins = $this->get_plugins_to_migrate($profile, $plugins);
+
+ if (is_wp_error($plugins)) {
+ return $plugins;
+ }
+
+ $profile['theme_plugin_files']['plugins_selected'] = $plugins;
+ }
+
+ if (in_array('muplugins', $stages)) {
+ $muplugins = $this->get_muplugins_to_migrate($profile, $muplugins);
+
+ if (is_wp_error($muplugins)) {
+ return $muplugins;
+ }
+
+ $profile['theme_plugin_files']['muplugins_selected'] = $muplugins;
+ }
+
+ if (in_array('others', $stages)) {
+ $others = $this->get_others_to_migrate($profile, $others);
+
+ if (is_wp_error($others)) {
+ return $others;
+ }
+
+ $profile['theme_plugin_files']['others_selected'] = $others;
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Gets the themes to migrate.
+ *
+ * @param array $profile The current migration profile.
+ * @param array $themes The themes that can be migrated.
+ *
+ * @return array|WP_Error
+ */
+ public function get_themes_to_migrate($profile, $themes)
+ {
+ $themes_option = isset($profile['theme_plugin_files']['themes_option'])
+ ? $profile['theme_plugin_files']['themes_option']
+ : null;
+ $themes_selected = isset($profile['theme_plugin_files']['themes_selected'])
+ ? $profile['theme_plugin_files']['themes_selected']
+ : null;
+ $themes_excluded = isset($profile['theme_plugin_files']['themes_excluded'])
+ ? $profile['theme_plugin_files']['themes_excluded']
+ : null;
+ $themes_to_migrate = [];
+
+ if ('all' === $themes_selected || 'all' === $themes_option) {
+ $theme_paths = array_map(function ($theme) {
+ return $theme[0]['path'];
+ }, $themes);
+
+ return array_values($theme_paths);
+ }
+
+ if ('active' === $themes_option) {
+ $active_themes = [];
+ foreach($themes as $theme) {
+ if ($theme[0]['active']) {
+ $active_themes[] = $theme[0]['path'];
+ }
+ }
+ return array_values($active_themes);
+ }
+
+ if ('except' === $themes_option) {
+ $filtered_excluded = [];
+ foreach($themes as $theme) {
+ if(in_array($theme[0]['path'], $themes_excluded)){
+ continue;
+ }
+ $filtered_excluded[] = $theme[0]['path'];
+ }
+ return array_values($filtered_excluded);
+ }
+
+ if (!is_array($themes_selected)) {
+ $themes_selected = explode(',', $themes_selected);
+ } else {
+ $themes_selected = array_map('wp_basename', $themes_selected);
+ }
+
+ foreach ($themes_selected as $theme) {
+ $theme = trim($theme);
+
+ if (!$theme) {
+ continue;
+ }
+
+ if (!isset($themes[$theme])) {
+ $message = sprintf(__('Theme not found on source server: %s', 'wp-migrate-db'), $theme);
+ return new \WP_Error('wpmdbpro_theme_plugin_files_error', $message);
+ }
+
+ $themes_to_migrate[] = $themes[$theme][0]['path'];
+ }
+
+ return $themes_to_migrate;
+ }
+
+ /**
+ * Gets the plugins to migrate.
+ *
+ * @param array $profile The current migration profile.
+ * @param array $plugins The plugins that can be migrated.
+ *
+ * @return array|WP_Error
+ */
+ public function get_plugins_to_migrate($profile, $plugins)
+ {
+ $plugins_option = isset($profile['theme_plugin_files']['plugins_option'])
+ ? $profile['theme_plugin_files']['plugins_option']
+ : null;
+ $plugins_selected = isset($profile['theme_plugin_files']['plugins_selected'])
+ ? $profile['theme_plugin_files']['plugins_selected']
+ : null;
+ $plugins_excluded = isset($profile['theme_plugin_files']['plugins_excluded'])
+ ? $profile['theme_plugin_files']['plugins_excluded']
+ : null;
+ $plugins_to_migrate = [];
+
+ $plugins = $this->exclude_migrate_plugins($plugins);
+ if ('all' === $plugins_selected || 'all' === $plugins_option) {
+ $plugin_paths = array_map(function ($plugin) {
+ return $plugin[0]['path'];
+ }, $plugins);
+
+ return array_values($plugin_paths);
+ }
+
+ if ('active' === $plugins_option) {
+ $active_plugins = [];
+ foreach($plugins as $plugin) {
+ if ($plugin[0]['active']) {
+ $active_plugins[] = $plugin[0]['path'];
+ }
+ }
+ return array_values($active_plugins);
+ }
+
+ if ('except' === $plugins_option) {
+ $filtered_excluded = [];
+ foreach($plugins as $plugin) {
+ if(in_array($plugin[0]['path'], $plugins_excluded)){
+ continue;
+ }
+ $filtered_excluded[] = $plugin[0]['path'];
+ }
+ return array_values($filtered_excluded);
+ }
+
+
+ if (!is_array($plugins_selected)) {
+ $plugins_selected = explode(',', $plugins_selected);
+ } else {
+ $plugins_selected = array_map(function ($plugin) {
+ return wp_basename($plugin, '.php');
+ }, $plugins_selected);
+ }
+
+ $plugin_slugs = [];
+
+ // Get things into a format we can work with.
+ foreach ($plugins as $key => $value) {
+ $slug = preg_replace('/\/(.*)\.php|\.php/i', '', $key);
+ $plugin_slugs[$slug] = $value;
+ }
+
+ foreach ($plugins_selected as $plugin) {
+ $plugin = trim($plugin);
+
+ if (!$plugin) {
+ continue;
+ }
+
+ if (!isset($plugin_slugs[$plugin])) {
+ $message = sprintf(__('Plugin not found on source server: %s', 'wp-migrate-db'), $plugin);
+
+ return new \WP_Error('wpmdbpro_theme_plugin_files_error', $message);
+ }
+
+ $plugins_to_migrate[] = $plugin_slugs[$plugin][0]['path'];
+ }
+
+ return $plugins_to_migrate;
+ }
+
+ /**
+ * Exclude the Migrate Plugins from the plugins list
+ *
+ * @param array $plugins
+ * @return array
+ **/
+ protected function exclude_migrate_plugins($plugins)
+ {
+ $filtered_plugins = [];
+ foreach ($plugins as $key => $plugin) {
+ if (0 === strpos($key, 'wp-migrate-db')) {
+ continue;
+ }
+ $filtered_plugins[$key] = $plugin;
+ }
+ return $filtered_plugins;
+ }
+
+ /**
+ * Gets the must-use plugins to migrate.
+ *
+ * @param array $profile The current migration profile.
+ * @param array $plugins The plugins that can be migrated.
+ *
+ * @return array|WP_Error
+ */
+ public function get_muplugins_to_migrate($profile, $muplugins)
+ {
+ $muplugins_option = isset($profile['theme_plugin_files']['muplugins_option'])
+ ? $profile['theme_plugin_files']['muplugins_option']
+ : '';
+ $muplugins_selected = isset($profile['theme_plugin_files']['muplugins_selected'])
+ ? $profile['theme_plugin_files']['muplugins_selected']
+ : [];
+ $muplugins_to_migrate = [];
+
+ if ('all' === $muplugins_selected || 'all' === $muplugins_option) {
+ $muplugin_paths = array_map(function ($muplugin) {
+ return $muplugin[0]['path'];
+ }, $muplugins);
+
+ return array_values($muplugin_paths);
+ }
+
+ if (!is_array($muplugins_selected)) {
+ $muplugins_selected = explode(',', $muplugins_selected);
+ }
+
+ $muplugin_slugs = [];
+
+ // Get things into a format we can work with.
+ foreach ($muplugins as $key => $value) {
+ $muplugin_slugs[$value[0]['path']] = $value;
+ }
+
+ foreach ($muplugins_selected as $muplugin) {
+ if (strpos($muplugin, WPMU_PLUGIN_DIR) === false) {
+ $muplugin = $this->get_path_from_name($muplugins, $muplugin);
+ }
+ $muplugin = trim($muplugin);
+
+ if (!$muplugin) {
+ continue;
+ }
+
+ if (!isset($muplugin_slugs[$muplugin])) {
+ $message = sprintf(__('Must-Use Plugin not found on source server: %s', 'wp-migrate-db'), $muplugin);
+
+ return new \WP_Error('wpmdbpro_theme_plugin_files_error', $message);
+ }
+
+ $muplugins_to_migrate[] = $muplugin_slugs[$muplugin][0]['path'];
+ }
+
+ return $muplugins_to_migrate;
+ }
+
+
+ /**
+ * Gets the other filess to migrate.
+ *
+ * @param array $profile The current migration profile.
+ * @param array $others The other files that can be migrated.
+ *
+ * @return array|WP_Error
+ */
+ public function get_others_to_migrate($profile, $others)
+ {
+ $others_option = isset($profile['theme_plugin_files']['others_option'])
+ ? $profile['theme_plugin_files']['others_option']
+ : '';
+ $others_selected = isset($profile['theme_plugin_files']['others_selected'])
+ ? $profile['theme_plugin_files']['others_selected']
+ : [];
+ $others_to_migrate = [];
+
+ if ('all' === $others_selected || 'all' === $others_option) {
+ $other_paths = array_map(function ($other) {
+ return $other[0]['path'];
+ }, $others);
+
+ return array_values($other_paths);
+ }
+
+ if (!is_array($others_selected)) {
+ $others_selected = explode(',', $others_selected);
+ }
+
+ $other_slugs = [];
+
+ // Get things into a format we can work with.
+ foreach ($others as $key => $value) {
+ $other_slugs[$value[0]['path']] = $value;
+ }
+
+ foreach ($others_selected as $other) {
+ if (strpos($other, WP_CONTENT_DIR) === false) {
+ $other = $this->get_path_from_name($others, $other);
+ }
+ $other = trim($other);
+
+ if (!$other) {
+ continue;
+ }
+
+ if (!isset($other_slugs[$other])) {
+ $message = sprintf(__('Other file not found on source server: %s', 'wp-migrate-db'), $other);
+
+ return new \WP_Error('wpmdbpro_theme_plugin_files_error', $message);
+ }
+
+ $others_to_migrate[] = $other_slugs[$other][0]['path'];
+ }
+
+ return $others_to_migrate;
+ }
+
+ /**
+ * Get file path to be used for array key
+ *
+ * @param array $files_array
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ public function get_path_from_name($files_array, $name)
+ {
+ foreach ($files_array as $key => $val) {
+ if ($val[0]['name'] === $name || $val[0]['path'] === $name) {
+ return $val[0]['path'];
+ }
+ }
+ return '';
+ }
+
+ /**
+ * Adds the Theme & Plugin Files stages to the current migration.
+ * Hooks on: wpmdb_cli_profile_before_migration
+ *
+ * @param array $profile
+ *
+ * @return array
+ */
+ public function add_tpf_stages($profile)
+ {
+ if (is_wp_error($profile)) {
+ return $profile;
+ }
+
+ $stages = $this->get_tpf_stages($profile);
+
+ if (in_array('themes', $stages)) {
+ $profile['current_migration']['stages'][] = 'theme_files';
+ }
+
+ if (in_array('plugins', $stages)) {
+ $profile['current_migration']['stages'][] = 'plugin_files';
+ }
+
+ if (in_array('muplugins', $stages)) {
+ $profile['current_migration']['stages'][] = 'muplugin_files';
+ }
+
+ if (in_array('others', $stages)) {
+ $profile['current_migration']['stages'][] = 'other_files';
+ }
+
+ return $profile;
+ }
+
+ /**
+ * Initialize the TPF stage.
+ *
+ * @param array $profile
+ * @param array $post_data
+ * @param string $stage
+ * @return array|WP_Error
+ */
+ public function initialize_tpf_migration($profile, $post_data, $stage)
+ {
+ $_POST = [
+ 'action' => $profile['action'],
+ 'migration_state_id' => $profile['current_migration']['migration_id'],
+ 'stage' => $stage,
+ 'is_cli_migration' => 1
+ ];
+
+ if ('themes' === $stage) {
+ $initiate_msg = __('Initiating themes migration...', 'wp-migrate-db');
+ $_POST['folders'] = json_encode($profile['theme_plugin_files']['themes_selected']);
+ $_POST['theme_folders'] = json_encode($profile['theme_plugin_files']['themes_selected']);
+ $_POST['themes_option'] = isset($profile['theme_plugin_files']['themes_option'])
+ ? $profile['theme_plugin_files']['themes_option']
+ : '';
+ if (isset($profile['theme_plugin_files']['themes_excludes'])) {
+ $_POST['themes_excludes'] = json_encode($profile['theme_plugin_files']['themes_excludes']);
+ }
+ } elseif('others' === $stage){
+ $initiate_msg = __('Initiating other files migration...', 'wp-migrate-db');
+ $_POST['folders'] = json_encode($profile['theme_plugin_files']['others_selected']);
+ $_POST['other_folders'] = json_encode($profile['theme_plugin_files']['others_selected']);
+ $_POST['others_option'] = isset($profile['theme_plugin_files']['others_option'])
+ ? $profile['theme_plugin_files']['others_option']
+ : '';
+ if (isset($profile['theme_plugin_files']['others_excludes'])) {
+ $_POST['others_excludes'] = json_encode($profile['theme_plugin_files']['others_excludes']);
+ }
+ } elseif('muplugins' === $stage){
+ $initiate_msg = __('Initiating must-use files migration...', 'wp-migrate-db');
+ $_POST['folders'] = json_encode($profile['theme_plugin_files']['muplugins_selected']);
+ $_POST['muplugin_folders'] = json_encode($profile['theme_plugin_files']['muplugins_selected']);
+ $_POST['muplugins_option'] = isset($profile['theme_plugin_files']['muplugins_option'])
+ ? $profile['theme_plugin_files']['muplugins_option']
+ : '';
+ if (isset($profile['theme_plugin_files']['muplugins_excludes'])) {
+ $_POST['muplugins_excludes'] = json_encode($profile['theme_plugin_files']['muplugins_excludes']);
+ }
+ } else {
+ $initiate_msg = __('Initiating plugins migration...', 'wp-migrate-db');
+ $_POST['folders'] = json_encode($profile['theme_plugin_files']['plugins_selected']);
+ $_POST['plugin_folders'] = json_encode($profile['theme_plugin_files']['plugins_selected']);
+ $_POST['plugins_option'] = isset($profile['theme_plugin_files']['plugins_option'])
+ ? $profile['theme_plugin_files']['plugins_option']
+ : '';
+ if (isset($profile['theme_plugin_files']['plugins_excludes'])) {
+ $_POST['plugins_excludes'] = json_encode($profile['theme_plugin_files']['plugins_excludes']);
+ }
+ }
+
+ \WP_CLI::log($initiate_msg);
+
+ $response = $this->tpf_local->ajax_initiate_file_migration();
+
+ return $this->cli->verify_cli_response($response, 'initialize_tpf_migration()');
+ }
+
+ /**
+ * Transfers files during the TPF stage.
+ *
+ * @param array $profile
+ * @param array $post_data
+ * @param string $stage
+ *
+ * @return array|WP_Error
+ */
+ public function tpf_transfer_files($profile, $post_data, $stage)
+ {
+ $_POST = [
+ 'action' => $profile['action'],
+ 'migration_state_id' => $profile['current_migration']['migration_id'],
+ 'stage' => $stage,
+ ];
+
+ $response = $this->tpf_local->ajax_transfer_files();
+
+ return $this->cli->verify_cli_response($response, 'tpf_transfer_files()');
+ }
+
+ /**
+ * Run the TPF migration from the CLI.
+ * Hooks on: wpmdb_pro_cli_finalize_migration
+ *
+ * @param bool $outcome
+ * @param array $profile
+ * @param array $verify_connection_response
+ * @param array $post_data
+ *
+ * @return bool|WP_Error
+ */
+ public function cli_migration($outcome, $profile, $verify_connection_response, $post_data)
+ {
+ $stages = $this->get_tpf_stages($profile);
+ if (true !== $outcome || empty($stages)) {
+ return $outcome;
+ }
+
+ foreach ($stages as $stage) {
+ $init = $this->initialize_tpf_migration($profile, $post_data, $stage);
+ if (is_wp_error($init)) {
+ return $init;
+ }
+
+ $queue_status = $init['queue_status'];
+ $total_size = isset($queue_status['size']) ? (int) $queue_status['size'] : 0;
+ $intent = $profile['action'];
+ $migrate_bar = $this->make_progress_bar($this->get_string('cli_migrating_' . $intent), 0);
+ $migrate_bar->setTotal($total_size);
+
+ $result = ['status' => 0];
+
+ while (!is_wp_error($result) && $result['status'] !== 'complete') {
+ // Delay between requests
+ do_action('wpmdb_theme_plugin_files_cli_before_migrate_files');
+
+ // Migrate the files.
+ $result = $this->tpf_transfer_files($profile, $post_data, $stage);
+
+ if (isset($result['status']['error'])) {
+ return new \WP_Error('wpmdb_cli_tpf_migration_failed', $result['status']['message']);
+ }
+
+ $batch_size = is_array($result['status']) ? array_sum(array_column($result['status'], 'batch_size')) : 0;
+
+ // Update progress.
+ $migrate_bar->tick($batch_size);
+ }
+
+ if (is_wp_error($result)) {
+ return $result;
+ }
+
+ // Finish things up.
+ $migrate_bar->finish();
+ }
+
+ return true;
+ }
+
+ /**
+ * Like WP_CLI\Utils\make_progress_bar, but uses our own wrapper classes
+ *
+ * @param $message
+ * @param $count
+ *
+ * @return ThemePluginFilesCliBar|ThemePluginFilesCliBarNoOp
+ */
+ public function make_progress_bar($message, $count)
+ {
+ if (method_exists('cli\Shell', 'isPiped') && \cli\Shell::isPiped()) {
+ return new ThemePluginFilesCliBarNoOp();
+ }
+
+ return new ThemePluginFilesCliBar($message, $count);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCliBar.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCliBar.php
new file mode 100644
index 000000000..a335deb36
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCliBar.php
@@ -0,0 +1,16 @@
+_message = $message;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCliBarNoOp.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCliBarNoOp.php
new file mode 100644
index 000000000..200631d34
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Cli/ThemePluginFilesCliBarNoOp.php
@@ -0,0 +1,36 @@
+_message = $message;
+ }
+
+ public function tick()
+ {
+ }
+
+ public function finish()
+ {
+ // log last _message to show count of files migrated
+ \WP_CLI::log($this->_message);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Manager.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Manager.php
new file mode 100644
index 000000000..32c59e837
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/Manager.php
@@ -0,0 +1,33 @@
+get(ThemePluginFilesAddon::class);
+ $theme_plugin->register();
+ $theme_plugin->set_licensed($licensed);
+
+ $container->get(ThemePluginFilesLocal::class)->register();
+ $container->get(ThemePluginFilesRemote::class)->register();
+ $container->get(ThemePluginFilesCli::class)->register();
+
+ add_filter('wpmdb_addon_registered_tpf', '__return_true');
+
+ return $theme_plugin;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/ThemePluginFilesRemote.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/ThemePluginFilesRemote.php
new file mode 100644
index 000000000..f6d29d2e0
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/TPF/ThemePluginFilesRemote.php
@@ -0,0 +1,186 @@
+queueManager = $queue_manager;
+ $this->transfer_util = $util;
+ $this->file_processor = $file_processor;
+ $this->transfer_manager = $transfer_manager;
+ $this->receiver = $receiver;
+ $this->http = $http;
+ $this->http_helper = $http_helper;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->settings = $settings->get_settings();
+ $this->properties = $properties;
+ $this->sender = $sender;
+ $this->filesystem = $filesystem;
+ $this->scrambler = $scramble;
+ $this->plugin_helper = $plugin_helper;
+ }
+
+ public function register()
+ {
+ add_action('wp_ajax_nopriv_wpmdbtp_respond_to_get_remote_themes', array($this, 'ajax_tp_respond_to_get_remote_themes'));
+ add_action('wp_ajax_nopriv_wpmdbtp_respond_to_get_remote_plugins', array($this, 'ajax_tp_respond_to_get_remote_plugins'));
+ add_action('wp_ajax_nopriv_wpmdbtp_respond_to_get_remote_muplugins', array($this, 'ajax_tp_respond_to_get_remote_muplugins'));
+ add_action('wp_ajax_nopriv_wpmdbtp_respond_to_get_remote_others', array($this, 'ajax_tp_respond_to_get_remote_others'));
+
+ add_action('wp_ajax_nopriv_wpmdbtp_respond_to_save_queue_status', array($this, 'ajax_tp_respond_to_save_queue_status'));
+ add_action('wp_ajax_nopriv_wpmdbtp_transfers_send_file', array($this, 'ajax_tp_respond_to_request_files',));
+ add_action('wp_ajax_nopriv_wpmdbtp_transfers_receive_file', array($this, 'ajax_tp_respond_to_post_file'));
+ }
+
+
+ public function ajax_tp_respond_to_get_remote_themes()
+ {
+ $this->respond_to_get_remote_folders('themes');
+ }
+
+ public function ajax_tp_respond_to_get_remote_plugins()
+ {
+ $this->respond_to_get_remote_folders('plugins');
+ }
+
+ public function ajax_tp_respond_to_get_remote_muplugins()
+ {
+ $this->respond_to_get_remote_folders('muplugins');
+ }
+
+ public function ajax_tp_respond_to_get_remote_others()
+ {
+ $this->respond_to_get_remote_folders('others');
+ }
+
+ /**
+ * @param $stage
+ *
+ * @return mixed|null
+ */
+ public function respond_to_get_remote_folders($stage)
+ {
+ return $this->plugin_helper->respond_to_get_remote_folders($stage);
+ }
+
+ /**
+ *
+ * Fired off a nopriv AJAX hook that listens to pull requests for file batches
+ *
+ * @return mixed
+ */
+ public function ajax_tp_respond_to_request_files()
+ {
+ return $this->plugin_helper->respond_to_request_files();
+ }
+
+ /**
+ *
+ * Respond to request to save queue status
+ *
+ * @return mixed|null
+ */
+ public function ajax_tp_respond_to_save_queue_status()
+ {
+ return $this->plugin_helper->respond_to_save_queue_status();
+ }
+
+ /**
+ * @return null
+ * @throws \Exception
+ */
+ public function ajax_tp_respond_to_post_file()
+ {
+ return $this->plugin_helper->respond_to_post_file();
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/IncrementalSizeController.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/IncrementalSizeController.php
new file mode 100644
index 000000000..04ebbb066
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/IncrementalSizeController.php
@@ -0,0 +1,165 @@
+currentSegment = 0;
+ $this->max = $max;
+ $this->fallback = $fallback;
+ $this->retry = $retryCount;
+
+ $this->initialize_seek_array($initialSize);
+ }
+
+
+ /**
+ * increments in the segment size if the seek array has further segments
+ * and returns the actual segment size.
+ *
+ * @return int
+ */
+ public function step_up_size() {
+ if(array_key_exists($this->currentSegment + 1, $this->seekArray)) {
+ ++ $this->currentSegment;
+ }
+
+ return $this->get_current_size();
+ }
+
+
+ /**
+ * Decrements the segment size if the seek array has lower values,
+ * or defaults to 0 if it doesn't, and returns the actual segment size.
+ *
+ * @param int $steps
+ *
+ * @return int
+ */
+ public function step_down_size($steps = 1) {
+ if(array_key_exists($this->currentSegment - 1, $this->seekArray)) {
+ $newSegment = $this->currentSegment - $steps;
+ if(array_key_exists($newSegment, $this->seekArray)) {
+ $this->currentSegment = $newSegment;
+ } else {
+ $this->currentSegment = 0;
+ }
+ }
+
+ return $this->get_current_size();
+ }
+
+
+ /**
+ * Returns current segment's actual size in bytes.
+ *
+ * @return int
+ */
+ public function get_current_size() {
+ return $this->seekArray[$this->currentSegment];
+ }
+
+
+ /**
+ * Returns the fallback size in bytes.
+ *
+ * @return int
+ */
+ public function get_fallback_size() {
+ return $this->fallback;
+ }
+
+
+ /**
+ * Initializes a seek array with different segments values, these sizes are used to set up and step down
+ * the payload size during HP mode migration.
+ *
+ * The first element of the seek array is the fallback size, then every other element is 20% larger of its
+ * predecessor. The last element of the array is the $max value.
+ *
+ * @param int $initialSize
+ * @return void
+ */
+ private function initialize_seek_array($initialSize = null) {
+ $this->seekArray = [ $this->fallback ];
+ $target = 0;
+ $i = 0;
+ while ( $target < $this->max ) {
+
+ $last_entry = end( $this->seekArray );
+ $new_value = (int)round(( ( $last_entry / 100 ) * 20 ) + $last_entry);
+
+ if ( $new_value > $this->max ) {
+ $new_value = $this->max;
+ }
+
+ $target = $new_value;
+ $i ++;
+
+ $this->seekArray[] = $new_value;
+ if ( $new_value === $initialSize ) {
+ $this->currentSegment = $i;
+ }
+
+ if ( $initialSize === null ) {
+ $this->currentSegment = 1;
+ }
+ }
+ }
+
+
+ /**
+ * Returns true of the current segment size equals the max payload size.
+ *
+ * @return bool
+ */
+ public function is_at_max_size() {
+ return $this->currentSegment === count($this->seekArray) - 1;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/Payload.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/Payload.php
new file mode 100644
index 000000000..13c276cd4
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/Payload.php
@@ -0,0 +1,492 @@
+util = $util;
+ $this->chunker = $chunker;
+ $this->filesystem = $filesystem;
+ $this->http = $http;
+ $this->common_util = $common_util;
+ }
+
+ /**
+ *
+ * Create a payload string based on an array of file data
+ *
+ * Write string to $stream resource
+ *
+ * @param array $file
+ * @param array $meta_data
+ * @param resource $stream
+ * @param string $file_path
+ * @param bool $chunked
+ *
+ * @return null
+ * @throws \Exception
+ */
+ public function assemble_payload($file, $meta_data, &$stream, $file_path)
+ {
+ if (!file_exists($file['absolute_path'])) {
+ throw new \Exception(sprintf(__('File does not exist - %s', 'wp-migrate-db'), $file['absolute_path']));
+ }
+
+ $file_name = $file['name'];
+ $file['type'] = 'file';
+ $file['md5'] = md5_file($file['absolute_path']);
+ $file['chunk_size'] = isset($file['chunk_path']) ? filesize($file['chunk_path']) : null;
+ $file['encoded'] = true;
+
+ if (!isset($file['size'])) {
+ $file['size'] = filesize($file['absolute_path']);
+ }
+
+ $meta_data['file'] = $file + $meta_data['file'];
+
+ $content = Sender::$start_meta . $file_name . "\n";
+ $content .= json_encode($meta_data) . "\n";
+ $content .= Sender::$end_meta . $file_name . "\n";
+ $content .= Sender::$start_payload . $file_name . "\n";
+
+ // Write first chunk of content to the payload
+ fwrite($stream, $content);
+
+ $file_stream = fopen($file_path, 'rb');
+
+ // Skirts memory limits by copying stream to stream - writes directly to stream
+ stream_copy_to_stream($file_stream, $stream);
+
+ $content = "\n" . Sender::$end_payload . $file_name;
+ $content .= "\n" . Sender::$end_sig . "\n";
+
+ fwrite($stream, $content);
+
+ return null;
+ }
+
+ /**
+ * @param array $file_list
+ * @param array $state_data
+ * @param string $bottleneck
+ *
+ * @return resource|array
+ */
+ public function create_payload($file_list, $state_data, $bottleneck)
+ {
+ /*
+ * Other options to use if large files aren't downloading are:
+ * $membuffer = 54 * 1024 * 1024; // 54MB
+ * $handle = apply_filters( 'wpmdb_transfers_payload_handle', fopen( 'php://temp/maxmemory:'. $membuffer ) );
+ *
+ * OR
+ *
+ * $handle = apply_filters( 'wpmdb_transfers_payload_handle', fopen( WP_CONTENT_DIR . '/.payload', 'wb+' ) );
+ */
+ $handle = apply_filters('wpmdb_transfers_payload_handle', tmpfile());
+
+ $count = 0;
+ $sent = [];
+ $chunked = [];
+ $chunking = false;
+ $chunks = 0;
+
+ foreach ($file_list as $key => $file) {
+ // Info on fopen() stream
+ $fstat = fstat($handle);
+
+ $added_size = $fstat['size'] + $file['size'];
+
+ // If the filesize is less than the bottleneck and adding the file to the payload would push it over the $bottleneck
+ // OR the payload already has stuff in it and the next file is a file larger than the bottleneck
+ if (($file['size'] < $bottleneck && $added_size >= $bottleneck)
+ || (0 !== $fstat['size'] && $file['size'] >= $bottleneck)) {
+ break;
+ }
+
+ $data = [
+ 'file' => $file,
+ 'stage' => $state_data['stage'],
+ ];
+
+ $file_path = $file['absolute_path'];
+ $file_size = filesize($file_path);
+
+ //Push and file is too large
+ if ($file_size >= $bottleneck && 'push' === $state_data['intent']) {
+ $chunks = ceil($file_size / $bottleneck);
+ $chunking = true;
+ }
+
+ list($chunked, $file, $file_path, $chunk_data) = $this->maybe_get_chunk_data($state_data, $bottleneck, $chunking, $file_path, $file, $chunks);
+
+ try {
+ $this->assemble_payload($file, $data, $handle, $file_path);
+ } catch (\Exception $e) {
+ $this->util->catch_general_error($e->getMessage());
+ }
+
+ $sent[] = $file;
+ $count++;
+ }
+
+ if ('pull' === $state_data['intent']) {
+ $handle = $this->assemble_payload_metadata($count, $sent, $handle);
+ }
+
+ fwrite($handle, "\n" . Sender::$end_bucket);
+
+ if ('push' === $state_data['intent']) {
+ return array($count, $sent, $handle, $chunked, $file, $chunk_data);
+ }
+
+ return $handle;
+ }
+
+ /**
+ * @param array $state_data
+ * @param int $bottleneck
+ * @param bool $chunking
+ * @param string $file_path
+ * @param array $file
+ * @param int $chunks
+ *
+ * @return array
+ */
+ public function maybe_get_chunk_data($state_data, $bottleneck, $chunking, $file_path, $file, $chunks)
+ {
+ if (!$chunking) {
+ return array(false, $file, $file_path, []);
+ }
+ // Checks if current migration is a 'push' and if the file is too large to transfer
+ list($chunked, $chunk_data) = $this->chunker->chunk_it($state_data, $bottleneck, $file_path, $file, $chunks);
+
+ if ($chunked && false !== $chunked['chunked']) {
+ $file = $chunked['file'];
+ $file_path = $chunked['file_path'];
+ }
+
+ return array($chunked, $file, $file_path, $chunk_data);
+ }
+
+ /**
+ *
+ * Read payload line by line and parse out contents
+ *
+ * @param array $state_data
+ * @param resource $stream
+ * @param bool $return
+ *
+ * @return bool
+ * @throws \Exception
+ */
+ public function process_payload($state_data, $stream, $return = false)
+ {
+ $is_meta = false;
+ $is_payload = false;
+ $end_payload = false;
+ $is_bucket_meta = false;
+ $bucket_meta = false;
+ $meta = [];
+
+ while (($line = fgets($stream)) !== false) {
+ if (false !== strpos($line, Sender::$start_meta)) {
+ $is_meta = true;
+ continue;
+ }
+
+ if ($is_meta) {
+ $meta = json_decode($line, true);
+ $is_meta = false;
+ continue;
+ }
+
+ if (false !== strpos($line, Sender::$start_payload)) {
+ $is_payload = true;
+
+ list($dest, $handle) = $this->get_handle_from_metadata($state_data, $meta);
+
+ //For pulls, we're not chunking so use the full filesize, for push check if a chunk size exists, otherwise use the full filesize.
+ $filesize = !empty($meta['file']['chunk_size']) ? $meta['file']['chunk_size'] : $meta['file']['size'];
+
+ // maybe we can stream the file without buffering
+ if (is_numeric($filesize)) {
+ // set up stream copy here
+ $streamed_bytes = stream_copy_to_stream($stream, $handle, $filesize);
+ if (false === $streamed_bytes || $streamed_bytes !== $filesize) {
+ error_log('Could not copy stream data to file. ' . print_r($dest, true));
+ throw new \Exception(sprintf(__('Could not copy stream data to file. File name: %s', 'wp-migrate-db'), $dest));
+ }
+ // yay! we did it. Next loop gets the end of payload
+ continue;
+ }
+
+ //We couldn't determine the filesize so let's bail
+ error_log('Could not determine payload filesize: ' . print_r($dest, true));
+ throw new \Exception(sprintf(__('Could not determine payload filesize. File name: %s', 'wp-migrate-db'), $dest));
+ }
+
+ if ($is_payload) {
+ /**
+ * Since we're in a large while loop we need to check if a file's payload
+ * has been read entirely. Files are added to the payload line by line so they
+ * need to read line by line. Sender::$end_payload is the deliminator to say that
+ * a file's contents ends _within_ the payload.
+ */
+ if (false !== strpos($line, Sender::$end_payload)) {
+ // Trim trailing newline from end of the created file, thanks fgets()...
+ $stat = fstat($handle);
+ ftruncate($handle, $stat['size'] - 1);
+
+ fclose($handle);
+
+ $is_payload = false;
+ $end_payload = true;
+ continue;
+ }
+
+ $this->create_file_by_line($line, $handle, $meta['file']);
+
+ }
+
+ if ($end_payload) {
+ if (isset($meta['file']['chunked']) && false !== $meta['file']['chunked']) {
+ if (isset($meta['file']['bytes_offset'], $meta['file']['size']) && ((int)$meta['file']['bytes_offset'] === (int)$meta['file']['size'])) {
+ //Chunking complete
+ $this->rename_part_file($dest, $meta);
+ }
+ } else {
+ $this->verify_file_from_payload($dest, $meta);
+ }
+
+
+ $end_payload = false;
+ continue;
+ }
+
+ /**
+ * Bucket meta is information about what's in the payload.
+ *
+ * Presently this includes a count of how many files it contains and
+ * file information from Filesystem::get_file_info() about each file within
+ *
+ */
+ if (false !== strpos($line, Sender::$start_bucket_meta)) {
+ $is_bucket_meta = true;
+ continue;
+ }
+
+ if ($is_bucket_meta) {
+ $bucket_meta = json_decode($line, true);
+ $is_bucket_meta = false;
+ continue;
+ }
+
+ if (false !== strpos($line, Sender::$end_bucket)) {
+ if (!$return) {
+ return $this->http->end_ajax($bucket_meta);
+ }
+
+ return $bucket_meta;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param $dest
+ * @param $meta
+ *
+ * @throws \Exception
+ */
+ public function verify_file_from_payload($dest, $meta)
+ {
+ // Verify size of file matches what it's supposed to be
+ if (!$this->util->verify_file($dest, (int)$meta['file']['size'])) {
+ $msg = sprintf(__('File size of source and destination do not match:
%s
Destination size: %s, Local size: %s', 'wp-migrate-db'), $dest, filesize($dest), $meta['file']['size']);
+ throw new \Exception($msg);
+ }
+
+ $md5 = md5_file($dest);
+
+ if ($meta['file']['md5'] !== $md5) {
+ $msg = sprintf(__("File MD5's do not match for file: %s \nLocal MD5: %s Remote MD5: %s", 'wp-migrate-db'), \dirname($dest), $md5, $meta['file']['md5']);
+
+ throw new \Exception($msg);
+ }
+ }
+
+ /**
+ *
+ * Give a line of data from fgets() write to a previously created resource(stream).
+ *
+ * @param $line
+ * @param $handle
+ *
+ * @return bool
+ * @throws \Exception
+ */
+ public function create_file_by_line($line, $handle, $file_data)
+ {
+ if (!$handle || !$bytes = fwrite($handle, $line)) {
+ error_log('Could not write line to file. ' . print_r($file_data, true));
+ throw new \Exception(sprintf(__('Could not write line to file. File name: %s', 'wp-migrate-db'), $file_data['name']));
+ }
+
+ return false;
+ }
+
+ /**
+ * @param array $state_data
+ * @param $meta
+ *
+ * @return string
+ */
+ public function assemble_filepath_from_payload($state_data, $meta)
+ {
+ $stage = $state_data['stage'];
+
+ $file = $this->filesystem->slash_one_direction($meta['file']['relative_path']);
+
+ if (isset($meta['file']['chunked']) && $meta['file']['chunked'] === true) {
+ $file .= self::PART_SUFFIX;
+ }
+
+ $dest = Util::get_temp_dir($stage) . $file;
+ if ($stage === 'media_files') {
+ // Filtered by MST
+ $uploads = apply_filters('wpmdb_mf_destination_uploads', Util::get_wp_uploads_dir(), $state_data);
+ $file = apply_filters('wpmdb_mf_destination_file', $file, $state_data);
+ $dest = $uploads . $file;
+ }
+
+ return $dest;
+ }
+
+
+ /**
+ * @param $stage
+ * @param $meta
+ *
+ * @return array
+ * @throws \Exception
+ */
+ public function get_handle_from_metadata($state_data, $meta)
+ {
+ $dest = $this->assemble_filepath_from_payload($state_data, $meta);
+
+ $dirname = \dirname($dest);
+
+ if (!is_dir($dirname)) {
+ if (!$this->filesystem->mkdir($dirname)) {
+ $msg = sprintf(__('Could not create directory: %s', 'wp-migrate-db'), dirname($dest));
+ throw new \Exception($msg);
+ }
+ }
+
+ $mode = isset($meta['file']['chunked']) ? 'ab' : 'wb';
+
+ // Files need to be deleted before hand when running media stage because they are copied in place
+ // 'w' fopen() mode truncates the file before opening to write
+ if (
+ isset($meta['file']['chunked']) && $meta['file']['chunk_number'] === 1 ||
+ !isset($meta['file']['chunked'])
+ ) {
+ $mode = 'w';
+ }
+
+ if (file_exists($dest) && !is_writable($dest)) {
+ $msg = sprintf(__('The `%s` file is not writable, please check the file permissions of the parent folder and ensure the web server can read from and write to this file.', 'wp-migrate-db'), $dest);
+ throw new \Exception($msg);
+ }
+
+ $handle = fopen($dest, $mode);
+
+ return array($dest, $handle);
+ }
+
+ /**
+ * @param $count
+ * @param $sent
+ * @param $handle
+ *
+ * @return resource
+ */
+ public function assemble_payload_metadata($count, $sent, $handle)
+ {
+ // Information about what's in the payload, number of files and an array of file data about the files included
+ $bucket_meta = json_encode(compact('count', 'sent'));
+
+ $bucket_meta_content = Sender::$start_bucket_meta . "\n";
+ $bucket_meta_content .= $bucket_meta . "\n";
+ $bucket_meta_content .= Sender::$end_bucket_meta;
+
+ fwrite($handle, $bucket_meta_content);
+
+ return $handle;
+ }
+
+ /**
+ * @param $dest
+ * @param $meta
+ */
+ public function rename_part_file($dest, $meta)
+ {
+ $original_filename = str_replace(self::PART_SUFFIX, '', $dest);
+
+ if ($this->filesystem->file_exists($original_filename)) {
+ $this->filesystem->unlink($original_filename);
+ }
+
+ $renamed = rename($dest, $original_filename);
+
+ if (!$renamed) {
+ return $this->http->end_ajax(new \WP_Error('wpmdb_chunk_file_rename_failed', sprintf(__('Unable to rename part file %s', 'wp-migrate-db'), $dest)));
+ }
+
+ return $this->verify_file_from_payload($original_filename, $meta);
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/PluginHelper.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/PluginHelper.php
new file mode 100644
index 000000000..5936b63e2
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/PluginHelper.php
@@ -0,0 +1,154 @@
+sender = $sender;
+ $this->receiver = $receiver;
+ }
+
+
+ /**
+ * @return null
+ * @throws \Exception
+ */
+ public function respond_to_post_file()
+ {
+ $key_rules = array(
+ 'action' => 'key',
+ 'remote_state_id' => 'key',
+ 'stage' => 'string',
+ 'intent' => 'string',
+ 'folders' => 'array',
+ 'theme_folders' => 'array',
+ 'themes_option' => 'string',
+ 'plugin_folders' => 'array',
+ 'plugins_option' => 'string',
+ 'muplugin_folders' => 'array',
+ 'muplugins_option' => 'string',
+ 'other_folders' => 'array',
+ 'others_option' => 'string',
+ 'sig' => 'string',
+ );
+
+ if(!isset($_POST['state_data'])) {
+ throw new \Exception(__('Failed to respond to payload post, empty state data.', 'wp-migrate-db'));
+ }
+
+ $decoded_json_state = json_decode(base64_decode($_POST['state_data']), true);
+
+ //Sending ALL local state data, probably too much data and should be paired down
+ $state_data = Persistence::setRemotePostData($key_rules, __METHOD__, 'wpmdb_remote_migration_state', $decoded_json_state);
+
+ $filtered_post = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'action',
+ 'remote_state_id',
+ 'stage',
+ 'intent',
+ )
+ );
+
+ $settings = $this->settings;
+
+ if (!isset($_FILES['content']) || !$this->http_helper->verify_signature($filtered_post, $settings['key'])) {
+ throw new \Exception(__('Failed to respond to payload post.', 'wp-migrate-db'));
+ }
+
+ $payload_content = fopen($_FILES['content']['tmp_name'], 'r');
+ $receiver = $this->receiver;
+
+ try {
+ $receiver->receive_post_data($state_data, $payload_content);
+ } catch (\Exception $e) {
+ return $this->transfer_util->catch_general_error($e->getMessage());
+ }
+ }
+
+ /**
+ *
+ * Fired off a nopriv AJAX hook that listens to pull requests for file batches
+ *
+ * @return mixed
+ */
+ public function respond_to_request_files()
+ {
+ $key_rules = array(
+ 'action' => 'key',
+ 'remote_state_id' => 'key',
+ 'stage' => 'string',
+ 'intent' => 'string',
+ 'bottleneck' => 'numeric',
+ 'sig' => 'string',
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data($key_rules, 'remote_state_id');
+ $filtered_post = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'action',
+ 'remote_state_id',
+ 'stage',
+ 'intent',
+ 'bottleneck',
+ )
+ );
+
+ $settings = $this->settings;
+
+ if (!$this->http_helper->verify_signature($filtered_post, $settings['key'])) {
+ return $this->transfer_util->ajax_error($this->properties->invalid_content_verification_error . ' (#100tp)', $filtered_post);
+ }
+
+ try {
+ $this->sender->respond_to_send_file($state_data);
+ } catch (\Exception $e) {
+ $this->transfer_util->catch_general_error($e->getMessage());
+ }
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/SizeControllerInterface.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/SizeControllerInterface.php
new file mode 100644
index 000000000..afc01ce2a
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Files/SizeControllerInterface.php
@@ -0,0 +1,51 @@
+queueManager = $manager;
+ $this->payload = $payload;
+ $this->util = $util;
+ $this->http_helper = $http_helper;
+ $this->receiver = $receiver;
+ $this->sender = $sender;
+ $this->http = $http;
+ $this->size_controller = $size_controller;
+ $this->dynamic_props = DynamicProperties::getInstance();
+ }
+
+ /**
+ * @param array $processed
+ * @param array $state_data
+ * @param string $remote_url
+ *
+ * @return array
+ */
+ public function handle_push($processed, $state_data, $remote_url)
+ {
+ $transfer_max = $state_data['site_details']['remote']['transfer_bottleneck'];
+ $actual_bottleneck = $state_data['site_details']['remote']['max_request_size'];
+ $high_performance_transfers = $state_data['site_details']['remote']['high_performance_transfers'];
+ $force_performance_transfers = isset($state_data['forceHighPerformanceTransfers']) ? $state_data['forceHighPerformanceTransfers'] : false;
+ $force_performance_transfers = apply_filters('wpmdb_force_high_performance_transfers', $force_performance_transfers, $state_data);
+
+ $bottleneck = apply_filters('wpmdb_transfers_push_bottleneck',
+ $actual_bottleneck); //Use slider value
+ $fallback_payload_size = 1000000;
+
+ $bottleneck = $this->maybeUseHighPerformanceTransfers($bottleneck, $high_performance_transfers, $force_performance_transfers, $transfer_max,
+ $fallback_payload_size, $state_data);
+
+ // Remove 1KB from the bottleneck as some hosts have a 1MB bottleneck
+ $bottleneck -= 1000;
+
+ $batch = [];
+ $total_size = 0;
+
+ // Get subset of files to combine into a payload
+ foreach ($processed as $key => $file) {
+ $batch[] = $file;
+
+ // This is a loose enforcement, actual payload size limit is implemented in Payload::create_payload()
+ if (($total_size + $file['size']) >= $bottleneck) {
+ break;
+ }
+
+ $total_size += $file['size'];
+ }
+
+ list($count, $sent, $handle, $chunked, $file, $chunk_data) = $this->payload->create_payload($batch, $state_data, $bottleneck);
+
+ $transfer_status = $this->attempt_post($state_data, $remote_url, $handle);
+ $code = $transfer_status->status_code;
+
+ if (!$transfer_status || 200 !== $code) {
+ return $this->util->fire_transfer_errors(sprintf(__('Payload transfer failed with code %s: %s', 'wp-migrate-db'), $code, $transfer_status->body));
+ }
+
+ list($total_sent, $sent_copy) = $this->process_sent_data_push($sent, $chunked);
+
+ // Convert 'file data' to 'folder data', as that's how the UI/Client displays progress
+ $result = $this->util->process_queue_data($sent_copy, $state_data, $total_sent);
+
+ // If we're not chunking
+ if ( empty( $chunked ) ) {
+ $this->queueManager->delete_data_from_queue($count);
+ }
+
+ if ( ! empty( $chunked ) ) {
+ $chunk_option_name = 'wpmdb_file_chunk_' . $state_data['migration_state_id'];
+
+ //chunking is complete, remove file(s) from queue and clean up the file chunk option
+ if ( (int) $chunked['bytes_offset'] === $file['size'] ) {
+ delete_site_option( $chunk_option_name );
+ $file['chunking_done'] = true;
+
+ $this->queueManager->delete_data_from_queue($count);
+ } else {
+ // Record chunk data to DB for next iteration
+ update_site_option( $chunk_option_name, $chunk_data );
+ }
+ }
+
+ $result['fallback_payload_size'] = $fallback_payload_size;
+
+ if ($this->canUseHighPerformanceTransfers($high_performance_transfers, $force_performance_transfers)) {
+ $result['current_payload_size'] = $this->size_controller->get_current_size();
+ $result['reached_max_payload_size'] = $this->size_controller->is_at_max_size();
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param array $processed
+ * @param array $state_data
+ * @param string $remote_url
+ *
+ * @return array
+ */
+ public function handle_pull($processed, $state_data, $remote_url)
+ {
+ $transfer_max = $this->util->get_transfer_bottleneck();
+ $actual_bottleneck = $state_data['site_details']['local']['max_request_size'];
+ $high_performance_transfers = $state_data['site_details']['local']['high_performance_transfers'];
+ $force_performance_transfers = isset($state_data['forceHighPerformanceTransfers']) ? $state_data['forceHighPerformanceTransfers'] : false;
+ $force_performance_transfers = apply_filters('wpmdb_force_high_performance_transfers', $force_performance_transfers, $state_data);
+
+ $bottleneck = apply_filters('wpmdb_transfers_pull_bottleneck',
+ $actual_bottleneck); //Use slider value
+ $fallback_payload_size = 2500000;
+
+ $bottleneck = $this->maybeUseHighPerformanceTransfers($bottleneck, $high_performance_transfers, $force_performance_transfers, $transfer_max,
+ $fallback_payload_size, $state_data);
+
+ $batch = [];
+ $total_size = 0;
+ $count = 0;
+
+ // Assign bottleneck to state data so remote can use it when assembling the payload
+ $state_data['bottleneck'] = $bottleneck;
+
+ foreach ($processed as $key => $file) {
+ if ($file['size'] > $bottleneck) {
+ $batch[] = $file;
+ break;
+ }
+
+ $batch[] = $file;
+ $count++;
+
+ $total_size += $file['size'];
+ }
+
+ $stage = $state_data['stage'];
+ $key = $stage === 'media_files' ? 'mf' : 'tp';
+
+ try {
+ list($resp, $meta) = $this->request_batch(base64_encode(str_rot13(json_encode($batch))), $state_data, "wpmdb{$key}_transfers_send_file", $remote_url);
+ } catch (\Exception $e) {
+ $this->util->catch_general_error($e->getMessage());
+ }
+
+ //Delete data from queue
+ $this->queueManager->delete_data_from_queue($meta['count']);
+
+ $total_sent = 0;
+
+ foreach ($meta['sent'] as $sent) {
+ $total_sent += $sent['size'];
+ }
+
+ // Convert 'file data' to 'folder data', as that's how the UI/Client displays progress
+ $result = $this->util->process_queue_data($meta['sent'], $state_data, $total_sent);
+
+ $result['fallback_payload_size'] = $fallback_payload_size;
+
+ if ($this->canUseHighPerformanceTransfers($high_performance_transfers, $force_performance_transfers)) {
+ $result['current_payload_size'] = $this->size_controller->get_current_size();
+ $result['reached_max_payload_size'] = $this->size_controller->is_at_max_size();
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param string $payload
+ * @param array $state_data
+ * @param string $action
+ * @param string $remote_url
+ *
+ * @return \Requests_Response
+ * @throws \Exception
+ */
+ public function post($payload, $state_data, $action, $remote_url)
+ {
+ $sig_data = [
+ 'action' => $action,
+ 'remote_state_id' => $state_data['migration_state_id'],
+ 'intent' => $state_data['intent'],
+ 'stage' => $state_data['stage'],
+ ];
+
+ $state_data['sig'] = $this->http_helper->create_signature($sig_data, $state_data['key']);
+
+ $state_data['action'] = $action;
+ $state_data['remote_state_id'] = $state_data['migration_state_id'];
+ $ajax_url = trailingslashit($remote_url) . 'wp-admin/admin-ajax.php';
+
+ $response = $this->sender->post_payload($payload, $state_data, $ajax_url);
+
+ $decoded = json_decode($response->body);
+
+ if (isset($decoded->wpmdb_error)) {
+ throw new \Exception($decoded->msg);
+ }
+
+ if (isset($decoded->success) && $decoded->success === false) {
+ return $this->http->end_ajax(new \WP_Error('wpmdb_transfer_failed', $decoded->data));
+ }
+
+ // Returns response directly
+ return $response;
+ }
+
+ /**
+ * @param string $batch
+ * @param array $state_data
+ * @param string $action
+ * @param string $remote_url
+ *
+ * @return array
+ * @throws \Exception
+ */
+ public function request_batch($batch, $state_data, $action, $remote_url)
+ {
+ $data = [
+ 'action' => $action,
+ 'intent' => $state_data['intent'],
+ 'stage' => $state_data['stage'],
+ 'bottleneck' => $state_data['bottleneck'],
+ ];
+
+ $sig_data = $data;
+ $data['sig'] = $this->http_helper->create_signature($sig_data, $state_data['key']);
+ $ajax_url = trailingslashit($remote_url) . 'wp-admin/admin-ajax.php';
+ $data['batch'] = $batch;
+
+ try {
+ $response = $this->receiver->send_request($data, $ajax_url);
+ } catch (\Exception $e) {
+ $this->util->catch_general_error($e->getMessage());
+ }
+
+ return $this->receiver->receive_stream_batch($response, $state_data);
+ }
+
+ /**
+ * @param array $sent
+ * @param array $chunked
+ *
+ * @return array
+ */
+ public function process_sent_data_push($sent, $chunked)
+ {
+ $total_sent = 0;
+ $filtered = [];
+
+ foreach ($sent as $files_sent) {
+ $item_size = $files_sent['size'];
+
+ if (isset($chunked['chunked']) && $chunked['chunked']) {
+ $item_size = $chunked['chunk_size'];
+ }
+
+ $total_sent += $item_size;
+ $files_sent['chunk_size'] = $item_size;
+ $filtered[] = $files_sent;
+ }
+
+ return array($total_sent, $filtered);
+ }
+
+ /**
+ * @param array $state_data
+ * @param string $remote_url
+ * @param resource $handle
+ *
+ * @return \Requests_Response
+ */
+ public function attempt_post($state_data, $remote_url, $handle)
+ {
+ rewind($handle);
+ $stage = $state_data['stage'];
+ $key = $stage === 'media_files' ? 'mf' : 'tp';
+
+ try {
+ $transfer_status = $this->post($handle, $state_data, "wpmdb{$key}_transfers_receive_file", $remote_url);
+ } catch (\Exception $e) {
+ $this->util->catch_general_error($e->getMessage());
+ }
+
+ return $transfer_status;
+ }
+
+ /**
+ * Calculates the high performance mode bottleneck size.
+ *
+ * @param array $state_data
+ *
+ * @return int
+ */
+ private function calculatePayLoadSize($state_data)
+ {
+ if ($state_data['stabilizePayloadSize'] !== true) {
+
+ if ($state_data['stepDownSize'] === true) {
+ return $this->size_controller->step_down_size($state_data['retries']);
+ }
+
+ return $this->size_controller->step_up_size();
+ }
+
+ return $this->size_controller->get_current_size();
+ }
+
+ /**
+ * If high performance mode can be used for current migration, the bottleneck value will be modified.
+ * Otherwise, the passed bottleneck will be returned unmodified.
+ *
+ * @param int $bottleneck
+ * @param bool $high_performance_transfers
+ * @param bool $force_performance_transfers
+ * @param int $transfer_max
+ * @param int $fallback
+ * @param array $state_data
+ *
+ * @return int
+ */
+ private function maybeUseHighPerformanceTransfers(
+ $bottleneck,
+ $high_performance_transfers,
+ $force_performance_transfers,
+ $transfer_max,
+ $fallback,
+ $state_data
+ ) {
+ if ($this->canUseHighPerformanceTransfers($high_performance_transfers, $force_performance_transfers)) {
+ $this->size_controller->initialize($transfer_max, $fallback, isset($state_data['payloadSize']) ? $state_data['payloadSize'] : null);
+ $bottleneck = apply_filters('wpmdb_high_performance_transfers_bottleneck',
+ $this->calculatePayLoadSize($state_data), $state_data['intent']);
+ }
+
+ return $bottleneck;
+ }
+
+ /**
+ * Checks if high performance mode can be enabled for current migration.
+ *
+ * @param bool $high_performance_transfers
+ * @param bool $force_performance_transfers
+ * @return bool
+ */
+ private function canUseHighPerformanceTransfers($high_performance_transfers, $force_performance_transfers)
+ {
+ return (true === $high_performance_transfers || true === $force_performance_transfers) && ! $this->dynamic_props->doing_cli_migration;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Receiver.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Receiver.php
new file mode 100644
index 000000000..306b6d85e
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Receiver.php
@@ -0,0 +1,157 @@
+wpmdb_settings = $settings->get_settings();
+ $this->util = $util;
+ $this->payload = $payload;
+ $this->error_log = $error_log;
+ $this->filesystem = $filesystem;
+ }
+
+ /**
+ * Set tmpfile class property to a stream handle to download payloads to
+ */
+ public function set_tmp_file() {
+ $this->tmpfile = apply_filters( 'wpmdb_transfers_stream_handle', tmpfile() );
+ }
+
+ /**
+ *
+ * Create a stream resource in the php://memory stream
+ *
+ * @param $content
+ *
+ * @return bool|resource
+ */
+ public static function create_memory_stream( $content ) {
+ $stream = fopen( 'php://memory', 'rb+' );
+ stream_copy_to_stream( $content, $stream );
+ rewind( $stream );
+
+ return $stream;
+ }
+
+ /**
+ * @param $data
+ * @param $url
+ *
+ * @return \Requests_Response
+ * @throws \Exception
+ */
+ public function send_request( $data, $url ) {
+ $requests_options = $this->util->get_requests_options();
+
+ $this->set_tmp_file();
+
+ // @TODO if other Requests hooks on 'curl.before_send' are invoked, this won't get called
+ $hooks = new \Requests_Hooks();
+ $hooks->register( 'curl.before_send', function ( $handle ) {
+ curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, 0 );
+ curl_setopt( $handle, CURLOPT_TIMEOUT, 0 );
+
+ $stream = $this->tmpfile;
+
+ // Save payload directly to tmp file to get around memory limits
+ curl_setopt( $handle, CURLOPT_FILE, $stream );
+ } );
+
+ $requests_options['hooks'] = $hooks;
+
+ $options = apply_filters( 'wpmdb_transfers_requests_options', $requests_options );
+
+ $compressed = false;
+
+ try {
+ $response = \Requests::post( $url, array(), $data, $options );
+ } catch ( \Exception $e ) {
+ $this->util->catch_general_error( $e->getMessage() );
+ }
+
+ // Use Requests interface to get response information
+ $code = $response->status_code;
+
+ // @TODO handle 500 error on remote
+ if ( 200 !== $code ) {
+ throw new \Exception( sprintf( __( 'Remote server responded with %s and body of %s', 'wp-migrate-db' ), $code, $response->body ) );
+ }
+
+ return $response;
+ }
+
+ /**
+ * @param $state_data
+ * @param $content
+ *
+ * @return bool
+ * @throws \Exception
+ */
+
+ public function receive_post_data( $state_data, $content ) {
+ return $this->payload->process_payload( $state_data, $content );
+ }
+
+ /**
+ *
+ * Grabs the payload received from a remote (on a pull) and sends it to be processed
+ *
+ * @param $response
+ * @param $state_data
+ *
+ * @return array
+ * @throws \Exception
+ */
+ public function receive_stream_batch( $response, $state_data ) {
+ $decoded_response = json_decode( $response->body );
+
+ if ( isset( $decoded_response->wpmdb_error ) ) {
+ throw new \Exception( $decoded_response->msg );
+ }
+
+ $handle = $this->tmpfile;
+
+
+ rewind( $handle );
+
+ $meta = $this->payload->process_payload( $state_data, $handle, true );
+
+ if ( ! $meta ) {
+ throw new \Exception( __( 'Unable to process payload.', 'wp-migrate-db' ) );
+ }
+
+ fclose( $handle );
+
+ return array( $response, $meta );
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Sender.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Sender.php
new file mode 100644
index 000000000..5493a7253
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/Transfers/Sender.php
@@ -0,0 +1,117 @@
+util = $util;
+ $this->payload = $payload;
+ }
+
+ /**
+ * HTTP POST payload to remote site
+ *
+ * @param string $payload
+ * @param string $url
+ *
+ * @return \Requests_Response
+ */
+ public function post_payload( $payload, $state_data, $url = '' ) {
+ $requests_options = $this->util->get_requests_options();
+ $options = apply_filters( 'wpmdb_transfers_requests_options', $requests_options );
+
+ //Prepare to send file as a stream
+ $meta_data = stream_get_meta_data($payload);
+ $filename = $meta_data["uri"];
+
+ //Encode state data as json to prevent issues with CURL
+ $payload_data = [
+ 'state_data' => base64_encode(json_encode($state_data)),
+ 'action' => $state_data['action']
+ ];
+ //Attach the payload to the request as an octet stream to be received in $_FILES
+ $payload_data['content'] = new \CURLFile($filename, 'application/octet-stream', 'payload');
+
+ $hooks = new \Requests_Hooks();
+ $hooks->register( 'curl.before_send', function ( $handle ) use ($payload_data) {
+ curl_setopt($handle, CURLOPT_POSTFIELDS, $payload_data);
+ } );
+
+ $options['hooks'] = $hooks;
+
+ //Set WPE Cookie if it exists
+ $remote_cookie = Persistence::getRemoteWPECookie();
+ if (false !== $remote_cookie) {
+ $options['cookies'] = [
+ 'wpe-auth' => $remote_cookie
+ ];
+ }
+
+ return \Requests::post( $url, array(), null, $options );
+ }
+
+ /**
+ * @param $state_data
+ *
+ * @return bool
+ * @throws \Exception
+ */
+ public function respond_to_send_file( $state_data ) {
+ if ( ! isset( $_POST['batch'] ) ) {
+ throw new \Exception( __( '$_POST[\'batch\'] is empty.', 'wp-migrate-db' ) );
+ }
+
+ $batch = filter_var( $_POST['batch'], FILTER_SANITIZE_FULL_SPECIAL_CHARS );
+ $batch = json_decode( str_rot13( base64_decode( $batch ) ), true );
+
+ if ( ! $batch || ! \is_array( $batch ) ) {
+ throw new \Exception( __( 'Request for batch of files failed.', 'wp-migrate-db' ) );
+ }
+
+ $handle = $this->payload->create_payload( $batch, $state_data, $state_data['bottleneck'] );
+ rewind( $handle );
+
+
+ // Read payload line by line and send each line to the output buffer
+ while ( ! feof( $handle ) ) {
+ $buffer = fread( $handle, 10 * 10 * 10000 );
+ echo $buffer;
+
+ @ob_flush();
+ flush();
+ }
+
+ fclose( $handle );
+ exit;
+ }
+
+ protected function print_end() {
+ echo "\n" . static::$end_sig;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/UI/Template.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/UI/Template.php
new file mode 100644
index 000000000..b5fb5355d
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/UI/Template.php
@@ -0,0 +1,181 @@
+notice = $notice;
+ $this->form_data = $form_data;
+
+
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->addon = $addon;
+ $this->plugin_manager = $plugin_manager;
+
+ // Insert backups tab into plugin_tabs array
+ array_splice($this->plugin_tabs, 1, 0, [
+ [
+ 'slug' => 'backups',
+ 'title' => _x('Backups', 'Get backups', 'wp-migrate-db'),
+ ],
+ ]);
+ }
+
+ public function register()
+ {
+ $this->license = WPMDBDI::getInstance()->get(License::class);
+ // templating actions
+ add_filter('wpmdb_notification_strings', [$this, 'notifications']);
+
+ $accepted_fields = $this->form_data->get_accepted_fields();
+ $accepted_fields = array_diff($accepted_fields, ['exclude_post_revisions']);
+ $this->form_data->set_accepted_fields($accepted_fields);
+ }
+
+ function notifications($notifications)
+ {
+ $notice_id = 'outdated_addons_warning';
+ $notice_links = $this->notice->check_notice($notice_id);
+
+ if (!$notice_links) {
+ return;
+ }
+
+ foreach ($this->addon->getAddons() as $addon_basename => $addon) {
+ if (false == $this->addon->is_addon_outdated($addon_basename) || false == is_plugin_active($addon_basename)) {
+ continue;
+ }
+ $update_url = wp_nonce_url(network_admin_url('update.php?action=upgrade-plugin&plugin=' . urlencode($addon_basename)), 'upgrade-plugin_' . $addon_basename);
+ $addon_slug = current(explode('/', $addon_basename));
+ if (isset($GLOBALS['wpmdb_meta'][$addon_slug]['version'])) {
+ $version = ' (' . $GLOBALS['wpmdb_meta'][$addon_slug]['version'] . ')';
+ } else {
+ $version = '';
+ }
+
+ $installed_version = $GLOBALS['wpmdb_meta'][$this->props->plugin_slug]['version'];
+ $is_core_beta = BetaManager::is_beta_version($installed_version);
+
+ // Maybe prompt user to disable addons if beta doesn't support them
+ if ($is_core_beta) {
+ $url = add_query_arg(array(
+ 'action' => 'deactivate',
+ 'plugin' => $addon_basename,
+ 'paged' => 1,
+ ), network_admin_url('plugins.php'));
+
+ $url = add_query_arg('_wpnonce', wp_create_nonce('deactivate-plugin_' . $addon_basename), $url);
+
+ $msg = 'Update Required — ' .
+ sprintf(__('The version of the %1$s addon you have installed%2$s is out-of-date and will not work with this beta version WP Migrate. There may be a
beta update available, otherwise please
deactivate this addon.', 'wp-migrate-db'), $addon['name'], $version, $update_url, $url) . '
';
+ } else {
+ $msg = 'Update Required — ' .
+ sprintf(__('The version of the %1$s addon you have installed%2$s is out-of-date and will not work with this version WP Migrate.
Update Now', 'wp-migrate-db'), $addon['name'], $version, $update_url) . '
';
+ }
+
+ // @TODO enable these notifications once Addons are released on 2.0 branch
+
+// $notifications[$addon_basename] = [
+// 'message' => $msg,
+// 'link' => false,
+// 'id' => $addon_basename,
+// ];
+ }
+
+ $secret_key_notice_id = 'secret_key_warning';
+
+ $secret_key_links = $this->notice->check_notice($secret_key_notice_id, true, 604800);
+
+ if (!$secret_key_links) {
+ return $notifications;
+ }
+
+ // Only show the warning if the key is 32 characters in length
+ if (strlen($this->settings['key']) <= 32) {
+ $notifications[$secret_key_notice_id] = [
+ 'message' => $this->template_to_string('secret-key-warning', 'pro', $secret_key_links),
+ 'link' => $secret_key_links,
+ 'id' => $secret_key_notice_id,
+ ];
+ }
+
+ if (!defined('WP_HTTP_BLOCK_EXTERNAL') || !WP_HTTP_BLOCK_EXTERNAL) {
+ return $notifications;
+ }
+
+ $notice_id = 'block_external_warning';
+
+ $notice_links = $this->notice->check_notice($notice_id, true, 604800);
+
+ if (!$notice_links) {
+ return $notifications;
+ }
+
+ $notifications[$notice_id] = [
+ 'message' => $this->template_to_string('block-external-warning', 'pro', $notice_links),
+ 'link' => $notice_links,
+ 'id' => $notice_id,
+ ];
+
+ return $notifications;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/UsageTracking.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/UsageTracking.php
new file mode 100644
index 000000000..321fa951d
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/UsageTracking.php
@@ -0,0 +1,414 @@
+state_container = $state_data_container;
+ $this->settings = $settings->get_settings();
+ $this->props = $properties;
+ $this->filesystem = $filesystem;
+ $this->error_log = $error_log;
+ $this->template = $template;
+ $this->dynamic_props = DynamicProperties::getInstance();
+ $this->form_data = $form_data;
+ $this->migration_state_manager = $migration_state_manager;
+ $this->http = $http;
+ $this->http_helper = $http_helper;
+
+ $this->api_url = apply_filters('wpmdb_logging_endpoint_url', 'https://api2.deliciousbrains.com');
+ $this->rest_API_server = $rest_API_server;
+ }
+
+ /**
+ * Adds/updates the `wpmdb_usage` option with most recent 'qualified' plugin use,
+ * stores time as well as the action (push/pull/export/find-replace)
+ *
+ * @param string $action
+ */
+ public static function log_usage($action = '')
+ {
+ update_site_option('wpmdb_usage', array('action' => $action, 'time' => time()));
+ }
+
+ /**
+ * Gets just the timestamp of the latest usage to send with the API requests
+ *
+ * @return int
+ */
+ public function get_last_usage_time()
+ {
+ $option = get_site_option('wpmdb_usage');
+
+ return ($option && $option['time']) ? $option['time'] : 0;
+ }
+
+ public function register()
+ {
+ add_action('wpmdb_additional_settings_advanced', array($this, 'template_toggle_usage_tracking'));
+
+ // REST endpoints
+ add_action('rest_api_init', [$this, 'register_rest_routes']);
+ add_action('wp_ajax_nopriv_wpmdb_track_migration_complete', [$this, 'log_migration_complete']);
+ add_action('wp_ajax_wpmdb_track_migration_complete', [$this, 'log_migration_complete']);
+ add_action('wpmdb_cancellation', [$this, 'log_migration_cancellation'], 50);
+ add_action('wpmdb_error_migration', [$this, 'log_migration_error'], 10, 1);
+ add_filter('wpmdb_notification_strings', [$this, 'template_notice_enable_usage_tracking']);
+ add_action('wpmdb_after_finalize_migration', [$this, 'log_migration_complete']);
+ }
+
+ public function register_rest_routes()
+ {
+ $this->rest_API_server->registerRestRoute(
+ '/log-migration',
+ [
+ 'methods' => 'POST',
+ 'callback' => [$this, 'log_migration_event'],
+ ]
+ );
+
+ }
+
+ /**
+ * Send migration update to usage DB
+ *
+ * @param string $status complete|error|cancelled
+ * @param array $data
+ * @throws error
+ **/
+ public function send_migration_update($status = 'complete', $data = [])
+ {
+ if ('complete' === $status && isset($_POST['intent']) && in_array($_POST['intent'], ['savefile', 'backup_local'])) {
+ $this->http->check_ajax_referer('flush');
+ }
+
+ $key_rules = array(
+ 'complete' => 'bool',
+ 'migration_id' => 'string',
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data($key_rules);
+
+ $existing_state_data = Persistence::getStateData();
+ $state_data += $existing_state_data;
+ //if not logged in
+ $filtered_post = $this->http_helper->filter_post_elements(
+ $state_data,
+ array(
+ 'action',
+ 'migration_state_id',
+ )
+ );
+
+ $settings = $this->settings;
+
+ if (true !== $settings['allow_tracking']) {
+ return false;
+ }
+
+ $migration_guid = isset($state_data['migration_id']) ? $state_data['migration_id'] : '';
+ if ($migration_guid === '') {
+ $form_data = json_decode($state_data['form_data']);
+ $migration_guid = $form_data->current_migration->migration_id;
+ }
+
+ $log_data = [
+ 'migration_complete_time' => time(),
+ 'migration_guid' => $migration_guid,
+ 'migration_status' => $status,
+ 'last_stage' => isset($state_data['stage']) ? $state_data['stage'] : null,
+ 'error_text' => isset($data['error_text']) ? $data['error_text'] : null
+ ];
+
+ $remote_post_args = array(
+ 'timeout' => 60,
+ 'method' => 'POST',
+ 'headers' => array('Content-Type' => 'application/json'),
+ 'body' => json_encode($log_data),
+ 'reject_unsafe_urls' => false,
+ );
+
+ $api_url = $this->api_url . '/complete';
+
+ $result = wp_remote_post($api_url, $remote_post_args);
+
+ if (is_wp_error($result) || $result['response']['code'] >= 400) {
+ if (defined('WP_DEBUG') && WP_DEBUG) {
+ error_log('Error logging migration event');
+ error_log(print_r($result, true));
+ }
+ $this->error_log->log_error('Error logging Migration event', $result);
+ $this->http->end_ajax(json_encode($result));
+ }
+
+ $this->http->end_ajax($result['body']);
+ }
+
+ /**
+ * Log migration cacellation
+ *
+ * Fires on 'wpmdb_track_migration_complete' hook
+ *
+ **/
+ public function log_migration_complete()
+ {
+ $this->send_migration_update('complete');
+ }
+
+ /**
+ * Log migration cacellation
+ *
+ * Fires on 'wpmdb_cancellation' hook
+ *
+ **/
+ public function log_migration_cancellation()
+ {
+ $this->send_migration_update('cancelled');
+ }
+
+ /**
+ * Logs migration error
+ *
+ * Fires on 'wpmdb_error_migration' hook
+ *
+ **/
+ public function log_migration_error($error_text)
+ {
+ $this->send_migration_update('error', ['error_text' => $error_text]);
+ }
+
+ /**
+ * Log Migration Event
+ *
+ * Callback for log-migration endpoint called at start of Migration
+ *
+ * @return void
+ */
+ public function log_migration_event()
+ {
+ $_POST = $this->http_helper->convert_json_body_to_post();
+ $license = WPMDBDI::getInstance()->get(License::class);
+
+ $license_key = $license->get_licence_key();
+ if (empty($license_key)) {
+ return;
+ }
+
+ $key_rules = array(
+ 'complete' => 'bool',
+ 'migration_id' => 'string',
+ );
+
+ $state_data = $this->migration_state_manager->set_post_data($key_rules);
+
+ $existing_state_data = Persistence::getStateData();
+ $state_data += $existing_state_data;
+
+ $settings = $this->settings;
+ do_action('wpmdb_log_migration_event', $state_data);
+ if (true !== $settings['allow_tracking']) {
+ return false;
+ }
+
+ $api_url = $this->api_url . '/event';
+ $cookie = false === Persistence::getRemoteWPECookie() ? 0 : 1;
+ $log_data = array(
+ 'local_timestamp' => time(),
+ 'licence_key' => $license_key,
+ 'cli' => $this->dynamic_props->doing_cli_migration,
+ 'setting-compatibility_plugin_installed' => $this->filesystem->file_exists($this->props->mu_plugin_dest),
+ 'remote_cookie' => $cookie,
+ 'local_platform' => $state_data['site_details']['local']['platform'],
+ 'remote_platform' => $state_data['site_details']['remote']['platform'],
+ );
+
+
+ // ***+=== @TODO - revisit usage of parse_migration_form_data
+ foreach ($this->form_data->parse_and_save_migration_form_data($state_data['form_data']) as $key => $val) {
+ if ('connection_info' === $key) {
+ continue;
+ }
+ $log_data['profile-' . $key] = $val;
+ }
+
+ foreach ($settings as $key => $val) {
+ if ('profiles' === $key || 'key' === $key) {
+ continue;
+ }
+ $log_data['setting-' . $key] = $val;
+ }
+
+ foreach ($GLOBALS['wpmdb_meta'] as $plugin => $arr) {
+ $log_data[$plugin . '-active'] = true;
+ $log_data[$plugin . '-version'] = $arr['version'];
+ }
+
+ foreach ($state_data['site_details'] as $site => $info) {
+ $log_data[$site . '-site_url'] = $info['site_url'];
+ $log_data[$site . '-home_url'] = $info['home_url'];
+ $log_data[$site . '-prefix'] = $info['prefix'];
+
+ $log_data[$site . '-is_multisite'] = $info['is_multisite'];
+
+ if (isset($info['subsites']) && is_array($info['subsites'])) {
+ $log_data[$site . '-subsite_count'] = count($info['subsites']);
+ }
+
+ $log_data[$site . '-is_subdomain_install'] = $info['is_subdomain_install'];
+ }
+
+ $diagnostic_log = [];
+
+ foreach ($this->error_log->get_diagnostic_info() as $group_name => $data) {
+ foreach ($data as $key => $val) {
+ if (0 === $key) {
+ continue 1;
+ }
+ $key_name = $group_name;
+ if (is_string($key)) {
+ $key_name .= "-{$key}";
+ }
+ $diagnostic_log[$key_name] = $val;
+ }
+ }
+
+ $log_data['diagnostic_log'] = $diagnostic_log;
+
+ foreach ($log_data as $key => $val) {
+ if (strpos($key, 'count') !== false || is_array($val)) {
+ continue;
+ }
+ if ('1' === $val) {
+ $log_data[$key] = true;
+ continue;
+ }
+ if ('0' === $val) {
+ $log_data[$key] = false;
+ continue;
+ }
+ if ('true' === $val) {
+ $log_data[$key] = true;
+ continue;
+ }
+ if ('false' === $val) {
+ $log_data[$key] = false;
+ continue;
+ }
+ }
+
+ $log_data['migration_guid'] = $state_data['migration_id'];
+
+ $remote_post_args = array(
+ 'timeout' => 60,
+ 'method' => 'POST',
+ 'headers' => array('Content-Type' => 'application/json'),
+ 'body' => json_encode($log_data),
+ 'reject_unsafe_urls' => false,
+ );
+
+ $result = wp_remote_post($api_url, $remote_post_args);
+ if (is_wp_error($result) || $result['response']['code'] >= 400) {
+ if (defined('WP_DEBUG') && WP_DEBUG) {
+ error_log('Error logging migration event');
+ error_log(print_r($result, true));
+ }
+ $this->error_log->log_error('Error logging Migration event', $result);
+ $this->http->end_ajax(json_encode($result));
+ }
+
+ $this->http->end_ajax($result['body']);
+ }
+
+ public function template_notice_enable_usage_tracking($notifications)
+ {
+ if (!is_bool($this->settings['allow_tracking'])) {
+ $notifications['notice-enable-usage-tracking'] = [
+ 'message' => $this->template->template_to_string('notice-enable-usage-tracking', 'pro'),
+ 'link' => false,
+ 'id' => 'notice-enable-usage-tracking',
+ 'custom_link' => 'usage_tracking',
+ ];
+ }
+
+ return $notifications;
+ }
+
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/Pro/WPMigrateDBPro.php b/wp-content/plugins/wp-migrate-db-pro/class/Pro/WPMigrateDBPro.php
new file mode 100644
index 000000000..4fafa5f97
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/Pro/WPMigrateDBPro.php
@@ -0,0 +1,28 @@
+register();
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/SetupProviders.php b/wp-content/plugins/wp-migrate-db-pro/class/SetupProviders.php
new file mode 100644
index 000000000..b799a9565
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/SetupProviders.php
@@ -0,0 +1,52 @@
+maybeAddProvider( $class );
+ }
+
+ if ( !empty( $this->providers ) ) {
+ $classes = $this->classes;
+ foreach ( $this->providers as $provider ) {
+ $vars = get_object_vars( $provider );
+ foreach ( $vars as $prop => $var ) {
+ if ( !\in_array( $var, $classes, true ) ) {
+ $classes[$prop] = $var;
+ }
+ }
+ }
+
+ $this->classes = $classes;
+ }
+ }
+
+ function maybeAddProvider( $class )
+ {
+ if ( class_exists( $class ) ) {
+ $this->providers[] = new $class;
+ }
+ }
+
+}
+
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/WPMDBDI.php b/wp-content/plugins/wp-migrate-db-pro/class/WPMDBDI.php
new file mode 100644
index 000000000..e301f2804
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/WPMDBDI.php
@@ -0,0 +1,27 @@
+addDefinitions(__DIR__ . '/WPMDBDI_Config.php');
+ $containerBuilder->useAutowiring(false);
+ $container = $containerBuilder->build();
+
+ return $container;
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/WPMDBDI_Config.php b/wp-content/plugins/wp-migrate-db-pro/class/WPMDBDI_Config.php
new file mode 100644
index 000000000..4a9891d94
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/WPMDBDI_Config.php
@@ -0,0 +1,38 @@
+setup($is_pro);
+
+$classes = [];
+
+if ($providers !== null) {
+ foreach ($providers->classes as $key => $class) {
+ if ($class === null) {
+ continue;
+ }
+ // Access by classname ex. Properties::class
+ $classes[get_class($class)] = function () use ($class) {
+ return $class;
+ };
+ // Access by 'shorthand' ex. 'properties'
+ $classes[$key] = function () use ($class) {
+ return $class;
+ };
+ }
+}
+
+if ($is_pro) {
+ DeliciousBrains\WPMDB\Pro\Compatibility\Layers\Addons\Addons::substitute_classes($classes);
+}
+
+if (!empty($classes)) {
+ return $classes;
+}
+
+throw new Exception(__("Classmap could not be generated.", 'wp-migrate-db'));
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/WPMigrateDB.php b/wp-content/plugins/wp-migrate-db-pro/class/WPMigrateDB.php
new file mode 100644
index 000000000..11dbf6d0a
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/WPMigrateDB.php
@@ -0,0 +1,82 @@
+props = $container->get(Properties::class);
+
+ $this->util = $container->get(Util::class);
+ $this->profile_manager = $container->get(ProfileManager::class);
+ $this->flush = $container->get(Flush::class);
+ $this->backup_export = $container->get(BackupExport::class);
+ $this->compatibility_manager = $container->get(CompatibilityManager::class);
+ $this->settings_manager = $container->get(SettingsManager::class);
+ $this->assets = $container->get(Assets::class);
+
+ add_action('init', array($this, 'loadPluginTextDomain'));
+ // For Firefox extend "Cache-Control" header to include 'no-store' so that refresh after migration doesn't override JS set values.
+ add_filter('nocache_headers', array($this->util, 'nocache_headers'));
+
+ $this->profile_manager->register();
+ $this->backup_export->register();
+ $this->compatibility_manager->register();
+ $this->settings_manager->register();
+ $this->assets->register();
+ $this->flush->register();
+ }
+
+ public function loadPluginTextDomain()
+ {
+ load_plugin_textdomain('wp-migrate-db', false, dirname(plugin_basename($this->props->plugin_file_path)) . '/languages/');
+ }
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/class/autoload.php b/wp-content/plugins/wp-migrate-db-pro/class/autoload.php
new file mode 100644
index 000000000..153e8f3d6
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/class/autoload.php
@@ -0,0 +1,30 @@
+=' ) ) {
+ return;
+}
+
+$GLOBALS['wpmdb_compatibility']['active'] = true;
+
+if ( defined( 'WP_PLUGIN_DIR' ) ) {
+ $plugins_dir = trailingslashit( WP_PLUGIN_DIR );
+
+} else if ( defined( 'WPMU_PLUGIN_DIR' ) ) {
+ $plugins_dir = trailingslashit( WPMU_PLUGIN_DIR );
+
+} else if ( defined( 'WP_CONTENT_DIR' ) ) {
+ $plugins_dir = trailingslashit( WP_CONTENT_DIR ) . 'plugins/';
+
+} else {
+ $plugins_dir = plugin_dir_path( __FILE__ ) . '../plugins/';
+}
+
+$compat_class_path = 'class/Common/Compatibility/Compatibility.php';
+$compat_class_name = 'DeliciousBrains\WPMDB\Common\Compatibility\Compatibility';
+$wpmdbpro_compatibility_class = $plugins_dir . 'wp-migrate-db-pro/' . $compat_class_path;
+$wpmdb_compatibility_class = $plugins_dir . 'wp-migrate-db/' . $compat_class_path;
+
+if ( file_exists( $wpmdbpro_compatibility_class ) ) {
+ include_once $wpmdbpro_compatibility_class;
+} elseif ( file_exists( $wpmdb_compatibility_class ) ) {
+ include_once $wpmdb_compatibility_class;
+}
+
+if ( class_exists( $compat_class_name ) ) {
+ $compatibility = new $compat_class_name;
+ $compatibility->register();
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/compatibility/wp-migrate-db-pro-compatibility.php-e b/wp-content/plugins/wp-migrate-db-pro/compatibility/wp-migrate-db-pro-compatibility.php-e
new file mode 100644
index 000000000..01e113e1d
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/compatibility/wp-migrate-db-pro-compatibility.php-e
@@ -0,0 +1,46 @@
+=' ) ) {
+ return;
+}
+
+$GLOBALS['wpmdb_compatibility']['active'] = true;
+
+if ( defined( 'WP_PLUGIN_DIR' ) ) {
+ $plugins_dir = trailingslashit( WP_PLUGIN_DIR );
+
+} else if ( defined( 'WPMU_PLUGIN_DIR' ) ) {
+ $plugins_dir = trailingslashit( WPMU_PLUGIN_DIR );
+
+} else if ( defined( 'WP_CONTENT_DIR' ) ) {
+ $plugins_dir = trailingslashit( WP_CONTENT_DIR ) . 'plugins/';
+
+} else {
+ $plugins_dir = plugin_dir_path( __FILE__ ) . '../plugins/';
+}
+
+$compat_class_path = 'class/Common/Compatibility/Compatibility.php';
+$compat_class_name = 'DeliciousBrains\WPMDB\Common\Compatibility\Compatibility';
+$wpmdbpro_compatibility_class = $plugins_dir . 'wp-migrate-db-pro/' . $compat_class_path;
+$wpmdb_compatibility_class = $plugins_dir . 'wp-migrate-db/' . $compat_class_path;
+
+if ( file_exists( $wpmdbpro_compatibility_class ) ) {
+ include_once $wpmdbpro_compatibility_class;
+} elseif ( file_exists( $wpmdb_compatibility_class ) ) {
+ include_once $wpmdb_compatibility_class;
+}
+
+if ( class_exists( $compat_class_name ) ) {
+ $compatibility = new $compat_class_name;
+ $compatibility->register();
+}
diff --git a/wp-content/plugins/wp-migrate-db-pro/frontend/build/asset-manifest.json b/wp-content/plugins/wp-migrate-db-pro/frontend/build/asset-manifest.json
new file mode 100644
index 000000000..81f2842c1
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/frontend/build/asset-manifest.json
@@ -0,0 +1,70 @@
+{
+ "main.js": "./static/js/main.542cc9adb3fa.js",
+ "wpmdb-runtime.js": "./static/js/wpmdb-runtime.390ece295331.js",
+ "static/js/879.fcb218c0be59.chunk.js": "./static/js/879.fcb218c0be59.chunk.js",
+ "static/js/965.124ef43232e7.chunk.js": "./static/js/965.124ef43232e7.chunk.js",
+ "static/js/613.1f032ee72087.chunk.js": "./static/js/613.1f032ee72087.chunk.js",
+ "static/js/406.64efee064556.chunk.js": "./static/js/406.64efee064556.chunk.js",
+ "static/js/537.8c9507b1c32b.chunk.js": "./static/js/537.8c9507b1c32b.chunk.js",
+ "static/js/605.86acde37a857.chunk.js": "./static/js/605.86acde37a857.chunk.js",
+ "styles.css": "./static/css/styles.d4d5e222.css",
+ "styles.js": "./static/js/styles.cea332651d4d.js",
+ "static/js/288.52a1e53f645f.js": "./static/js/288.52a1e53f645f.js",
+ "static/js/358.abfefc366410.chunk.js": "./static/js/358.abfefc366410.chunk.js",
+ "static/js/135.d717e42327db.chunk.js": "./static/js/135.d717e42327db.chunk.js",
+ "static/media/wp-migrate-2-6-0.png": "./static/media/wp-migrate-2-6-0.8d26599e.png",
+ "static/media/testimonial-avatar.png": "./static/media/testimonial-avatar.309cd834.png",
+ "static/media/mdb-branding-transparent.svg": "./static/media/mdb-branding-transparent.edbb2b6f.svg",
+ "static/media/mdb-medallion.svg": "./static/media/mdb-medallion.451f212d.svg",
+ "static/media/local.svg": "./static/media/local.8a615e13.svg",
+ "static/media/mdb-banner.svg": "./static/media/mdb-banner.afefa48f.svg",
+ "static/media/addons-multisitetools.svg": "./static/media/addons-multisitetools.34c15ecc.svg",
+ "static/media/subsite.svg": "./static/media/subsite.426375ab.svg",
+ "static/media/multisite.svg": "./static/media/multisite.b8c0ef24.svg",
+ "static/media/save-profile-btn.svg": "./static/media/save-profile-btn.cebaf8c1.svg",
+ "static/media/oval-question.svg": "./static/media/oval-question.93e3f70e.svg",
+ "static/media/search-replace-casesensitive.svg": "./static/media/search-replace-casesensitive.874c1930.svg",
+ "static/media/status-error.svg": "./static/media/status-error.26cffd9f.svg",
+ "static/media/rating-star-active.svg": "./static/media/rating-star-active.45821ac6.svg",
+ "static/media/addons-theme-plugins-files.svg": "./static/media/addons-theme-plugins-files.cc98a300.svg",
+ "static/media/twitter.svg": "./static/media/twitter.0843a854.svg",
+ "static/media/oval-close.svg": "./static/media/oval-close.55372175.svg",
+ "static/media/search-replace-arrow.svg": "./static/media/search-replace-arrow.1795aa05.svg",
+ "static/media/license-checked.svg": "./static/media/license-checked.44034878.svg",
+ "static/media/singlesite.svg": "./static/media/singlesite.e8b6fea0.svg",
+ "static/media/arrow.svg": "./static/media/arrow.c76607c1.svg",
+ "static/media/info.svg": "./static/media/info.6957118a.svg",
+ "static/media/search-replace-regex.svg": "./static/media/search-replace-regex.3cf90f8f.svg",
+ "static/media/close.svg": "./static/media/close.265fcfb5.svg",
+ "static/media/question.svg": "./static/media/question.c7be2b1a.svg",
+ "static/media/video.svg": "./static/media/video.753d44eb.svg",
+ "static/media/addons-other-files.svg": "./static/media/addons-other-files.eace2a2c.svg",
+ "static/media/action-locked.svg": "./static/media/action-locked.524d8e4b.svg",
+ "static/media/success.svg": "./static/media/success.27b62f11.svg",
+ "static/media/calendar.svg": "./static/media/calendar.2c8b6ac1.svg",
+ "static/media/license-checked-blue.svg": "./static/media/license-checked-blue.c304fe20.svg",
+ "static/media/backups-none.svg": "./static/media/backups-none.031287fd.svg",
+ "static/media/action-backup-database.svg": "./static/media/action-backup-database.c3c73850.svg",
+ "static/media/downCircle.svg": "./static/media/downCircle.eb822116.svg",
+ "static/media/upCircle.svg": "./static/media/upCircle.01eb07ce.svg",
+ "static/media/warning.svg": "./static/media/warning.57362294.svg",
+ "static/media/error.svg": "./static/media/error.58f4992a.svg",
+ "static/media/equalCircle.svg": "./static/media/equalCircle.0d086278.svg",
+ "static/media/addons-plugin-files.svg": "./static/media/addons-plugin-files.0d01933a.svg",
+ "static/media/add-white.svg": "./static/media/add-white.972fe87d.svg",
+ "static/media/action-import-database.svg": "./static/media/action-import-database.40875856.svg",
+ "static/media/action-export-database.svg": "./static/media/action-export-database.52b56dcc.svg",
+ "static/media/migration-failed.svg": "./static/media/migration-failed.5471ea63.svg",
+ "static/media/danger.svg": "./static/media/danger.e1678f77.svg",
+ "static/media/addons-cli.svg": "./static/media/addons-cli.acc2852c.svg",
+ "static/media/progress-spinner.svg": "./static/media/progress-spinner.121400ac.svg",
+ "static/media/search-replace.svg": "./static/media/search-replace.92ae08a9.svg",
+ "static/media/addons-mediafiles.svg": "./static/media/addons-mediafiles.ecedfa39.svg",
+ "static/media/action-push.svg": "./static/media/action-push.ebccd3b6.svg",
+ "static/media/action-pull.svg": "./static/media/action-pull.cb4ccd00.svg",
+ "static/media/action-search-replace.svg": "./static/media/action-search-replace.9c9bf81f.svg",
+ "static/media/add.svg": "./static/media/add.0e409332.svg",
+ "static/media/plusCircle.svg": "./static/media/plusCircle.940ad8be.svg",
+ "static/media/check-circular.svg": "./static/media/check-circular.02c190e9.svg",
+ "static/media/progress-section-check.svg": "./static/media/progress-section-check.2e9bfc60.svg"
+}
\ No newline at end of file
diff --git a/wp-content/plugins/wp-migrate-db-pro/frontend/build/index.html b/wp-content/plugins/wp-migrate-db-pro/frontend/build/index.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/wp-content/plugins/wp-migrate-db-pro/frontend/build/noop.css b/wp-content/plugins/wp-migrate-db-pro/frontend/build/noop.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/wp-content/plugins/wp-migrate-db-pro/frontend/build/noop.js b/wp-content/plugins/wp-migrate-db-pro/frontend/build/noop.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/wp-content/plugins/wp-migrate-db-pro/frontend/build/static/css/styles.d4d5e222.css b/wp-content/plugins/wp-migrate-db-pro/frontend/build/static/css/styles.d4d5e222.css
new file mode 100644
index 000000000..a12fb8bb1
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/frontend/build/static/css/styles.d4d5e222.css
@@ -0,0 +1,9 @@
+.wpmdb{min-width:800px}.wpmdb a{cursor:pointer;margin:0;text-decoration:none}.wpmdb a.docs-link{text-decoration:underline}.wpmdb a.docs-link:hover{text-decoration:none}.wpmdb .header-wrap{background:#a5ddf1}.wpmdb .nav-wrap{background:#fff;border-bottom:1px solid #d6d6d6;margin-bottom:2.8rem}.wpmdb .wrapper{margin-left:30px;max-width:1280px;min-width:920px}@media(max-width:1500px){.wpmdb .wrapper{margin:0 30px}}.wpmdb a.hover{text-decoration:underline}.wpmdb a,.wpmdb a.link{border-radius:4px}.wpmdb a.link:focus-visible,.wpmdb a:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb button{background:none;border:0;cursor:pointer;margin:0;padding:0}.wpmdb button.link{border-radius:4px}.wpmdb button.link:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .btn-no-outline{outline:0}.wpmdb .full-opacity{opacity:1!important}.wpmdb .margin-bottom{margin-bottom:1rem}.wpmdb .align-right{margin-left:auto}.wpmdb .align-center{margin:0 auto}.wpmdb .text-center{text-align:center}.wpmdb .uppercase{text-transform:uppercase}.wpmdb label,.wpmdb p{font-size:13px;font-weight:400;line-height:1.8}.wpmdb h1,.wpmdb h2,.wpmdb h3,.wpmdb h4,.wpmdb h5,.wpmdb label,.wpmdb p{letter-spacing:.2px}.wpmdb ul{margin:0}.wpmdb label{margin-left:.5rem}.wpmdb li,.wpmdb ul{padding:0}.wpmdb li{list-style:none;margin-bottom:0!important;margin-bottom:0;margin-top:.25rem}.wpmdb .pos-relative{position:relative}.wpmdb .regular{font-weight:400}.wpmdb .semibold{font-weight:600}.wpmdb .bold{font-weight:700}.wpmdb .underline{text-decoration:underline}.wpmdb h1,.wpmdb h2,.wpmdb h3,.wpmdb h4,.wpmdb h5{color:#04223f}.wpmdb h1{font-size:1.3125rem;font-weight:500;padding:0}.wpmdb h2{font-size:1rem;font-weight:600}.wpmdb h3{font-size:.88rem;font-weight:600}.wpmdb h4{font-size:.95rem}.wpmdb h5{color:#000;font-size:.88rem;font-weight:600;opacity:.8;text-transform:uppercase}.wpmdb .icon-16{height:16px;width:16px}.wpmdb .icon-20{height:20px;width:20px}.wpmdb .icon-24{height:24px;width:24px}.wpmdb .icon-32{height:32px;width:32px}.wpmdb .icon-64{height:64px;width:64px}.wpmdb .icon-128{height:128px;width:128px}.wpmdb .width-10{width:10%}.wpmdb input[type=checkbox]{box-shadow:inset 0 0 0 #fff;cursor:pointer;margin-right:.5rem;margin-top:-2px}.wpmdb input[type=checkbox]:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .panel-header-wrap input[type=checkbox]{margin-top:0}.wpmdb .checkbox-default{border-radius:2px!important;margin-top:0!important}.wpmdb .consolas{font-family:Consolas,monaco,monospace}.wpmdb input[type=text]{line-height:normal;min-height:inherit;padding:12px 10px}.wpmdb input[type=text],.wpmdb textarea{background:#fff;border:1px solid #d6d6d6;border-radius:5px;box-sizing:border-box;font-size:.8rem}.wpmdb textarea{padding:.6rem;width:22rem}.wpmdb select{background-position:calc(100% - 14px);border:1px solid #d6d6d6;box-shadow:0 1px 1px 0 rgba(0,0,0,.1);font-size:13px;height:2.75rem;padding-left:16px;width:23rem}.wpmdb .container-shadow{background:#fff;border:1px solid #d6d6d6;border-radius:4px;box-shadow:0 2px 8px 0 rgba(0,0,0,.05),0 2px 1px 0 rgba(0,0,0,.05)}.wpmdb .slider-switch-bg{background:#d6d6d6;border-radius:10rem;height:.25rem;width:25rem}.wpmdb .slider-switch-active{background:#236de7;border-radius:10rem;height:.25rem;width:5rem}.wpmdb .slider-switch{background:#236de7;border-radius:10rem;height:1rem;margin-top:-1.25rem;position:absolute;width:1rem}.wpmdb .link{border-radius:4px;color:#0073aa;cursor:pointer;font-size:.88rem;font-weight:600;opacity:1;text-decoration:underline}.wpmdb .link:hover{text-decoration:none}.wpmdb .bg-black{background:#000}.wpmdb .text-black{color:#000}.wpmdb .bg-white{background:#fff}.wpmdb .text-white{color:#fff}.wpmdb .text-primary{color:#236de7}.wpmdb .bg-primary{background:#236de7}.wpmdb .text-brand-dark{color:#04223f}.wpmdb .bg-brand-dark{background:#04223f}.wpmdb .text-brand-light{color:#a5ddf1}.wpmdb .bg-brand-light{background:#a5ddf1}.wpmdb .db-accordion-expanded{background:#45719b}.wpmdb .bg-success{background:#53aa59}.wpmdb .text-error{color:#dc3232}.wpmdb .bg-error{background:#cf2a27}.wpmdb .bg-alert{background:#ffba00}.wpmdb .text-grey-light{color:#d6d6d6}.wpmdb .text-grey-dark{color:#999}.wpmdb .bg-grey-dark{background:#999}.wpmdb .bg-trans{background:transparent!important}.wpmdb .bg-grey-light{background:#d6d6d6}.wpmdb .grid-container{display:grid}.wpmdb .flex-container{-ms-box-orient:horizontal;align-items:center;display:-moz-flex;display:flex;list-style:none}.wpmdb .column{flex-direction:column}.wpmdb .flex-align-baseline{align-items:baseline}.wpmdb .wrap{flex-wrap:wrap}.wpmdb .header,.wpmdb .padded-container{background-position:top;background-repeat:no-repeat;background-size:38%;padding:1.3rem 0}.wpmdb .header .btn{border-radius:4px}.wpmdb .medallion{-webkit-filter:drop-shadow(0 1px 4px rgba(0,0,0,.2));filter:drop-shadow(0 1px 4px rgba(0,0,0,.2));height:52px;margin-right:1rem;width:52px}.wpmdb .license-message{flex-grow:0;flex-shrink:0;margin-left:auto}.wpmdb .license-message.flex-grow{flex-basis:auto}.wpmdb .license-message .license-icon{position:relative;top:7px}.wpmdb .license-message .license-level{font-size:13px;text-align:right}.wpmdb .license-message .license-level a{font-weight:500;text-decoration:underline}.wpmdb .license-message .license-level a:hover{text-decoration:none}.wpmdb .license-message .license-level a:focus-visible{box-shadow:0 0 0 2px #a5ddf1,0 0 0 4px #4f8aec}.wpmdb .license-message p{font-size:13px;margin:0}.wpmdb .license-block{grid-gap:26px;align-items:center;display:flex;gap:26px;justify-items:flex-end}.wpmdb .license-block.active-license{display:block;flex-grow:0}.wpmdb .license-block.active-license .license-status span{padding:0}.wpmdb .license-block .user-label{color:#314248;font-size:13px;font-weight:400;padding-right:0}.wpmdb .license-block .license-status{font-size:.75rem;font-weight:600;margin:0;opacity:1;position:relative;top:-3px}.wpmdb .license-block .license-status .license-icon{left:-2px}.wpmdb .license-block .license-status span{display:inline-block}.wpmdb .license-block .license-status span.license-label{color:#236de7;display:inline}.wpmdb .license-block .license-status.valid-licence span{padding-right:.4rem}.wpmdb .license-block .license-expired{text-align:right}.wpmdb .license-block .license-expired span{padding:0}.wpmdb .license-block .btn{display:block;padding:10px 20px}.wpmdb .license-block .license-info{position:relative;top:-3px}.wpmdb .license{text-decoration:underline}.wpmdb .nav{align-items:baseline;font-size:.75rem;font-weight:500;padding:1.25rem 0 0;text-transform:uppercase}.wpmdb .nav li{margin-right:1.7rem;padding:.6rem .2rem .4rem}.wpmdb .nav-item-active{border-bottom:3px solid #000}.wpmdb .nav li a{color:#707070}.wpmdb .nav li a:focus{box-shadow:none}.wpmdb .nav li a:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .nav-item-active a{color:#04223f!important}.wpmdb .subnav{align-items:baseline;border-bottom:1px solid #bbb;padding:0 1.5rem}.wpmdb .subnav li{font-size:.94rem;font-weight:600;margin-right:2rem;opacity:.4;padding-bottom:1rem;padding-left:.5rem;padding-right:.5rem}.wpmdb .subnav-item-active{border-bottom:3px solid #236de7;opacity:1!important}.wpmdb .subnav li a{color:#000}.wpmdb .subnav-item-active a{color:#236de7!important}.wpmdb .accordion-collapsed{padding:.5rem 1.5rem}.wpmdb .accordion-label-container{width:10rem}.wpmdb .accordion-locked{border:1px solid #999;border-radius:4px;opacity:.5;padding:.5rem 1.5rem}.wpmdb .locked-accordion-link{color:rgba(0,0,0,.8)}.wpmdb .accordion-icon-locked{height:20px;margin-right:1rem;opacity:1!important;width:20px}.wpmdb .accordion-locked h2,.wpmdb .accordion-locked p{opacity:1}.wpmdb .accordion-wrap{min-width:70rem}.wpmdb .accordion-expand{border-radius:0 0 4px 4px!important;justify-content:space-between;margin-top:-.15rem;padding:1rem 1rem 2.5rem}.wpmdb .nested-accordion{background:#fff;border:1px solid #d6d6d6;border-radius:3px;box-shadow:0 1px 4px 0 rgba(0,0,0,.05),0 1px 1px 0 rgba(0,0,0,.05);cursor:pointer;flex-grow:1;margin:1.25rem .6rem 0;min-height:3.5rem;padding:0 1.25rem}.wpmdb .nested-accordion-arrow{margin-right:1rem}.wpmdb .nested-accordion-title{color:#000;font-size:.88rem;font-weight:600;margin:0;opacity:.7}.wpmdb .nested-accordion-preview{margin:.1rem 0 0;opacity:.6}.wpmdb .nested-accordion-preview-divider{margin:0 .5rem;opacity:.3}.wpmdb .accordion-checkbox{border-radius:2px!important;margin-right:1rem!important}.wpmdb .nested-accordion-expanded{background:#45719b;border-radius:4px 4px 0 0;box-shadow:0 -2px 1px 0 rgba(0,0,0,.05),0 -1px 1px 0 rgba(0,0,0,.05)}.wpmdb .nested-accordion-expanded-body{border-radius:0 0 4px 4px;cursor:inherit;margin:-.1rem .6rem;min-height:1rem;padding:1.5rem 1.25rem}.wpmdb .nested-accordion-arrow-expanded{fill:#fff;margin-right:1rem;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.wpmdb .nested-accordion-expanded-title{color:#fff;font-size:.88rem;font-weight:700;margin:0}.wpmdb .accordion-divider{height:2.1rem;margin-right:2rem;width:1px}.wpmdb .accordion-preview{font-size:.8rem;font-weight:400;opacity:.6}.wpmdb .btn{background-color:#236de7;border:1px solid #236de7;border-radius:4px;color:#fff;cursor:pointer;font-size:.8rem;font-weight:700;letter-spacing:.05rem;padding:1.1rem 2.25rem;text-align:center;text-transform:uppercase;white-space:nowrap}.wpmdb .btn svg{position:relative;right:10px}.wpmdb .btn:hover{background-color:#397be9;border-color:#397be9;outline:0}.wpmdb .btn:active{background-color:#1f61cf;border-color:#1f61cf;outline:0}.wpmdb .btn:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .btn.delete{background:#dc3232;border:1px solid #dc3232;margin-left:0;padding-top:.4rem}.wpmdb .btn.delete:hover{background:#c1201d;border-color:#c1201d;outline:0}.wpmdb .btn.delete:active{background-color:#ac1714;border-color:#ac1714}.wpmdb .btn.delete:focus{box-shadow:0 0 0 2px #fff,0 0 1px 4px rgba(207,42,39,.6)}.wpmdb .btn-stroke{background:transparent;border:1px solid #236de7;box-shadow:none;color:#236de7;outline:0}.wpmdb .btn-stroke:hover{background:rgba(7,119,239,.15);outline:0}.wpmdb .btn-stroke:active{background:rgba(7,119,239,.3);outline:0}.wpmdb .btn-disabled,.wpmdb .btn-disabled:hover{background:transparent;border:1px solid #dadada;box-shadow:none;color:#dadada}.wpmdb .btn-disabled:hover{cursor:default}.wpmdb .btn-disabled:active{background:transparent;border:1px solid #dadada;box-shadow:none;color:#dadada}.wpmdb .btn-disabled:focus{box-shadow:none;outline:0}.wpmdb .btn-sm{min-width:6.5rem;padding:.65rem 1.5rem}.wpmdb .btn-tooltip,.wpmdb .btn-tooltip-stroke{font-weight:600;height:30px;letter-spacing:0;opacity:1;padding:.4rem .6rem .5rem;position:relative;text-transform:none}.wpmdb .btn-tooltip-stroke.icon,.wpmdb .btn-tooltip.icon{padding-left:20px}.wpmdb .btn-tooltip svg,.wpmdb .btn-tooltip-stroke svg{left:4px;position:absolute;top:5px}.wpmdb .btn-tooltip-stroke.save-profile,.wpmdb .btn-tooltip.save-profile{height:30px;margin-left:6px;padding:.4rem .7rem .5rem 1.6rem}.wpmdb .btn-tooltip-stroke.save-profile svg,.wpmdb .btn-tooltip.save-profile svg{left:6px;top:5px}.wpmdb .btn-tooltip{margin:0 .5rem}.wpmdb .btn-tooltip.delete{padding:.4rem .7rem .5rem 1.6rem}.wpmdb .btn-tooltip.profile-screen #el_0iWebJDPz{height:26px;width:24px}.wpmdb .btn-tooltip-icon{margin-right:.2rem}.wpmdb .btn-tooltip-stroke{background:transparent;border:1px solid #9a9a9a;border-radius:4px;box-shadow:none;color:#9a9a9a;cursor:pointer;font-size:.8rem;text-align:center}.wpmdb .btn-tooltip-stroke:hover{background:hsla(0,0%,57%,.15);border-color:#9a9a9a}.wpmdb .btn-tooltip-stroke:active{background:hsla(0,0%,57%,.3);border-color:#9a9a9a}.wpmdb .btn-tooltip-stroke:focus{box-shadow:0 0 0 2px #fff,0 0 1px 4px hsla(0,0%,57%,.6)}.wpmdb .btn-sm-icon{margin-left:5px;padding-right:.1rem}.wpmdb .notification{border-left:5px solid #ffba00;border-radius:3px;box-shadow:0 1px 8px 0 rgba(0,0,0,.05),0 1px 3px 0 rgba(0,0,0,.1);font-size:.8rem;margin:.75rem 0;padding:1rem 1.5rem}.wpmdb .notification.old-profile-notice{margin:0 0 1.1rem}.wpmdb .notification p{line-height:1.6}.wpmdb .notification strong:first-child{font-size:14px}.wpmdb .notification.h4{font-size:1rem}.wpmdb .notification.success-notice{border-left:5px solid #7ad03a}.wpmdb .notification.error-notice{border-left:5px solid #dc3232}.wpmdb .notification a{font-weight:600;text-decoration:underline}.wpmdb .notification a:hover{text-decoration:none}.wpmdb .notification ul{margin:initial;margin-left:30px;padding:initial}.wpmdb .notification li{list-style:initial;margin:initial;padding:6px 0}.wpmdb .import-panel .notification{margin:1rem 0}.wpmdb .connect-to-remote{grid-column-gap:.6rem;-webkit-column-gap:.6rem;column-gap:.6rem;display:grid;grid-template-columns:repeat(3,1fr)}.wpmdb .connect-to-remote .error-message{font-weight:500;margin-top:.8rem}.wpmdb .connect-to-remote .auth-form{grid-gap:5px;gap:5px;grid-row:2;grid-template-columns:repeat(2,1fr);margin-top:.4rem}.wpmdb .connect-to-remote .version-mismatch-error{font-weight:400}.wpmdb .connect-to-remote .version-mismatch-error .svg-spinner{top:-9px}.wpmdb .connect-to-remote textarea{margin-bottom:0;width:405px}.wpmdb .connect-to-remote .full{grid-column:1/4;margin-bottom:0}.wpmdb .connect-to-remote .notification{margin-top:1rem}@media screen and (max-width:900px){.wpmdb .connect-to-remote{grid-template-columns:repeat(2,1fr)}}.wpmdb .connect-panel.panel-body{padding:32px 40px}.wpmdb .connect-panel .btn{margin-top:.9rem}.wpmdb .connect-panel .connecting-message{margin-bottom:0}.wpmdb .global-notice{margin-bottom:1.25rem}.wpmdb .notification-content{line-height:1.5;max-width:44rem}.wpmdb .button-group{float:left;margin-right:1%;width:28.3333%}.wpmdb .button-group button p{margin:0 0 10px}.wpmdb .actions-panel .panel-header-wrap.has-summary-no-child.panel-open{grid-template-columns:1fr 6fr 0}.wpmdb .actions-panel .action-tooltip{max-width:200px}.wpmdb .action-buttons{padding:1rem 1.7rem .5rem 1.25rem}.wpmdb .action-buttons h4{font-size:.85rem;font-weight:500;margin-bottom:1.75rem;margin-top:0}@media(max-width:1350px){.wpmdb .action-buttons h4{font-size:.8rem}}.wpmdb .action-buttons .action-tooltip.wack:after,.wpmdb .action-buttons .action-tooltip.wack:before{left:20%!important}.wpmdb .migration-buttons{display:table;margin-top:30px}.wpmdb .migration-buttons .run-migration,.wpmdb .migration-buttons span{display:table-cell}.wpmdb .speech-bubble-left{background:#fff;border-radius:5px;max-width:10rem;padding:.75rem 1rem;position:relative}.wpmdb .speech-bubble-left p{margin-top:0}.wpmdb .speech-shadow{box-shadow:0 5px 24px 0 rgba(4,34,63,.16)}.wpmdb .speech-bubble-left:after{border:10px solid transparent;border-left:0;border-right-color:#fff;content:"";display:flex;height:0;left:0;margin-left:-.6rem;margin-top:-.6rem;position:absolute;top:50%;width:0}.wpmdb .speech-bubble-top{background:#fff;border-radius:3px;max-width:6rem;padding:.75rem 1rem;position:relative}.wpmdb .speech-bubble-top:after{border:10px solid transparent;border-left:0;border-right-color:#fff;content:"";display:flex;height:0;left:1.5rem;margin-top:-.9rem;position:absolute;top:0;-webkit-transform:rotate(90deg);transform:rotate(90deg);width:0}.wpmdb .backup-dir{color:#000;font-weight:500;margin-bottom:0;opacity:.5;padding-left:0}.wpmdb .tooltip-saved{align-items:center;background:#fff;border-radius:3px;box-shadow:0 1px 4px rgba(0,0,9,.12);display:flex;padding:.2rem .4rem}.wpmdb .tooltip-saved-icon{margin-right:.25rem}.wpmdb .tooltip-saved p{font-size:.8rem;font-weight:600;line-height:1}.wpmdb .arrow-search-replace{margin-top:.1rem;padding:0 .3rem}.wpmdb .search-replace-heading .td-1{flex-grow:10}.wpmdb .search-replace-heading .td-2{flex-grow:2.3}.wpmdb .search-replace-data .td-1,.wpmdb .search-replace-heading .td-3,.wpmdb .search-replace-heading .td-4{flex-grow:10}.wpmdb .search-replace-data .td-2{flex-grow:2}.wpmdb .search-replace-data .td-3{flex-grow:10}.wpmdb .custom-search .header-row{grid-template-columns:0 5fr .5fr 5fr .5fr;padding:10px 0}.wpmdb .custom-search .replace-old.disabled:hover{color:rgba(51,51,51,.5)}.wpmdb .custom-search-replace-wrapper tr{padding:.4rem 0}.wpmdb .custom-search-replace-heading .td-1{flex-grow:6}.wpmdb .custom-search-replace-heading .td-2{flex-grow:2.3}.wpmdb .custom-search-replace-heading .td-3{flex-grow:6}.wpmdb .custom-search-replace-heading .td-4{flex-grow:1}.wpmdb .custom-search-replace-data .td-1{flex-grow:.5}.wpmdb .custom-search-replace-data .td-2{flex-grow:6}.wpmdb .custom-search-replace-data .td-3{flex-grow:2}.wpmdb .custom-search-replace-data .td-4{flex-grow:6}.wpmdb .custom-search-replace-data .td-5{flex-grow:.5}.wpmdb tr:nth-child(2n){background:#f7f7f7}.wpmdb tr:nth-child(odd){background:#fff}.wpmdb .table{width:100%}.wpmdb .table td{padding:.5rem}.wpmdb .styled-check{margin-right:3px;position:relative;top:3px}.wpmdb .profile-header-arrow{margin:0 10px;opacity:.5;position:relative;top:4px;-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.wpmdb .shadow-div{background:#fff;border:1px solid #d6d6d6;border-radius:6px;box-shadow:0 1px 8px 0 rgba(0,0,0,.05),0 2px 1px 0 rgba(0,0,0,.03)}.wpmdb .backups-table .styled-check polyline,.wpmdb .profile-table .styled-check polyline{stroke:#fff}.wpmdb .backups-table tr.toggled,.wpmdb .profile-table tr.toggled{padding-bottom:0;padding-right:8px;padding-top:0}.wpmdb .backups-table td,.wpmdb .profile-table td{padding:.45rem}.wpmdb .backups-table td.date,.wpmdb .profile-table td.date{width:30%}.wpmdb .backups-table td .confirm-delete,.wpmdb .profile-table td .confirm-delete{left:.6rem;position:absolute;top:-2.8rem}.wpmdb .backups-table td.relative,.wpmdb .profile-table td.relative{padding:0;position:relative}.wpmdb .backups-table td:first-child,.wpmdb .profile-table td:first-child{overflow-wrap:break-word;padding-left:0}.wpmdb .backups-table td .delete-profile,.wpmdb .profile-table td .delete-profile{padding-right:0}.wpmdb .backups-table td:nth-child(4),.wpmdb .profile-table td:nth-child(4){padding-left:1.05rem}.wpmdb .table-heading{padding:.15rem 1.5rem}.wpmdb .table-divider-line{height:1px;width:100%}.wpmdb tr{padding:.4rem 1.25rem}.wpmdb table{width:100%}.wpmdb .table-col-date{flex-grow:4}.wpmdb .table-col-item{flex-grow:16}.wpmdb .table-col-action{flex-grow:1;margin-left:.5rem}.wpmdb .container-default{padding:1rem 1.25rem}.wpmdb .container-default,.wpmdb .container-table{background:#fff;border:1px solid #d6d6d6;border-radius:3px}.wpmdb .container-table{overflow:scroll}.wpmdb .container-table li{margin:.2rem!important;padding:.1rem 1rem}.wpmdb .container-table li:active,.wpmdb .container-table li:hover{background:#a5ddf1}.wpmdb .container-toggle{background:#fff;border:1px solid #d6d6d6;border-radius:3px;padding:1.25rem 1.5rem;width:20rem}.wpmdb .btn-container-toggle{cursor:pointer;text-align:left}.wpmdb .container-toggle-active{background:rgba(128,189,212,.2);border:1px solid #236de7}.wpmdb .migration-progress{background:#fff;border-radius:4px;box-shadow:0 2px 8px 0 rgba(0,0,0,.05),0 2px 1px 0 rgba(0,0,0,.05);box-sizing:border-box;display:flex;max-height:none;padding:40px;width:100%}@media(min-height:680px){.wpmdb .migration-progress{max-height:calc(100vh - 128px)}.wpmdb .migration-progress:not(:last-child){max-height:none}}@media(min-height:840px){.wpmdb .migration-progress:not(:last-child){max-height:calc(100vh - 280px)}}.wpmdb .migration-progress input[type=checkbox]{cursor:pointer;margin-right:.05rem;margin-top:-1px!important}.wpmdb .error-icon{margin-right:10px}.wpmdb .migration-title{font-weight:400;margin:.5rem 0 0;max-width:645px;overflow:hidden;padding:0;text-overflow:ellipsis;white-space:nowrap}.wpmdb .migration-title .error-icon{margin-right:15px}.wpmdb .migration-timer{-webkit-font-feature-settings:"tnum";font-feature-settings:"tnum";align-self:center;color:#707070;font-size:.88rem;font-variant-numeric:tabular-nums;letter-spacing:-.01rem;text-align:right}.wpmdb .migration-progress-steps{align-items:center;display:flex;font-size:.75rem;margin-right:1rem;margin-top:1.2rem}.wpmdb .migration-progress-steps span,.wpmdb .migration-progress-steps svg{margin-right:3px}.wpmdb .migration-progress-btn{clear:both;float:left;margin-right:.75rem;padding:.6rem 2.4rem}.wpmdb .migration-progress-controls{margin-top:3rem;width:100%}.wpmdb .migration-error .migration-percentage{color:#dc3232}.wpmdb .migration-percentage{-webkit-font-feature-settings:"tnum";font-feature-settings:"tnum";color:#236de7;font-size:1.7rem!important;font-variant-numeric:tabular-nums;font-weight:300;margin:.5rem .5rem 0 0!important}.wpmdb .migration-data-transferred{-webkit-font-feature-settings:"tnum";font-feature-settings:"tnum";color:#707070!important;font-size:.75rem;font-variant-numeric:tabular-nums;line-height:1;margin-bottom:.85rem!important;text-transform:uppercase}.wpmdb .migration-tables-searched{color:#333;font-weight:400;line-height:1;margin-bottom:.85rem!important;margin-left:50px}.wpmdb .migration-progress-bar{overflow:hidden;width:100%}.wpmdb .migration-progress-bar,.wpmdb .migration-progress-bar-running{border-radius:5rem;height:.5rem}.wpmdb .migration-error .migration-progress-bar-running{background:#dc3232}.wpmdb .migration-error .migration-timer{align-self:center;display:flex;margin-bottom:2rem}.wpmdb .migration-complete{width:100%}.wpmdb .migration-complete .migration-title{font-weight:500;margin-bottom:0;margin-top:1rem}.wpmdb .migration-complete .backup-path{display:block;margin-top:.6rem}.wpmdb .migration-complete .backup-path:hover{text-decoration:underline}.wpmdb .migration-progress-content{display:flex;flex-direction:column;width:100%}.wpmdb .migration-progress-content p{margin-bottom:2rem}.wpmdb .migration-progress-content .btn-sm{min-width:7.5rem}.wpmdb .preview-migration-heading{color:#707070;font-weight:400;margin-left:10px}.wpmdb .migration-complete-summary{margin-bottom:2rem;margin-top:0;text-align:center}.wpmdb .mdb-migration-error h4,.wpmdb .migration-complete-summary{font-size:.95rem!important;font-weight:400!important;line-height:1.5!important;opacity:1}.wpmdb .migration-complete-close-btn{margin:.5rem auto 0;padding-left:2rem;padding-right:2rem}.wpmdb .csv-download-btn,.wpmdb .migration-complete-close-btn{min-width:220px}.wpmdb .popup-close{cursor:pointer;height:16px;margin-right:-.8rem;margin-top:-.8rem}.wpmdb .popup-close:focus-visible{border-radius:4px;box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .twitter{height:24px;position:absolute;right:0;top:.8rem}.wpmdb .review-container{background:rgba(35,109,231,.05);border:1px solid #236de7;border-radius:4px;padding:1.25rem}.wpmdb .review-container-text{color:#236de7;font-size:1rem}.wpmdb .review-stars{cursor:pointer;margin-left:1rem}.wpmdb .migration-free-advert{max-width:56rem;padding:3rem 3.25rem}.wpmdb .migration-free-advert h2{margin-bottom:2rem}.wpmdb .migration-free-advert li{padding:.35rem 0}.wpmdb .migration-free-advert-icon{margin-right:.5rem}.wpmdb .migration-free-advert-btn{margin-top:2rem}.wpmdb .video-container{margin-top:1.1rem;padding:.65rem 1.5rem}.wpmdb .video-container:hover{cursor:pointer}.wpmdb .video-container .column:nth-child(2){margin-left:2rem}.wpmdb .video-container:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .video-container-icon{margin-right:2rem;margin-top:.25rem}.wpmdb .video-container h3{margin-bottom:.5rem}.wpmdb .tweet-testimonial{background:#04223f;border-radius:4px;margin-top:1rem;padding:1.5rem}.wpmdb .twitter-handle{margin-left:.5rem}.wpmdb .tweet-testimonial-text{margin-top:1rem}.wpmdb .avatar{height:3.5rem;margin-right:1.5rem;width:3.5rem}.wpmdb .sidebar{border-radius:4px;max-width:16rem}.wpmdb .sidebar-content{padding:1.25rem}.wpmdb .sidebar-graphic{width:100%}.wpmdb .sidebar h3{font-size:1.3rem;font-weight:400;margin-bottom:1rem}.wpmdb .sidebar-input-text{background:#fff;border:1px solid #d6d6d6;border-radius:3px;box-sizing:border-box;font-size:.8rem;margin-top:.5rem;padding:.4rem;width:100%}.wpmdb .coupon-text{margin-bottom:1rem}.wpmdb .sidebar-btn{font-weight:500;margin-top:1rem;padding:.6rem;text-align:center;text-transform:none;width:100%}.wpmdb .sidebar-smalltext{color:#000;font-size:.75rem;margin-bottom:1rem;margin-top:1.5rem;opacity:.5}.wpmdb .sidebar-tweet-testimonial{background:#04223f;padding:1rem}.wpmdb .sidebar-tweet-testmonial h5{font-size:1rem}.wpmdb .sidebar-tweet-text{color:#fff;font-size:.88rem;margin-bottom:.25rem;margin-top:1rem}.wpmdb .documentation-panel{padding:2rem}.wpmdb .documentation-panel li{padding:.35rem 0}.wpmdb .documentation-link{font-size:1rem}.wpmdb .custom-search-replace,.wpmdb .standard-search-replace{padding-top:.8rem}.wpmdb .custom-search-replace .row,.wpmdb .standard-search-replace .row{border-radius:5px}.wpmdb .custom-search-replace .row .header-row,.wpmdb .standard-search-replace .row .header-row{padding-top:0}.wpmdb .custom-search-replace p:last-of-type,.wpmdb .standard-search-replace p:last-of-type{margin-bottom:0}.wpmdb .__react_component_tooltip.type-dark a{color:#fff;text-decoration:underline}.wpmdb .action-tooltip{border-color:#d6d6d6!important;border-radius:5px;box-shadow:0 5px 24px 0 rgba(4,34,63,.16);color:#343434;font-weight:400;line-height:1.2rem;max-width:460px;opacity:1;padding:.75rem;position:fixed;z-index:99}.wpmdb .action-tooltip .dark{color:gray;font-weight:500}.wpmdb .action-tooltip.place-left:after,.wpmdb .action-tooltip.place-left:before{border-color:#d6d6d6!important}.wpmdb .action-tooltip.place-right{margin-left:25px}.wpmdb .action-tooltip.place-right:after,.wpmdb .action-tooltip.place-right:before,.wpmdb .action-tooltip.place-top:after,.wpmdb .action-tooltip.place-top:before{border-color:#d6d6d6!important}.wpmdb .action-tooltip.place-bottom{margin-top:30px}.wpmdb .action-tooltip.place-bottom:after,.wpmdb .action-tooltip.place-bottom:before{border-color:#d6d6d6!important}@media(max-width:700px){.wpmdb .action-tooltip.action-backup_local.place-bottom:after,.wpmdb .action-tooltip.action-backup_local.place-bottom:before{left:80%}}.wpmdb #welcome-wrap{box-shadow:0 1px 1px 0 rgba(0,0,0,.1);clear:both;height:170px;margin-top:20px}.wpmdb #welcome-wrap #welcome-img{float:left;height:170px;width:170px}.wpmdb #welcome-wrap .welcome-text{background:#fff;height:130px;padding:20px 25px 20px 195px}.wpmdb #welcome-wrap .welcome-text h3{font-size:22px;font-weight:lighter;margin:10px 0}.wpmdb #welcome-wrap .welcome-text p{font-size:15px;line-height:1.5}.wpmdb #welcome-wrap .welcome-text a{font-weight:700}.wpmdb input.readonly,.wpmdb input[readonly],.wpmdb textarea.readonly,.wpmdb textarea[readonly]{background-color:#e1e7ea;border-color:#c0cad1}.wpmdb .relative{position:relative}.wpmdb .sr-only{clip:rect(0,0,0,0);border:0;height:1px!important;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px!important}.wpmdb input[type=text]:focus,.wpmdb select:focus,.wpmdb textarea:focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;color:initial;outline:2px solid transparent}.wpmdb input[type=text]:hover,.wpmdb select:hover,.wpmdb textarea:hover{color:initial}.wpmdb .float-left{float:left}.wpmdb .float-right{float:right}.wpmdb .header-wrap>.wrapper{display:grid}.wpmdb .header-wrap .inline-message.warning,.wpmdb .header-wrap .notification-message.warning-notice{background-color:#fff;border-left:4px solid #ffba00}.wpmdb .header-wrap .notification{margin-top:1.25rem}.wpmdb .header-wrap .inline-message,.wpmdb .header-wrap .notification-message{border:0;border-radius:0;box-shadow:0 1px 1px 0 rgba(0,0,0,.1);font-size:13px;padding:10px 14px}.wpmdb .header-wrap .wpmdb div.below-title.warning,.wpmdb .header-wrap div.updated.warning{box-sizing:border-box;margin:10px 0}.wpmdb .child-panel .panel-summary,.wpmdb .muted,.wpmdb .panel-summary .panel-summary{padding-left:0}.wpmdb .collapsed-panel{margin-bottom:.8rem;padding:.75rem}.wpmdb .svg-spinner{left:2px;position:absolute;top:-2px}.wpmdb .error-btn{margin-top:50px}.wpmdb .mdb-migration-error h4{margin:2rem 0 2.5rem}.wpmdb .mdb-migration-error h4 span{color:#236de7;font-weight:600}.wpmdb .mdb-migration-error h4 span .black{color:#000}.wpmdb .mdb-migration-error .mdb-migration-error-message a{color:#80ddff;text-decoration:underline}.wpmdb .mdb-migration-error .mdb-migration-error-message a:hover{text-decoration:none}.wpmdb .mdb-migration-error .mdb-migration-error-message .error-block{background:#424141;border-radius:4px;color:#fff;margin-bottom:38px;max-height:200px;overflow:scroll;padding:15px}.wpmdb .wpmdb-profiles{display:initial}.wpmdb .wpmdb-profiles .new-migration{display:inline-block;line-height:normal;margin-top:30px;padding:12px 35px 16px}.wpmdb .wpmdb-profiles .new-migration svg{position:relative;top:3px}.wpmdb #import-file{clip:rect(0,0,0,0);border:0;height:1px;overflow:hidden;padding:0;position:absolute!important;white-space:nowrap;width:1px}.wpmdb #import-file+label{max-width:82px}.wpmdb .import-message input:focus-visible+label{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .import-message label{margin-left:0}.wpmdb .import-message .btn{padding:6px 20px}.wpmdb .import-message span{display:inline-block;margin-left:0;padding-left:1rem}.wpmdb .action-panel input[type=radio]:focus-visible,.wpmdb .child-panel input[type=radio]:focus-visible,.wpmdb .wpmdb-form input[type=radio]:focus-visible{border:1px solid #7e8993;box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .action-panel label,.wpmdb .child-panel label,.wpmdb .wpmdb-form label{margin-left:.25rem}.wpmdb .action-panel input[type=radio],.wpmdb .child-panel input[type=radio],.wpmdb .wpmdb-form input[type=radio]{height:16px;margin-top:0;min-width:auto;width:16px}.wpmdb .action-panel input[type=radio]:checked,.wpmdb .child-panel input[type=radio]:checked,.wpmdb .wpmdb-form input[type=radio]:checked{background:#236de7;border:none}.wpmdb .action-panel input[type=radio]:checked:before,.wpmdb .child-panel input[type=radio]:checked:before,.wpmdb .wpmdb-form input[type=radio]:checked:before{background-color:#fff;height:6px;left:5px;margin:0;position:relative;top:5px;width:6px}.wpmdb .action-panel input[type=checkbox],.wpmdb .child-panel input[type=checkbox],.wpmdb .wpmdb-form input[type=checkbox]{height:16px!important;width:16px!important}.wpmdb .action-panel input[type=checkbox]:disabled,.wpmdb .child-panel input[type=checkbox]:disabled,.wpmdb .wpmdb-form input[type=checkbox]:disabled{background:hsla(229,9%,75%,.3);cursor:not-allowed;opacity:1}.wpmdb .action-panel input[type=checkbox]:disabled+label,.wpmdb .child-panel input[type=checkbox]:disabled+label,.wpmdb .wpmdb-form input[type=checkbox]:disabled+label{color:#3f3f3f;cursor:not-allowed}.wpmdb .action-panel input[type=checkbox]:focus,.wpmdb .child-panel input[type=checkbox]:focus,.wpmdb .wpmdb-form input[type=checkbox]:focus{border:1px solid #7e8993}.wpmdb .action-panel input[type=checkbox]:checked,.wpmdb .child-panel input[type=checkbox]:checked,.wpmdb .wpmdb-form input[type=checkbox]:checked{background-color:#236de7;border:none;border-radius:4px}.wpmdb .action-panel input[type=checkbox]:checked:disabled,.wpmdb .child-panel input[type=checkbox]:checked:disabled,.wpmdb .wpmdb-form input[type=checkbox]:checked:disabled{background:#bbbdc6}.wpmdb .action-panel input[type=checkbox]:checked:before,.wpmdb .child-panel input[type=checkbox]:checked:before,.wpmdb .wpmdb-form input[type=checkbox]:checked:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgc3R5bGU9ImZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkIj48cGF0aCBkPSJNNCA4LjUgNy4yIDExIDEyIDUiIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmZmY7c3Ryb2tlLXdpZHRoOjJweCIvPjwvc3ZnPg==);height:16px!important;margin:initial;position:relative;width:16px!important}.wpmdb select option:checked{background-color:#a5ddf1!important}.wpmdb .table-size{color:gray;font-weight:500}.wpmdb .select-wrap{margin-top:16px}.wpmdb .select-wrap ul{padding:8px}.wpmdb .select-wrap ul li{align-items:center;border-radius:4px;display:flex;height:32px;margin:0}.wpmdb .select-wrap ul li:hover{background-color:#f7f7f7}.wpmdb .select-wrap ul li input{margin:0 .25rem 0 8px}.wpmdb .select-wrap ul li label{margin-left:.25rem;width:100%}.wpmdb .multiselect-options{margin-bottom:0}.wpmdb .multiselect-options button{border-radius:4px;color:#0073aa;display:inline-block;font-weight:500;margin-bottom:0;padding:0 .5rem;text-decoration:underline}.wpmdb .multiselect-options button:hover{text-decoration:none}.wpmdb .multiselect-options button:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .multiselect-options button:first-child{padding-left:0}.wpmdb .options-list{margin:.15rem 0}.wpmdb .options-list .dark{color:gray;font-weight:500}.wpmdb .options-list>div{margin:.4rem 0}.wpmdb .options-list>div:first-child{margin-top:0}.wpmdb .options-list>div:last-child{margin-bottom:0}.wpmdb .options-list small{display:block;margin:15px 0 0 10px}.wpmdb .question-mark{border-radius:50%}.wpmdb .question-mark:focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}@-webkit-keyframes spinner-animation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes spinner-animation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}.wpmdb #el_6X7lquFKkl{fill:#d6d6d6}.wpmdb .styled-spinner-wrap{-webkit-animation:spinner-animation .6s infinite;animation:spinner-animation .6s infinite;-webkit-animation-play-state:running;animation-play-state:running;-webkit-animation-timing-function:cubic-bezier(.42,.44,.74,.74);animation-timing-function:cubic-bezier(.42,.44,.74,.74);display:inline-block;height:1.2rem;width:1.2rem}.wpmdb .styled-spinner-wrap.paused{-webkit-animation-play-state:paused;animation-play-state:paused}.wpmdb .red{color:#dc3232}.wpmdb .migration-message{color:#000;font-weight:600;opacity:.6;padding:8px 0}.wpmdb .notification .licence-action{margin-top:.8rem}.wpmdb .beta-badge{background:#236de7;border-radius:5px;color:#fff;font-size:11px;margin-left:8px;padding:1px 10px;text-transform:uppercase}.wpmdb .high-performance-setting{margin-top:1rem}.wpmdb .fs-stats-container{align-items:flex-start;background:#f8f8f8;border:1px solid #eee;border-radius:4px;display:flex;flex-direction:row;font-size:11px;font-weight:400;justify-content:space-evenly;line-height:11px;margin-top:20px;text-align:left}.wpmdb .fs-stats-container .fs-stats{align-items:flex-start;border-right:1px solid #eee;display:flex;flex-direction:column;flex-grow:1;padding:16px 0 16px 24px;text-align:left}.wpmdb .fs-stats-container .fs-stats:first-child{padding-left:16px}.wpmdb .fs-stats-container .fs-stats .fs-stat-value{font-size:12.5px;font-weight:500;line-height:12.5px;margin-top:6px}.wp-admin .wpmdb .compatibility-mode-block select[multiple]{height:7rem}.settings-node-enter{opacity:.1}.settings-node-enter-active{opacity:1;transition:opacity .8s}.settings-node-exit{opacity:1}.settings-node-exit-active{opacity:0;transition:opacity .45s}.btn.settings-node-exit-active{display:none}.settings-spinner{left:2px;position:absolute;top:-2px}.settings-spinner.relative{height:22px;left:5px;position:relative;top:4px;width:22px}.settings-spinner #el_6X7lquFKkl{fill:#236de7}.wrap{margin:0}#wpcontent{padding-left:0}.wp-core-ui select[multiple]{padding:2px}option{letter-spacing:-.015rem;padding:3px}.panel-summary{line-height:1.5!important;padding-left:2rem}.version-mismatch-error button{display:inline!important;font-size:inherit!important}.version-mismatch-error .blue-check{display:inline;margin-top:-2px;position:absolute}.remote-update-spinner{position:absolute!important}@media screen and (max-width:782px){.auto-fold #wpcontent{padding-left:0}}.__react_component_tooltip.show{opacity:1!important}.license-notification-spinner{left:2px;position:absolute;top:-10px}.modal-container{max-width:960px;padding-bottom:64px;width:100%}#wpfooter{font-style:italic}.profiles-list{grid-gap:.5rem;grid-column-gap:2rem;display:grid;grid-column:1/3;grid-row:3;grid-template-columns:repeat(2,1fr)}.profiles-list tr.disabled{opacity:.5}.profiles-list tr.disabled .link:hover{cursor:default;text-decoration:underline}.profiles-list .first-col{grid-column:1}.profiles-list .first-col h3{border-bottom:1px solid #000}.profiles-list .second-col{grid-column:2}.profiles-list .second-col .header{border-bottom:1px solid #000;display:grid;grid-template-columns:repeat(2,1fr)}.profiles-list .second-col .header h3{margin-bottom:0}.profiles-list .second-col .header a{align-self:end;justify-self:end;padding-bottom:.5rem}.profiles-list .first-col,.profiles-list .second-col{padding:0 .5rem .5rem}.profiles-list .no-items-wrap.one-profile{align-self:center}.profiles-list .no-items-wrap .no-items{color:#999;padding:1rem 1.5rem}.profiles-list .table{display:grid;grid-template-rows:55px 0 1.3fr;padding-bottom:0}.profiles-list input[type=text]{display:flex;font-size:inherit;justify-self:start;margin:0 18px 0 0;max-width:382px;padding:10px;width:70%}.profiles-list .button-wrap{align-items:center;display:flex;height:40px}.profiles-list .action-btn.in-progress{opacity:.5}.profiles-list .action-btn.in-progress:hover{cursor:not-allowed}.profiles-list .action-btn.btn-success{display:flex;height:80%}.profiles-list .action-btn.btn-success svg{flex:1 1;top:-1px}.profiles-list .profile-table.one-profile{align-self:center}.profiles-list .profile-table .toggled td.table-col-action{padding:3px .4rem}.migrate-notice{grid-gap:16px;border-radius:4px;border-style:solid;border-width:1px;display:grid;font-size:13px;gap:16px;grid-column:1;grid-column-end:-1;grid-template-columns:16px 1fr;margin-bottom:16px;margin-top:16px;padding:10px 16px}.migrate-notice svg{margin-top:2px}.migrate-notice-content :first-child{margin-top:0;padding-top:0}.migrate-notice-content :last-child{margin-bottom:0;padding-bottom:0}.migrate-notice.danger{background:#fef2f1;border-color:#dc3232;color:#b12006}.migrate-notice.info{background:#edf6ff;border-color:#0968ff;color:#3b5ed5}.migrate-notice.success{background:#ecfdf5;border-color:#46b450;color:#196049}.migrate-notice.warning{background:#fffbea;border-color:#ffb92b;color:#ab5a19}.local-callout{grid-gap:24px;align-items:center;background-color:#e4f0e7;border-color:#51bb7b;display:flex;gap:24px;padding:18px 24px}.local-callout .local-logo{flex-basis:94px;flex-grow:0;flex-shrink:0}.local-callout .callout-content{grid-gap:5px;color:#282a2c;display:flex;flex-grow:1;flex-wrap:wrap;gap:5px;line-height:1.2;margin:0}.local-callout .callout-content .question{font-weight:700}.local-callout .btn-stroke{border-color:#267048;color:#267048}.local-callout .btn-stroke:hover{background-color:#add9b8;border-color:#267048}.wpmdb .dry-run-results-table{background-color:#f7f7f7;border-collapse:collapse;display:flex;flex:1 1;flex-direction:column;margin:1.5rem 0;overflow:hidden;position:relative}.wpmdb .dry-run-results-table thead{border-bottom:1px solid #d6d6d6}.wpmdb .dry-run-results-table tbody{overflow-y:auto}.wpmdb .dry-run-results-table a.upgrade{color:#236de7;font-weight:700;text-transform:uppercase}.wpmdb .dry-run-results-table .flex-container{flex-wrap:nowrap;height:25px}.wpmdb .dry-run-results-table .flex-container :first-child{flex:0 0 40%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wpmdb .dry-run-results-table .flex-container :last-child{-webkit-font-feature-settings:"tnum";font-feature-settings:"tnum";font-variant-numeric:tabular-nums}.wpmdb .dry-run-results-table .flex-container :not(:first-child){text-align:right;width:100%}.wpmdb .dry-run-results-table .flex-container .table-heading{color:#707070;padding:.5rem}.wpmdb .dry-run-results-table .result-item .table-results-button{-webkit-font-feature-settings:"tnum";font-feature-settings:"tnum";border-radius:4px;color:#0473aa;font-variant-numeric:tabular-nums;font-weight:600;text-decoration:underline}.wpmdb .dry-run-results-table .result-item .table-results-button:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .dry-run-results-table .result-item .table-name{font-weight:500}.wpmdb .dry-run-results-table .action-tooltip{max-width:15rem!important;text-align:left!important}.wpmdb .dry-run-results-table .waiting-dash{color:#707070}.wpmdb .dry-run-results-table th{color:#9fa0a7;font-size:1em;font-weight:500;position:-webkit-sticky;position:sticky;text-align:left;text-transform:uppercase;top:0}.wpmdb .dry-run-results-single-table{border:1px solid #d6d6d6;border-radius:5px;margin-top:1.5rem;overflow-y:scroll}.wpmdb .dry-run-results-single-table:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .dry-run-results-single-table li{border-bottom:1px solid #d6d6d6;margin:0;padding:15px}.wpmdb .dry-run-results-single-table del{background-color:#ffe6e6;color:#950202;text-decoration:line-through}.wpmdb .dry-run-results-single-table ins{background-color:#daf6da;color:#258d26;text-decoration:none}.wpmdb .dry-run-results-single-table code{background-color:#f7f7f7;border-radius:4px;display:block;line-height:1.5;margin:0;overflow-wrap:anywhere;padding:10px 15px}.wpmdb .dry-run-results-single-table .header{border-bottom:none}.wpmdb .dry-run-results-single-table .header :last-child{margin-left:10px}.wpmdb .dry-run-result-meta{grid-gap:1rem;display:flex;gap:1rem;margin-bottom:.5rem}.wpmdb .no-top-margin{margin-top:10px!important}.wpmdb .hidden{display:none!important}.wpmdb .dry-run-notice{margin:0 0 1.5rem}.wpmdb .dry-run-controls{margin-top:0}.wpmdb .dry-run-controls .pagination-controls{align-items:center;display:flex;flex-direction:row;margin-left:auto}.wpmdb .dry-run-controls .pagination-controls span{margin-right:1.5rem}.wpmdb .dry-run-controls .pagination-controls button{min-width:3.5rem}.wpmdb .dry-run-controls .pagination-controls :last-child{margin-right:0}.wpmdb .dry-run-back-btn{align-self:flex-start;color:#0473aa;font-weight:600;margin-bottom:20px!important;text-decoration:underline}.database_panel_has_controls #wpmdb-database{display:grid;grid-template-columns:35px 1fr auto}.database_panel_no_controls #wpmdb-database{display:grid;grid-template-columns:1fr auto}.panel-arrow{transition:-webkit-transform .15s ease-out;transition:transform .15s ease-out;transition:transform .15s ease-out,-webkit-transform .15s ease-out}.panel-arrow.open{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.panel-arrow.open path{fill:#fff}.action-panel{border-radius:4px;display:grid;margin-bottom:1.1rem;opacity:1}.action-panel.child-panel{margin-bottom:.95rem}.action-panel.disabled{opacity:.85}.action-panel.shadows{border-radius:4px;box-shadow:0 -2px 1px 0 rgba(0,0,0,.05),0 -1px 1px 0 rgba(0,0,0,.05)}.action-panel .panel-header-wrap{align-items:center;align-self:center;display:grid;min-height:40px;padding:8px 1.25rem;transition:background .2s ease-out}.action-panel .panel-header-wrap a{justify-self:end;padding:.3rem}.action-panel .panel-header-wrap:hover{cursor:pointer}.action-panel .panel-header-wrap.has-summary-no-child{display:grid;grid-template-columns:195px 1px 4fr;min-height:40px;padding:8px 1.25rem}.action-panel .panel-header-wrap.has-summary-no-child .button-wrap{justify-self:right}.action-panel .panel-header-wrap.has-summary-no-child .button-wrap button{border-radius:4px;height:20px;width:20px}.action-panel .panel-header-wrap.has-summary-no-child .button-wrap button:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.action-panel .panel-header-wrap.no-child{display:grid;grid-template-columns:minmax(0,.8fr) 4fr}.action-panel .panel-header-wrap.child-panel,.action-panel .panel-header-wrap.child-panel .panel-title{color:#575757}.action-panel .panel-header-wrap.child-panel.panel-open{background:#396c9c;border-radius:4px 4px 0 0;color:#fff;margin:-1px -1px 0}.action-panel .panel-header-wrap.child-panel.panel-open .panel-title{color:#fff;font-size:.9rem;opacity:1}.action-panel .panel-header-wrap.child-panel.panel-open #accordion_collapsed_default_active{fill:#fff}.action-panel.actions-panel .panel-header-wrap.has-summary-no-child.panel-open,.action-panel.connect-panel .panel-header-wrap.has-summary-no-child.panel-open,.action-panel.import-panel .panel-header-wrap.has-summary-no-child.panel-open{grid-template-columns:1fr 6fr 0}.action-panel.disabled .panel-header-wrap{border-radius:3px}.action-panel .panel-title{align-self:center;font-size:.95rem;margin:0}.action-panel .panel-title .count{font-size:.8rem}.action-panel .panel-header{text-align:right}.action-panel .panel-header.has-summary{display:grid;grid-template-columns:5fr .3fr;text-align:left}.action-panel .panel-header p{color:#575757;font-size:.8rem;font-weight:400}.action-panel .panel-header.summary{display:grid;grid-template-columns:repeat(2,1fr)}.action-panel .panel-header.child-panel-header{display:grid;grid-template-columns:20px 5fr;text-align:left}.action-panel .panel-header.child-panel-header .button-wrap{align-items:center;display:flex}.action-panel .panel-header.child-panel-header .button-wrap button{border-radius:4px;height:20px;width:20px}.action-panel .panel-header.child-panel-header .button-wrap button:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.action-panel .panel-header.child-panel-header p{margin:.1rem 0 0}.action-panel .panel-header.child-panel-header .child-summary{overflow:hidden;padding-left:1rem}.action-panel .panel-header button:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.action-panel .panel-body-wrap{position:relative}.action-panel .panel-body-wrap.panel-closed{visibility:hidden}.action-panel .panel-body{background:#fff;border-radius:0 0 6px 6px;border-top:1px solid #d6d6d6;display:grid;grid-template-columns:none;padding:26px 22px}.action-panel .panel-body>div:last-of-type{margin-bottom:0!important}.action-panel .panel-body.panel-closed{visibility:hidden}.action-panel .panel-body .panel-instructions{margin-top:0}.action-panel.error-panel{border-left:4px solid #dc3232}.action-panel.error-panel .panel-header-wrap.has-summary-no-child.panel-closed{grid-template-columns:35px 180px 0 1fr;padding-left:16px}.action-panel.error-panel .panel-header{grid-column:auto;grid-template-columns:5fr .3fr}.action-panel.error-panel .panel-header .panel-summary{grid-gap:8px;align-items:center;display:flex;gap:8px;justify-content:flex-start}.action-panel.error-panel .panel-header .panel-summary a{padding:0}.actions-panel .action-button-group{display:flex;flex-direction:column;height:100%}.actions-panel .action-buttons{display:grid;grid-template-columns:repeat(1,minmax(0,1fr));padding:1rem 1.7rem 1.5rem 1.25rem}.actions-panel .action-buttons h4{flex:1 1;font-size:.8rem;font-weight:500;letter-spacing:0;margin-bottom:1.75rem;margin-top:0;text-transform:uppercase}@media(max-width:1350px){.actions-panel .action-buttons h4{font-size:.8rem}}.actions-panel .action-buttons .action-tooltip.wack:after,.actions-panel .action-buttons .action-tooltip.wack:before{left:20%!important}.actions-panel .action-row{grid-column-gap:5%;align-items:end;display:grid;grid-row:2;grid-template-columns:repeat(3,minmax(0,1fr));max-width:990px;padding-right:33px}.actions-panel .buttons{grid-gap:15px;display:grid;gap:15px;grid-template-columns:repeat(2,minmax(0,1fr));padding-right:10px}.actions-panel .action-btn-wrap{padding-bottom:100%;position:relative}.actions-panel .action-btn-wrap a.upgrade{color:#236de7;font-weight:700;text-transform:uppercase}.actions-panel .action-icon{background:#fff;border:1px solid #d6d6d6;border-radius:6px;color:#747474;height:100%;left:50%;position:absolute;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:100%}.actions-panel .action-icon:hover{box-shadow:0 15px 13px 0 rgba(0,0,0,.15),0 2px 3px 0 rgba(0,0,0,.05);cursor:pointer;transition-duration:.15s;transition-timing-function:linear}.actions-panel .action-icon:focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.actions-panel .action-title{font-size:.88rem;font-weight:500;line-height:1.5;margin:.5rem auto 1rem;max-width:70%;text-align:center}.actions-panel .disabled .action-title{opacity:.5}.actions-panel .action-pull svg,.actions-panel .action-push svg{position:relative;top:10px}.actions-panel .action-icon.active .action-title,.actions-panel .action-icon:hover .action-title{color:#236de7;opacity:1}.actions-panel .disabled{cursor:default}.actions-panel .disabled.action-icon.active .action-title,.actions-panel .disabled.action-icon:hover .action-title{color:initial;opacity:.5}.actions-panel .disabled.action-icon:hover{box-shadow:none;cursor:default}.actions-panel .action-title-locked{font-size:.88rem;font-weight:400;line-height:1.3;margin-bottom:1rem;margin-top:1.5rem;opacity:.5;width:50%}.wpmdb-save-profile .row{padding:0 1.4rem}.wpmdb-save-profile .flex-group{display:flex;flex-direction:column;width:100%}.wpmdb-save-profile .header-row{border-bottom:1px solid #d6d6d6;padding:1.4rem 1.4rem 0}.wpmdb-save-profile .header-row button{color:#a9a9a9;display:inline-block;font-weight:500;padding:13px 0}.wpmdb-save-profile .header-row button:nth-child(2){margin-left:1.5rem}.wpmdb-save-profile .header-row button.active{border-bottom:2px solid #236de7;color:#236de7;padding-bottom:11px;transition:none}.wpmdb-save-profile.save-profile-wrap{position:relative}.wpmdb-save-profile .recent-list{display:grid}.wpmdb-save-profile .save-profile-box{background:#fff;border:1px solid #d6d6d6;border-radius:5px;box-shadow:0 3px 60px rgba(0,0,0,.15),0 2px 5px rgba(0,0,0,.2);left:137px;min-height:330px;position:absolute;top:-410px;width:350px}.wpmdb-save-profile .save-profile-box .save-btn{height:40px;margin-top:auto;padding:.65rem 1.5rem;width:100%}.wpmdb-save-profile .save-profile-box .save-btn svg{margin-top:-7px}.wpmdb-save-profile .save-profile-box li{margin:0!important;padding:2px}.wpmdb-save-profile .save-profile-box input[type=text]{width:100%}.wpmdb-save-profile .save-profile-box input#save-new-profile{font-size:14px;margin:5px 0 0;padding:13px 10px}.wpmdb-save-profile .save-profile-box .save-profile-input{margin-top:-24px}.wpmdb-save-profile .save-profile-box .save-profile-input label{font-size:.7rem;margin:0}.wpmdb-save-profile .save-profile-box .close-wrap{height:20px;position:absolute;right:8px;top:8px}.wpmdb-save-profile .save-profile-box .close-wrap .close-picker:focus{box-shadow:none}.wpmdb-save-profile .scroll-div{border:1px solid #d6d6d6;border-radius:6px;max-height:90px;overflow-y:scroll;padding:.5rem}.wpmdb-save-profile .scroll-div.empty{overflow-y:initial}.wpmdb-save-profile .scroll-div label{align-items:center;display:grid;grid-template-columns:10% 12% 5fr;text-overflow:ellipsis;white-space:nowrap}.wpmdb-save-profile .scroll-div label span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wpmdb-save-profile .scroll-div b{padding-left:8px}.wpmdb-save-profile .save-inner{align-items:center;display:flex;height:200px}.wpmdb-save-profile .profile-save-error{margin:18px 0;max-width:100%}.combo-button-container{display:inline-block;position:relative}.combo-button-container .btn-large{background-color:#236de7;border:1px solid #236de7;border-radius:4px;color:#fff;cursor:pointer;display:flex;min-width:210px;padding:0;text-align:center;white-space:nowrap;width:-webkit-max-content;width:max-content}.combo-button-container .btn-large .primary-btn{border-radius:4px 0 0 4px;border-right:1px solid #1c57b9;flex:0 0 78%}.combo-button-container .btn-large .secondary-btn{border-radius:0 4px 4px 0}.combo-button-container .btn-large button{color:#fff;font-size:.8rem;font-weight:700;letter-spacing:.05rem;padding:1.1rem;text-transform:uppercase}.combo-button-container .btn-large button:hover{background-color:#2062d0}.combo-button-container .btn-large button:focus-visible{background-color:#2062d0;box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.combo-button-container .btn-large button:active{background-color:#1c57b9}.combo-button-container .btn-large svg{right:0;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.combo-button-container .btn-large svg g{stroke:#fff;fill:#fff}.combo-button-container .btn-large svg.expanded{-webkit-transform:rotate(1turn);transform:rotate(1turn)}.combo-button-container .combo-button-children{background:#fff;border:1px solid #c6d1dd;border-radius:6px;box-shadow:0 2px 6px rgba(0,0,0,.1);display:none;margin-top:-211px;position:absolute;z-index:9999}.combo-button-container .combo-button-children.show{display:flex;flex-direction:column;width:-webkit-max-content;width:max-content}.combo-button-container .combo-button-children .combo-button-child-container{border-bottom:1px solid #c6d1dd}.combo-button-container .combo-button-children .combo-button-child-container:first-child{border-top-left-radius:6px;border-top-right-radius:6px}.combo-button-container .combo-button-children .combo-button-child-container:last-child{border-bottom-left-radius:6px;border-bottom-right-radius:6px}.combo-button-container .combo-button-children .combo-button-child-container:focus-visible,.combo-button-container .combo-button-children .combo-button-child-container:hover{background-color:#f1f5f9}.combo-button-container .combo-button-children .combo-button-child-container:active{background-color:#e2e8f0}.combo-button-container .combo-button-children .combo-button-child-container .combo-button-child{cursor:pointer;display:grid;grid-template-columns:1fr;padding:16px}.combo-button-container .combo-button-children .combo-button-child-container .combo-button-child .text span{clear:both;float:left}.combo-button-container .combo-button-children .combo-button-child-container .combo-button-child .text .label{font-size:13.75px;font-weight:500}.combo-button-container .combo-button-children .combo-button-child-container .combo-button-child .text .description{font-size:13px;font-style:normal;font-weight:400;margin-top:5px;text-align:left}.combo-button-container .combo-button-children .combo-button-child-container .combo-button-child svg polyline{stroke:#fff}.combo-button-container .combo-button-children .combo-button-child-container .combo-button-child .label{font-weight:600;text-transform:uppercase}.combo-button-container .combo-button-children .combo-button-child-container .combo-button-child.selected .check{display:inline-block}.combo-button-container .combo-button-children .combo-button-child-container .combo-button-child.selected .check polyline{stroke:#236de7}.combo-button-container .combo-button-children:last-child{border-bottom:none}.wpmdb-backups hr{border-bottom:0;border-top:2px solid #e1e1e1!important}.wpmdb-backups .backups-header{grid-column-gap:1rem;align-items:center;-webkit-column-gap:1rem;column-gap:1rem;display:grid;grid-template-columns:.63fr 4fr;margin-bottom:38px}.wpmdb-backups .backups-header .create-new-backup{display:inline-block;min-width:120px}.wpmdb-backups .backups-header .create-new-backup svg{margin-bottom:1px;vertical-align:middle}.wpmdb-backups .backups-header .backups-save-location{align-items:center;display:flex;margin-left:auto}.wpmdb-backups .no-items{margin-top:2rem}.wpmdb-backups .backups-loading .backups-spinner{position:relative;top:5px}.wpmdb-backups .no-backups-icon{background:#e4e4e4;border-radius:50%;color:#999;height:40px;margin-right:15px;padding:10px;vertical-align:middle;width:40px}.wpmdb-backups .backups-table tr td:first-child{flex:0 0 150px}.wpmdb-backups .backups-table tr.in-progress{opacity:.5}.wpmdb-backups .backups-table tr.in-progress:hover{cursor:not-allowed}@media(max-width:1650px){.wpmdb-backups .backups-table .confirm-delete{left:-15rem!important}.wpmdb-backups .backups-table .confirm-delete .speech-bubble-left:after{border:10px solid transparent;border-left-color:#fff;border-right:0;left:auto;margin-right:-.6rem;margin-top:-.6rem;right:1px;top:50%}}.wpmdb-addons .addons-container{grid-gap:1.5rem;display:grid;gap:1.5rem;grid-template-columns:repeat(2,minmax(240px,1fr))}@media(min-width:1150px){.wpmdb-addons .addons-container{grid-template-columns:repeat(3,minmax(0,1fr))}}.wpmdb-addons .addons-notice{margin:0 0 1.75rem}.wpmdb-addons .addons-tab-spinner{position:relative;top:5px}.wpmdb-addons .addon-container{background:#fff;display:flex;flex:1 1;flex-direction:column;padding:1.5rem}.wpmdb-addons .addon-container .migrate-notice{margin-bottom:0}.wpmdb-addons .addon-icon{display:block;margin:0 auto}.wpmdb-addons .addon-title{margin-bottom:1.5rem;margin-top:.75rem;text-align:center;text-transform:uppercase}.wpmdb-addons .addon-btn{display:block;padding:.6rem 2.8rem}.wpmdb-addons .addon-content{border-top:1px solid #d6d6d6;flex:1 1;margin-top:.9rem;padding-top:1rem}.wpmdb-addons .more-details-link{font-weight:500;text-decoration:underline}.wpmdb-addons .more-details-link:hover{text-decoration:none}.wpmdb-addons .addon-bottom-controls{align-self:center;flex:1 1}.wpmdb-addons .installed-activated-text{font-weight:500;margin-left:-3px;padding:.48rem;text-align:center}.wpmdb-addons .installed-activated-text svg{margin:-3px 3px 0 0;vertical-align:middle}.wpmdb-addons .addon-link{display:block;font-weight:700;margin-bottom:.5rem;margin-top:1.5rem;text-align:center;text-decoration:underline}.wpmdb-addons .addon-link:hover{text-decoration:none}.wpmdb-addons .licence-action{margin-top:.8rem}.wpmdb-addons .addon-upgrade-container{padding-top:20px;text-align:center}.wpmdb-addons .addon-upgrade-container .edit-key-link{margin-top:.5rem}.wpmdb .wpmdb-help-wrap>.wrapper{margin-right:0}.wpmdb-help-wrap{border-bottom:1px solid #bbb}.wpmdb-help-wrap .wrapper{margin-left:0}.wpmdb-help-wrap.success{border:none}.wpmdb-help-wrap .subnav{border-bottom:none;padding-left:0}.wpmdb-help-wrap .subnav button:focus-visible{border-radius:4px;box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb-help-wrap.email{border-bottom:2px solid #e1e1e1}.wpmdb-help-wrap.email .licence-action{margin-top:.8rem}.wpmdb-help-wrap.videos{border-bottom:none}.wpmdb-help-wrap.videos .additional-help{margin-left:100px}.wpmdb-help-wrap .email-support{margin-top:-4px}.wpmdb-help-tab{min-height:500px}.wpmdb-help-tab .notification{margin:1.4rem 0;padding:1rem 1.5rem}.wpmdb-help-tab .help-spinner{position:relative;top:5px}.wpmdb-help-tab .help-spinner.send-email{left:7px}.wpmdb-help-tab .help-spinner #el_6X7lquFKkl{fill:#b7b7b7}.wpmdb-help-tab .support-form{padding-left:9px;width:500px}.wpmdb-help-tab .support-form .diagnostic-log{padding-left:0}.wpmdb-help-tab .support-form .valid-license{font-size:14px;margin-bottom:35px}.wpmdb-help-tab .support-form h3{margin-top:0}.wpmdb-help-tab .support-form .diagnostic-log.success h2{margin-top:2.5rem}.wpmdb-help-tab .support-videos{padding-left:9px;width:640px}.wpmdb-help-tab .support-videos iframe{border:1px solid #d6d6d6;box-shadow:0 1px 1px 0 rgba(0,0,0,.05),0 0 1px 0 rgba(0,0,0,.05)}.wpmdb-help-tab .additional-help{align-self:baseline;width:265px}.wpmdb-help-tab .additional-help .documentation-panel{margin-left:0}.wpmdb-help-tab .additional-help li{font-size:1rem}.wpmdb-help-tab .additional-help li a{font-weight:600}.wpmdb-help-tab .additional-help h2{font-size:1rem;font-weight:600;margin-bottom:1.6rem;margin-top:0}.wpmdb-help-tab .additional-help h2 a{color:#000}.wpmdb-help-tab .support-wrapper{grid-gap:20px;display:grid;grid-template-columns:2fr 1.42fr;margin-top:3.5rem}.wpmdb-help-tab input[type=text],.wpmdb-help-tab select,.wpmdb-help-tab textarea{height:2.5rem;width:100%!important}.wpmdb-help-tab .help-form{margin-top:5px}.wpmdb-help-tab .help-form a{color:#236de7;font-weight:600;text-decoration:underline}.wpmdb-help-tab .help-form a:hover{text-decoration:none}.wpmdb-help-tab .help-form .field,.wpmdb-help-tab .help-form label,.wpmdb-help-tab .help-form p{font-size:14px;line-height:19px}.wpmdb-help-tab .help-form p{font-size:14px;line-height:22px}.wpmdb-help-tab .help-form .field{color:#000;opacity:.8}.wpmdb-help-tab .help-form p.note{margin-bottom:0;margin-left:2px}.wpmdb-help-tab .help-form p.note a{color:#236de7}.wpmdb-help-tab .help-form input[type=text],.wpmdb-help-tab .help-form select,.wpmdb-help-tab .help-form textarea{border-radius:5px;color:#000;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:13px;margin:0}.wpmdb-help-tab .help-form input[type=text]::-webkit-input-placeholder,.wpmdb-help-tab .help-form textarea::-webkit-input-placeholder{color:#555}.wpmdb-help-tab .help-form input[type=text]::placeholder,.wpmdb-help-tab .help-form textarea::placeholder{color:#555}.wpmdb-help-tab .help-form .from{margin:28px 0 35px}.wpmdb-help-tab .help-form .from p.note{margin-top:16px}.wpmdb-help-tab .help-form .from .select-email{display:grid;grid-template-columns:.65fr 5fr}.wpmdb-help-tab .help-form .from .select-email label{font-weight:500;margin-top:10px}.wpmdb-help-tab .help-form .from .select-email select{line-height:1;max-width:100%;padding-left:12px}.wpmdb-help-tab .help-form .email-message,.wpmdb-help-tab .help-form .subject{margin-bottom:.9rem}.wpmdb-help-tab .help-form .remote-diagnostic{margin-bottom:1rem}.wpmdb-help-tab .help-form .local-diagnostic,.wpmdb-help-tab .help-form .remote-diagnostic.checked{margin-bottom:.43rem}.wpmdb-help-tab .help-form .remote-diagnostic-content{margin-bottom:1.1rem}.wpmdb-help-tab .help-form .remote-diagnostic-content ol{list-style-type:decimal;margin:.9rem 0 .9rem 2em}.wpmdb-help-tab .help-form .remote-diagnostic-content ol li{color:#555;font-size:13px;list-style:decimal;margin-left:1rem}.wpmdb-help-tab .help-form .remote-diagnostic-content textarea{border-radius:5px;min-height:100px}.wpmdb-help-tab .help-form.error .remote-diagnostic-content{margin-bottom:0}.wpmdb-help-tab .help-form p.note.trouble{margin:38px 0 56px}.wpmdb-help-tab .help-form .email-message textarea{border-radius:5px;min-height:180px}.wpmdb-help-tab .help-form .btn{font-size:12px}.diagnostic-log{max-width:500px;padding-left:0}.diagnostic-log.wrapper{margin-left:9px}.diagnostic-log h2{font-size:16px;margin-bottom:20px;margin-top:56px}.diagnostic-log .clear-log-btn{display:block;line-height:18.2px;margin:0 .75rem}.diagnostic-log textarea[readonly]{background:#fff;border-radius:5px;color:#000;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:13px;height:210px;line-height:19px;margin-bottom:.85rem;max-width:500px;width:100%}.flex-col{flex:1 1}.toggle-switch{display:flex}.toggle-switch input:focus-visible+label{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.toggle-switch input[type=checkbox]{all:unset;height:0;width:0}.toggle-switch input[type=checkbox]:focus{box-shadow:none}.toggle-switch label{background:#cbd5e0;border-radius:100px;cursor:pointer;display:block;height:20px;position:relative;text-indent:-9999px;width:36px}.toggle-switch label:after{background:#fff;border-radius:90px;box-shadow:0 1px 2px 0 rgba(0,0,0,.06),0 1px 3px 0 rgba(0,0,0,.1);content:"";height:16px;left:2px;position:absolute;top:2px;transition:.3s;width:16px}.toggle-switch label.checked{background:#236de7;transition:.3s}.toggle-switch input:checked+label:after{left:calc(100% - 2px);-webkit-transform:translateX(-100%);transform:translateX(-100%)}.wpmdb-settings-page .slider-title,.wpmdb-settings-page .toggle-title{font-size:15px;font-weight:500;margin:0;position:relative;text-transform:none}.wpmdb-settings-page .slider-title .has-tooltip,.wpmdb-settings-page .toggle-title .has-tooltip{display:flex}.wpmdb-settings-page .slider-title .has-tooltip svg,.wpmdb-settings-page .toggle-title .has-tooltip svg{border-radius:50%;margin-left:8px;margin-top:-3px}.wpmdb-settings-page .slider-title .has-tooltip svg:focus,.wpmdb-settings-page .toggle-title .has-tooltip svg:focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb-settings-page h6{font-size:14px}.wpmdb-settings-page input[type=text]{display:block;margin-bottom:1.1rem;width:48%}.wpmdb-settings-page .flex{display:flex}.wpmdb-settings-page .settings-row{border-bottom:2px solid #e1e1e1;padding:56px 0}.wpmdb-settings-page .settings-row:first-child{padding-top:0}.wpmdb-settings-page .settings-row>.flex-container{align-items:baseline;margin-bottom:1.6rem}.wpmdb-settings-page .settings-row>.flex-container.licence-buttons{margin-bottom:0}.wpmdb-settings-page .settings-row h4{margin:0}.wpmdb-settings-page .settings-row .migration-permissions .flex-container:nth-child(3){margin-top:1.6rem}.wpmdb-settings-page .settings-row .migration-permissions .settings-spinner{top:-10px}.wpmdb-settings-page .settings-row .migration-action{margin-left:1.2rem}.wpmdb-settings-page .settings-row .migration-action .toggle-error,.wpmdb-settings-page .settings-row .migration-action .toggle-success{top:-30px}.wpmdb-settings-page label{margin:0}.wpmdb-settings-page .section-title{font-size:16px;margin-bottom:1.5rem}.wpmdb-settings-page .section-title:first-child{margin-top:0}.wpmdb-settings-page textarea{border-radius:5px;margin-bottom:1.1rem;max-height:200px;min-height:58px;width:100%}.wpmdb-settings-page .btn:first-child{margin-right:.75rem}.wpmdb-settings-page .connection-info{align-self:flex-start;padding-right:4%}.wpmdb-settings-page .connection-info .btn{padding:.65rem 1.5rem;position:relative}.wpmdb-settings-page .connection-info .btn.copied{min-width:189px}.wpmdb-settings-page .connection-info .styled-check{left:27%;position:absolute;top:9px}@media screen and (max-width:1024px){.wpmdb-settings-page .connection-info .btn.copied{min-width:166px}}.wpmdb-settings-page .migration-permissions{align-self:flex-start}.wpmdb-settings-page .migration-permissions>.flex-container{align-items:normal}.wpmdb-settings-page .request-settings{padding-bottom:40px}.wpmdb-settings-page .request-settings .flex-container{align-items:flex-start}.wpmdb-settings-page .request-settings .tooltip-saved.flex-container{align-items:center}.wpmdb-settings-page .notification{margin-bottom:1rem}.wpmdb-settings-page .settings-tooltip{left:10px;position:absolute;top:-17px;z-index:9}.wpmdb-settings-page .settings-tooltip svg{float:left;margin-right:.2rem;min-width:24px}.wpmdb-settings-page .licence-error .settings-tooltip.toggle-error,.wpmdb-settings-page .licence-error .settings-tooltip.toggle-success{top:-13px}.wpmdb-settings-page .licence-action{margin-top:.8rem}.wpmdb-settings-page .license-row .btn{margin-right:0}.wpmdb-settings-page .license-row h3.no-licence{margin-bottom:0}.wpmdb-settings-page .license-row h3.licence-header{margin-bottom:1rem}.wpmdb-settings-page .license-row .settings-spinner{top:-16px}.wpmdb-settings-page .license-row .licence-error{margin-top:0}.wpmdb-settings-page .slider-wrap{margin:28px 0 0;width:30rem}.wpmdb-settings-page .slider-wrap .slider-header{grid-template-columns:1fr 1fr}.wpmdb-settings-page .slider-wrap .settings-tooltip{left:auto!important;right:-90px}.wpmdb-settings-page .slider-wrap .slider-details{align-self:self-end;font-weight:500;justify-self:end;opacity:.8}.wpmdb-settings-page .slider-wrap h4#slider-delay_between_requests{margin-top:.5rem}.wpmdb-settings-page .slider-wrap .settings-spinner{left:auto;right:-28px;top:-19px}.wpmdb-settings-page .slider-wrap .MuiSlider-thumb.Mui-focusVisible{box-shadow:none}.wpmdb-settings-page .slider-wrap .MuiSlider-thumb:focus-visible{box-shadow:0 0 0 14px rgba(63,81,181,.16)}.wpmdb-settings-page .slider-wrap .Mui-disabled .MuiSlider-thumb{height:18px;margin-left:0;margin-top:-8px;width:18px}.wpmdb-settings-page .compatibility-mode-block .settings-tooltip{top:-30px}.wpmdb-settings-page .compatibility-mode-block .settings-spinner{top:0}.wpmdb-settings-page .compatibility-mode-block .title{font-size:14px;margin-bottom:10px}.wpmdb-settings-page .compatibility-mode-block .select-group{margin-top:16px}.wpmdb-settings-page .advanced-settings{border-bottom:none}.wpmdb-settings-page .advanced-settings .switch-1{margin-top:-5px!important}.wpmdb-settings-page .advanced-settings .flex-container{align-items:start}.wpmdb-settings-page .advanced-settings .flex-container.tooltip-saved{align-items:center}.wpmdb-settings-page .advanced-settings .tooltip-saved,.wpmdb-settings-page .settings-tooltip{align-self:center}.wpmdb-settings-page .compat-plugin-list{background:#fff;max-width:560px}.wpmdb-settings-page.free .advanced-settings{border-bottom:2px solid #e1e1e1}.wpmdb-settings-page.free .request-settings{border:0}.wpmdb .search-replace{padding-top:0}.wpmdb .search-replace a.upgrade{color:#236de7;font-weight:700;text-decoration:underline;text-transform:uppercase}.wpmdb .search-replace .content-row-wrap{max-width:calc(100% - 32px)}.wpmdb .search-replace .content-row-wrap:nth-child(2n){background:#f7f7f7}.wpmdb .search-replace .extra-row{align-items:center;display:grid;grid-template-columns:1fr;margin:0;padding-left:35px}.wpmdb .search-replace .extra-row .cell{width:50em}.wpmdb .search-replace .row{align-items:center;display:grid;grid-template-columns:0fr 5fr 1.2fr 5fr 1fr .5fr;margin:0;padding:11px 0 11px 22px}.wpmdb .search-replace .row.header-row{grid-template-columns:0fr 5fr 1.53fr 5fr .5fr;max-width:100%;padding:18px 0 15px}.wpmdb .search-replace .row.custom-search.header-row{padding-bottom:0}.wpmdb .search-replace .row .header-2{font-weight:600;grid-column:2;text-transform:uppercase}.wpmdb .search-replace .row .header-4{font-weight:600;grid-column:4;text-transform:uppercase}.wpmdb .search-replace .row.custom-search:nth-child(2n){background:initial}.wpmdb .search-replace.standard-search-replace .row{grid-template-columns:20px 1fr 45px 1fr 76px}.wpmdb .search-replace.standard-search-replace .header-row{grid-template-columns:51px 2fr 43px 2fr 104px}.wpmdb .search-replace .custom-search .row{grid-gap:8px;gap:8px;grid-template-columns:43px 2fr 43px 2fr 24px 76px;max-width:100%}.wpmdb .search-replace .custom-search .custom-search-row{padding-bottom:4px;padding-left:0}.wpmdb .search-replace .custom-search .custom-search-row:focus-visible{outline:none}.wpmdb .search-replace .custom-search .custom-search-row:focus-visible .handle{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .search-replace .custom-search .custom-search-row:focus-visible .handle svg g{fill:#236de7}.wpmdb .search-replace .custom-search .custom-search-row .handle{align-items:center;border:1px solid #fff;border-radius:4px;display:flex;height:43px;justify-content:center;line-height:0}.wpmdb .search-replace .custom-search .header-row{padding-bottom:0}.wpmdb .search-replace .custom-search .cell{line-height:1}.wpmdb .search-replace .custom-search .cell input{margin:0}.wpmdb .search-replace .custom-search .cell input.invalid-expression{border:1px solid #dc3232}.wpmdb .search-replace .custom-search .cell input.invalid-expression:focus{box-shadow:0 0 4px 1px rgba(220,50,50,.3)}.wpmdb .search-replace.standard-search-replace .cell.replace,.wpmdb .search-replace.standard-search-replace .cell.search{padding-left:11px;text-overflow:ellipsis;white-space:nowrap}.wpmdb .search-replace .cell{font-size:.8rem}.wpmdb .search-replace .cell svg g,.wpmdb .search-replace .cell svg path{fill:#aaa}.wpmdb .search-replace .cell button:focus-visible svg path{fill:#236de7}.wpmdb .search-replace .cell.close svg path{fill:#eee}.wpmdb .search-replace .cell.close svg circle{fill:#999}.wpmdb .search-replace .cell.close button{border-radius:50%;height:24px}.wpmdb .search-replace .cell.close button:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .search-replace .cell.close button:focus-visible svg path{fill:#eee}.wpmdb .search-replace .cell.close button:focus-visible svg circle{fill:#236de7}.wpmdb .search-replace .cell.custom-search-arrow{text-align:center}.wpmdb .search-replace .cell.custom-search-arrow svg{height:28px;width:28px}.wpmdb .search-replace .cell.custom-search-arrow button{align-items:center;border-radius:4px;display:flex;height:43px;justify-content:center;width:43px}.wpmdb .search-replace .cell.custom-search-arrow button:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .search-replace .cell.question{position:relative}.wpmdb .search-replace .cell.question svg{cursor:pointer;position:absolute;right:-32px;top:-14px}.wpmdb .search-replace .cell.question svg path{fill:#eee}.wpmdb .search-replace .cell input[type=text]{width:100%}.wpmdb .search-replace .cell input::-webkit-input-placeholder{color:#aaa}.wpmdb .search-replace .cell input::placeholder{color:#aaa}.wpmdb .search-replace .search-replace-row-options{grid-gap:12px;display:flex;gap:12px;justify-content:flex-end}.wpmdb .search-replace .search-replace-option input:focus-visible+label,.wpmdb .search-replace .search-replace-option input:hover:not([disabled])+label:hover{border:1px solid #236de7}.wpmdb .search-replace .search-replace-option input:focus-visible+label .option-icon circle,.wpmdb .search-replace .search-replace-option input:focus-visible+label .option-icon path,.wpmdb .search-replace .search-replace-option input:hover:not([disabled])+label:hover .option-icon circle,.wpmdb .search-replace .search-replace-option input:hover:not([disabled])+label:hover .option-icon path{fill:#65707c}.wpmdb .search-replace .search-replace-option .toggle{margin:0}.wpmdb .search-replace .search-replace-row-options fieldset{grid-gap:12px;display:flex;gap:12px;justify-content:flex-end}.wpmdb .search-replace .search-replace-option{display:inline-block;outline:none}.wpmdb .search-replace .search-replace-option input{clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.wpmdb .search-replace .search-replace-option input:focus-visible+label{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.wpmdb .search-replace .search-replace-option input:hover:not([disabled])+label:hover{border:1px solid #236de7}.wpmdb .search-replace .search-replace-option input:hover:not([disabled])+label:hover .option-icon circle,.wpmdb .search-replace .search-replace-option input:hover:not([disabled])+label:hover .option-icon path{fill:#65707c}.wpmdb .search-replace .search-replace-option input:disabled+label{cursor:not-allowed}.wpmdb .search-replace .search-replace-option input.option-disabled+label{background-color:#eaf0ea;border:1px solid #eaf0ea}.wpmdb .search-replace .search-replace-option input.option-disabled+label .option-icon circle,.wpmdb .search-replace .search-replace-option input.option-disabled+label .option-icon path{opacity:.5}.wpmdb .search-replace .search-replace-option .toggle{align-items:center;border:1px solid #b1b1b1;border-radius:3px;display:flex;height:30px;justify-content:center;text-align:center;width:30px}.wpmdb .search-replace .search-replace-option .toggle:active:enabled{background:#d6ecf1!important}.wpmdb .search-replace .search-replace-option .toggle:active:enabled .option-icon circle,.wpmdb .search-replace .search-replace-option .toggle:active:enabled .option-icon path{fill:#041b36!important}.wpmdb .search-replace .search-replace-option .toggle .option-icon circle,.wpmdb .search-replace .search-replace-option .toggle .option-icon path{fill:#b1b1b1}.wpmdb .search-replace .search-replace-option .toggle.active{background-color:#edf6f7;border:1px solid #236de7}.wpmdb .search-replace .search-replace-option .toggle.active:hover .option-icon circle,.wpmdb .search-replace .search-replace-option .toggle.active:hover .option-icon path{fill:#041b36!important;opacity:1!important}.wpmdb .search-replace .search-replace-option .toggle.active .option-icon circle,.wpmdb .search-replace .search-replace-option .toggle.active .option-icon path{fill:#05203f}.wpmdb .search-replace .search-replace-option .search-replace-spinner{margin-top:3px}.wpmdb .search-replace .search-replace-option .search-replace-spinner svg{margin-top:0}.wpmdb .search-replace .search-replace-option .search-replace-option-tooltip{width:15em}.wpmdb .search-replace .search-replace-option .search-replace-option-tooltip.action-tooltip a:not(.underline){text-decoration:none}.wpmdb .search-replace .invalid-expression .toggle.active{background-color:pink;border:1px solid #dc3232;box-shadow:0 2px 1px rgba(0,12,204,.1);color:#05203f}.wpmdb .search-replace .invalid-expression p{color:#dc3232;margin:0}.wpmdb .search-replace .regex-info{margin-left:35px;padding-top:5px}.whats-new{max-width:700px}.whats-new .release-posts .release-post{background-color:#fff;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.05),0 2px 1px rgba(0,0,0,.05);margin-bottom:30px;padding:32px}.whats-new .release-posts .release-post h2{align-items:flex-start;display:flex;flex-direction:column;font-size:22px;margin-bottom:24px;margin-top:0}.whats-new .release-posts .release-post h2 .release-version{background-color:#edf9fb;border-radius:30px;font-size:13.5px;font-weight:400;margin-bottom:16px;padding:5px 10px}.whats-new .release-posts .release-post img{border-radius:6px;margin-bottom:24px;width:100%}.whats-new .release-posts .release-post .release-content>:first-child{margin-top:0;padding-top:0}.whats-new .release-posts .release-post .release-content>:last-child{margin-bottom:0;padding-bottom:0}.whats-new .release-posts .release-post .release-post-link{display:inline-block;margin-top:32px}.whats-new .release-posts .more-releases{background-color:#fff;background-position:0 100%;background-repeat:no-repeat;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.05),0 2px 1px rgba(0,0,0,.05);padding:32px 32px 32px 235px}.whats-new .release-posts .more-releases h2{font-size:22px;margin:0}.whats-new .release-posts .more-releases .release-post-link{display:inline-block;margin-top:24px}.rate-mdb{background:#f4f7fd;left:0;padding:15px;position:absolute;right:0;text-align:center}.rate-mdb a{color:#236de7;text-decoration:underline}.rate-mdb a:hover{text-decoration:none}.rate-mdb span{display:inline-block;justify-self:end}.rate-mdb span svg{padding:0 2px}.upgrade-to-pro{grid-gap:15px;background:#fff;border-radius:4px;display:grid;grid-template-columns:2fr 2.3fr;margin-top:40px;padding:40px}.upgrade-to-pro h3{color:#04223f;font-size:23px;font-weight:400;margin-bottom:32px;margin-top:0}.upgrade-to-pro h3 span{font-weight:700}.upgrade-to-pro ul{margin-bottom:32px}.upgrade-to-pro ul li{background-position:0 9px;background-repeat:no-repeat;line-height:39px;margin-top:0;padding-left:32px}.upgrade-to-pro .col-left,.upgrade-to-pro .col-right{display:flex;flex:1 1;flex-direction:column}.upgrade-to-pro .col-left .btn{display:inline-block;margin-top:auto;max-width:120px}.upgrade-to-pro .testimonial{grid-gap:15px;background:#04223f;border-radius:3px;color:#fff;display:grid;grid-template-columns:1fr 8fr;margin-top:auto;padding:21px 20px}.upgrade-to-pro .testimonial figure{align-self:center;margin:0}.upgrade-to-pro .testimonial figure img{width:60px}.upgrade-to-pro .testimonial h4{color:#fff;font-size:.9rem;font-weight:400;margin:0}.upgrade-to-pro .testimonial .testimonial-header{display:grid;grid-template-columns:2.1fr .5fr 1.4fr}.upgrade-to-pro .testimonial .testimonial-header span{display:inline-block;font-size:96%;font-weight:400;position:relative;right:-6px}.upgrade-to-pro .testimonial .testimonial-header span a{color:#fff}.upgrade-to-pro .testimonial .testimonial-header b{justify-self:end}.upgrade-to-pro .testimonial .testimonial-header b svg{padding:0 2px}.upgrade-to-pro .testimonial p{font-size:.8rem;margin-bottom:0}@media(max-width:1200px){.upgrade-to-pro .testimonial figure{align-self:auto}.upgrade-to-pro .testimonial p{line-height:18px}.upgrade-to-pro .testimonial .testimonial-header span{left:-10px;position:relative}.upgrade-to-pro .testimonial .testimonial-header b svg{width:12px}}.media-files select{margin-bottom:10px;min-height:180px}.media-files .excludes-wrap{align-items:flex-start;display:flex;flex-direction:column}.media-files .excludes-wrap textarea{align-self:self-start;flex-grow:1}.media-files .excludes-wrap .exclude-files-title{text-transform:uppercase}.media-files .excludes-wrap p{font-size:13px;margin:0 0 .5rem}.media-files .panel-header-wrap{min-height:56px;padding:0 1.25rem}.media-files .panel-header-wrap.has-summary-no-child button{display:none}.media-files .enabled .panel-header-wrap.has-summary-no-child button{display:block}.media-files h4 label{font-size:.9rem;font-weight:600}.media-files .panel-title{min-width:95px}.media-files .panel-summary{max-width:1023px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.media-files .panel-summary .empty-warning{color:red}.media-files .disabled .panel-summary{max-width:none;text-overflow:clip;white-space:normal}.media-files .disabled .panel-summary a{padding:0;text-decoration:underline}.media-files .panel-header-wrap.no-child{grid-template-columns:20px .4fr 4fr}.media-files .panel-header-wrap.has-summary-no-child{grid-template-columns:35px 180px 2fr 2fr}.media-files .has-divider .panel-header-wrap.has-summary-no-child{grid-template-columns:35px 180px 0 2fr 2fr}.media-files .has-divider .panel-header-wrap.has-summary-no-child .panel-header{grid-column:4/span 2}.media-files tr:nth-child(2n){background:initial}.media-files select{height:36px!important;margin:0 5px!important;min-height:auto;padding-left:8px!important;width:auto;width:120px!important}.media-files input[type=number]{font-size:.8rem!important;height:38px!important;margin:0!important}.media-files h4{margin:0}.media-files .panel-body{padding:32px 40px}.media-files .media-files-header{display:grid;grid-column:1/span 2;grid-template-columns:30px 2fr}.media-files .media-files-header label{margin:0}.media-files .media-files-header input[type=checkbox]{margin:0;position:relative;top:4px}.media-files .panel-header{display:grid;grid-column:4/span 2;grid-template-columns:2fr 32px;text-align:left}.media-files .disabled .panel-header{grid-column:3/span 2}.media-files .media-files-inner-wrap{grid-column-gap:25px;display:grid;grid-template-columns:repeat(2,1fr)}.media-files .media-files-options{border:1px solid #d6d6d6;border-radius:5px}.media-files .media-option{border-bottom:1px solid #d6d6d6;cursor:pointer;display:flex;line-height:1.4rem;padding:24px 20px;position:relative;text-align:left}.media-files .media-option strong{color:#052240;display:block;font-size:.8rem;text-transform:uppercase}.media-files .media-option .media-option-radio{margin-right:15px}.media-files .media-option:last-child{border-bottom:none}.media-files .media-option .media-date{flex-basis:100%;margin-left:30px;width:100%}.media-files .media-option .datepicker-container{position:relative}.media-files .option-wrap{flex-wrap:wrap}.media-files .action-tooltip{max-width:455px}.media-files .action-tooltip.place-right{margin-left:14px}.media-files .action-tooltip h4{font-size:.8rem;margin:0 0 .7rem}.media-files .action-tooltip p{margin:0}.media-files textarea{border-radius:5px;margin-bottom:0;max-width:100%;min-height:120px;width:100%}.media-files .migration-date{margin-top:1rem}.media-files .error{margin-bottom:0}.media-files .datepicker-wrap{display:flex;margin-top:1rem;max-width:280px}.media-files .datepicker-wrap input[type=number]{border:1px solid #d6d6d6;box-shadow:none}.media-files .datepicker-wrap input[type=number]:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.media-files .datepicker-wrap .media-date{background:#fff;border:1px solid #d6d6d6;border-radius:4px;box-shadow:0 2px 4px rgba(0,0,9,.05);display:inline-block;height:22px;padding:10px;transition:background .2s}.media-files .datepicker-wrap .media-date:hover{background:#e5e4e4;cursor:pointer}.media-files .datepicker-wrap .media-date:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.media-files .datepicker-wrap .media-date span{display:inline-block;vertical-align:top}.media-files .datepicker-wrap .media-date svg{height:20px;margin-left:5px;margin-right:15px;width:20px}.media-files .datepicker-wrap select{border-radius:4px;box-shadow:none}.media-files .datepicker-wrap button{padding:0 8px}.media-files .datepicker-wrap .datepicker-box{background:#fff;border:1px solid #d6d6d6;border-radius:5px;box-shadow:0 3px 60px rgba(0,0,0,.15),0 2px 5px rgba(0,0,0,.2);display:grid;min-height:280px;position:absolute;top:-290px;width:290px;z-index:99}.media-files .datepicker-wrap .components-datetime__time .components-datetime__time-wrapper{align-items:center;display:flex}.media-files .datepicker-wrap .components-button-group{display:flex;margin-left:auto;margin-right:6px}.media-files .datepicker-wrap .components-datetime__time-separator{font-weight:600;padding:0 4px!important}.media-files .datepicker-wrap .components-datetime__date .DayPickerNavigation_leftButton__horizontalDefault{left:23px}.media-files .datepicker-wrap .DayPickerNavigation_rightButton__horizontalDefault{right:7px}.media-files .datepicker-wrap .components-datetime__buttons{display:none}.media-files .datepicker-wrap .components-button{border-radius:0 4px 4px 0}.media-files .datepicker-wrap .components-button:first-child{border-radius:4px 0 0 4px}.media-files .datepicker-wrap .components-button.is-primary:focus:enabled{box-shadow:none}.media-files .datepicker-wrap .components-button.is-secondary{border:1px solid #d6d6d6;box-shadow:none;color:#444;height:36px!important}.media-files .datepicker-wrap .components-button.is-secondary:focus:enabled{border:1px solid #d6d6d6}.media-files .datepicker-wrap .components-button.is-secondary:hover{border-color:#236de7;color:#444}.media-files .datepicker-wrap .components-button.is-secondary:active{background:hsla(0,0%,78%,.4)!important}.media-files .datepicker-wrap .components-button.is-primary{background:rgba(128,189,212,.3);border:1px solid #236de7;box-shadow:none!important;color:#444;height:36px!important}.media-files .datepicker-wrap .components-button.is-primary:hover{background:rgba(128,189,212,.2);color:#444}.media-files .datepicker-wrap .components-button.is-primary:active{background:hsla(0,0%,78%,.5)!important}.media-files .datepicker-wrap .components-datetime{display:grid;padding-bottom:0}.media-files .datepicker-wrap .components-datetime__time{align-items:center;padding:15px 0 15px 15px;width:226px}.media-files .datepicker-wrap .components-datetime__date .CalendarDay__selected{background:#236de7}.media-files .datepicker-wrap .components-datetime__date .DayPickerNavigation_button__horizontalDefault:focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;color:#236de7;outline:2px solid transparent}.media-files .datepicker-wrap .components-datetime__time-field-month-select:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}.media-files .datepicker-wrap .close-wrap{height:20px;position:absolute;right:-6px;top:4px}.media-files .datepicker-wrap .close-wrap .close-picker{display:block;height:27px;left:87%;top:7px;width:42px}.media-files .datepicker-wrap .close-wrap .close-picker svg{height:18px;width:18px}.media-files .datepicker-wrap .close-wrap .close-picker:focus-visible{box-shadow:none;outline:none}.media-files .datepicker-wrap .close-wrap .close-picker:focus-visible svg{box-shadow:0 0 0 2px #fff,0 0 0 4px #4f8aec;outline:2px solid transparent}:root{--wp-admin-theme-color:#007cba;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-20:#005a87}.components-animate__appear{-webkit-animation:components-animate__appear-animation .1s cubic-bezier(0,0,.2,1) 0s;animation:components-animate__appear-animation .1s cubic-bezier(0,0,.2,1) 0s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@media (prefers-reduced-motion:reduce){.components-animate__appear{-webkit-animation-duration:1ms;animation-duration:1ms}}.components-animate__appear.is-from-top,.components-animate__appear.is-from-top.is-from-left{-webkit-transform-origin:top left;transform-origin:top left}.components-animate__appear.is-from-top.is-from-right{-webkit-transform-origin:top right;transform-origin:top right}.components-animate__appear.is-from-bottom,.components-animate__appear.is-from-bottom.is-from-left{-webkit-transform-origin:bottom left;transform-origin:bottom left}.components-animate__appear.is-from-bottom.is-from-right{-webkit-transform-origin:bottom right;transform-origin:bottom right}@-webkit-keyframes components-animate__appear-animation{0%{-webkit-transform:translateY(-2em) scaleY(0) scaleX(0);transform:translateY(-2em) scaleY(0) scaleX(0)}to{-webkit-transform:translateY(0) scaleY(1) scaleX(1);transform:translateY(0) scaleY(1) scaleX(1)}}@keyframes components-animate__appear-animation{0%{-webkit-transform:translateY(-2em) scaleY(0) scaleX(0);transform:translateY(-2em) scaleY(0) scaleX(0)}to{-webkit-transform:translateY(0) scaleY(1) scaleX(1);transform:translateY(0) scaleY(1) scaleX(1)}}.components-animate__slide-in{-webkit-animation:components-animate__slide-in-animation .1s cubic-bezier(0,0,.2,1);animation:components-animate__slide-in-animation .1s cubic-bezier(0,0,.2,1);-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@media (prefers-reduced-motion:reduce){.components-animate__slide-in{-webkit-animation-duration:1ms;animation-duration:1ms}}.components-animate__slide-in.is-from-left{-webkit-transform:translateX(100%);transform:translateX(100%)}.components-animate__slide-in.is-from-right{-webkit-transform:translateX(-100%);transform:translateX(-100%)}@-webkit-keyframes components-animate__slide-in-animation{to{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes components-animate__slide-in-animation{to{-webkit-transform:translateX(0);transform:translateX(0)}}.components-animate__loading{-webkit-animation:components-animate__loading 1.6s ease-in-out infinite;animation:components-animate__loading 1.6s ease-in-out infinite}@-webkit-keyframes components-animate__loading{0%{opacity:.5}50%{opacity:1}to{opacity:.5}}@keyframes components-animate__loading{0%{opacity:.5}50%{opacity:1}to{opacity:.5}}.components-angle-picker-control{width:50%}.components-angle-picker-control.components-base-control .components-base-control__label{display:block}.components-angle-picker-control__input-field[type=number]{border:1px solid #757575;border-radius:2px;box-shadow:0 0 0 transparent;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:16px;line-height:normal;max-width:100px;padding:6px 8px;transition:box-shadow .1s linear;width:calc(100% - 36px)}@media (prefers-reduced-motion:reduce){.components-angle-picker-control__input-field[type=number]{transition-duration:0s}}@media (min-width:600px){.components-angle-picker-control__input-field[type=number]{font-size:13px;line-height:normal}}.components-angle-picker-control__input-field[type=number]:focus{border-color:#007cba;border-color:var(--wp-admin-theme-color);box-shadow:0 0 0 .5px #007cba;box-shadow:0 0 0 .5px var(--wp-admin-theme-color);outline:2px solid transparent}.components-angle-picker-control__input-field[type=number]::-webkit-input-placeholder{color:rgba(14,28,46,.62)}.components-angle-picker-control__input-field[type=number]::-moz-placeholder{color:rgba(14,28,46,.62);opacity:1}.components-angle-picker-control__input-field[type=number]:-ms-input-placeholder{color:rgba(14,28,46,.62)}.is-dark-theme .components-angle-picker-control__input-field[type=number]::-webkit-input-placeholder{color:hsla(0,0%,100%,.65)}.is-dark-theme .components-angle-picker-control__input-field[type=number]::-moz-placeholder{color:hsla(0,0%,100%,.65);opacity:1}.is-dark-theme .components-angle-picker-control__input-field[type=number]:-ms-input-placeholder{color:hsla(0,0%,100%,.65)}.components-angle-picker-control__angle-circle{border:2px solid #555d66;border-radius:50%;cursor:grab;float:left;height:28px;margin-right:4px;width:28px}.components-angle-picker-control__angle-circle-indicator-wrapper{height:100%;position:relative;width:100%}.components-angle-picker-control__angle-circle-indicator{background:#555d66;border:3px solid #555d66;border-radius:50%;bottom:0;display:block;height:1px;left:0;margin:auto;position:absolute;right:0;top:-14px;width:1px}.components-autocomplete__popover .components-popover__content>div{padding:16px}.components-autocomplete__result.components-button{display:flex;height:auto;min-height:36px;text-align:left;width:100%}.components-autocomplete__result.components-button.is-selected{box-shadow:0 0 0 2px #007cba;box-shadow:0 0 0 2px var(--wp-admin-theme-color)}.components-base-control{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px}.components-base-control .components-base-control__field{margin-bottom:8px}.components-panel__row .components-base-control .components-base-control__field{margin-bottom:inherit}.components-base-control .components-base-control__label{display:inline-block;margin-bottom:8px}.components-base-control .components-base-control__help{font-style:italic;margin-top:-8px}.components-button-group{display:inline-block}.components-button-group .components-button{border-radius:0;box-shadow:inset 0 0 0 1px #1e1e1e;color:#1e1e1e;display:inline-flex}.components-button-group .components-button+.components-button{margin-left:-1px}.components-button-group .components-button:first-child{border-radius:2px 0 0 2px}.components-button-group .components-button:last-child{border-radius:0 2px 2px 0}.components-button-group .components-button.is-primary,.components-button-group .components-button:focus{position:relative;z-index:1}.components-button-group .components-button.is-primary{box-shadow:inset 0 0 0 1px #1e1e1e}.components-button{align-items:center;-webkit-appearance:none;background:none;border:0;border-radius:2px;box-sizing:border-box;color:#1e1e1e;cursor:pointer;display:inline-flex;font-size:13px;height:36px;margin:0;overflow:hidden;padding:6px 12px;text-decoration:none;transition:box-shadow .1s linear}@media (prefers-reduced-motion:reduce){.components-button{transition-duration:0s}}.components-button:hover,.components-button[aria-expanded=true]{color:#007cba;color:var(--wp-admin-theme-color)}.components-button[aria-disabled=true]:hover{color:initial}.components-button:focus:not(:disabled){box-shadow:0 0 0 1.5px #007cba;box-shadow:0 0 0 1.5px var(--wp-admin-theme-color);outline:1px solid transparent}.components-button.is-primary{background:#007cba;background:var(--wp-admin-theme-color);color:#fff;text-decoration:none;text-shadow:none;white-space:nowrap}.components-button.is-primary:hover:not(:disabled){background:#006ba1;background:var(--wp-admin-theme-color-darker-10);color:#fff}.components-button.is-primary:active:not(:disabled){background:#005a87;background:var(--wp-admin-theme-color-darker-20);border-color:#005a87;border-color:var(--wp-admin-theme-color-darker-20);color:#fff}.components-button.is-primary:focus:not(:disabled){box-shadow:inset 0 0 0 1px #fff,0 0 0 1.5px #007cba;box-shadow:inset 0 0 0 1px #fff,0 0 0 1.5px var(--wp-admin-theme-color);outline:1px solid transparent}.components-button.is-primary:disabled,.components-button.is-primary:disabled:active:enabled,.components-button.is-primary[aria-disabled=true],.components-button.is-primary[aria-disabled=true]:active:enabled,.components-button.is-primary[aria-disabled=true]:enabled{background:#007cba;background:var(--wp-admin-theme-color);border-color:#007cba;border-color:var(--wp-admin-theme-color);color:hsla(0,0%,100%,.4);opacity:1}.components-button.is-primary:disabled:active:enabled:focus:enabled,.components-button.is-primary:disabled:focus:enabled,.components-button.is-primary[aria-disabled=true]:active:enabled:focus:enabled,.components-button.is-primary[aria-disabled=true]:enabled:focus:enabled,.components-button.is-primary[aria-disabled=true]:focus:enabled{box-shadow:0 0 0 1px #fff,0 0 0 3px #007cba;box-shadow:0 0 0 1px #fff,0 0 0 3px var(--wp-admin-theme-color)}.components-button.is-primary.is-busy,.components-button.is-primary.is-busy:disabled,.components-button.is-primary.is-busy[aria-disabled=true]{background-image:linear-gradient(-45deg,#007cba 28%,#005a87 0,#005a87 72%,#007cba 0);background-image:linear-gradient(-45deg,var(--wp-admin-theme-color) 28%,var(--wp-admin-theme-color-darker-20) 28%,var(--wp-admin-theme-color-darker-20) 72%,var(--wp-admin-theme-color) 72%);background-size:100px 100%;border-color:#007cba;border-color:var(--wp-admin-theme-color);color:#fff}.components-button.is-secondary:active:not(:disabled),.components-button.is-tertiary:active:not(:disabled){background:#e7e8e9;box-shadow:none;color:#006ba1;color:var(--wp-admin-theme-color-darker-10)}.components-button.is-secondary:hover:not(:disabled),.components-button.is-tertiary:hover:not(:disabled){box-shadow:inset 0 0 0 1px #006ba1;box-shadow:inset 0 0 0 1px var(--wp-admin-theme-color-darker-10);color:#006ba1;color:var(--wp-admin-theme-color-darker-10)}.components-button.is-secondary:disabled,.components-button.is-secondary[aria-disabled=true],.components-button.is-secondary[aria-disabled=true]:hover,.components-button.is-tertiary:disabled,.components-button.is-tertiary[aria-disabled=true],.components-button.is-tertiary[aria-disabled=true]:hover{background:#f4f5f5;box-shadow:none;color:#828282;opacity:1;-webkit-transform:none;transform:none}.components-button.is-secondary{box-shadow:inset 0 0 0 1px #007cba;box-shadow:inset 0 0 0 1px var(--wp-admin-theme-color);outline:1px solid transparent}.components-button.is-secondary,.components-button.is-tertiary{background:transparent;color:#007cba;color:var(--wp-admin-theme-color);white-space:nowrap}.components-button.is-tertiary{padding:6px}.components-button.is-tertiary .dashicon{display:inline-block;flex:0 0 auto}.components-button.is-link{background:none;border:0;border-radius:0;box-shadow:none;color:#0073aa;height:auto;margin:0;outline:none;padding:0;text-align:left;text-decoration:underline;transition-duration:.05s;transition-property:border,background,color;transition-timing-function:ease-in-out}@media (prefers-reduced-motion:reduce){.components-button.is-link{transition-duration:0s}}.components-button.is-link:active:not(:disabled),.components-button.is-link:hover:not(:disabled){color:#00a0d2}.components-button.is-link:focus{box-shadow:0 0 0 1px #5b9dd9,0 0 1.5px 1px rgba(30,140,190,.8);color:#124964}.components-button.is-link.is-destructive{color:#d94f4f}.components-button:not([aria-disabled=true]):active{color:inherit}.components-button:disabled,.components-button[aria-disabled=true]{cursor:default;opacity:.3}.components-button.is-busy,.components-button.is-secondary.is-busy,.components-button.is-secondary.is-busy:disabled,.components-button.is-secondary.is-busy[aria-disabled=true]{-webkit-animation:components-button__busy-animation 2.5s linear infinite;animation:components-button__busy-animation 2.5s linear infinite;background-image:linear-gradient(-45deg,#fafafa 28%,#e0e0e0 0,#e0e0e0 72%,#fafafa 0);background-size:100px 100%;opacity:1}.components-button.is-small{font-size:11px;height:24px;line-height:22px;padding:0 8px}.components-button.is-small.has-icon:not(.has-text){padding:0 8px;width:24px}.components-button.has-icon{justify-content:center;min-width:36px;padding:6px}.components-button.has-icon .dashicon{display:inline-block;flex:0 0 auto}.components-button.has-icon.has-text{justify-content:left}.components-button.has-icon.has-text svg{margin-right:8px}.components-button.is-pressed{background:#1e1e1e;color:#fff}.components-button.is-pressed:focus:not(:disabled){box-shadow:inset 0 0 0 1px #fff,0 0 0 1.5px #007cba;box-shadow:inset 0 0 0 1px #fff,0 0 0 1.5px var(--wp-admin-theme-color);outline:2px solid transparent}.components-button.is-pressed:hover:not(:disabled){background:#1e1e1e}.components-button svg{fill:currentColor;outline:none}.components-button .components-visually-hidden{height:auto}@-webkit-keyframes components-button__busy-animation{0%{background-position:200px 0}}@keyframes components-button__busy-animation{0%{background-position:200px 0}}.components-checkbox-control__input[type=checkbox]{-webkit-appearance:none;appearance:none;background:#fff;border:2px solid #757575;border-radius:2px;box-shadow:0 0 0 transparent;clear:none;color:#1e1e1e;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:16px;height:24px;line-height:normal;line-height:0;margin:0 4px 0 0;outline:0;padding:6px 8px;padding:0!important;text-align:center;transition:box-shadow .1s linear;transition:none;transition:border-color .1s ease-in-out;vertical-align:top;width:24px}@media (min-width:600px){.components-checkbox-control__input[type=checkbox]{font-size:13px;line-height:normal}}.components-checkbox-control__input[type=checkbox]:focus{border-color:#007cba;border-color:var(--wp-admin-theme-color);box-shadow:0 0 0 .5px #007cba;box-shadow:0 0 0 .5px var(--wp-admin-theme-color)}.components-checkbox-control__input[type=checkbox]::-webkit-input-placeholder{color:rgba(14,28,46,.62)}.components-checkbox-control__input[type=checkbox]::-moz-placeholder{color:rgba(14,28,46,.62);opacity:1}.components-checkbox-control__input[type=checkbox]:-ms-input-placeholder{color:rgba(14,28,46,.62)}.is-dark-theme .components-checkbox-control__input[type=checkbox]::-webkit-input-placeholder{color:hsla(0,0%,100%,.65)}.is-dark-theme .components-checkbox-control__input[type=checkbox]::-moz-placeholder{color:hsla(0,0%,100%,.65);opacity:1}.is-dark-theme .components-checkbox-control__input[type=checkbox]:-ms-input-placeholder{color:hsla(0,0%,100%,.65)}.components-checkbox-control__input[type=checkbox]:focus{border-color:#757575;box-shadow:0 0 0 1px #757575}.components-checkbox-control__input[type=checkbox]:checked:focus{box-shadow:0 0 0 1.5px #757575}.components-checkbox-control__input[type=checkbox]:checked:before,.components-checkbox-control__input[type=checkbox][aria-checked=mixed]:before{color:#fff;margin:-3px -5px}@media (min-width:782px){.components-checkbox-control__input[type=checkbox]:checked:before,.components-checkbox-control__input[type=checkbox][aria-checked=mixed]:before{margin:-4px 0 0 -5px}}.components-checkbox-control__input[type=checkbox][aria-checked=mixed]{background:#007cba;background:var(--wp-admin-theme-color);border-color:#007cba;border-color:var(--wp-admin-theme-color)}.components-checkbox-control__input[type=checkbox][aria-checked=mixed]:before{speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:"\f460";display:inline-block;float:left;font:normal 30px/1 dashicons;vertical-align:middle;width:16px}@media (min-width:782px){.components-checkbox-control__input[type=checkbox][aria-checked=mixed]:before{float:none;font-size:21px}}.components-checkbox-control__input[type=checkbox][aria-checked=mixed]:focus{box-shadow:0 0 0 1.5px #555d66}@media (min-width:600px){.components-checkbox-control__input[type=checkbox]{height:20px;width:20px}}@media (prefers-reduced-motion:reduce){.components-checkbox-control__input[type=checkbox]{transition-duration:0s}}.components-checkbox-control__input[type=checkbox]:focus{box-shadow:0 0 0 2px #007cba;box-shadow:0 0 0 2px var(--wp-admin-theme-color);outline:2px solid transparent}.components-checkbox-control__input[type=checkbox]:checked{background:#007cba;background:var(--wp-admin-theme-color);border-color:#007cba;border-color:var(--wp-admin-theme-color)}.components-checkbox-control__input[type=checkbox]:checked::-ms-check{opacity:0}.components-checkbox-control__input[type=checkbox]:focus:checked{border:none}.components-checkbox-control__input[type=checkbox]:checked:before{content:none}.components-checkbox-control__input-container{display:inline-block;height:24px;margin-right:12px;position:relative;vertical-align:middle;width:24px}@media (min-width:600px){.components-checkbox-control__input-container{height:20px;width:20px}}svg.components-checkbox-control__checked{fill:#fff;cursor:pointer;height:24px;left:0;pointer-events:none;position:absolute;top:0;-webkit-user-select:none;user-select:none;width:24px}@media (min-width:600px){svg.components-checkbox-control__checked{left:-2px;top:-2px}}.components-circular-option-picker{display:inline-block;margin-top:.6rem;width:100%}.components-circular-option-picker .components-circular-option-picker__custom-clear-wrapper{display:flex;justify-content:flex-end}.components-circular-option-picker__option-wrapper{display:inline-block;height:28px;margin-bottom:12px;margin-right:12px;-webkit-transform:scale(1);transform:scale(1);transition:-webkit-transform .1s ease;transition:transform .1s ease;transition:transform .1s ease,-webkit-transform .1s ease;vertical-align:top;width:28px}@media (prefers-reduced-motion:reduce){.components-circular-option-picker__option-wrapper{transition-duration:0s}}.components-circular-option-picker__option-wrapper:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.components-circular-option-picker__option-wrapper>div{height:100%;width:100%}.components-circular-option-picker__option-wrapper:before{background:url("data:image/svg+xml;charset=utf-8,%3Csvg width='28' height='28' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6 8V6H4v2h2zm2 0V6h2v2H8zm2 8H8v-2h2v2zm2 0v-2h2v2h-2zm0 2v-2h-2v2H8v2h2v-2h2zm2 0v2h-2v-2h2zm2 0h-2v-2h2v2z' fill='%23555D65'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M18 18h2v-2h-2v-2h2v-2h-2v-2h2V8h-2v2h-2V8h-2v2h2v2h-2v2h2v2h2v2zm-2-4v-2h2v2h-2z' fill='%23555D65'/%3E%3Cpath d='M18 18v2h-2v-2h2z' fill='%23555D65'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M8 10V8H6v2H4v2h2v2H4v2h2v2H4v2h2v2H4v2h2v-2h2v2h2v-2h2v2h2v-2h2v2h2v-2h2v2h2v-2h2v-2h-2v-2h2v-2h-2v-2h2v-2h-2v-2h2V8h-2V6h2V4h-2v2h-2V4h-2v2h-2V4h-2v2h-2V4h-2v2h2v2h-2v2H8zm0 2v-2H6v2h2zm2 0v-2h2v2h-2zm0 2v-2H8v2H6v2h2v2H6v2h2v2h2v-2h2v2h2v-2h2v2h2v-2h2v2h2v-2h-2v-2h2v-2h-2v-2h2v-2h-2v-2h2V8h-2V6h-2v2h-2V6h-2v2h-2v2h2v2h-2v2h-2z' fill='%23555D65'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M4 0H2v2H0v2h2v2H0v2h2v2H0v2h2v2H0v2h2v2H0v2h2v2H0v2h2v2H0v2h2v-2h2v2h2v-2h2v2h2v-2h2v2h2v-2h2v2h2v-2h2v2h2v-2h2v2h2v-2h2v-2h-2v-2h2v-2h-2v-2h2v-2h-2v-2h2v-2h-2v-2h2V8h-2V6h2V4h-2V2h2V0h-2v2h-2V0h-2v2h-2V0h-2v2h-2V0h-2v2h-2V0h-2v2H8V0H6v2H4V0zm0 4V2H2v2h2zm2 0V2h2v2H6zm0 2V4H4v2H2v2h2v2H2v2h2v2H2v2h2v2H2v2h2v2H2v2h2v2h2v-2h2v2h2v-2h2v2h2v-2h2v2h2v-2h2v2h2v-2h2v2h2v-2h-2v-2h2v-2h-2v-2h2v-2h-2v-2h2v-2h-2v-2h2V8h-2V6h2V4h-2V2h-2v2h-2V2h-2v2h-2V2h-2v2h-2V2h-2v2H8v2H6z' fill='%23555D65'/%3E%3C/svg%3E");border-radius:50%;bottom:1px;content:"";left:1px;position:absolute;right:1px;top:1px;z-index:-1}.components-circular-option-picker__option{background:transparent;border:none;border-radius:50%;box-shadow:inset 0 0 0 14px;cursor:pointer;display:inline-block;height:100%;transition:box-shadow .1s ease;vertical-align:top;width:100%}@media (prefers-reduced-motion:reduce){.components-circular-option-picker__option{transition-duration:0s}}.components-circular-option-picker__option:hover{box-shadow:inset 0 0 0 14px!important}.components-circular-option-picker__option.is-pressed{box-shadow:inset 0 0 0 4px;overflow:visible;position:relative;z-index:1}.components-circular-option-picker__option.is-pressed+svg{border-radius:50%;left:2px;pointer-events:none;position:absolute;top:2px;z-index:2}.components-circular-option-picker__option:after{border:1px solid transparent;border-radius:50%;bottom:-1px;box-shadow:inset 0 0 0 1px rgba(0,0,0,.2);content:"";left:-1px;position:absolute;right:-1px;top:-1px}.components-circular-option-picker__option:focus:after{border:2px solid #606a73;border-radius:50%;box-shadow:inset 0 0 0 2px #fff;content:"";height:32px;left:-2px;position:absolute;top:-2px;width:32px}.components-circular-option-picker__option.components-button:focus{background-color:initial;box-shadow:inset 0 0 0 14px;outline:none}.components-circular-option-picker__button-action .components-circular-option-picker__option{background:#fff;color:#fff}.components-circular-option-picker__dropdown-link-action{margin-right:16px}.components-circular-option-picker__dropdown-link-action .components-button{line-height:22px}.component-color-indicator{border:1px solid #dadada;display:inline-block;height:16px;margin-left:.8rem;width:25px}.component-color-indicator+.component-color-indicator{margin-left:.5rem}.components-color-picker{width:100%}.components-color-picker *{box-sizing:border-box}.components-color-picker__saturation{padding-bottom:55%;position:relative;width:100%}.components-color-picker__body{padding:16px 16px 12px}.components-color-picker__controls{display:flex}.components-color-picker__alpha-pointer,.components-color-picker__hue-pointer,.components-color-picker__saturation-pointer{border:none;box-shadow:none;cursor:pointer;padding:0;position:absolute}.components-color-picker__swatch{background-image:linear-gradient(45deg,#ddd 25%,transparent 0),linear-gradient(-45deg,#ddd 25%,transparent 0),linear-gradient(45deg,transparent 75%,#ddd 0),linear-gradient(-45deg,transparent 75%,#ddd 0);background-position:0 0,0 5px,5px -5px,-5px 0;background-size:10px 10px;border-radius:50%;height:32px;margin-right:8px;overflow:hidden;position:relative;width:32px}.is-alpha-disabled .components-color-picker__swatch{height:12px;margin-top:0;width:12px}.components-color-picker__active{border-radius:50%;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);z-index:2}.components-color-picker__active,.components-color-picker__saturation-black,.components-color-picker__saturation-color,.components-color-picker__saturation-white{bottom:0;left:0;position:absolute;right:0;top:0}.components-color-picker__saturation-color{overflow:visible}.components-color-picker__saturation-white{background:linear-gradient(90deg,#fff,hsla(0,0%,100%,0))}.components-color-picker__saturation-black{background:linear-gradient(0deg,#000,transparent)}.components-button.components-color-picker__saturation-pointer{background-color:initial;border-radius:50%;box-shadow:0 0 0 1px #fff,inset 0 0 0 1px #000,0 0 0 2px #000;height:14px;padding:0;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:14px}.components-button.components-color-picker__saturation-pointer:focus:not(:disabled){box-shadow:0 0 0 2px #fff,inset 0 0 0 1px #000,0 0 0 3px #000}.components-color-picker__toggles{flex:1 1}.components-color-picker__alpha{background-image:linear-gradient(45deg,#ddd 25%,transparent 0),linear-gradient(-45deg,#ddd 25%,transparent 0),linear-gradient(45deg,transparent 75%,#ddd 0),linear-gradient(-45deg,transparent 75%,#ddd 0);background-position:0 0,0 5px,5px -5px,-5px 0;background-size:10px 10px}.components-color-picker__alpha-gradient,.components-color-picker__hue-gradient{bottom:0;left:0;position:absolute;right:0;top:0}.components-color-picker__alpha,.components-color-picker__hue{height:12px;position:relative}.is-alpha-enabled .components-color-picker__hue{margin-bottom:8px}.components-color-picker__alpha-bar,.components-color-picker__hue-bar{height:100%;margin:0 3px;padding:0 2px;position:relative}.components-color-picker__hue-gradient{background:linear-gradient(90deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red)}.components-color-picker__alpha-pointer,.components-color-picker__hue-pointer{background:#fff;border-radius:50%;box-shadow:0 1px 4px 0 rgba(0,0,0,.37);height:14px;left:0;-webkit-transform:translate(-7px,-1px);transform:translate(-7px,-1px);width:14px}.components-color-picker__hue-pointer,.components-color-picker__saturation-pointer{transition:box-shadow .1s linear}@media (prefers-reduced-motion:reduce){.components-color-picker__hue-pointer,.components-color-picker__saturation-pointer{transition-duration:0s}}.components-color-picker__saturation-pointer:focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #00a0d2,0 0 5px 0 #00a0d2,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4)}.components-color-picker__alpha-pointer:focus,.components-color-picker__hue-pointer:focus{border-color:#00a0d2;box-shadow:0 0 0 2px #00a0d2,0 0 3px 0 #00a0d2;outline:2px solid transparent;outline-offset:-2px}.components-color-picker__inputs-wrapper{align-items:flex-end;display:flex;margin:0 -4px;padding-top:16px}.components-color-picker__inputs-wrapper fieldset{border:none;flex:1 1;margin:0;padding:0}.components-color-picker__inputs-wrapper .components-color-picker__inputs-fields .components-text-control__input[type=number]{padding:6px 8px}.components-color-picker__inputs-field{width:100%}.components-color-picker__inputs-fields{direction:ltr;display:flex;flex-grow:1;margin-right:4px}.components-color-picker__inputs-fields .components-base-control+.components-base-control{margin-top:0}.components-color-picker__inputs-fields .components-base-control__field{margin:0 4px}.components-color-picker__inputs-toggle{height:30px;padding:0 5px}.components-custom-gradient-picker{margin-top:8px}.components-custom-gradient-picker__gradient-bar:not(.has-gradient){opacity:.4}.components-custom-gradient-picker__gradient-bar{border-radius:24px;height:24px;margin-bottom:8px;padding-left:3px;padding-right:21px;width:100%}.components-custom-gradient-picker__gradient-bar .components-custom-gradient-picker__markers-container{position:relative}.components-custom-gradient-picker__gradient-bar .components-custom-gradient-picker__insert-point{background:#fff;border-radius:50%;height:24px;min-width:24px;padding:2px;position:relative;width:24px}.components-custom-gradient-picker__gradient-bar .components-custom-gradient-picker__insert-point svg{height:100%;width:100%}.components-custom-gradient-picker__gradient-bar .components-custom-gradient-picker__control-point-button{border:2px solid #fff;border-radius:50%;height:18px;padding:0;position:absolute;top:3px;width:18px}.components-custom-gradient-picker__gradient-bar .components-custom-gradient-picker__control-point-button.is-active{background:#fafafa;border-color:#999;box-shadow:0 0 0 1px #fff,0 0 0 3px #007cba;box-shadow:0 0 0 1px #fff,0 0 0 3px var(--wp-admin-theme-color);color:#23282d}.components-custom-gradient-picker__color-picker-popover .components-custom-gradient-picker__remove-control-point{display:block;margin-bottom:8px;margin-left:auto;margin-right:auto}.components-custom-gradient-picker__inserter{width:100%}.components-custom-gradient-picker__liner-gradient-indicator{display:inline-block;flex:0 1 auto;height:20px;width:20px}.components-custom-gradient-picker__ui-line{display:flex;justify-content:space-between}.components-custom-gradient-picker .components-custom-gradient-picker__ui-line .components-base-control.components-angle-picker,.components-custom-gradient-picker .components-custom-gradient-picker__ui-line .components-base-control.components-custom-gradient-picker__type-picker{margin-bottom:0}.components-custom-gradient-picker .components-custom-gradient-picker__toolbar{border:none}.components-custom-gradient-picker .components-custom-gradient-picker__toolbar>div+div{margin-left:1px}.components-custom-gradient-picker .components-custom-gradient-picker__toolbar button.is-pressed>svg{background:#fff;border:1px solid #7e8993;border-radius:2px}.components-custom-select-control{position:relative}.components-custom-select-control__label{display:block;margin-bottom:8px}.components-custom-select-control__button{border:1px solid #757575;border-radius:2px;min-height:30px;min-width:130px;position:relative;text-align:left}.components-custom-select-control__button.components-custom-select-control__button{padding-right:24px}.components-custom-select-control__button:focus:not(:disabled){border-color:#007cba;border-color:var(--wp-admin-theme-color);box-shadow:0 0 0 .5px #007cba;box-shadow:0 0 0 .5px var(--wp-admin-theme-color)}.components-custom-select-control__button .components-custom-select-control__button-icon{height:100%;padding:0;position:absolute;right:0;top:0}.components-custom-select-control__menu{background-color:#fff;max-height:400px;min-width:100%;overflow:auto;padding:0;position:absolute;z-index:1000000}.components-custom-select-control__menu:focus{border:1px solid #1e1e1e;border-radius:2px;outline:none;transition:none}.components-custom-select-control__item{align-items:center;cursor:default;display:flex;list-style-type:none;padding:10px 5px 10px 25px}.components-custom-select-control__item.is-highlighted{background:#e2e4e7}.components-custom-select-control__item-icon{margin-left:-20px;margin-right:0}svg.dashicon{fill:currentColor;outline:none}.PresetDateRangePicker_panel{padding:0 22px 11px}.PresetDateRangePicker_button{background:0 0;border:2px solid #00a699;box-sizing:border-box;color:#00a699;cursor:pointer;font:inherit;font-weight:700;height:100%;line-height:normal;margin-right:8px;overflow:visible;padding:4px 12px;position:relative;text-align:center}.PresetDateRangePicker_button:active{outline:0}.PresetDateRangePicker_button__selected{background:#00a699;color:#fff}.SingleDatePickerInput{background-color:#fff;display:inline-block}.SingleDatePickerInput__withBorder{border:1px solid #dbdbdb;border-radius:2px}.SingleDatePickerInput__rtl{direction:rtl}.SingleDatePickerInput__disabled{background-color:#f2f2f2}.SingleDatePickerInput__block{display:block}.SingleDatePickerInput__showClearDate{padding-right:30px}.SingleDatePickerInput_clearDate{background:0 0;border:0;color:inherit;cursor:pointer;font:inherit;line-height:normal;margin:0 10px 0 5px;overflow:visible;padding:10px;position:absolute;right:0;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.SingleDatePickerInput_clearDate__default:focus,.SingleDatePickerInput_clearDate__default:hover{background:#dbdbdb;border-radius:50%}.SingleDatePickerInput_clearDate__small{padding:6px}.SingleDatePickerInput_clearDate__hide{visibility:hidden}.SingleDatePickerInput_clearDate_svg{fill:#82888a;height:12px;vertical-align:middle;width:15px}.SingleDatePickerInput_clearDate_svg__small{height:9px}.SingleDatePickerInput_calendarIcon{background:0 0;border:0;color:inherit;cursor:pointer;display:inline-block;font:inherit;line-height:normal;margin:0 5px 0 10px;overflow:visible;padding:10px;vertical-align:middle}.SingleDatePickerInput_calendarIcon_svg{fill:#82888a;height:15px;vertical-align:middle;width:14px}.SingleDatePicker{display:inline-block;position:relative}.SingleDatePicker__block{display:block}.SingleDatePicker_picker{background-color:#fff;position:absolute;z-index:1}.SingleDatePicker_picker__rtl{direction:rtl}.SingleDatePicker_picker__directionLeft{left:0}.SingleDatePicker_picker__directionRight{right:0}.SingleDatePicker_picker__portal{background-color:rgba(0,0,0,.3);height:100%;left:0;position:fixed;top:0;width:100%}.SingleDatePicker_picker__fullScreenPortal{background-color:#fff}.SingleDatePicker_closeButton{background:0 0;border:0;color:inherit;cursor:pointer;font:inherit;line-height:normal;overflow:visible;padding:15px;position:absolute;right:0;top:0;z-index:2}.SingleDatePicker_closeButton:focus,.SingleDatePicker_closeButton:hover{color:#b0b3b4;text-decoration:none}.SingleDatePicker_closeButton_svg{fill:#cacccd;height:15px;width:15px}.DayPickerKeyboardShortcuts_buttonReset{background:0 0;border:0;border-radius:0;color:inherit;cursor:pointer;font:inherit;font-size:14px;line-height:normal;overflow:visible;padding:0}.DayPickerKeyboardShortcuts_buttonReset:active{outline:0}.DayPickerKeyboardShortcuts_show{position:absolute;width:22px;z-index:2}.DayPickerKeyboardShortcuts_show__bottomRight{border-right:33px solid #00a699;border-top:26px solid transparent;bottom:0;right:0}.DayPickerKeyboardShortcuts_show__bottomRight:hover{border-right:33px solid #008489}.DayPickerKeyboardShortcuts_show__topRight{border-bottom:26px solid transparent;border-right:33px solid #00a699;right:0;top:0}.DayPickerKeyboardShortcuts_show__topRight:hover{border-right:33px solid #008489}.DayPickerKeyboardShortcuts_show__topLeft{border-bottom:26px solid transparent;border-left:33px solid #00a699;left:0;top:0}.DayPickerKeyboardShortcuts_show__topLeft:hover{border-left:33px solid #008489}.DayPickerKeyboardShortcuts_showSpan{color:#fff;position:absolute}.DayPickerKeyboardShortcuts_showSpan__bottomRight{bottom:0;right:-28px}.DayPickerKeyboardShortcuts_showSpan__topRight{right:-28px;top:1px}.DayPickerKeyboardShortcuts_showSpan__topLeft{left:-28px;top:1px}.DayPickerKeyboardShortcuts_panel{background:#fff;border:1px solid #dbdbdb;border-radius:2px;bottom:0;left:0;margin:33px;overflow:auto;padding:22px;position:absolute;right:0;top:0;z-index:2}.DayPickerKeyboardShortcuts_title{font-size:16px;font-weight:700;margin:0}.DayPickerKeyboardShortcuts_list{font-size:14px;list-style:none;padding:0}.DayPickerKeyboardShortcuts_close{position:absolute;right:22px;top:22px;z-index:2}.DayPickerKeyboardShortcuts_close:active{outline:0}.DayPickerKeyboardShortcuts_closeSvg{fill:#cacccd;height:15px;width:15px}.DayPickerKeyboardShortcuts_closeSvg:focus,.DayPickerKeyboardShortcuts_closeSvg:hover{fill:#82888a}.CalendarDay{box-sizing:border-box;cursor:pointer;font-size:14px;text-align:center}.CalendarDay:active{outline:0}.CalendarDay__defaultCursor{cursor:default}.CalendarDay__default{background:#fff;border:1px solid #e4e7e7;color:#484848}.CalendarDay__default:hover{background:#e4e7e7;border:1px double #e4e7e7;color:inherit}.CalendarDay__hovered_offset{background:#f4f5f5;border:1px double #e4e7e7;color:inherit}.CalendarDay__outside{background:#fff;border:0;color:#484848}.CalendarDay__outside:hover{border:0}.CalendarDay__blocked_minimum_nights{background:#fff;border:1px solid #eceeee;color:#cacccd}.CalendarDay__blocked_minimum_nights:active,.CalendarDay__blocked_minimum_nights:hover{background:#fff;color:#cacccd}.CalendarDay__highlighted_calendar{background:#ffe8bc;color:#484848}.CalendarDay__highlighted_calendar:active,.CalendarDay__highlighted_calendar:hover{background:#ffce71;color:#484848}.CalendarDay__selected_span{background:#66e2da;border:1px solid #33dacd;color:#fff}.CalendarDay__selected_span:active,.CalendarDay__selected_span:hover{background:#33dacd;border:1px solid #33dacd;color:#fff}.CalendarDay__last_in_range{border-right:#00a699}.CalendarDay__selected,.CalendarDay__selected:active,.CalendarDay__selected:hover{background:#00a699;border:1px solid #00a699;color:#fff}.CalendarDay__hovered_span,.CalendarDay__hovered_span:hover{background:#b2f1ec;border:1px solid #80e8e0;color:#007a87}.CalendarDay__hovered_span:active{background:#80e8e0;border:1px solid #80e8e0;color:#007a87}.CalendarDay__blocked_calendar,.CalendarDay__blocked_calendar:active,.CalendarDay__blocked_calendar:hover{background:#cacccd;border:1px solid #cacccd;color:#82888a}.CalendarDay__blocked_out_of_range,.CalendarDay__blocked_out_of_range:active,.CalendarDay__blocked_out_of_range:hover{background:#fff;border:1px solid #e4e7e7;color:#cacccd}.CalendarMonth{background:#fff;text-align:center;-webkit-user-select:none;user-select:none;vertical-align:top}.CalendarMonth_table{border-collapse:collapse;border-spacing:0}.CalendarMonth_verticalSpacing{border-collapse:initial}.CalendarMonth_caption{caption-side:top;color:#484848;font-size:18px;padding-bottom:37px;padding-top:22px;text-align:center}.CalendarMonth_caption__verticalScrollable{padding-bottom:7px;padding-top:12px}.CalendarMonthGrid{background:#fff;text-align:left;z-index:0}.CalendarMonthGrid__animating{z-index:1}.CalendarMonthGrid__horizontal{left:9px;position:absolute}.CalendarMonthGrid__vertical{margin:0 auto}.CalendarMonthGrid__vertical_scrollable{margin:0 auto;overflow-y:scroll}.CalendarMonthGrid_month__horizontal{display:inline-block;min-height:100%;vertical-align:top}.CalendarMonthGrid_month__hideForAnimation{opacity:0;pointer-events:none;position:absolute;z-index:-1}.CalendarMonthGrid_month__hidden{visibility:hidden}.DayPickerNavigation{position:relative;z-index:2}.DayPickerNavigation__horizontal{height:0}.DayPickerNavigation__verticalDefault{bottom:0;height:52px;left:0;position:absolute;width:100%}.DayPickerNavigation__verticalScrollableDefault{position:relative}.DayPickerNavigation_button{border:0;cursor:pointer;margin:0;padding:0;-webkit-user-select:none;user-select:none}.DayPickerNavigation_button__default{background-color:#fff;border:1px solid #e4e7e7;color:#757575}.DayPickerNavigation_button__default:focus,.DayPickerNavigation_button__default:hover{border:1px solid #c4c4c4}.DayPickerNavigation_button__default:active{background:#f2f2f2}.DayPickerNavigation_button__horizontalDefault{border-radius:3px;line-height:.78;padding:6px 9px;position:absolute;top:18px}.DayPickerNavigation_leftButton__horizontalDefault{left:22px}.DayPickerNavigation_rightButton__horizontalDefault{right:22px}.DayPickerNavigation_button__verticalDefault{background:#fff;box-shadow:0 0 5px 2px rgba(0,0,0,.1);display:inline-block;height:100%;padding:5px;position:relative;width:50%}.DayPickerNavigation_nextButton__verticalDefault{border-left:0}.DayPickerNavigation_nextButton__verticalScrollableDefault{width:100%}.DayPickerNavigation_svg__horizontal{fill:#82888a;display:block;height:19px;width:19px}.DayPickerNavigation_svg__vertical{fill:#484848;display:block;height:42px;width:42px}.DayPicker{position:relative;text-align:left}.DayPicker,.DayPicker__horizontal{background:#fff}.DayPicker__verticalScrollable{height:100%}.DayPicker__hidden{visibility:hidden}.DayPicker__withBorder{border-radius:3px;box-shadow:0 2px 6px rgba(0,0,0,.05),0 0 0 1px rgba(0,0,0,.07)}.DayPicker_portal__horizontal{box-shadow:none;left:50%;position:absolute;top:50%}.DayPicker_portal__vertical{position:static}.DayPicker_focusRegion{outline:0}.DayPicker_calendarInfo__horizontal,.DayPicker_wrapper__horizontal{display:inline-block;vertical-align:top}.DayPicker_weekHeaders{position:relative}.DayPicker_weekHeaders__horizontal{margin-left:9px}.DayPicker_weekHeader{color:#757575;position:absolute;text-align:left;top:62px;z-index:2}.DayPicker_weekHeader__vertical{left:50%}.DayPicker_weekHeader__verticalScrollable{background:#fff;border-bottom:1px solid #dbdbdb;display:table-row;left:0;margin-left:0;text-align:center;top:0;width:100%}.DayPicker_weekHeader_ul{font-size:14px;list-style:none;margin:1px 0;padding-left:0;padding-right:0}.DayPicker_weekHeader_li{display:inline-block;text-align:center}.DayPicker_transitionContainer{border-radius:3px;overflow:hidden;position:relative}.DayPicker_transitionContainer__horizontal{transition:height .2s ease-in-out}.DayPicker_transitionContainer__vertical{width:100%}.DayPicker_transitionContainer__verticalScrollable{bottom:0;height:100%;left:0;overflow-y:scroll;padding-top:20px;position:absolute;right:0;top:0}.DateInput{background:#fff;display:inline-block;margin:0;padding:0;position:relative;vertical-align:middle;width:130px}.DateInput__small{width:97px}.DateInput__block{width:100%}.DateInput__disabled{background:#f2f2f2;color:#dbdbdb}.DateInput_input{background-color:#fff;border:0;border-bottom:2px solid transparent;border-radius:0;color:#484848;font-size:19px;font-weight:200;line-height:24px;padding:11px 11px 9px;width:100%}.DateInput_input__small{font-size:15px;letter-spacing:.2px;line-height:18px;padding:7px 7px 5px}.DateInput_input__regular{font-weight:auto}.DateInput_input__readOnly{-webkit-user-select:none;user-select:none}.DateInput_input__focused{background:#fff;border:0;border-bottom:2px solid #008489;outline:0}.DateInput_input__disabled{background:#f2f2f2;font-style:italic}.DateInput_screenReaderMessage{clip:rect(0,0,0,0);border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.DateInput_fang{height:10px;left:22px;position:absolute;width:20px;z-index:2}.DateInput_fangShape{fill:#fff}.DateInput_fangStroke{stroke:#dbdbdb;fill:transparent}.DateRangePickerInput{background-color:#fff;display:inline-block}.DateRangePickerInput__disabled{background:#f2f2f2}.DateRangePickerInput__withBorder{border:1px solid #dbdbdb;border-radius:2px}.DateRangePickerInput__rtl{direction:rtl}.DateRangePickerInput__block{display:block}.DateRangePickerInput__showClearDates{padding-right:30px}.DateRangePickerInput_arrow{color:#484848;display:inline-block;vertical-align:middle}.DateRangePickerInput_arrow_svg{fill:#484848;height:24px;vertical-align:middle;width:24px}.DateRangePickerInput_clearDates{background:0 0;border:0;color:inherit;cursor:pointer;font:inherit;line-height:normal;margin:0 10px 0 5px;overflow:visible;padding:10px;position:absolute;right:0;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.DateRangePickerInput_clearDates__small{padding:6px}.DateRangePickerInput_clearDates_default:focus,.DateRangePickerInput_clearDates_default:hover{background:#dbdbdb;border-radius:50%}.DateRangePickerInput_clearDates__hide{visibility:hidden}.DateRangePickerInput_clearDates_svg{fill:#82888a;height:12px;vertical-align:middle;width:15px}.DateRangePickerInput_clearDates_svg__small{height:9px}.DateRangePickerInput_calendarIcon{background:0 0;border:0;color:inherit;cursor:pointer;display:inline-block;font:inherit;line-height:normal;margin:0 5px 0 10px;overflow:visible;padding:10px;vertical-align:middle}.DateRangePickerInput_calendarIcon_svg{fill:#82888a;height:15px;vertical-align:middle;width:14px}.DateRangePicker{display:inline-block;position:relative}.DateRangePicker__block{display:block}.DateRangePicker_picker{background-color:#fff;position:absolute;z-index:1}.DateRangePicker_picker__rtl{direction:rtl}.DateRangePicker_picker__directionLeft{left:0}.DateRangePicker_picker__directionRight{right:0}.DateRangePicker_picker__portal{background-color:rgba(0,0,0,.3);height:100%;left:0;position:fixed;top:0;width:100%}.DateRangePicker_picker__fullScreenPortal{background-color:#fff}.DateRangePicker_closeButton{background:0 0;border:0;color:inherit;cursor:pointer;font:inherit;line-height:normal;overflow:visible;padding:15px;position:absolute;right:0;top:0;z-index:2}.DateRangePicker_closeButton:focus,.DateRangePicker_closeButton:hover{color:#b0b3b4;text-decoration:none}.DateRangePicker_closeButton_svg{fill:#cacccd;height:15px;width:15px}.components-datetime{padding:0}.components-datetime .components-datetime__calendar-help{padding:16px}.components-datetime .components-datetime__calendar-help h4{margin:0}.components-datetime .components-datetime__buttons{display:flex;justify-content:space-between}.components-datetime .components-datetime__date-help-toggle{display:block;margin-left:auto}.components-datetime fieldset{border:0;margin:0;padding:0}.components-datetime input,.components-datetime select{border:1px solid #757575;border-radius:2px;box-shadow:0 0 0 transparent;transition:box-shadow .1s linear}@media (prefers-reduced-motion:reduce){.components-datetime input,.components-datetime select{transition-duration:0s}}.components-datetime .components-button,.components-datetime input[type=number],.components-datetime select{height:30px;margin-bottom:0;margin-top:0}.components-datetime__date{border-top:1px solid #e2e4e7;min-height:236px}.components-datetime__date .DayPickerNavigation_leftButton__horizontalDefault{left:0}.components-datetime__date .CalendarMonth_caption{font-size:13px}.components-datetime__date .CalendarMonth_table{border-collapse:initial;border-spacing:2px}.components-datetime__date .CalendarDay{border:none;border-radius:50%;font-size:13px;text-align:center}.components-datetime__date .CalendarDay:focus{box-shadow:inset 0 0 0 1.5px #007cba,inset 0 0 0 2.5px #fff;box-shadow:inset 0 0 0 1.5px var(--wp-admin-theme-color),inset 0 0 0 2.5px #fff;outline:2px solid transparent}.components-datetime__date .CalendarDay__selected{background:#007cba;background:var(--wp-admin-theme-color);border:2px solid transparent}.components-datetime__date .CalendarDay__selected:hover{background:#005a87;background:var(--wp-admin-theme-color-darker-20)}.components-datetime__date .CalendarDay__selected:focus{box-shadow:inset 0 0 0 1px #fff}.components-datetime__date .DayPickerNavigation_button__horizontalDefault{padding:2px 8px;top:20px}.components-datetime__date .DayPickerNavigation_button__horizontalDefault:focus{border-color:#007cba;border-color:var(--wp-admin-theme-color);box-shadow:0 0 0 .5px #007cba;box-shadow:0 0 0 .5px var(--wp-admin-theme-color);outline:2px solid transparent}.components-datetime__date .DayPicker_weekHeader{top:50px}.components-datetime__date .DayPicker_weekHeader .DayPicker_weekHeader_ul{margin:1px 0;padding-left:0;padding-right:0}.components-datetime__date.is-description-visible .DayPicker{visibility:hidden}.components-datetime__time{padding-bottom:16px}.components-datetime__time fieldset{margin-bottom:.5em;position:relative}.components-datetime__time fieldset+fieldset{margin-bottom:0}.components-datetime__time .components-datetime__time-field-am-pm fieldset{margin-top:0}.components-datetime__time .components-datetime__time-wrapper{display:flex}.components-datetime__time .components-datetime__time-wrapper .components-datetime__time-separator{color:#555d66;display:inline-block;padding:0 3px 0 0}.components-datetime__time .components-datetime__time-wrapper .components-datetime__time-field-time{direction:ltr}.components-datetime__time .components-datetime__time-wrapper .components-datetime__time-field select{margin-right:4px}.components-datetime__time .components-datetime__time-wrapper .components-datetime__time-field select:focus{position:relative;z-index:1}.components-datetime__time .components-datetime__time-wrapper .components-datetime__time-field input[type=number]{-moz-appearance:textfield;margin-right:4px;padding:2px;text-align:center}.components-datetime__time .components-datetime__time-wrapper .components-datetime__time-field input[type=number]:focus{position:relative;z-index:1}.components-datetime__time .components-datetime__time-wrapper .components-datetime__time-field input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.components-datetime__time.is-12-hour .components-datetime__time-field-day input{border-radius:4px 0 0 4px!important;margin:-4px 0 0!important}.components-datetime__time.is-12-hour .components-datetime__time-field-year input{border-radius:0 4px 4px 0!important}.components-datetime__time-legend{font-weight:600;margin-top:.5em}.components-datetime__time-legend.invisible{left:-999em;position:absolute;top:-999em}.components-datetime__time-field-day-input,.components-datetime__time-field-hours-input,.components-datetime__time-field-minutes-input{width:35px}.components-datetime__time-field-year-input{width:55px}.components-datetime__time-field-month-select{max-width:145px}.components-popover .components-datetime__date{padding-left:4px}.block-editor-dimension-control .components-base-control__field{align-items:center;display:flex}.block-editor-dimension-control .components-base-control__label{align-items:center;display:flex;margin-bottom:0;margin-right:1em}.block-editor-dimension-control .components-base-control__label .dashicon{margin-right:.5em}.block-editor-dimension-control.is-manual .components-base-control__label{width:10em}.components-disabled{pointer-events:none;position:relative}.components-disabled:after{bottom:0;content:"";left:0;position:absolute;right:0;top:0}.components-disabled *{pointer-events:none}body.is-dragging-components-draggable{cursor:move;cursor:grabbing!important}.components-draggable__invisible-drag-image{height:50px;left:-1000px;position:fixed;width:50px}.components-draggable__clone{background:transparent;padding:0;pointer-events:none;position:fixed;z-index:1000000000}.components-draggable__clone>*{margin-bottom:0!important;margin-top:0!important}.components-drop-zone{border:2px solid #0071a1;border-radius:2px;bottom:0;left:0;opacity:0;position:absolute;right:0;top:0;transition:opacity .3s,background-color .3s,visibility 0s .3s;visibility:hidden;z-index:40}@media (prefers-reduced-motion:reduce){.components-drop-zone{transition-duration:0s}}.components-drop-zone.is-active{opacity:1;transition:opacity .3s,background-color .3s;visibility:visible}@media (prefers-reduced-motion:reduce){.components-drop-zone.is-active{transition-duration:0s}}.components-drop-zone.is-dragging-over-element{background-color:rgba(0,113,161,.8)}.components-drop-zone__content{color:#fff;left:0;position:absolute;right:0;text-align:center;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);transition:-webkit-transform .2s ease-in-out;transition:transform .2s ease-in-out;transition:transform .2s ease-in-out,-webkit-transform .2s ease-in-out;width:100%;z-index:50}@media (prefers-reduced-motion:reduce){.components-drop-zone__content{transition-duration:0s}}.components-drop-zone.is-dragging-over-element .components-drop-zone__content{-webkit-transform:translateY(-50%) scale(1.05);transform:translateY(-50%) scale(1.05)}.components-drop-zone__content-icon,.components-drop-zone__content-text{display:block}.components-drop-zone__content-icon{fill:currentColor;line-height:0;margin:0 auto}.components-drop-zone__content-text{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif}.components-drop-zone__provider{height:100%}.components-dropdown{display:inline-block}.components-dropdown__content .components-popover__content>div{padding:12px}.components-dropdown-menu__popover .components-popover__content{width:200px}.components-dropdown-menu__menu{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px;line-height:1.4;width:100%}.components-dropdown-menu__menu .components-dropdown-menu__menu-item,.components-dropdown-menu__menu .components-menu-item{cursor:pointer;margin-bottom:4px;outline:none;padding:6px;width:100%}.components-dropdown-menu__menu .components-dropdown-menu__menu-item.has-separator,.components-dropdown-menu__menu .components-menu-item.has-separator{margin-top:6px;overflow:visible;position:relative}.components-dropdown-menu__menu .components-dropdown-menu__menu-item.has-separator:before,.components-dropdown-menu__menu .components-menu-item.has-separator:before{background-color:#e2e4e7;box-sizing:initial;content:"";display:block;height:1px;left:0;position:absolute;right:0;top:-3px}.components-dropdown-menu__menu .components-dropdown-menu__menu-item.is-active svg,.components-dropdown-menu__menu .components-menu-item.is-active svg{background:#1e1e1e;border-radius:1px;box-shadow:0 0 0 1px #1e1e1e;color:#fff}.components-dropdown-menu__menu .components-dropdown-menu__menu-item>svg,.components-dropdown-menu__menu .components-menu-item>svg{border-radius:2px;height:24px;width:24px}.components-dropdown-menu__menu .components-menu-item__button,.components-dropdown-menu__menu .components-menu-item__button.components-button{height:auto;min-height:36px;padding-left:40px;text-align:left}.components-dropdown-menu__menu .components-menu-item__button.components-button.has-icon,.components-dropdown-menu__menu .components-menu-item__button.has-icon{padding-left:8px}.components-dropdown-menu__menu .components-menu-group{margin:0 -12px;padding:12px}.components-dropdown-menu__menu .components-menu-group:first-child{margin-top:-12px}.components-dropdown-menu__menu .components-menu-group:last-child{margin-bottom:-12px}.components-dropdown-menu__menu .components-menu-group+.components-menu-group{border-top:1px solid #ccc;margin-top:0;padding:12px}.is-alternate .components-dropdown-menu__menu .components-menu-group+.components-menu-group{border-color:#1e1e1e}.components-dropdown-menu__menu.no-icons .components-menu-item__button.components-button{padding:0 12px}.components-external-link__icon{fill:currentColor;height:1.4em;margin:-.2em .1em 0;vertical-align:middle;width:1.4em}.components-focal-point-picker-wrapper{background-color:initial;border:1px solid #e2e4e7;height:200px;padding:14px;width:100%}.components-focal-point-picker{align-items:center;cursor:pointer;display:flex;height:100%;justify-content:center;position:relative;width:100%}.components-focal-point-picker img{height:auto;max-height:100%;max-width:100%;-webkit-user-select:none;user-select:none;width:auto}.components-focal-point-picker__icon_container{background-color:initial;cursor:grab;height:30px;opacity:.8;position:absolute;width:30px;will-change:transform;z-index:10000}.components-focal-point-picker__icon_container.is-dragging{cursor:grabbing}.components-focal-point-picker__icon{display:block;height:100%;left:-15px;position:absolute;top:-15px;width:100%}.components-focal-point-picker__icon .components-focal-point-picker__icon-outline{fill:#fff}.components-focal-point-picker__icon .components-focal-point-picker__icon-fill{fill:#007cba;fill:var(--wp-admin-theme-color)}.components-focal-point-picker_position-display-container{display:flex;margin:1em 0}.components-focal-point-picker_position-display-container .components-base-control__field{margin:0 1em 0 0}.components-focal-point-picker_position-display-container input[type=number].components-text-control__input{max-width:4em;padding:6px 4px}.components-focal-point-picker_position-display-container span{margin:0 0 0 .2em}.components-font-size-picker__controls{align-items:center;display:flex;flex-wrap:wrap;margin-bottom:24px;max-width:248px}.components-font-size-picker__controls .components-font-size-picker__number{border:1px solid #757575;border-radius:2px;box-shadow:0 0 0 transparent;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:16px;font-weight:500;height:30px;line-height:normal;margin:8px 8px 0 0;padding:6px 8px;transition:box-shadow .1s linear;width:54px}@media (prefers-reduced-motion:reduce){.components-font-size-picker__controls .components-font-size-picker__number{transition-duration:0s}}@media (min-width:600px){.components-font-size-picker__controls .components-font-size-picker__number{font-size:13px;line-height:normal}}.components-font-size-picker__controls .components-font-size-picker__number:focus{border-color:#007cba;border-color:var(--wp-admin-theme-color);box-shadow:0 0 0 .5px #007cba;box-shadow:0 0 0 .5px var(--wp-admin-theme-color);outline:2px solid transparent}.components-font-size-picker__controls .components-font-size-picker__number::-webkit-input-placeholder{color:rgba(14,28,46,.62)}.components-font-size-picker__controls .components-font-size-picker__number::-moz-placeholder{color:rgba(14,28,46,.62);opacity:1}.components-font-size-picker__controls .components-font-size-picker__number:-ms-input-placeholder{color:rgba(14,28,46,.62)}.is-dark-theme .components-font-size-picker__controls .components-font-size-picker__number::-webkit-input-placeholder{color:hsla(0,0%,100%,.65)}.is-dark-theme .components-font-size-picker__controls .components-font-size-picker__number::-moz-placeholder{color:hsla(0,0%,100%,.65);opacity:1}.is-dark-theme .components-font-size-picker__controls .components-font-size-picker__number:-ms-input-placeholder{color:hsla(0,0%,100%,.65)}.components-font-size-picker__controls .components-font-size-picker__number[value=""]+.components-button{cursor:default;opacity:.3;pointer-events:none}.components-font-size-picker__controls .components-font-size-picker__number-container{display:flex;flex-direction:column}.components-font-size-picker__controls .components-font-size-picker__select{margin-right:8px}.components-font-size-picker__controls .components-color-palette__clear{height:30px;margin-top:26px}.components-font-size-picker__custom-input .components-range-control__slider+.dashicon{height:30px;width:30px}.components-font-size-picker{border:0;margin:0;padding:0}.components-form-toggle{display:inline-block;position:relative}.components-form-toggle .components-form-toggle__track{background-color:#fff;border:2px solid #6c7781;border-radius:9px;box-sizing:border-box;content:"";display:inline-block;height:18px;transition:background .2s ease;vertical-align:top;width:36px}@media (prefers-reduced-motion:reduce){.components-form-toggle .components-form-toggle__track{transition-duration:0s}}.components-form-toggle .components-form-toggle__thumb{background-color:#6c7781;border:5px solid #6c7781;border-radius:50%;box-sizing:border-box;display:block;height:10px;left:4px;position:absolute;top:4px;transition:-webkit-transform .1s ease;transition:transform .1s ease;transition:transform .1s ease,-webkit-transform .1s ease;width:10px}@media (prefers-reduced-motion:reduce){.components-form-toggle .components-form-toggle__thumb{transition-duration:0s}}.components-form-toggle:hover .components-form-toggle__track{border:2px solid #555d66}.components-form-toggle:hover .components-form-toggle__thumb{background-color:#555d66;border:5px solid #6c7781}.components-form-toggle:hover .components-form-toggle__off{color:#555d66}.components-form-toggle.is-checked .components-form-toggle__track{background-color:#007cba;background-color:var(--wp-admin-theme-color);border:9px solid transparent}.components-form-toggle__input:focus+.components-form-toggle__track{box-shadow:0 0 0 2px #fff,0 0 0 3.5px #007cba;box-shadow:0 0 0 2px #fff,0 0 0 3.5px var(--wp-admin-theme-color);outline:2px solid transparent;outline-offset:2px}.components-form-toggle.is-checked .components-form-toggle__thumb{background-color:#fff;border-width:0;-webkit-transform:translateX(18px);transform:translateX(18px)}.components-disabled .components-form-toggle{opacity:.3}.components-form-toggle input.components-form-toggle__input[type=checkbox]{border:none;height:100%;left:0;margin:0;opacity:0;padding:0;position:absolute;top:0;width:100%;z-index:1}.components-form-toggle input.components-form-toggle__input[type=checkbox]:checked{background:none}.components-form-toggle input.components-form-toggle__input[type=checkbox]:before{content:""}.components-form-token-field__input-container{align-items:flex-start;border:1px solid #757575;border-radius:2px;box-shadow:0 0 0 transparent;cursor:text;display:flex;flex-wrap:wrap;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:16px;line-height:normal;margin:0 0 8px;padding:4px;transition:box-shadow .1s linear;width:100%}@media (prefers-reduced-motion:reduce){.components-form-token-field__input-container{transition-duration:0s}}@media (min-width:600px){.components-form-token-field__input-container{font-size:13px;line-height:normal}}.components-form-token-field__input-container:focus{border-color:#007cba;border-color:var(--wp-admin-theme-color);box-shadow:0 0 0 .5px #007cba;box-shadow:0 0 0 .5px var(--wp-admin-theme-color);outline:2px solid transparent}.components-form-token-field__input-container::-webkit-input-placeholder{color:rgba(14,28,46,.62)}.components-form-token-field__input-container::-moz-placeholder{color:rgba(14,28,46,.62);opacity:1}.components-form-token-field__input-container:-ms-input-placeholder{color:rgba(14,28,46,.62)}.is-dark-theme .components-form-token-field__input-container::-webkit-input-placeholder{color:hsla(0,0%,100%,.65)}.is-dark-theme .components-form-token-field__input-container::-moz-placeholder{color:hsla(0,0%,100%,.65);opacity:1}.is-dark-theme .components-form-token-field__input-container:-ms-input-placeholder{color:hsla(0,0%,100%,.65)}.components-form-token-field__input-container.is-disabled{background:#e2e4e7;border-color:#ccd0d4}.components-form-token-field__input-container input[type=text].components-form-token-field__input{background:inherit;border:0;box-shadow:none;color:#23282d;display:inline-block;margin-left:4px;max-width:100%;min-height:24px;padding:0;width:100%}.components-form-token-field.is-active .components-form-token-field__input-container input[type=text].components-form-token-field__input,.components-form-token-field__input-container input[type=text].components-form-token-field__input:focus{box-shadow:none;outline:none}.components-form-token-field__input-container .components-form-token-field__token+input[type=text].components-form-token-field__input{width:auto}.components-form-token-field__label{display:inline-block;margin-bottom:4px}.components-form-token-field__help{font-style:italic}.components-form-token-field__token{color:#32373c;display:flex;font-size:13px;margin:2px 4px 2px 0;overflow:hidden}.components-form-token-field__token.is-success .components-form-token-field__remove-token,.components-form-token-field__token.is-success .components-form-token-field__token-text{background:#4ab866}.components-form-token-field__token.is-error .components-form-token-field__remove-token,.components-form-token-field__token.is-error .components-form-token-field__token-text{background:#d94f4f}.components-form-token-field__token.is-validating .components-form-token-field__remove-token,.components-form-token-field__token.is-validating .components-form-token-field__token-text{color:#555d66}.components-form-token-field__token.is-borderless{padding:0 16px 0 0;position:relative}.components-form-token-field__token.is-borderless .components-form-token-field__token-text{background:transparent;color:#007cba;color:var(--wp-admin-theme-color)}.components-form-token-field__token.is-borderless .components-form-token-field__remove-token{background:transparent;color:#555d66;position:absolute;right:0;top:1px}.components-form-token-field__token.is-borderless.is-success .components-form-token-field__token-text{color:#4ab866}.components-form-token-field__token.is-borderless.is-error .components-form-token-field__token-text{border-radius:4px 0 0 4px;color:#d94f4f;padding:0 4px 0 6px}.components-form-token-field__token.is-borderless.is-validating .components-form-token-field__token-text{color:#23282d}.components-form-token-field__token.is-disabled .components-form-token-field__remove-token{cursor:default}.components-form-token-field__remove-token.components-button,.components-form-token-field__token-text{background:#e2e4e7;display:inline-block;height:auto;line-height:24px;transition:all .2s cubic-bezier(.4,1,.4,1)}@media (prefers-reduced-motion:reduce){.components-form-token-field__remove-token.components-button,.components-form-token-field__token-text{-webkit-animation-duration:1ms;animation-duration:1ms;transition-duration:0s}}.components-form-token-field__token-text{border-radius:12px 0 0 12px;overflow:hidden;padding:0 4px 0 8px;text-overflow:ellipsis;white-space:nowrap}.components-form-token-field__remove-token.components-button{border-radius:0 12px 12px 0;color:#555d66;cursor:pointer;line-height:10px;overflow:initial;padding:0 2px}.components-form-token-field__remove-token.components-button:hover{color:#32373c}.components-form-token-field__suggestions-list{border-top:1px solid #6c7781;flex:1 0 100%;list-style:none;margin:4px -4px -4px;max-height:9em;min-width:100%;overflow-y:scroll;padding-top:3px;transition:all .15s ease-in-out}@media (prefers-reduced-motion:reduce){.components-form-token-field__suggestions-list{transition-duration:0s}}.components-form-token-field__suggestion{color:#555d66;cursor:pointer;display:block;font-size:13px;padding:4px 8px}.components-form-token-field__suggestion.is-selected{background:#0071a1;color:#fff}.components-form-token-field__suggestion-match{text-decoration:underline}@media (min-width:600px){.components-guide{width:600px}}.components-guide .components-modal__header{background:none;border-bottom:none;margin:0;padding:0;width:100%}.components-guide .components-modal__header .components-button{align-self:flex-start;margin:8px 8px 0 0;position:static}.components-guide .components-modal__header .components-button:hover svg{fill:#fff}.components-guide__container{display:flex;flex-direction:column;justify-content:space-between;margin-top:-60px;min-height:100%}.components-guide__page{display:flex;flex-direction:column;justify-content:center;position:relative}@media (min-width:600px){.components-guide__page{min-height:300px}}.components-guide__footer{align-content:center;display:flex;height:30px;justify-content:center;margin:0 0 24px;padding:0 32px;position:relative;width:100%}@media (max-width:600px){.components-guide__footer{bottom:0;position:absolute}}.components-guide__page-control{margin:8px 0;text-align:center}.components-guide__page-control li{display:inline-block}.components-guide__page-control .components-button{height:30px;min-width:20px}.components-guide .components-modal__content{padding:0}.components-modal__frame.components-guide{border:none;height:80vh;max-height:575px;min-width:312px}@media (max-width:600px){.components-modal__frame.components-guide{bottom:5%;left:16px;margin:0 auto;right:16px;top:5%}}.components-button.components-guide__back-button,.components-button.components-guide__finish-button,.components-button.components-guide__forward-button{height:30px;position:absolute}.components-button.components-guide__back-button,.components-button.components-guide__forward-button{font-size:13px;padding:4px 2px}.components-button.components-guide__back-button.has-text svg,.components-button.components-guide__forward-button.has-text svg{margin:0}.components-button.components-guide__back-button:hover,.components-button.components-guide__forward-button:hover{text-decoration:underline}.components-button.components-guide__back-button{left:32px}.components-button.components-guide__forward-button{color:#1386bf;font-weight:700;right:32px}.components-button.components-guide__finish-button{right:32px}.components-button.components-guide__inline-finish-button{display:none}.components-navigate-regions.is-focusing-regions [role=region]{position:relative}.components-navigate-regions.is-focusing-regions [role=region]:focus:after{bottom:0;box-shadow:inset 0 0 0 4px #33b3db;content:"";left:0;outline:4px solid transparent;pointer-events:none;position:absolute;right:0;top:0}@supports (outline-offset:1px){.components-navigate-regions.is-focusing-regions [role=region]:focus:after{content:none}.components-navigate-regions.is-focusing-regions [role=region]:focus{outline-color:#33b3db;outline-offset:-4px;outline-style:solid;outline-width:4px}}.components-menu-group+.components-menu-group{border-top:1px solid #1e1e1e;margin-top:8px;padding-top:8px}.components-menu-group__label{color:#757575;font-size:11px;font-weight:500;margin-bottom:12px;padding:0;text-transform:uppercase}.components-menu-item__button,.components-menu-item__button.components-button{width:100%}.components-menu-item__button .components-menu-items__item-icon,.components-menu-item__button .dashicon,.components-menu-item__button svg.components-menu-items__item-icon,.components-menu-item__button.components-button .components-menu-items__item-icon,.components-menu-item__button.components-button .dashicon,.components-menu-item__button.components-button svg.components-menu-items__item-icon,.components-menu-item__button.components-button>span>svg,.components-menu-item__button>span>svg{margin-right:8px}.components-menu-item__button .components-menu-items__item-icon,.components-menu-item__button.components-button .components-menu-items__item-icon{display:inline-block;flex:0 0 auto}.components-menu-item__info-wrapper{display:flex;flex-direction:column}.components-menu-item__info{color:#757575;font-size:12px;margin-top:4px}.components-menu-item__shortcut{align-self:center;color:currentColor;display:none;margin-left:auto;margin-right:0;padding-left:12px}@media (min-width:480px){.components-menu-item__shortcut{display:inline}}.components-menu-items-choice,.components-menu-items-choice.components-button{padding-left:40px}.components-menu-items-choice svg,.components-menu-items-choice.components-button svg{margin-right:8px}.components-menu-items-choice.components-button.has-icon,.components-menu-items-choice.has-icon{padding-left:8px}.components-modal__screen-overlay{-webkit-animation:edit-post__fade-in-animation .2s ease-out 0s;animation:edit-post__fade-in-animation .2s ease-out 0s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;background-color:rgba(0,0,0,.7);bottom:0;left:0;position:fixed;right:0;top:0;z-index:100000}@media (prefers-reduced-motion:reduce){.components-modal__screen-overlay{-webkit-animation-duration:1ms;animation-duration:1ms}}.components-modal__frame{background:#fff;border:1px solid #e2e4e7;bottom:0;box-shadow:0 3px 30px rgba(25,30,35,.2);box-sizing:border-box;left:0;margin:0;overflow:auto;position:absolute;right:0;top:0}@media (min-width:600px){.components-modal__frame{-webkit-animation:components-modal__appear-animation .1s ease-out;animation:components-modal__appear-animation .1s ease-out;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;bottom:auto;left:50%;max-height:calc(100% - 120px);max-width:calc(100% - 32px);min-width:360px;right:auto;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}}@media (min-width:600px) and (prefers-reduced-motion:reduce){.components-modal__frame{-webkit-animation-duration:1ms;animation-duration:1ms}}@-webkit-keyframes components-modal__appear-animation{0%{margin-top:32px}to{margin-top:0}}@keyframes components-modal__appear-animation{0%{margin-top:32px}to{margin-top:0}}.components-modal__header{align-items:center;background:#fff;border-bottom:1px solid #e2e4e7;box-sizing:border-box;display:flex;flex-direction:row;height:60px;justify-content:space-between;margin:0 -24px 24px;padding:0 24px;position:relative;position:-webkit-sticky;position:sticky;top:0;z-index:10}@supports (-ms-ime-align:auto){.components-modal__header{position:fixed;width:100%}}.components-modal__header .components-modal__header-heading{font-size:1rem;font-weight:600}.components-modal__header h1{line-height:1;margin:0}.components-modal__header .components-button{left:8px;position:relative}.components-modal__header-heading-container{align-items:center;display:flex;flex-direction:row;flex-grow:1;justify-content:left}.components-modal__header-icon-container{display:inline-block}.components-modal__header-icon-container svg{max-height:36px;max-width:36px;padding:8px}.components-modal__content{box-sizing:border-box;height:100%;padding:0 24px 24px}@supports (-ms-ime-align:auto){.components-modal__content{padding-top:60px}}.components-notice{align-items:center;background-color:#e5f5fa;border-left:4px solid #00a0d2;display:flex;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px;margin:5px 15px 2px;padding:8px 12px}.components-notice.is-dismissible{padding-right:36px;position:relative}.components-notice.is-success{background-color:#eff9f1;border-left-color:#4ab866}.components-notice.is-warning{background-color:#fef8ee;border-left-color:#f0b849}.components-notice.is-error{background-color:#f9e2e2;border-left-color:#d94f4f}.components-notice__content{flex-grow:1;margin:4px 25px 4px 0}.components-notice__action.components-button,.components-notice__action.components-button.is-link{margin-left:12px}.components-notice__action.components-button.is-secondary{vertical-align:initial}.components-notice__dismiss{align-self:flex-start;color:#6c7781;flex-shrink:0}.components-notice__dismiss:not(:disabled):not([aria-disabled=true]):focus,.components-notice__dismiss:not(:disabled):not([aria-disabled=true]):not(.is-secondary):active,.components-notice__dismiss:not(:disabled):not([aria-disabled=true]):not(.is-secondary):hover{background-color:initial;color:#191e23}.components-notice__dismiss:not(:disabled):not([aria-disabled=true]):not(.is-secondary):hover{box-shadow:none}.components-notice-list{box-sizing:border-box;max-width:100vw;z-index:29}.components-notice-list .components-notice__content{line-height:2;margin-bottom:12px;margin-top:12px}.components-notice-list .components-notice__action.components-button{display:block;margin-left:0;margin-top:8px}.components-panel{background:#fff;border:1px solid #e2e4e7}.components-panel>.components-panel__body:first-child,.components-panel>.components-panel__header:first-child{margin-top:-1px}.components-panel>.components-panel__body:last-child,.components-panel>.components-panel__header:last-child{border-bottom-width:0}.components-panel+.components-panel{margin-top:-1px}.components-panel__body{border-bottom:1px solid #e2e4e7;border-top:1px solid #e2e4e7}.components-panel__body h3{margin:0 0 .5em}.components-panel__body.is-opened{padding:16px}.components-panel__header{align-items:center;border-bottom:1px solid #e2e4e7;border-top:1px solid #e2e4e7;display:flex;height:48px;justify-content:space-between;padding:0 16px}.components-panel__header h2{color:inherit;font-size:inherit;margin:0}.components-panel__body+.components-panel__body,.components-panel__body+.components-panel__header,.components-panel__header+.components-panel__body,.components-panel__header+.components-panel__header{margin-top:-1px}.components-panel__body>.components-panel__body-title{display:block;font-size:inherit;margin-bottom:0;margin-top:0;padding:0;transition:background .1s ease-in-out}@media (prefers-reduced-motion:reduce){.components-panel__body>.components-panel__body-title{transition-duration:0s}}.components-panel__body.is-opened>.components-panel__body-title{margin:-16px -16px 5px}.components-panel__body>.components-panel__body-title:hover{background:#f3f4f5;border:none}.components-panel__body-toggle.components-button{border:none;box-shadow:none;color:#1e1e1e;font-weight:500;height:auto;outline:none;padding:16px;position:relative;text-align:left;transition:background .1s ease-in-out;width:100%}@media (prefers-reduced-motion:reduce){.components-panel__body-toggle.components-button{transition-duration:0s}}.components-panel__body-toggle.components-button:focus{border-radius:0;box-shadow:inset 0 0 0 1.5px #007cba;box-shadow:inset 0 0 0 1.5px var(--wp-admin-theme-color)}.components-panel__body-toggle.components-button .components-panel__arrow{fill:currentColor;color:#191e23;position:absolute;right:16px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);transition:color .1s ease-in-out}@media (prefers-reduced-motion:reduce){.components-panel__body-toggle.components-button .components-panel__arrow{transition-duration:0s}}body.rtl .components-panel__body-toggle.components-button .dashicons-arrow-right{-ms-filter:fliph;-webkit-filter:FlipH;filter:FlipH;margin-top:-10px;-webkit-transform:scaleX(-1);transform:scaleX(-1)}.components-panel__icon{color:#555d66;margin:-2px 0 -2px 6px}.components-panel__body-toggle-icon{margin-right:-5px}.components-panel__color-title{float:left;height:19px}.components-panel__row{align-items:center;display:flex;justify-content:space-between;margin-top:20px}.components-panel__row select{min-width:0}.components-panel__row label{flex-shrink:0;margin-right:10px;max-width:75%}.components-panel__row:empty,.components-panel__row:first-of-type{margin-top:0}.components-panel .circle-picker{padding-bottom:20px}.components-placeholder.components-placeholder{-moz-font-smoothing:subpixel-antialiased;-webkit-font-smoothing:subpixel-antialiased;background-color:#fff;border-radius:2px;box-shadow:inset 0 0 0 1px #1e1e1e;color:#1e1e1e;margin:0;min-height:200px;outline:1px solid transparent;padding:1em;position:relative;text-align:left;width:100%}@supports ((position:-webkit-sticky) or (position:sticky)){.components-placeholder.components-placeholder{align-items:flex-start;display:flex;flex-direction:column;justify-content:center}}.components-placeholder.components-placeholder .components-base-control__label{font-size:13px}.components-placeholder__error,.components-placeholder__fieldset,.components-placeholder__instructions,.components-placeholder__label{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px}.components-placeholder__label{align-items:center;display:flex;font-weight:600;margin-bottom:16px}.components-placeholder__label .block-editor-block-icon,.components-placeholder__label .dashicon,.components-placeholder__label>svg{fill:currentColor;margin-right:1ch}.components-placeholder__fieldset,.components-placeholder__fieldset form{display:flex;flex-direction:row;flex-wrap:wrap;width:100%}.components-placeholder__fieldset form p,.components-placeholder__fieldset p{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px}.components-placeholder__fieldset.is-column-layout,.components-placeholder__fieldset.is-column-layout form{flex-direction:column}.components-placeholder__input[type=url]{border:1px solid #757575;border-radius:2px;box-shadow:0 0 0 transparent;flex:1 1 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:16px;line-height:normal;margin:0 8px 0 0;padding:6px 8px;transition:box-shadow .1s linear}@media (prefers-reduced-motion:reduce){.components-placeholder__input[type=url]{transition-duration:0s}}@media (min-width:600px){.components-placeholder__input[type=url]{font-size:13px;line-height:normal}}.components-placeholder__input[type=url]:focus{border-color:#007cba;border-color:var(--wp-admin-theme-color);box-shadow:0 0 0 .5px #007cba;box-shadow:0 0 0 .5px var(--wp-admin-theme-color);outline:2px solid transparent}.components-placeholder__input[type=url]::-webkit-input-placeholder{color:rgba(14,28,46,.62)}.components-placeholder__input[type=url]::-moz-placeholder{color:rgba(14,28,46,.62);opacity:1}.components-placeholder__input[type=url]:-ms-input-placeholder{color:rgba(14,28,46,.62)}.is-dark-theme .components-placeholder__input[type=url]::-webkit-input-placeholder{color:hsla(0,0%,100%,.65)}.is-dark-theme .components-placeholder__input[type=url]::-moz-placeholder{color:hsla(0,0%,100%,.65);opacity:1}.is-dark-theme .components-placeholder__input[type=url]:-ms-input-placeholder{color:hsla(0,0%,100%,.65)}.components-placeholder__instructions{margin-bottom:1em}.components-placeholder__error{margin-top:1em;width:100%}.components-placeholder__preview img{margin:3%;width:50%}.components-placeholder__fieldset .components-button{margin-bottom:12px;margin-right:12px}.components-placeholder__fieldset .components-button:last-child{margin-bottom:0;margin-right:0}.components-placeholder__fieldset .components-button:not(.is-link)~.components-button.is-link{margin-left:10px;margin-right:10px}.components-placeholder__fieldset .components-button:not(.is-link)~.components-button.is-link:last-child{margin-right:0}.components-placeholder.is-large .components-placeholder__label{font-size:18pt;font-weight:400}.components-placeholder.is-medium .components-placeholder__instructions,.components-placeholder.is-small .components-placeholder__instructions{display:none}.components-placeholder.is-medium .components-placeholder__fieldset,.components-placeholder.is-medium .components-placeholder__fieldset form,.components-placeholder.is-small .components-placeholder__fieldset,.components-placeholder.is-small .components-placeholder__fieldset form{flex-direction:column}.components-placeholder.is-medium .components-placeholder__fieldset .components-button,.components-placeholder.is-small .components-placeholder__fieldset .components-button{margin-right:auto}.components-placeholder.is-small .components-button{padding:0 8px 2px}
+
+/*!rtl:begin:ignore*/.components-popover{left:0;opacity:0;position:fixed;top:0;z-index:1000000}.components-popover.is-expanded,.components-popover[data-x-axis][data-y-axis]{opacity:1}.components-popover.is-expanded{bottom:0;left:0;right:0;top:0;z-index:1000000!important}.components-popover:not(.is-without-arrow){margin-left:2px}.components-popover:not(.is-without-arrow):before{border:8px solid #1e1e1e}.components-popover:not(.is-without-arrow):after{border:8px solid #fff}.components-popover:not(.is-without-arrow):after,.components-popover:not(.is-without-arrow):before{content:"";height:0;line-height:0;position:absolute;width:0}.components-popover:not(.is-without-arrow)[data-y-axis=top]{margin-top:-8px}.components-popover:not(.is-without-arrow)[data-y-axis=top]:before{bottom:-8px}.components-popover:not(.is-without-arrow)[data-y-axis=top]:after{bottom:-6px}.components-popover:not(.is-without-arrow)[data-y-axis=top]:after,.components-popover:not(.is-without-arrow)[data-y-axis=top]:before{border-bottom:none;border-left-color:transparent;border-right-color:transparent;border-top-style:solid;margin-left:-10px}.components-popover:not(.is-without-arrow)[data-y-axis=bottom]{margin-top:8px}.components-popover:not(.is-without-arrow)[data-y-axis=bottom]:before{top:-8px}.components-popover:not(.is-without-arrow)[data-y-axis=bottom]:after{top:-6px}.components-popover:not(.is-without-arrow)[data-y-axis=bottom]:after,.components-popover:not(.is-without-arrow)[data-y-axis=bottom]:before{border-bottom-style:solid;border-left-color:transparent;border-right-color:transparent;border-top:none;margin-left:-10px}.components-popover:not(.is-without-arrow)[data-y-axis=middle][data-x-axis=left]{margin-left:-8px}.components-popover:not(.is-without-arrow)[data-y-axis=middle][data-x-axis=left]:before{right:-8px}.components-popover:not(.is-without-arrow)[data-y-axis=middle][data-x-axis=left]:after{right:-6px}.components-popover:not(.is-without-arrow)[data-y-axis=middle][data-x-axis=left]:after,.components-popover:not(.is-without-arrow)[data-y-axis=middle][data-x-axis=left]:before{border-bottom-color:transparent;border-left-style:solid;border-right:none;border-top-color:transparent}.components-popover:not(.is-without-arrow)[data-y-axis=middle][data-x-axis=right]{margin-left:8px}.components-popover:not(.is-without-arrow)[data-y-axis=middle][data-x-axis=right]:before{left:-8px}.components-popover:not(.is-without-arrow)[data-y-axis=middle][data-x-axis=right]:after{left:-6px}.components-popover:not(.is-without-arrow)[data-y-axis=middle][data-x-axis=right]:after,.components-popover:not(.is-without-arrow)[data-y-axis=middle][data-x-axis=right]:before{border-bottom-color:transparent;border-left:none;border-right-style:solid;border-top-color:transparent}.components-popover[data-y-axis=top]{bottom:100%}.components-popover[data-y-axis=bottom]{top:100%}.components-popover[data-y-axis=middle]{align-items:center;display:flex}.components-popover.is-from-top{margin-top:12px}.components-popover.is-from-bottom{margin-top:-12px}.components-popover.is-from-left:not(.is-from-top):not(.is-from-bottom){margin-left:12px}.components-popover.is-from-right:not(.is-from-top):not(.is-from-bottom){margin-right:12px}.components-popover__content{background:#fff;border:1px solid #ccc;border-radius:2px;box-shadow:0 2px 6px rgba(0,0,0,.05);height:100%}.is-alternate .components-popover__content{border:1px solid #1e1e1e;box-shadow:none}.components-popover .components-popover__content{height:auto;min-width:260px;overflow-y:auto;position:absolute}.components-popover.is-expanded .components-popover__content{border:none;border-top:1px solid #1e1e1e;height:calc(100% - 48px);min-width:auto;overflow-y:visible;position:static}.components-popover[data-y-axis=top] .components-popover__content{bottom:100%}.components-popover[data-x-axis=center] .components-popover__content{left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.components-popover[data-x-axis=right] .components-popover__content{left:100%;position:absolute}.components-popover:not([data-y-axis=middle])[data-x-axis=right] .components-popover__content{margin-left:-25px}.components-popover[data-x-axis=left] .components-popover__content{position:absolute;right:100%}.components-popover:not([data-y-axis=middle])[data-x-axis=left] .components-popover__content{margin-right:-25px}.components-popover__header{align-items:center;background:#fff;display:flex;height:48px;justify-content:space-between;padding:0 8px 0 16px}.components-popover__header-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}.components-popover__close.components-button{z-index:5}
+
+/*!rtl:end:ignore*/.components-radio-control{display:flex;flex-direction:column}.components-radio-control .components-base-control__help{margin-top:0}.components-radio-control .components-base-control__field{margin-bottom:0}.components-radio-control__option:not(:last-child){margin-bottom:4px}.components-radio-control__input[type=radio]{border:2px solid #757575;border-radius:2px;border-radius:50%;box-shadow:0 0 0 transparent;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:16px;line-height:normal;margin-right:6px;margin-top:0;padding:6px 8px;transition:box-shadow .1s linear;transition:none}@media (prefers-reduced-motion:reduce){.components-radio-control__input[type=radio]{transition-duration:0s}}@media (min-width:600px){.components-radio-control__input[type=radio]{font-size:13px;line-height:normal}}.components-radio-control__input[type=radio]:focus{border-color:#007cba;border-color:var(--wp-admin-theme-color);box-shadow:0 0 0 .5px #007cba;box-shadow:0 0 0 .5px var(--wp-admin-theme-color);outline:2px solid transparent}.components-radio-control__input[type=radio]::-webkit-input-placeholder{color:rgba(14,28,46,.62)}.components-radio-control__input[type=radio]::-moz-placeholder{color:rgba(14,28,46,.62);opacity:1}.components-radio-control__input[type=radio]:-ms-input-placeholder{color:rgba(14,28,46,.62)}.is-dark-theme .components-radio-control__input[type=radio]::-webkit-input-placeholder{color:hsla(0,0%,100%,.65)}.is-dark-theme .components-radio-control__input[type=radio]::-moz-placeholder{color:hsla(0,0%,100%,.65);opacity:1}.is-dark-theme .components-radio-control__input[type=radio]:-ms-input-placeholder{color:hsla(0,0%,100%,.65)}.components-radio-control__input[type=radio]:checked:before{background-color:#fff;height:6px;margin:6px 0 0 6px;width:6px}@media (min-width:782px){.components-radio-control__input[type=radio]:checked:before{margin:3px 0 0 3px}}.components-radio-control__input[type=radio]:focus{border-color:#757575;box-shadow:0 0 0 1px #757575}.components-radio-control__input[type=radio]:checked{background:#007cba;background:var(--wp-admin-theme-color);border-color:#007cba;border-color:var(--wp-admin-theme-color)}.components-radio-control__input[type=radio]:checked:focus{box-shadow:0 0 0 1.5px #757575}.components-resizable-box__handle{display:none;height:23px;width:23px}.components-resizable-box__container.has-show-handle .components-resizable-box__handle{display:block}.components-resizable-box__handle:after{border:2px solid #fff;border-radius:50%;height:15px;right:calc(50% - 8px);top:calc(50% - 8px);width:15px}.components-resizable-box__handle:after,.components-resizable-box__side-handle:before{background:#007cba;background:var(--wp-admin-theme-color);content:"";cursor:inherit;display:block;position:absolute}.components-resizable-box__side-handle:before{border:2px solid #fff;height:7px;opacity:0;right:calc(50% - 4px);top:calc(50% - 4px);transition:-webkit-transform .1s ease-in;transition:transform .1s ease-in;transition:transform .1s ease-in,-webkit-transform .1s ease-in;width:7px}@media (prefers-reduced-motion:reduce){.components-resizable-box__side-handle:before{transition-duration:0s}}.is-dark-theme .components-resizable-box__handle:after,.is-dark-theme .components-resizable-box__side-handle:before{border-color:#d7dade}.components-resizable-box__corner-handle,.components-resizable-box__handle,.components-resizable-box__side-handle{z-index:2}.components-resizable-box__side-handle.components-resizable-box__handle-bottom,.components-resizable-box__side-handle.components-resizable-box__handle-bottom:before,.components-resizable-box__side-handle.components-resizable-box__handle-top,.components-resizable-box__side-handle.components-resizable-box__handle-top:before{border-left:0;border-right:0;left:0;width:100%}.components-resizable-box__side-handle.components-resizable-box__handle-left,.components-resizable-box__side-handle.components-resizable-box__handle-left:before,.components-resizable-box__side-handle.components-resizable-box__handle-right,.components-resizable-box__side-handle.components-resizable-box__handle-right:before{border-bottom:0;border-top:0;height:100%;top:0}.components-resizable-box__side-handle.components-resizable-box__handle-bottom:active:before,.components-resizable-box__side-handle.components-resizable-box__handle-bottom:hover:before,.components-resizable-box__side-handle.components-resizable-box__handle-top:active:before,.components-resizable-box__side-handle.components-resizable-box__handle-top:hover:before{-webkit-animation:components-resizable-box__top-bottom-animation .1s ease-out 0s;animation:components-resizable-box__top-bottom-animation .1s ease-out 0s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@media (prefers-reduced-motion:reduce){.components-resizable-box__side-handle.components-resizable-box__handle-bottom:active:before,.components-resizable-box__side-handle.components-resizable-box__handle-bottom:hover:before,.components-resizable-box__side-handle.components-resizable-box__handle-top:active:before,.components-resizable-box__side-handle.components-resizable-box__handle-top:hover:before{-webkit-animation-duration:1ms;animation-duration:1ms}}.components-resizable-box__side-handle.components-resizable-box__handle-left:active:before,.components-resizable-box__side-handle.components-resizable-box__handle-left:hover:before,.components-resizable-box__side-handle.components-resizable-box__handle-right:active:before,.components-resizable-box__side-handle.components-resizable-box__handle-right:hover:before{-webkit-animation:components-resizable-box__left-right-animation .1s ease-out 0s;animation:components-resizable-box__left-right-animation .1s ease-out 0s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@media (prefers-reduced-motion:reduce){.components-resizable-box__side-handle.components-resizable-box__handle-left:active:before,.components-resizable-box__side-handle.components-resizable-box__handle-left:hover:before,.components-resizable-box__side-handle.components-resizable-box__handle-right:active:before,.components-resizable-box__side-handle.components-resizable-box__handle-right:hover:before{-webkit-animation-duration:1ms;animation-duration:1ms}}@-webkit-keyframes components-resizable-box__top-bottom-animation{0%{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}to{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes components-resizable-box__top-bottom-animation{0%{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}to{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}}@-webkit-keyframes components-resizable-box__left-right-animation{0%{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}to{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1)}}@keyframes components-resizable-box__left-right-animation{0%{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}to{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1)}}
+
+/*!rtl:begin:ignore*/.components-resizable-box__handle-right{right:-11.5px}.components-resizable-box__handle-left{left:-11.5px}.components-resizable-box__handle-top{top:-11.5px}.components-resizable-box__handle-bottom{bottom:-11.5px}
+
+/*!rtl:end:ignore*/.components-responsive-wrapper{max-width:100%;position:relative}.components-responsive-wrapper,.components-responsive-wrapper>span{display:block}.components-responsive-wrapper__content{bottom:0;height:100%;left:0;margin:auto;position:absolute;right:0;top:0;width:100%}.components-sandbox{overflow:hidden}iframe.components-sandbox{width:100%}body.lockscroll,html.lockscroll{overflow:hidden}.components-select-control__input{-webkit-tap-highlight-color:rgba(0,0,0,0)!important;background:#fff;height:36px;line-height:36px;margin:1px;outline:0;width:100%}@media (min-width:782px){.components-select-control__input{height:28px;line-height:28px}}@media (max-width:782px){.components-base-control .components-base-control__field .components-select-control__input{font-size:16px}}.components-snackbar{background-color:#32373c;border-radius:4px;box-shadow:0 2px 4px rgba(0,0,0,.3);box-sizing:border-box;color:#fff;cursor:pointer;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px;max-width:600px;padding:16px 24px;width:100%}@media (min-width:600px){.components-snackbar{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}}.components-snackbar:hover{background-color:#191e23}.components-snackbar:focus{background-color:#191e23;box-shadow:0 0 0 1px #fff,0 0 0 3px #007cba;box-shadow:0 0 0 1px #fff,0 0 0 3px var(--wp-admin-theme-color)}.components-snackbar__action.components-button{color:#fff;flex-shrink:0;height:auto;line-height:1.4;margin-left:32px;padding:0}.components-snackbar__action.components-button:not(:disabled):not([aria-disabled=true]):not(.is-secondary){background-color:initial;text-decoration:underline}.components-snackbar__action.components-button:not(:disabled):not([aria-disabled=true]):not(.is-secondary):focus{box-shadow:none;color:#fff;outline:1px dotted #fff}.components-snackbar__action.components-button:not(:disabled):not([aria-disabled=true]):not(.is-secondary):hover{color:#33b3db}.components-snackbar__content{align-items:baseline;display:flex;justify-content:space-between;line-height:1.4}.components-snackbar-list{box-sizing:border-box;position:absolute;width:100%;z-index:100000}.components-snackbar-list__notice-container{padding-top:8px;position:relative}.components-spinner{background-color:#7e8993;border-radius:100%;display:inline-block;height:18px;margin:5px 11px 0;opacity:.7;position:relative;width:18px}.components-spinner:before{-webkit-animation:components-spinner__animation 1s linear infinite;animation:components-spinner__animation 1s linear infinite;background-color:#fff;border-radius:100%;content:"";height:4px;left:3px;position:absolute;top:3px;-webkit-transform-origin:6px 6px;transform-origin:6px 6px;width:4px}@-webkit-keyframes components-spinner__animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes components-spinner__animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.components-tab-panel__tabs{align-items:stretch;display:flex}.components-tab-panel__tabs-item{background:transparent;border:none;border-radius:0;box-shadow:none;box-sizing:border-box;cursor:pointer;font-weight:500;height:48px;margin-left:0;padding:3px 16px;transition:box-shadow .1s linear}.components-tab-panel__tabs-item:after{speak:none;content:attr(data-label);display:block;height:0;overflow:hidden;visibility:hidden}.components-tab-panel__tabs-item:focus:not(:disabled){box-shadow:inset 0 1.5px #007cba;box-shadow:inset 0 1.5px var(--wp-admin-theme-color)}.components-tab-panel__tabs-item.is-active{box-shadow:inset 0 0 0 1.5px transparent,inset 0 -4px 0 0 #007cba;box-shadow:inset 0 0 0 1.5px transparent,inset 0 -4px 0 0 var(--wp-admin-theme-color);position:relative}.components-tab-panel__tabs-item.is-active:before{border-bottom:4px solid transparent;bottom:1px;content:"";left:0;position:absolute;right:0;top:0}.components-tab-panel__tabs-item:focus{box-shadow:inset 0 0 0 1.5px #007cba;box-shadow:inset 0 0 0 1.5px var(--wp-admin-theme-color)}.components-tab-panel__tabs-item.is-active:focus{box-shadow:inset 0 0 0 1.5px #007cba,inset 0 -4px 0 0 #007cba;box-shadow:inset 0 0 0 1.5px var(--wp-admin-theme-color),inset 0 -4px 0 0 var(--wp-admin-theme-color)}.components-text-control__input,.components-text-control__input[type=color],.components-text-control__input[type=date],.components-text-control__input[type=datetime-local],.components-text-control__input[type=datetime],.components-text-control__input[type=email],.components-text-control__input[type=month],.components-text-control__input[type=number],.components-text-control__input[type=password],.components-text-control__input[type=tel],.components-text-control__input[type=text],.components-text-control__input[type=time],.components-text-control__input[type=url],.components-text-control__input[type=week]{border:1px solid #757575;border-radius:2px;box-shadow:0 0 0 transparent;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:16px;line-height:normal;padding:6px 8px;transition:box-shadow .1s linear;width:100%}@media (prefers-reduced-motion:reduce){.components-text-control__input,.components-text-control__input[type=color],.components-text-control__input[type=date],.components-text-control__input[type=datetime-local],.components-text-control__input[type=datetime],.components-text-control__input[type=email],.components-text-control__input[type=month],.components-text-control__input[type=number],.components-text-control__input[type=password],.components-text-control__input[type=tel],.components-text-control__input[type=text],.components-text-control__input[type=time],.components-text-control__input[type=url],.components-text-control__input[type=week]{transition-duration:0s}}@media (min-width:600px){.components-text-control__input,.components-text-control__input[type=color],.components-text-control__input[type=date],.components-text-control__input[type=datetime-local],.components-text-control__input[type=datetime],.components-text-control__input[type=email],.components-text-control__input[type=month],.components-text-control__input[type=number],.components-text-control__input[type=password],.components-text-control__input[type=tel],.components-text-control__input[type=text],.components-text-control__input[type=time],.components-text-control__input[type=url],.components-text-control__input[type=week]{font-size:13px;line-height:normal}}.components-text-control__input:focus,.components-text-control__input[type=color]:focus,.components-text-control__input[type=date]:focus,.components-text-control__input[type=datetime-local]:focus,.components-text-control__input[type=datetime]:focus,.components-text-control__input[type=email]:focus,.components-text-control__input[type=month]:focus,.components-text-control__input[type=number]:focus,.components-text-control__input[type=password]:focus,.components-text-control__input[type=tel]:focus,.components-text-control__input[type=text]:focus,.components-text-control__input[type=time]:focus,.components-text-control__input[type=url]:focus,.components-text-control__input[type=week]:focus{border-color:#007cba;border-color:var(--wp-admin-theme-color);box-shadow:0 0 0 .5px #007cba;box-shadow:0 0 0 .5px var(--wp-admin-theme-color);outline:2px solid transparent}.components-text-control__input::-webkit-input-placeholder,.components-text-control__input[type=color]::-webkit-input-placeholder,.components-text-control__input[type=date]::-webkit-input-placeholder,.components-text-control__input[type=datetime-local]::-webkit-input-placeholder,.components-text-control__input[type=datetime]::-webkit-input-placeholder,.components-text-control__input[type=email]::-webkit-input-placeholder,.components-text-control__input[type=month]::-webkit-input-placeholder,.components-text-control__input[type=number]::-webkit-input-placeholder,.components-text-control__input[type=password]::-webkit-input-placeholder,.components-text-control__input[type=tel]::-webkit-input-placeholder,.components-text-control__input[type=text]::-webkit-input-placeholder,.components-text-control__input[type=time]::-webkit-input-placeholder,.components-text-control__input[type=url]::-webkit-input-placeholder,.components-text-control__input[type=week]::-webkit-input-placeholder{color:rgba(14,28,46,.62)}.components-text-control__input::-moz-placeholder,.components-text-control__input[type=color]::-moz-placeholder,.components-text-control__input[type=date]::-moz-placeholder,.components-text-control__input[type=datetime-local]::-moz-placeholder,.components-text-control__input[type=datetime]::-moz-placeholder,.components-text-control__input[type=email]::-moz-placeholder,.components-text-control__input[type=month]::-moz-placeholder,.components-text-control__input[type=number]::-moz-placeholder,.components-text-control__input[type=password]::-moz-placeholder,.components-text-control__input[type=tel]::-moz-placeholder,.components-text-control__input[type=text]::-moz-placeholder,.components-text-control__input[type=time]::-moz-placeholder,.components-text-control__input[type=url]::-moz-placeholder,.components-text-control__input[type=week]::-moz-placeholder{color:rgba(14,28,46,.62);opacity:1}.components-text-control__input:-ms-input-placeholder,.components-text-control__input[type=color]:-ms-input-placeholder,.components-text-control__input[type=date]:-ms-input-placeholder,.components-text-control__input[type=datetime-local]:-ms-input-placeholder,.components-text-control__input[type=datetime]:-ms-input-placeholder,.components-text-control__input[type=email]:-ms-input-placeholder,.components-text-control__input[type=month]:-ms-input-placeholder,.components-text-control__input[type=number]:-ms-input-placeholder,.components-text-control__input[type=password]:-ms-input-placeholder,.components-text-control__input[type=tel]:-ms-input-placeholder,.components-text-control__input[type=text]:-ms-input-placeholder,.components-text-control__input[type=time]:-ms-input-placeholder,.components-text-control__input[type=url]:-ms-input-placeholder,.components-text-control__input[type=week]:-ms-input-placeholder{color:rgba(14,28,46,.62)}.is-dark-theme .components-text-control__input::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=color]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=date]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=datetime-local]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=datetime]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=email]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=month]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=number]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=password]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=tel]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=text]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=time]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=url]::-webkit-input-placeholder,.is-dark-theme .components-text-control__input[type=week]::-webkit-input-placeholder{color:hsla(0,0%,100%,.65)}.is-dark-theme .components-text-control__input::-moz-placeholder,.is-dark-theme .components-text-control__input[type=color]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=date]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=datetime-local]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=datetime]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=email]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=month]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=number]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=password]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=tel]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=text]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=time]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=url]::-moz-placeholder,.is-dark-theme .components-text-control__input[type=week]::-moz-placeholder{color:hsla(0,0%,100%,.65);opacity:1}.is-dark-theme .components-text-control__input:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=color]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=date]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=datetime-local]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=datetime]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=email]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=month]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=number]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=password]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=tel]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=text]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=time]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=url]:-ms-input-placeholder,.is-dark-theme .components-text-control__input[type=week]:-ms-input-placeholder{color:hsla(0,0%,100%,.65)}.components-textarea-control__input{border:1px solid #757575;border-radius:2px;box-shadow:0 0 0 transparent;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:16px;line-height:normal;padding:6px 8px;transition:box-shadow .1s linear;width:100%}@media (prefers-reduced-motion:reduce){.components-textarea-control__input{transition-duration:0s}}@media (min-width:600px){.components-textarea-control__input{font-size:13px;line-height:normal}}.components-textarea-control__input:focus{border-color:#007cba;border-color:var(--wp-admin-theme-color);box-shadow:0 0 0 .5px #007cba;box-shadow:0 0 0 .5px var(--wp-admin-theme-color);outline:2px solid transparent}.components-textarea-control__input::-webkit-input-placeholder{color:rgba(14,28,46,.62)}.components-textarea-control__input::-moz-placeholder{color:rgba(14,28,46,.62);opacity:1}.components-textarea-control__input:-ms-input-placeholder{color:rgba(14,28,46,.62)}.is-dark-theme .components-textarea-control__input::-webkit-input-placeholder{color:hsla(0,0%,100%,.65)}.is-dark-theme .components-textarea-control__input::-moz-placeholder{color:hsla(0,0%,100%,.65);opacity:1}.is-dark-theme .components-textarea-control__input:-ms-input-placeholder{color:hsla(0,0%,100%,.65)}.components-tip{color:#555d66;display:flex}.components-tip svg{fill:#f0b849;align-self:center;flex-shrink:0;margin-right:16px}.components-tip p{margin:0}.components-toggle-control .components-base-control__field{align-items:center;display:flex;line-height:normal;margin-bottom:12px}.components-toggle-control .components-base-control__field .components-form-toggle{margin-right:12px}.components-toggle-control .components-base-control__field .components-toggle-control__label{display:block}.components-accessible-toolbar{border:1px solid #1e1e1e;border-radius:2px;display:inline-flex;flex-shrink:0}.components-accessible-toolbar>.components-toolbar-group:last-child{border-right:none}.components-accessible-toolbar .components-button,.components-toolbar .components-button{height:48px;padding-left:16px;padding-right:16px;position:relative;z-index:1}.components-accessible-toolbar .components-button:focus:enabled,.components-toolbar .components-button:focus:enabled{box-shadow:none;outline:none}.components-accessible-toolbar .components-button:before,.components-toolbar .components-button:before{-webkit-animation:components-button__appear-animation .1s ease;animation:components-button__appear-animation .1s ease;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;border-radius:2px;content:"";display:block;height:32px;left:8px;position:absolute;right:8px;z-index:-1}@media (prefers-reduced-motion:reduce){.components-accessible-toolbar .components-button:before,.components-toolbar .components-button:before{-webkit-animation-duration:1ms;animation-duration:1ms}}.components-accessible-toolbar .components-button svg,.components-toolbar .components-button svg{margin-left:auto;margin-right:auto;position:relative}.components-accessible-toolbar .components-button.is-pressed,.components-accessible-toolbar .components-button.is-pressed:hover,.components-toolbar .components-button.is-pressed,.components-toolbar .components-button.is-pressed:hover{background:transparent}.components-accessible-toolbar .components-button.is-pressed:before,.components-toolbar .components-button.is-pressed:before{background:#1e1e1e}.components-accessible-toolbar .components-button:focus:before,.components-toolbar .components-button:focus:before{box-shadow:inset 0 0 0 1.5px #007cba,inset 0 0 0 4px #fff;box-shadow:inset 0 0 0 1.5px var(--wp-admin-theme-color),inset 0 0 0 4px #fff;outline:2px solid transparent}.components-accessible-toolbar .components-button.has-icon,.components-toolbar .components-button.has-icon{justify-content:center;min-width:48px;padding-left:8px;padding-right:8px}.components-accessible-toolbar .components-button.components-tab-button,.components-toolbar .components-button.components-tab-button{font-weight:500}.components-accessible-toolbar .components-button.components-tab-button span,.components-toolbar .components-button.components-tab-button span{display:inline-block;padding-left:0;padding-right:0;position:relative}@-webkit-keyframes components-button__appear-animation{0%{-webkit-transform:scaleY(0);transform:scaleY(0)}to{-webkit-transform:scaleY(1);transform:scaleY(1)}}@keyframes components-button__appear-animation{0%{-webkit-transform:scaleY(0);transform:scaleY(0)}to{-webkit-transform:scaleY(1);transform:scaleY(1)}}.components-toolbar__control.components-button{position:relative}.components-toolbar__control.components-button[data-subscript] svg{padding:5px 10px 5px 0}.components-toolbar__control.components-button[data-subscript]:after{bottom:10px;content:attr(data-subscript);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px;font-weight:600;line-height:12px;position:absolute;right:8px}.components-toolbar__control.components-button:active:before{display:none}.components-toolbar__control.components-button:not(:disabled).is-pressed[data-subscript]:after{color:#fff}.components-toolbar-group{background-color:#fff;border-right:1px solid #1e1e1e;display:inline-flex;flex-shrink:0;flex-wrap:wrap;line-height:0;min-height:48px}.components-toolbar-group .components-toolbar-group{border-width:0;margin:0}.components-toolbar{background-color:#fff;border:1px solid #1e1e1e;border-radius:2px;display:inline-flex;flex-shrink:0;flex-wrap:wrap;margin:0;min-height:48px}div.components-toolbar>div{display:block;margin:0}@supports ((position:-webkit-sticky) or (position:sticky)){div.components-toolbar>div{display:flex}}div.components-toolbar>div+div.has-left-divider{margin-left:6px;overflow:visible;position:relative}div.components-toolbar>div+div.has-left-divider:before{background-color:#e2e4e7;box-sizing:initial;content:"";display:inline-block;height:20px;left:-3px;position:absolute;top:8px;width:1px}.components-accessible-toolbar .components-toolbar-group>.components-button.components-button.has-icon,.components-toolbar div>.components-button.components-button.has-icon{min-width:36px;padding-left:6px;padding-right:6px}.components-accessible-toolbar .components-toolbar-group>.components-button.components-button.has-icon svg,.components-toolbar div>.components-button.components-button.has-icon svg{min-width:24px}.components-accessible-toolbar .components-toolbar-group>.components-button.components-button.has-icon:before,.components-toolbar div>.components-button.components-button.has-icon:before{left:2px;right:2px}.components-accessible-toolbar .components-toolbar-group>.components-button:first-child.has-icon,.components-accessible-toolbar .components-toolbar-group>div:first-child>.components-button.has-icon,.components-toolbar div:first-child .components-button.has-icon{min-width:42px;padding-left:11px;padding-right:6px}.components-accessible-toolbar .components-toolbar-group>.components-button:first-child.has-icon:before,.components-accessible-toolbar .components-toolbar-group>div:first-child>.components-button.has-icon:before,.components-toolbar div:first-child .components-button.has-icon:before{left:8px;right:2px}.components-accessible-toolbar .components-toolbar-group>.components-button:last-child.has-icon,.components-accessible-toolbar .components-toolbar-group>div:last-child>.components-button.has-icon,.components-toolbar div:last-child .components-button.has-icon{min-width:42px;padding-left:6px;padding-right:11px}.components-accessible-toolbar .components-toolbar-group>.components-button:last-child.has-icon:before,.components-accessible-toolbar .components-toolbar-group>div:last-child>.components-button.has-icon:before,.components-toolbar div:last-child .components-button.has-icon:before{left:2px;right:8px}.components-accessible-toolbar .components-toolbar-group>.components-button:first-of-type:last-of-type.has-icon,.components-accessible-toolbar .components-toolbar-group>div:first-child:last-child>.components-button.has-icon,.components-toolbar div:first-child:last-child>.components-button.has-icon{min-width:48px;padding-left:12px;padding-right:12px}.components-accessible-toolbar .components-toolbar-group>.components-button:first-of-type:last-of-type.has-icon:before,.components-accessible-toolbar .components-toolbar-group>div:first-child:last-child>.components-button.has-icon:before,.components-toolbar div:first-child:last-child>.components-button.has-icon:before{left:8px;right:8px}.components-tooltip.components-popover{z-index:1000002}.components-tooltip.components-popover .components-popover__content{min-width:0}.components-tooltip .components-popover__content{background:#1e1e1e;border-radius:2px;border-width:0;color:#fff;pointer-events:none;text-align:center;white-space:nowrap}.components-tooltip .components-popover__content>div{padding:4px 8px}.components-tooltip__shortcut{display:inline-block;margin-left:8px}.components-visually-hidden{clip:rect(1px,1px,1px,1px);word-wrap:normal!important;border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.components-visually-hidden:focus{clip:auto!important;background-color:#e2e4e7;-webkit-clip-path:none;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}.theme-plugin-files select{margin-bottom:10px;min-height:180px}.theme-plugin-files .excludes-wrap{align-items:flex-start;display:flex;flex-direction:column}.theme-plugin-files .excludes-wrap textarea{align-self:self-start;flex-grow:1}.theme-plugin-files .excludes-wrap .exclude-files-title{text-transform:uppercase}.theme-plugin-files .excludes-wrap p{font-size:13px;margin:0 0 .5rem}.theme-plugin-files .panel-header-wrap{min-height:56px;padding:0 1.25rem}.theme-plugin-files .panel-header-wrap.has-summary-no-child button{display:none}.theme-plugin-files .enabled .panel-header-wrap.has-summary-no-child button{display:block}.theme-plugin-files h4 label{font-size:.9rem;font-weight:600}.theme-plugin-files .panel-title{min-width:95px}.theme-plugin-files .panel-summary{max-width:1023px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.theme-plugin-files .panel-summary .empty-warning{color:red}.theme-plugin-files .disabled .panel-summary{max-width:none;text-overflow:clip;white-space:normal}.theme-plugin-files .disabled .panel-summary a{padding:0;text-decoration:underline}.theme-plugin-files .panel-header-wrap.no-child{grid-template-columns:20px .4fr 4fr}.theme-plugin-files .panel-header-wrap.has-summary-no-child{grid-template-columns:35px 180px 2fr 2fr}.theme-plugin-files .has-divider .panel-header-wrap.has-summary-no-child{grid-template-columns:35px 180px 0 2fr 2fr}.theme-plugin-files .has-divider .panel-header-wrap.has-summary-no-child .panel-header{grid-column:4/span 2}.theme-plugin-files .panel-body{padding:32px 40px}.theme-plugin-files .tpf-panel-header{display:grid;grid-column:1/span 2;grid-template-columns:30px 2fr}.theme-plugin-files .tpf-panel-header label{margin:0}.theme-plugin-files .tpf-panel-header input[type=checkbox]{margin:0;position:relative;top:6px}.theme-plugin-files .panel-header{display:grid;grid-column:3/span 2;grid-template-columns:2fr 32px;text-align:left}.theme-plugin-files .tpf-panel-body{grid-column-gap:25px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));padding-top:25px}.theme-plugin-files .tpf-panel-body h4{margin:0}.theme-plugin-files .tpf-panel-body select,.theme-plugin-files .tpf-panel-body textarea{border-radius:5px;margin-bottom:0;max-width:100%;width:100%}.theme-plugin-files .tpf-panel-body select option{overflow:hidden}.theme-plugin-files .tpf-panel-body ul li label{display:flex;width:92%}.theme-plugin-files .tpf-panel-body ul li label .name{flex:1 1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.theme-plugin-files .tpf-panel-body ul li label .version-compare{cursor:pointer;white-space:nowrap}.theme-plugin-files .tpf-panel-body .select-wrap li .version-tooltip{background-color:#fff;border:1px solid #d6d6d6!important;border-radius:5px;border-width:1px;box-shadow:0 5px 24px 0 rgba(4,34,63,.16);color:#343434;display:none;font-weight:400;line-height:1.2rem;max-width:460px;opacity:1;padding:.75rem;position:absolute;right:28px;z-index:99}.theme-plugin-files .tpf-panel-body .select-wrap li .version-tooltip:first-letter{text-transform:capitalize}.theme-plugin-files .tpf-panel-body .select-wrap li .version-tooltip:before{border-bottom:6px solid transparent;border-left:6px solid #d6d6d6;border-top:6px solid transparent;content:"";position:absolute;right:-7px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.theme-plugin-files .tpf-panel-body .select-wrap li .version-tooltip:after{border-bottom:5px solid transparent;border-left:6px solid #fff;border-top:5px solid transparent;content:"";position:absolute;right:-6px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.theme-plugin-files .tpf-panel-body .select-wrap li .version-tooltip:hover{display:block}.theme-plugin-files .tpf-panel-body .select-wrap li input:focus-visible+label .version-tooltip{display:block}.theme-plugin-files .tpf-panel-body .version-compare{align-items:center;display:flex;position:relative}.theme-plugin-files .tpf-panel-body .version-compare .action-tooltip:first-letter{text-transform:capitalize}.theme-plugin-files .tpf-panel-body .version-compare:hover .version-tooltip{display:block}.theme-plugin-files .compare-icon{display:flex;text-align:center}.theme-plugin-files .compare-icon svg{height:16px;width:16px}.theme-plugin-files .radiogroup>div{margin:.4rem 0}.theme-plugin-files .radiogroup>div:first-child{margin-top:0}.theme-plugin-files .error-msg{grid-row:3}.boxed-options{border:1px solid #d6d6d6;border-radius:5px}.boxed-options .option{border-bottom:1px solid #d6d6d6;cursor:pointer;display:flex;flex-wrap:wrap;line-height:1.4rem;padding:24px 20px;position:relative;text-align:left}.boxed-options .option input{background-color:#fff;border-color:#8c8f94}.boxed-options .option .label{color:#052240;display:block;font-size:.8rem;font-weight:600;text-transform:uppercase}.boxed-options .option .option-radio{margin-right:15px}.boxed-options .option:last-child{border-bottom:none}.mst.no-summary .panel-header-wrap.has-summary-no-child{grid-template-columns:minmax(0,.8fr) 4fr}.mst .panel-header-wrap.has-summary-no-child.panel-open{grid-template-columns:1fr 6fr 0}.mst .select-subsite-wrap{grid-column-gap:6px;display:grid;grid-template-columns:20px 1fr}.mst .select-subsite-wrap .checkbox-wrap{position:relative;top:2px}.mst .subsite-selector{display:flex;flex-direction:column}.mst .subsite-selector.disabled label{opacity:.7}.mst .subsite-selector label{display:block;font-size:13px;font-weight:600;margin:0 0 10px;text-transform:uppercase}.mst .subsites-row{width:100%}.mst .subsites-row .subsites-selects{grid-gap:12px;align-items:center;display:flex;flex-basis:100%;flex-wrap:wrap;gap:12px;margin:10px 29px}.mst .subsites-row .subsites-selects .subsite-arrow{align-items:center;display:flex;height:100%}.mst .subsites-row .subsites-selects .subsite-arrow svg{height:28px;width:28px}.mst .subsites-row .subsites-selects:last-child{margin-bottom:0}.mst .subsites-row .subsites-selects.subsite-to-single label{word-wrap:normal!important;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.mst .migration-warning,.mst .subsites-row .migrate-notice{margin-left:29px;margin-right:29px}.mst .migration-warning{flex-basis:100%}.mst .migration-warning .red{margin:1em 27px}.mst .mst-blurb{color:#04223f;font-size:13px;margin-bottom:20px;margin-top:0}.mst .new-prefix{display:inline-flex;flex-basis:100%;font-size:13px;margin-right:6px;margin-top:15px}.mst .new-prefix>span{display:inline-flex;margin-right:7px}.mst .new-prefix.has-form{align-items:center;display:flex}.mst .new-prefix.has-form>span{margin-right:10px}.mst .new-prefix-input{width:6rem}.mst .invalid-prefix{padding-left:10px}.mst .mst-errors p{margin-bottom:0}.mst .mst-site-summary{grid-gap:10px;align-items:center;display:flex;gap:10px}
\ No newline at end of file
diff --git a/wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/135.d717e42327db.chunk.js b/wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/135.d717e42327db.chunk.js
new file mode 100644
index 000000000..6f500e6bd
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/135.d717e42327db.chunk.js
@@ -0,0 +1 @@
+"use strict";(self.webpackJSONPwpmdb=self.webpackJSONPwpmdb||[]).push([[135],{83135:function(e,t,n){var r=n(4665),l=n(62295),a=n(42233),i=n(75338);t.Z=function(e){var t="exclude-files-".concat(e.type),n=function(e){var t=e.type,n=(0,l.v9)((function(e){return e.migrations})),s=n.current_migration,u=n.local_site,c=n.remote_site;return r.createElement("p",null,(0,i.ZP)((0,a.gB)((0,a.__)('Use gitignore patterns to exclude files relative to %s
',"wp-migrate-db"),"https://deliciousbrains.com/wp-migrate-db-pro/doc/ignored-files/",function(){var e,n,r,l,a,i="pull"===s.intent,p=i?c:u,o=p.site_details,d=o.themes_path,m=o.plugins_path,g=o.muplugins_path,_=o.content_dir,h=i?p.path:p.this_path,f=function(e,t){return void 0===e?null:e.replace(t,"").replace(/^\/|\/$/g,"")};switch(t){case"themes":return null!==(e=f(d,h))&&void 0!==e?e:"wp-content/themes";case"plugins":return null!==(n=f(m,h))&&void 0!==n?n:"wp-content/plugins";case"muplugins":return null!==(r=f(g,h))&&void 0!==r?r:"wp-content/mu-plugins";case"media":return null!==(l=f(i?p.wp_upload_dir:p.this_wp_upload_dir,h))&&void 0!==l?l:"wp-content/uploads";case"others":return null!==(a=f(_,h))&&void 0!==a?a:"wp-content";default:return""}}())))};return r.createElement(r.Fragment,null,r.createElement("h4",{className:"exclude-files-title",id:t},(0,i.ZP)((0,a.gB)((0,a.__)('Excluded Files %s',"wp-migrate-db"),e.type))),r.createElement(n,{type:e.type}),r.createElement("textarea",{onChange:function(t){e.excludesUpdater(t.target.value,e.type)},value:e.excludes||"","aria-labelledby":t}))}}}]);
\ No newline at end of file
diff --git a/wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/288.52a1e53f645f.js b/wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/288.52a1e53f645f.js
new file mode 100644
index 000000000..ac0712ce4
--- /dev/null
+++ b/wp-content/plugins/wp-migrate-db-pro/frontend/build/static/js/288.52a1e53f645f.js
@@ -0,0 +1,2 @@
+/*! For license information please see 288.52a1e53f645f.js.LICENSE.txt */
+(self.webpackJSONPwpmdb=self.webpackJSONPwpmdb||[]).push([[288],{8214:function(e,t,r){"use strict";function n(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=r(94089),O=r.n(l);function f(){return f=Object.assign?Object.assign.bind():function(e){for(var t=1;t<+~=|^:(),"'`\s])/g,N="undefined"!==typeof CSS&&CSS.escape,E=function(e){return N?N(e):e.replace(_,"\\$1")},T=function(){function e(e,t,r){this.type="style",this.isProcessed=!1;var n=r.sheet,o=r.Renderer;this.key=e,this.options=r,this.style=t,n?this.renderer=n.renderer:o&&(this.renderer=new o)}return e.prototype.prop=function(e,t,r){if(void 0===t)return this.style[e];var n=!!r&&r.force;if(!n&&this.style[e]===t)return this;var o=t;r&&!1===r.process||(o=this.options.jss.plugins.onChangeValue(t,e,this));var i=null==o||!1===o,a=e in this.style;if(i&&!a&&!n)return this;var p=i&&a;if(p?delete this.style[e]:this.style[e]=o,this.renderable&&this.renderer)return p?this.renderer.removeProperty(this.renderable,e):this.renderer.setProperty(this.renderable,e,o),this;var c=this.options.sheet;return c&&c.attached,this},e}(),C=function(e){function t(t,r,n){var o;o=e.call(this,t,r,n)||this;var i=n.selector,a=n.scoped,p=n.sheet,c=n.generateId;return i?o.selectorText=i:!1!==a&&(o.id=c(v(v(o)),p),o.selectorText="."+E(o.id)),o}g(t,e);var r=t.prototype;return r.applyTo=function(e){var t=this.renderer;if(t){var r=this.toJSON();for(var n in r)t.setProperty(e,n,r[n])}return this},r.toJSON=function(){var e={};for(var t in this.style){var r=this.style[t];"object"!==typeof r?e[t]=r:Array.isArray(r)&&(e[t]=w(r))}return e},r.toString=function(e){var t=this.options.sheet,r=!!t&&t.options.link?f({},e,{allowEmpty:!0}):e;return B(this.selectorText,this.style,r)},q(t,[{key:"selector",set:function(e){if(e!==this.selectorText){this.selectorText=e;var t=this.renderer,r=this.renderable;if(r&&t)t.setSelector(r,e)||t.replaceRule(r,this)}},get:function(){return this.selectorText}}]),t}(T),k={onCreateRule:function(e,t,r){return"@"===e[0]||r.parent&&"keyframes"===r.parent.type?null:new C(e,t,r)}},P={indent:1,children:!0},D=/@([\w-]+)/,X=function(){function e(e,t,r){this.type="conditional",this.isProcessed=!1,this.key=e;var n=e.match(D);for(var o in this.at=n?n[1]:"unknown",this.query=r.name||"@"+this.at,this.options=r,this.rules=new be(f({},r,{parent:this})),t)this.rules.add(o,t[o]);this.rules.process()}var t=e.prototype;return t.getRule=function(e){return this.rules.get(e)},t.indexOf=function(e){return this.rules.indexOf(e)},t.addRule=function(e,t,r){var n=this.rules.add(e,t,r);return n?(this.options.jss.plugins.onProcessRule(n),n):null},t.replaceRule=function(e,t,r){var n=this.rules.replace(e,t,r);return n&&this.options.jss.plugins.onProcessRule(n),n},t.toString=function(e){void 0===e&&(e=P);var t=x(e).linebreak;if(null==e.indent&&(e.indent=P.indent),null==e.children&&(e.children=P.children),!1===e.children)return this.query+" {}";var r=this.rules.toString(e);return r?this.query+" {"+t+r+t+"}":""},e}(),I=/@container|@media|@supports\s+/,F={onCreateRule:function(e,t,r){return I.test(e)?new X(e,t,r):null}},j={indent:1,children:!0},V=/@keyframes\s+([\w-]+)/,U=function(){function e(e,t,r){this.type="keyframes",this.at="@keyframes",this.isProcessed=!1;var n=e.match(V);n&&n[1]?this.name=n[1]:this.name="noname",this.key=this.type+"-"+this.name,this.options=r;var o=r.scoped,i=r.sheet,a=r.generateId;for(var p in this.id=!1===o?this.name:E(a(this,i)),this.rules=new be(f({},r,{parent:this})),t)this.rules.add(p,t[p],f({},r,{parent:this}));this.rules.process()}return e.prototype.toString=function(e){void 0===e&&(e=j);var t=x(e).linebreak;if(null==e.indent&&(e.indent=j.indent),null==e.children&&(e.children=j.children),!1===e.children)return this.at+" "+this.id+" {}";var r=this.rules.toString(e);return r&&(r=""+t+r+t),this.at+" "+this.id+" {"+r+"}"},e}(),G=/@keyframes\s+/,H=/\$([\w-]+)/g,Y=function(e,t){return"string"===typeof e?e.replace(H,(function(e,r){return r in t?t[r]:e})):e},Z=function(e,t,r){var n=e[t],o=Y(n,r);o!==n&&(e[t]=o)},K={onCreateRule:function(e,t,r){return"string"===typeof e&&G.test(e)?new U(e,t,r):null},onProcessStyle:function(e,t,r){return"style"===t.type&&r?("animation-name"in e&&Z(e,"animation-name",r.keyframes),"animation"in e&&Z(e,"animation",r.keyframes),e):e},onChangeValue:function(e,t,r){var n=r.options.sheet;if(!n)return e;switch(t){case"animation":case"animation-name":return Y(e,n.keyframes);default:return e}}},$=function(e){function t(){return e.apply(this,arguments)||this}return g(t,e),t.prototype.toString=function(e){var t=this.options.sheet,r=!!t&&t.options.link?f({},e,{allowEmpty:!0}):e;return B(this.key,this.style,r)},t}(T),Q={onCreateRule:function(e,t,r){return r.parent&&"keyframes"===r.parent.type?new $(e,t,r):null}},J=function(){function e(e,t,r){this.type="font-face",this.at="@font-face",this.isProcessed=!1,this.key=e,this.style=t,this.options=r}return e.prototype.toString=function(e){var t=x(e).linebreak;if(Array.isArray(this.style)){for(var r="",n=0;n=this.index)t.push(e);else for(var n=0;nr)return void t.splice(n,0,e)},t.reset=function(){this.registry=[]},t.remove=function(e){var t=this.registry.indexOf(e);this.registry.splice(t,1)},t.toString=function(e){for(var t=void 0===e?{}:e,r=t.attached,n=function(e,t){if(null==e)return{};var r,n,o={},i=Object.keys(e);for(n=0;n=0||(o[r]=e[r]);return o}(t,["attached"]),o=x(n).linebreak,i="",a=0;a-1?n.substr(0,o-1):n;e.style.setProperty(t,i,o>-1?"important":"")}}catch(a){return!1}return!0},ge=function(e,t){try{e.attributeStyleMap?e.attributeStyleMap.delete(t):e.style.removeProperty(t)}catch(r){}},ve=function(e,t){return e.selectorText=t,e.selectorText===t},ye=Ae((function(){return document.querySelector("head")}));function We(e){var t=le.registry;if(t.length>0){var r=function(e,t){for(var r=0;rt.index&&n.options.insertionPoint===t.insertionPoint)return n}return null}(t,e);if(r&&r.renderer)return{parent:r.renderer.element.parentNode,node:r.renderer.element};if(r=function(e,t){for(var r=e.length-1;r>=0;r--){var n=e[r];if(n.attached&&n.options.insertionPoint===t.insertionPoint)return n}return null}(t,e),r&&r.renderer)return{parent:r.renderer.element.parentNode,node:r.renderer.element.nextSibling}}var n=e.insertionPoint;if(n&&"string"===typeof n){var o=function(e){for(var t=ye(),r=0;rr?r:t},xe=function(){function e(e){this.getPropertyValue=qe,this.setProperty=me,this.removeProperty=ge,this.setSelector=ve,this.hasInsertedRules=!1,this.cssRules=[],e&&le.add(e),this.sheet=e;var t=this.sheet?this.sheet.options:{},r=t.media,n=t.meta,o=t.element;this.element=o||function(){var e=document.createElement("style");return e.textContent="\n",e}(),this.element.setAttribute("data-jss",""),r&&this.element.setAttribute("media",r),n&&this.element.setAttribute("data-meta",n);var i=Re();i&&this.element.setAttribute("nonce",i)}var t=e.prototype;return t.attach=function(){if(!this.element.parentNode&&this.sheet){!function(e,t){var r=t.insertionPoint,n=We(t);if(!1!==n&&n.parent)n.parent.insertBefore(e,n.node);else if(r&&"number"===typeof r.nodeType){var o=r,i=o.parentNode;i&&i.insertBefore(e,o.nextSibling)}else ye().appendChild(e)}(this.element,this.sheet.options);var e=Boolean(this.sheet&&this.sheet.deployed);this.hasInsertedRules&&e&&(this.hasInsertedRules=!1,this.deploy())}},t.detach=function(){if(this.sheet){var e=this.element.parentNode;e&&e.removeChild(this.element),this.sheet.options.link&&(this.cssRules=[],this.element.textContent="\n")}},t.deploy=function(){var e=this.sheet;e&&(e.options.link?this.insertRules(e.rules):this.element.textContent="\n"+e.toString()+"\n")},t.insertRules=function(e,t){for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:{},t=e.baseClasses,r=e.newClasses;e.Component;if(!r)return t;var n=(0,u.Z)({},t);return Object.keys(r).forEach((function(e){r[e]&&(n[e]="".concat(t[e]," ").concat(r[e]))})),n}var Ce={set:function(e,t,r,n){var o=e.get(t);o||(o=new Map,e.set(t,o)),o.set(r,n)},get:function(e,t,r){var n=e.get(t);return n?n.get(r):void 0},delete:function(e,t,r){e.get(t).delete(r)}},ke=Ce,Pe=r(54772),De=r(3953),Xe=["checked","disabled","error","focused","focusVisible","required","expanded","selected"];var Ie=Date.now(),Fe="fnValues"+Ie,je="fnStyle"+ ++Ie,Ve=function(){return{onCreateRule:function(e,t,r){if("function"!==typeof t)return null;var n=R(e,{},r);return n[je]=t,n},onProcessStyle:function(e,t){if(Fe in t||je in t)return e;var r={};for(var n in e){var o=e[n];"function"===typeof o&&(delete e[n],r[n]=o)}return t[Fe]=r,e},onUpdate:function(e,t,r,n){var o=t,i=o[je];i&&(o.style=i(e)||{});var a=o[Fe];if(a)for(var p in a)o.prop(p,a[p](e),n)}}};function Ue(){return Ue=Object.assign?Object.assign.bind():function(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=new Array(t);r-1){var o=Kt[e];if(!Array.isArray(o))return wt.js+Et(o)in t&&wt.css+o;if(!n)return!1;for(var i=0;it?1:-1:e.length-t.length};return{onProcessStyle:function(t,r){if("style"!==r.type)return t;for(var n={},o=Object.keys(t).sort(e),i=0;i0&&void 0!==arguments[0]?arguments[0]:{},t=e.disableGlobal,r=void 0!==t&&t,n=e.productionPrefix,o=void 0===n?"jss":n,i=e.seed,a=void 0===i?"":i,p=""===a?"":"".concat(a,"-"),c=0,s=function(){return c+=1};return function(e,t){var n=t.options.name;if(n&&0===n.indexOf("Mui")&&!t.options.link&&!r){if(-1!==Xe.indexOf(e.key))return"Mui-".concat(e.key);var i="".concat(p).concat(n,"-").concat(e.key);return t.options.theme[De.Z]&&""===a?"".concat(i,"-").concat(s()):i}return"".concat(p).concat(o).concat(s())}}(),dr={disableGeneration:!1,generateClassName:fr,jss:Or,sheetsCache:null,sheetsManager:new Map,sheetsRegistry:null},hr=s.createContext(dr);var Ar=-1e9;var qr=r(78801);var mr={};function gr(e,t){var r=e.state,n=e.theme,o=e.stylesOptions,i=e.stylesCreator,a=e.name;if(!o.disableGeneration){var p=ke.get(o.sheetsManager,i,n);p||(p={refs:0,staticSheet:null,dynamicStyles:null},ke.set(o.sheetsManager,i,n,p));var c=(0,u.Z)({},i.options,o,{theme:n,flip:"boolean"===typeof o.flip?o.flip:"rtl"===n.direction});c.generateId=c.serverGenerateClassName||c.generateClassName;var s=o.sheetsRegistry;if(0===p.refs){var b;o.sheetsCache&&(b=ke.get(o.sheetsCache,i,n));var M=i.create(n,a);b||((b=o.jss.createStyleSheet(M,(0,u.Z)({link:!1},c))).attach(),o.sheetsCache&&ke.set(o.sheetsCache,i,n,b)),s&&s.add(b),p.staticSheet=b,p.dynamicStyles=Ee(M)}if(p.dynamicStyles){var z=o.jss.createStyleSheet(p.dynamicStyles,(0,u.Z)({link:!0},c));z.update(t),z.attach(),r.dynamicSheet=z,r.classes=Te({baseClasses:p.staticSheet.classes,newClasses:z.classes}),s&&s.add(z)}else r.classes=p.staticSheet.classes;p.refs+=1}}function vr(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.name,n=t.classNamePrefix,o=t.Component,i=t.defaultTheme,a=void 0===i?mr:i,p=z(t,["name","classNamePrefix","Component","defaultTheme"]),c=function(e){var t="function"===typeof e;return{create:function(r,n){var o;try{o=t?e(r):e}catch(p){throw p}if(!n||!r.overrides||!r.overrides[n])return o;var i=r.overrides[n],a=(0,u.Z)({},o);return Object.keys(i).forEach((function(e){a[e]=(0,qr.Z)(a[e],i[e])})),a},options:{}}}(e),b=r||n||"makeStyles";c.options={index:Ar+=1,name:r,meta:b,classNamePrefix:b};return function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=(0,Pe.Z)()||a,n=(0,u.Z)({},s.useContext(hr),p),i=s.useRef(),b=s.useRef();!function(e,t){var r,n=s.useRef([]),o=s.useMemo((function(){return{}}),t);n.current!==o&&(n.current=o,r=e()),s.useEffect((function(){return function(){r&&r()}}),[o])}((function(){var o={name:r,state:{},stylesCreator:c,stylesOptions:n,theme:t};return gr(o,e),b.current=!1,i.current=o,function(){!function(e){var t=e.state,r=e.theme,n=e.stylesOptions,o=e.stylesCreator;if(!n.disableGeneration){var i=ke.get(n.sheetsManager,o,r);i.refs-=1;var a=n.sheetsRegistry;0===i.refs&&(ke.delete(n.sheetsManager,o,r),n.jss.removeStyleSheet(i.staticSheet),a&&a.remove(i.staticSheet)),t.dynamicSheet&&(n.jss.removeStyleSheet(t.dynamicSheet),a&&a.remove(t.dynamicSheet))}}(o)}}),[t,c]),s.useEffect((function(){b.current&&function(e,t){var r=e.state;r.dynamicSheet&&r.dynamicSheet.update(t)}(i.current,e),b.current=!0}));var M=function(e,t,r){var n=e.state;if(e.stylesOptions.disableGeneration)return t||{};n.cacheClasses||(n.cacheClasses={value:null,lastProp:null,lastJSS:{}});var o=!1;return n.classes!==n.cacheClasses.lastJSS&&(n.cacheClasses.lastJSS=n.classes,o=!0),t!==n.cacheClasses.lastProp&&(n.cacheClasses.lastProp=t,o=!0),o&&(n.cacheClasses.value=Te({baseClasses:n.cacheClasses.lastJSS,newClasses:t,Component:r})),n.cacheClasses.value}(i.current,e.classes,o);return M}}var yr=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return function(r){var n=t.defaultTheme,o=t.withTheme,i=void 0!==o&&o,a=t.name,p=z(t,["defaultTheme","withTheme","name"]);var c=a,b=vr(e,(0,u.Z)({defaultTheme:n,Component:r,name:a||r.displayName,classNamePrefix:c},p)),M=s.forwardRef((function(e,t){e.classes;var o,p=e.innerRef,c=z(e,["classes","innerRef"]),M=b((0,u.Z)({},r.defaultProps,e)),l=c;return("string"===typeof a||i)&&(o=(0,Pe.Z)()||n,a&&(l=function(e){var t=e.theme,r=e.name,n=e.props;if(!t||!t.props||!t.props[r])return n;var o,i=t.props[r];for(o in i)void 0===n[o]&&(n[o]=i[o]);return n}({theme:o,name:a,props:c})),i&&!l.theme&&(l.theme=o)),s.createElement(r,(0,u.Z)({ref:p||t,classes:M},l))}));return O()(M,r),M}},Wr=(0,r(14107).Z)();var Rr=function(e,t){return yr(e,(0,c.Z)({defaultTheme:Wr},t))};var Lr=r(7356),wr=r(8096),xr=!0,Sr=!1,Br=null,_r={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function Nr(e){e.metaKey||e.altKey||e.ctrlKey||(xr=!0)}function Er(){xr=!1}function Tr(){"hidden"===this.visibilityState&&Sr&&(xr=!0)}function Cr(e){var t=e.target;try{return t.matches(":focus-visible")}catch(r){}return xr||function(e){var t=e.type,r=e.tagName;return!("INPUT"!==r||!_r[t]||e.readOnly)||"TEXTAREA"===r&&!e.readOnly||!!e.isContentEditable}(t)}function kr(){Sr=!0,window.clearTimeout(Br),Br=window.setTimeout((function(){Sr=!1}),100)}function Pr(){return{isFocusVisible:Cr,onBlurVisible:kr,ref:s.useCallback((function(e){var t,r=wr.findDOMNode(e);null!=r&&((t=r.ownerDocument).addEventListener("keydown",Nr,!0),t.addEventListener("mousedown",Er,!0),t.addEventListener("pointerdown",Er,!0),t.addEventListener("touchstart",Er,!0),t.addEventListener("visibilitychange",Tr,!0))}),[])}}function Dr(e){return e&&e.ownerDocument||document}var Xr="undefined"!==typeof window?s.useLayoutEffect:s.useEffect;function Ir(e){var t=s.useRef(e);return Xr((function(){t.current=e})),s.useCallback((function(){return t.current.apply(void 0,arguments)}),[])}function Fr(e,t){"function"===typeof e?e(t):e&&(e.current=t)}function jr(e,t){return s.useMemo((function(){return null==e&&null==t?null:function(r){Fr(e,r),Fr(t,r)}}),[e,t])}var Vr=r(76452);function Ur(e){if("string"!==typeof e)throw new Error((0,Vr.Z)(7));return e.charAt(0).toUpperCase()+e.slice(1)}var Gr=Rr((function(e){return{thumb:{"&$open":{"& $offset":{transform:"scale(1) translateY(-10px)"}}},open:{},offset:(0,c.Z)({zIndex:1},e.typography.body2,{fontSize:e.typography.pxToRem(12),lineHeight:1.2,transition:e.transitions.create(["transform"],{duration:e.transitions.duration.shortest}),top:-34,transformOrigin:"bottom center",transform:"scale(0)",position:"absolute"}),circle:{display:"flex",alignItems:"center",justifyContent:"center",width:32,height:32,borderRadius:"50% 50% 50% 0",backgroundColor:"currentColor",transform:"rotate(-45deg)"},label:{color:e.palette.primary.contrastText,transform:"rotate(45deg)"}}}),{name:"PrivateValueLabel"})((function(e){var t=e.children,r=e.classes,n=e.className,o=e.open,i=e.value,a=e.valueLabelDisplay;return"off"===a?t:s.cloneElement(t,{className:M(t.props.className,(o||"on"===a)&&r.open,r.thumb)},s.createElement("span",{className:M(r.offset,n)},s.createElement("span",{className:r.circle},s.createElement("span",{className:r.label},i))))}));function Hr(e,t){return e-t}function Yr(e,t,r){return Math.min(Math.max(t,e),r)}function Zr(e,t){var r=e.reduce((function(e,r,n){var o=Math.abs(t-r);return null===e||o0&&be.some((function(e){return e.label}))&&b.marked,!1===I&&b.trackFalse,"vertical"===N&&b.vertical,"inverted"===I&&b.trackInverted),onMouseDown:Ne},Z),s.createElement("span",{className:b.rail}),s.createElement("span",{className:b.track,style:Ce}),s.createElement("input",{value:se.join(","),name:w,type:"hidden"}),be.map((function(e,t){var r,n=$r(e.value,L,W),o=tn[we].offset(n);return r=!1===I?-1!==se.indexOf(e.value):"normal"===I&&(ce?e.value>=se[0]&&e.value<=se[se.length-1]:e.value<=se[0])||"inverted"===I&&(ce?e.value<=se[0]||e.value>=se[se.length-1]:e.value>=se[0]),s.createElement(s.Fragment,{key:e.value},s.createElement("span",{style:o,"data-index":t,className:M(b.mark,r&&b.markActive)}),null!=e.label?s.createElement("span",{"aria-hidden":!0,"data-index":t,style:o,className:M(b.markLabel,r&&b.markLabelActive)},e.label):null)})),se.map((function(e,t){var i=$r(e,L,W),a=tn[we].offset(i);return s.createElement(V,{key:t,valueLabelFormat:Y,valueLabelDisplay:G,className:b.valueLabel,value:"function"===typeof Y?Y(T(e),t):Y,index:t,open:re===t||J===t||"on"===G,disabled:A},s.createElement(D,{className:M(b.thumb,b["thumbColor".concat(Ur(l))],J===t&&b.active,A&&b.disabled,fe===t&&b.focusVisible),tabIndex:A?null:0,role:"slider",style:a,"data-index":t,"aria-label":q?q(t):r,"aria-labelledby":n,"aria-orientation":N,"aria-valuemax":T(W),"aria-valuemin":T(L),"aria-valuenow":T(e),"aria-valuetext":m?m(T(e),t):o,onKeyDown:Re,onFocus:me,onBlur:ge,onMouseOver:ve,onMouseLeave:ye}))})))})),on=Rr((function(e){return{root:{height:2,width:"100%",boxSizing:"content-box",padding:"13px 0",display:"inline-block",position:"relative",cursor:"pointer",touchAction:"none",color:e.palette.primary.main,WebkitTapHighlightColor:"transparent","&$disabled":{pointerEvents:"none",cursor:"default",color:e.palette.grey[400]},"&$vertical":{width:2,height:"100%",padding:"0 13px"},"@media (pointer: coarse)":{padding:"20px 0","&$vertical":{padding:"0 20px"}},"@media print":{colorAdjust:"exact"}},colorPrimary:{},colorSecondary:{color:e.palette.secondary.main},marked:{marginBottom:20,"&$vertical":{marginBottom:"auto",marginRight:20}},vertical:{},disabled:{},rail:{display:"block",position:"absolute",width:"100%",height:2,borderRadius:1,backgroundColor:"currentColor",opacity:.38,"$vertical &":{height:"100%",width:2}},track:{display:"block",position:"absolute",height:2,borderRadius:1,backgroundColor:"currentColor","$vertical &":{width:2}},trackFalse:{"& $track":{display:"none"}},trackInverted:{"& $track":{backgroundColor:"light"===e.palette.type?(0,Lr.$n)(e.palette.primary.main,.62):(0,Lr._j)(e.palette.primary.main,.5)},"& $rail":{opacity:1}},thumb:{position:"absolute",width:12,height:12,marginLeft:-6,marginTop:-5,boxSizing:"border-box",borderRadius:"50%",outline:0,backgroundColor:"currentColor",display:"flex",alignItems:"center",justifyContent:"center",transition:e.transitions.create(["box-shadow"],{duration:e.transitions.duration.shortest}),"&::after":{position:"absolute",content:'""',borderRadius:"50%",left:-15,top:-15,right:-15,bottom:-15},"&$focusVisible,&:hover":{boxShadow:"0px 0px 0px 8px ".concat((0,Lr.Fq)(e.palette.primary.main,.16)),"@media (hover: none)":{boxShadow:"none"}},"&$active":{boxShadow:"0px 0px 0px 14px ".concat((0,Lr.Fq)(e.palette.primary.main,.16))},"&$disabled":{width:8,height:8,marginLeft:-4,marginTop:-3,"&:hover":{boxShadow:"none"}},"$vertical &":{marginLeft:-5,marginBottom:-6},"$vertical &$disabled":{marginLeft:-3,marginBottom:-4}},thumbColorPrimary:{},thumbColorSecondary:{"&$focusVisible,&:hover":{boxShadow:"0px 0px 0px 8px ".concat((0,Lr.Fq)(e.palette.secondary.main,.16))},"&$active":{boxShadow:"0px 0px 0px 14px ".concat((0,Lr.Fq)(e.palette.secondary.main,.16))}},active:{},focusVisible:{},valueLabel:{left:"calc(-50% - 4px)"},mark:{position:"absolute",width:2,height:2,borderRadius:1,backgroundColor:"currentColor"},markActive:{backgroundColor:e.palette.background.paper,opacity:.8},markLabel:(0,c.Z)({},e.typography.body2,{color:e.palette.text.secondary,position:"absolute",top:26,transform:"translateX(-50%)",whiteSpace:"nowrap","$vertical &":{top:"auto",left:26,transform:"translateY(50%)"},"@media (pointer: coarse)":{top:40,"$vertical &":{left:31}}}),markLabelActive:{color:e.palette.text.primary}}}),{name:"MuiSlider"})(nn)},7356:function(e,t,r){"use strict";r.d(t,{$n:function(){return M},Fq:function(){return s},_j:function(){return b},mi:function(){return p}});var n=r(76452);function o(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;return Math.min(Math.max(t,e),r)}function i(e){if(e.type)return e;if("#"===e.charAt(0))return i(function(e){e=e.substr(1);var t=new RegExp(".{1,".concat(e.length>=6?2:1,"}"),"g"),r=e.match(t);return r&&1===r[0].length&&(r=r.map((function(e){return e+e}))),r?"rgb".concat(4===r.length?"a":"","(").concat(r.map((function(e,t){return t<3?parseInt(e,16):Math.round(parseInt(e,16)/255*1e3)/1e3})).join(", "),")"):""}(e));var t=e.indexOf("("),r=e.substring(0,t);if(-1===["rgb","rgba","hsl","hsla"].indexOf(r))throw new Error((0,n.Z)(3,e));var o=e.substring(t+1,e.length-1).split(",");return{type:r,values:o=o.map((function(e){return parseFloat(e)}))}}function a(e){var t=e.type,r=e.values;return-1!==t.indexOf("rgb")?r=r.map((function(e,t){return t<3?parseInt(e,10):e})):-1!==t.indexOf("hsl")&&(r[1]="".concat(r[1],"%"),r[2]="".concat(r[2],"%")),"".concat(t,"(").concat(r.join(", "),")")}function p(e,t){var r=c(e),n=c(t);return(Math.max(r,n)+.05)/(Math.min(r,n)+.05)}function c(e){var t="hsl"===(e=i(e)).type?i(function(e){var t=(e=i(e)).values,r=t[0],n=t[1]/100,o=t[2]/100,p=n*Math.min(o,1-o),c=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:(e+r/30)%12;return o-p*Math.max(Math.min(t-3,9-t,1),-1)},s="rgb",b=[Math.round(255*c(0)),Math.round(255*c(8)),Math.round(255*c(4))];return"hsla"===e.type&&(s+="a",b.push(t[3])),a({type:s,values:b})}(e)).values:e.values;return t=t.map((function(e){return(e/=255)<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4)})),Number((.2126*t[0]+.7152*t[1]+.0722*t[2]).toFixed(3))}function s(e,t){return e=i(e),t=o(t),"rgb"!==e.type&&"hsl"!==e.type||(e.type+="a"),e.values[3]=t,a(e)}function b(e,t){if(e=i(e),t=o(t),-1!==e.type.indexOf("hsl"))e.values[2]*=1-t;else if(-1!==e.type.indexOf("rgb"))for(var r=0;r<3;r+=1)e.values[r]*=1-t;return a(e)}function M(e,t){if(e=i(e),t=o(t),-1!==e.type.indexOf("hsl"))e.values[2]+=(100-e.values[2])*t;else if(-1!==e.type.indexOf("rgb"))for(var r=0;r<3;r+=1)e.values[r]+=(255-e.values[r])*t;return a(e)}},14107:function(e,t,r){"use strict";r.d(t,{Z:function(){return J}});var n=r(44168),o=r(78801),i=r(81225),a=["xs","sm","md","lg","xl"];function p(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function c(e,t,r){var n;return(0,i.Z)({gutters:function(){var r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return console.warn(["Material-UI: theme.mixins.gutters() is deprecated.","You can use the source of the mixin directly:","\n paddingLeft: theme.spacing(2),\n paddingRight: theme.spacing(2),\n [theme.breakpoints.up('sm')]: {\n paddingLeft: theme.spacing(3),\n paddingRight: theme.spacing(3),\n },\n "].join("\n")),(0,i.Z)({paddingLeft:t(2),paddingRight:t(2)},r,p({},e.up("sm"),(0,i.Z)({paddingLeft:t(3),paddingRight:t(3)},r[e.up("sm")])))},toolbar:(n={minHeight:56},p(n,"".concat(e.up("xs")," and (orientation: landscape)"),{minHeight:48}),p(n,e.up("sm"),{minHeight:64}),n)},r)}var s=r(76452),b={black:"#000",white:"#fff"},M={50:"#fafafa",100:"#f5f5f5",200:"#eeeeee",300:"#e0e0e0",400:"#bdbdbd",500:"#9e9e9e",600:"#757575",700:"#616161",800:"#424242",900:"#212121",A100:"#d5d5d5",A200:"#aaaaaa",A400:"#303030",A700:"#616161"},u={50:"#e8eaf6",100:"#c5cae9",200:"#9fa8da",300:"#7986cb",400:"#5c6bc0",500:"#3f51b5",600:"#3949ab",700:"#303f9f",800:"#283593",900:"#1a237e",A100:"#8c9eff",A200:"#536dfe",A400:"#3d5afe",A700:"#304ffe"},z={50:"#fce4ec",100:"#f8bbd0",200:"#f48fb1",300:"#f06292",400:"#ec407a",500:"#e91e63",600:"#d81b60",700:"#c2185b",800:"#ad1457",900:"#880e4f",A100:"#ff80ab",A200:"#ff4081",A400:"#f50057",A700:"#c51162"},l={50:"#ffebee",100:"#ffcdd2",200:"#ef9a9a",300:"#e57373",400:"#ef5350",500:"#f44336",600:"#e53935",700:"#d32f2f",800:"#c62828",900:"#b71c1c",A100:"#ff8a80",A200:"#ff5252",A400:"#ff1744",A700:"#d50000"},O={50:"#fff3e0",100:"#ffe0b2",200:"#ffcc80",300:"#ffb74d",400:"#ffa726",500:"#ff9800",600:"#fb8c00",700:"#f57c00",800:"#ef6c00",900:"#e65100",A100:"#ffd180",A200:"#ffab40",A400:"#ff9100",A700:"#ff6d00"},f={50:"#e3f2fd",100:"#bbdefb",200:"#90caf9",300:"#64b5f6",400:"#42a5f5",500:"#2196f3",600:"#1e88e5",700:"#1976d2",800:"#1565c0",900:"#0d47a1",A100:"#82b1ff",A200:"#448aff",A400:"#2979ff",A700:"#2962ff"},d={50:"#e8f5e9",100:"#c8e6c9",200:"#a5d6a7",300:"#81c784",400:"#66bb6a",500:"#4caf50",600:"#43a047",700:"#388e3c",800:"#2e7d32",900:"#1b5e20",A100:"#b9f6ca",A200:"#69f0ae",A400:"#00e676",A700:"#00c853"},h=r(7356),A={text:{primary:"rgba(0, 0, 0, 0.87)",secondary:"rgba(0, 0, 0, 0.54)",disabled:"rgba(0, 0, 0, 0.38)",hint:"rgba(0, 0, 0, 0.38)"},divider:"rgba(0, 0, 0, 0.12)",background:{paper:b.white,default:M[50]},action:{active:"rgba(0, 0, 0, 0.54)",hover:"rgba(0, 0, 0, 0.04)",hoverOpacity:.04,selected:"rgba(0, 0, 0, 0.08)",selectedOpacity:.08,disabled:"rgba(0, 0, 0, 0.26)",disabledBackground:"rgba(0, 0, 0, 0.12)",disabledOpacity:.38,focus:"rgba(0, 0, 0, 0.12)",focusOpacity:.12,activatedOpacity:.12}},q={text:{primary:b.white,secondary:"rgba(255, 255, 255, 0.7)",disabled:"rgba(255, 255, 255, 0.5)",hint:"rgba(255, 255, 255, 0.5)",icon:"rgba(255, 255, 255, 0.5)"},divider:"rgba(255, 255, 255, 0.12)",background:{paper:M[800],default:"#303030"},action:{active:b.white,hover:"rgba(255, 255, 255, 0.08)",hoverOpacity:.08,selected:"rgba(255, 255, 255, 0.16)",selectedOpacity:.16,disabled:"rgba(255, 255, 255, 0.3)",disabledBackground:"rgba(255, 255, 255, 0.12)",disabledOpacity:.38,focus:"rgba(255, 255, 255, 0.12)",focusOpacity:.12,activatedOpacity:.24}};function m(e,t,r,n){var o=n.light||n,i=n.dark||1.5*n;e[t]||(e.hasOwnProperty(r)?e[t]=e[r]:"light"===t?e.light=(0,h.$n)(e.main,o):"dark"===t&&(e.dark=(0,h._j)(e.main,i)))}function g(e){return Math.round(1e5*e)/1e5}function v(e){return g(e)}var y={textTransform:"uppercase"},W='"Roboto", "Helvetica", "Arial", sans-serif';function R(e,t){var r="function"===typeof t?t(e):t,a=r.fontFamily,p=void 0===a?W:a,c=r.fontSize,s=void 0===c?14:c,b=r.fontWeightLight,M=void 0===b?300:b,u=r.fontWeightRegular,z=void 0===u?400:u,l=r.fontWeightMedium,O=void 0===l?500:l,f=r.fontWeightBold,d=void 0===f?700:f,h=r.htmlFontSize,A=void 0===h?16:h,q=r.allVariants,m=r.pxToRem,R=(0,n.Z)(r,["fontFamily","fontSize","fontWeightLight","fontWeightRegular","fontWeightMedium","fontWeightBold","htmlFontSize","allVariants","pxToRem"]);var L=s/14,w=m||function(e){return"".concat(e/A*L,"rem")},x=function(e,t,r,n,o){return(0,i.Z)({fontFamily:p,fontWeight:e,fontSize:w(t),lineHeight:r},p===W?{letterSpacing:"".concat(g(n/t),"em")}:{},o,q)},S={h1:x(M,96,1.167,-1.5),h2:x(M,60,1.2,-.5),h3:x(z,48,1.167,0),h4:x(z,34,1.235,.25),h5:x(z,24,1.334,0),h6:x(O,20,1.6,.15),subtitle1:x(z,16,1.75,.15),subtitle2:x(O,14,1.57,.1),body1:x(z,16,1.5,.15),body2:x(z,14,1.43,.15),button:x(O,14,1.75,.4,y),caption:x(z,12,1.66,.4),overline:x(z,12,2.66,1,y)};return(0,o.Z)((0,i.Z)({htmlFontSize:A,pxToRem:w,round:v,fontFamily:p,fontSize:s,fontWeightLight:M,fontWeightRegular:z,fontWeightMedium:O,fontWeightBold:d},S),R,{clone:!1})}var L=.2,w=.14,x=.12;function S(){return["".concat(arguments.length<=0?void 0:arguments[0],"px ").concat(arguments.length<=1?void 0:arguments[1],"px ").concat(arguments.length<=2?void 0:arguments[2],"px ").concat(arguments.length<=3?void 0:arguments[3],"px rgba(0,0,0,").concat(L,")"),"".concat(arguments.length<=4?void 0:arguments[4],"px ").concat(arguments.length<=5?void 0:arguments[5],"px ").concat(arguments.length<=6?void 0:arguments[6],"px ").concat(arguments.length<=7?void 0:arguments[7],"px rgba(0,0,0,").concat(w,")"),"".concat(arguments.length<=8?void 0:arguments[8],"px ").concat(arguments.length<=9?void 0:arguments[9],"px ").concat(arguments.length<=10?void 0:arguments[10],"px ").concat(arguments.length<=11?void 0:arguments[11],"px rgba(0,0,0,").concat(x,")")].join(",")}var B=["none",S(0,2,1,-1,0,1,1,0,0,1,3,0),S(0,3,1,-2,0,2,2,0,0,1,5,0),S(0,3,3,-2,0,3,4,0,0,1,8,0),S(0,2,4,-1,0,4,5,0,0,1,10,0),S(0,3,5,-1,0,5,8,0,0,1,14,0),S(0,3,5,-1,0,6,10,0,0,1,18,0),S(0,4,5,-2,0,7,10,1,0,2,16,1),S(0,5,5,-3,0,8,10,1,0,3,14,2),S(0,5,6,-3,0,9,12,1,0,3,16,2),S(0,6,6,-3,0,10,14,1,0,4,18,3),S(0,6,7,-4,0,11,15,1,0,4,20,3),S(0,7,8,-4,0,12,17,2,0,5,22,4),S(0,7,8,-4,0,13,19,2,0,5,24,4),S(0,7,9,-4,0,14,21,2,0,5,26,4),S(0,8,9,-5,0,15,22,2,0,6,28,5),S(0,8,10,-5,0,16,24,2,0,6,30,5),S(0,8,11,-5,0,17,26,2,0,6,32,5),S(0,9,11,-5,0,18,28,2,0,7,34,6),S(0,9,12,-6,0,19,29,2,0,7,36,6),S(0,10,13,-6,0,20,31,3,0,8,38,7),S(0,10,13,-6,0,21,33,3,0,8,40,7),S(0,10,14,-6,0,22,35,3,0,8,42,7),S(0,11,14,-7,0,23,36,3,0,9,44,8),S(0,11,15,-7,0,24,38,3,0,9,46,8)],_={borderRadius:4};function N(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r2){if(!I[e])return[e];e=I[e]}var t=E(e.split(""),2),r=t[0],n=t[1],o=D[r],i=X[n]||"";return Array.isArray(i)?i.map((function(e){return o+e})):[o+i]})),j=["m","mt","mr","mb","ml","mx","my","p","pt","pr","pb","pl","px","py","margin","marginTop","marginRight","marginBottom","marginLeft","marginX","marginY","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","paddingX","paddingY"];function V(e){var t=e.spacing||8;return"number"===typeof t?function(e){return t*e}:Array.isArray(t)?function(e){return t[e]}:"function"===typeof t?t:function(){}}function U(e,t){return function(r){return e.reduce((function(e,n){return e[n]=function(e,t){if("string"===typeof t||null==t)return t;var r=e(Math.abs(t));return t>=0?r:"number"===typeof r?-r:"-".concat(r)}(t,r),e}),{})}}function G(e){var t=V(e.theme);return Object.keys(e).map((function(r){if(-1===j.indexOf(r))return null;var n=U(F(r),t),o=e[r];return function(e,t,r){if(Array.isArray(t)){var n=e.theme.breakpoints||k;return t.reduce((function(e,o,i){return e[n.up(n.keys[i])]=r(t[i]),e}),{})}if("object"===T(t)){var o=e.theme.breakpoints||k;return Object.keys(t).reduce((function(e,n){return e[o.up(n)]=r(t[n]),e}),{})}return r(t)}(e,o,n)})).reduce(P,{})}G.propTypes={},G.filterProps=j;var H={easeInOut:"cubic-bezier(0.4, 0, 0.2, 1)",easeOut:"cubic-bezier(0.0, 0, 0.2, 1)",easeIn:"cubic-bezier(0.4, 0, 1, 1)",sharp:"cubic-bezier(0.4, 0, 0.6, 1)"},Y={shortest:150,shorter:200,short:250,standard:300,complex:375,enteringScreen:225,leavingScreen:195};function Z(e){return"".concat(Math.round(e),"ms")}var K={easing:H,duration:Y,create:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["all"],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.duration,o=void 0===r?Y.standard:r,i=t.easing,a=void 0===i?H.easeInOut:i,p=t.delay,c=void 0===p?0:p;(0,n.Z)(t,["duration","easing","delay"]);return(Array.isArray(e)?e:[e]).map((function(e){return"".concat(e," ").concat("string"===typeof o?o:Z(o)," ").concat(a," ").concat("string"===typeof c?c:Z(c))})).join(",")},getAutoHeightDuration:function(e){if(!e)return 0;var t=e/36;return Math.round(10*(4+15*Math.pow(t,.25)+t/5))}},$={mobileStepper:1e3,speedDial:1050,appBar:1100,drawer:1200,modal:1300,snackbar:1400,tooltip:1500};function Q(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.breakpoints,r=void 0===t?{}:t,p=e.mixins,g=void 0===p?{}:p,v=e.palette,y=void 0===v?{}:v,W=e.spacing,L=e.typography,w=void 0===L?{}:L,x=(0,n.Z)(e,["breakpoints","mixins","palette","spacing","typography"]),S=function(e){var t=e.primary,r=void 0===t?{light:u[300],main:u[500],dark:u[700]}:t,a=e.secondary,p=void 0===a?{light:z.A200,main:z.A400,dark:z.A700}:a,c=e.error,g=void 0===c?{light:l[300],main:l[500],dark:l[700]}:c,v=e.warning,y=void 0===v?{light:O[300],main:O[500],dark:O[700]}:v,W=e.info,R=void 0===W?{light:f[300],main:f[500],dark:f[700]}:W,L=e.success,w=void 0===L?{light:d[300],main:d[500],dark:d[700]}:L,x=e.type,S=void 0===x?"light":x,B=e.contrastThreshold,_=void 0===B?3:B,N=e.tonalOffset,E=void 0===N?.2:N,T=(0,n.Z)(e,["primary","secondary","error","warning","info","success","type","contrastThreshold","tonalOffset"]);function C(e){return(0,h.mi)(e,q.text.primary)>=_?q.text.primary:A.text.primary}var k=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:500,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:300,n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:700;if(!(e=(0,i.Z)({},e)).main&&e[t]&&(e.main=e[t]),!e.main)throw new Error((0,s.Z)(4,t));if("string"!==typeof e.main)throw new Error((0,s.Z)(5,JSON.stringify(e.main)));return m(e,"light",r,E),m(e,"dark",n,E),e.contrastText||(e.contrastText=C(e.main)),e},P={dark:q,light:A};return(0,o.Z)((0,i.Z)({common:b,type:S,primary:k(r),secondary:k(p,"A400","A200","A700"),error:k(g),warning:k(y),info:k(R),success:k(w),grey:M,contrastThreshold:_,getContrastText:C,augmentColor:k,tonalOffset:E},P[S]),T)}(y),N=function(e){var t=e.values,r=void 0===t?{xs:0,sm:600,md:960,lg:1280,xl:1920}:t,o=e.unit,p=void 0===o?"px":o,c=e.step,s=void 0===c?5:c,b=(0,n.Z)(e,["values","unit","step"]);function M(e){var t="number"===typeof r[e]?r[e]:e;return"@media (min-width:".concat(t).concat(p,")")}function u(e,t){var n=a.indexOf(t);return n===a.length-1?M(e):"@media (min-width:".concat("number"===typeof r[e]?r[e]:e).concat(p,") and ")+"(max-width:".concat((-1!==n&&"number"===typeof r[a[n+1]]?r[a[n+1]]:t)-s/100).concat(p,")")}return(0,i.Z)({keys:a,values:r,up:M,down:function(e){var t=a.indexOf(e)+1,n=r[a[t]];return t===a.length?M("xs"):"@media (max-width:".concat(("number"===typeof n&&t>0?n:e)-s/100).concat(p,")")},between:u,only:function(e){return u(e,e)},width:function(e){return r[e]}},b)}(r),E=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:8;if(e.mui)return e;var t=V({spacing:e}),r=function(){for(var e=arguments.length,r=new Array(e),n=0;n1?C-1:0),P=1;P2&&void 0!==arguments[2]?arguments[2]:{clone:!0},o=r.clone?n({},e):e;return i(e)&&i(t)&&Object.keys(t).forEach((function(n){"__proto__"!==n&&(i(t[n])&&n in e?o[n]=a(e[n],t[n],r):o[n]=t[n])})),o}r.d(t,{Z:function(){return a}})},76452:function(e,t,r){"use strict";function n(e){for(var t="https://mui.com/production-error/?code="+e,r=1;r0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]1&&void 0!==arguments[1]?arguments[1]:new Date,i=[],a=o()(n);for(t=0;t1&&void 0!==arguments[1]?arguments[1]:new Date,r=o()(t).utc();return r.locale(a.l10n.locale),s(e,r)}p()},35061:function(e,t,r){"use strict";r.d(t,{JQ:function(){return z},Kw:function(){return l}});var n=function(e){return"string"!==typeof e||""===e?(console.error("The namespace must be a non-empty string."),!1):!!/^[a-zA-Z][a-zA-Z0-9_.\-\/]*$/.test(e)||(console.error("The namespace can only contain numbers, letters, dashes, periods, underscores and slashes."),!1)};var o=function(e){return"string"!==typeof e||""===e?(console.error("The hook name must be a non-empty string."),!1):/^__/.test(e)?(console.error("The hook name cannot begin with `__`."),!1):!!/^[a-zA-Z][a-zA-Z0-9_.-]*$/.test(e)||(console.error("The hook name can only contain numbers, letters, dashes, periods and underscores."),!1)};var i=function(e,t){return function(r,i,a){var p=arguments.length>3&&void 0!==arguments[3]?arguments[3]:10,c=e[t];if(o(r)&&n(i))if("function"===typeof a)if("number"===typeof p){var s={callback:a,priority:p,namespace:i};if(c[r]){var b,M=c[r].handlers;for(b=M.length;b>0&&!(p>=M[b-1].priority);b--);b===M.length?M[b]=s:M.splice(b,0,s),c.__current.forEach((function(e){e.name===r&&e.currentIndex>=b&&e.currentIndex++}))}else c[r]={handlers:[s],runs:0};"hookAdded"!==r&&e.doAction("hookAdded",r,i,a,p)}else console.error("If specified, the hook priority must be a number.");else console.error("The hook callback must be a function.")}};var a=function(e,t){var r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return function(i,a){var p=e[t];if(o(i)&&(r||n(a))){if(!p[i])return 0;var c=0;if(r)c=p[i].handlers.length,p[i]={runs:p[i].runs,handlers:[]};else for(var s=p[i].handlers,b=function(e){s[e].namespace===a&&(s.splice(e,1),c++,p.__current.forEach((function(t){t.name===i&&t.currentIndex>=e&&t.currentIndex--})))},M=s.length-1;M>=0;M--)b(M);return"hookRemoved"!==i&&e.doAction("hookRemoved",i,a),c}}};var p=function(e,t){return function(r,n){var o=e[t];return"undefined"!==typeof n?r in o&&o[r].handlers.some((function(e){return e.namespace===n})):r in o}};var c=function(e,t){var r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return function(n){var o=e[t];o[n]||(o[n]={handlers:[],runs:0}),o[n].runs++;var i=o[n].handlers;for(var a=arguments.length,p=new Array(a>1?a-1:0),c=1;c1?t-1:0),n=1;n":5,">=":5,"==":4,"!=":4,"&&":3,"||":2,"?":1,"?:":1},o=["(","?"],i={")":["("],":":["?","?:"]},a=/<=|>=|==|!=|&&|\|\||\?:|\(|!|\*|\/|%|\+|-|<|>|\?|\)|:/;var l={"!":function(e){return!e},"*":function(e,t){return e*t},"/":function(e,t){return e/t},"%":function(e,t){return e%t},"+":function(e,t){return e+t},"-":function(e,t){return e-t},"<":function(e,t){return e":function(e,t){return e>t},">=":function(e,t){return e>=t},"==":function(e,t){return e===t},"!=":function(e,t){return e!==t},"&&":function(e,t){return e&&t},"||":function(e,t){return e||t},"?:":function(e,t,r){if(e)throw t;return r}};function O(e){var t=function(e){for(var t,r,p,c,s=[],b=[];t=e.match(a);){for(r=t[0],(p=e.substr(0,t.index).trim())&&s.push(p);c=b.pop();){if(i[r]){if(i[r][0]===c){r=i[r][1]||r;break}}else if(o.indexOf(c)>=0||n[c]1&&void 0!==arguments[1]?arguments[1]:"default";n.data[t]=A(A(A({},q),n.data[t]),e),n.data[t][""]=A(A({},q[""]),n.data[t][""])},p=function(e,t){a(e,t),i()},c=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"default",t=arguments.length>1?arguments[1]:void 0,r=arguments.length>2?arguments[2]:void 0,o=arguments.length>3?arguments[3]:void 0,i=arguments.length>4?arguments[4]:void 0;return n.data[e]||a(void 0,e),n.dcnpgettext(e,t,r,o,i)},s=function(){return arguments.length>0&&void 0!==arguments[0]?arguments[0]:"default"},b=function(e,t,n){var o=c(n,t,e);return r?(o=r.applyFilters("i18n.gettext_with_context",o,e,t,n),r.applyFilters("i18n.gettext_with_context_"+s(n),o,e,t,n)):o};if(e&&p(e,t),r){var M=function(e){m.test(e)&&i()};r.addAction("hookAdded","core/i18n",M),r.addAction("hookRemoved","core/i18n",M)}return{getLocaleData:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"default";return n.data[e]},setLocaleData:p,resetLocaleData:function(e,t){n.data={},n.pluralForms={},p(e,t)},subscribe:function(e){return o.add(e),function(){return o.delete(e)}},__:function(e,t){var n=c(t,void 0,e);return r?(n=r.applyFilters("i18n.gettext",n,e,t),r.applyFilters("i18n.gettext_"+s(t),n,e,t)):n},_x:b,_n:function(e,t,n,o){var i=c(o,void 0,e,t,n);return r?(i=r.applyFilters("i18n.ngettext",i,e,t,n,o),r.applyFilters("i18n.ngettext_"+s(o),i,e,t,n,o)):i},_nx:function(e,t,n,o,i){var a=c(i,o,e,t,n);return r?(a=r.applyFilters("i18n.ngettext_with_context",a,e,t,n,o,i),r.applyFilters("i18n.ngettext_with_context_"+s(i),a,e,t,n,o,i)):a},isRTL:function(){return"rtl"===b("ltr","text direction")},hasTranslation:function(e,t,o){var i,a,p=t?t+"\x04"+e:e,c=!(null===(i=n.data)||void 0===i||null===(a=i[null!==o&&void 0!==o?o:"default"])||void 0===a||!a[p]);return r&&(c=r.applyFilters("i18n.has_translation",c,e,t,o),c=r.applyFilters("i18n.has_translation_"+s(o),c,e,t,o)),c}}}(void 0,void 0,r(35061).JQ),v=(g.getLocaleData.bind(g),g.setLocaleData.bind(g),g.resetLocaleData.bind(g),g.subscribe.bind(g),g.__.bind(g)),y=g._x.bind(g);g._n.bind(g),g._nx.bind(g),g.isRTL.bind(g),g.hasTranslation.bind(g)},7630:function(e,t,r){e.exports={default:r(51909),__esModule:!0}},99532:function(e,t,r){e.exports={default:r(38700),__esModule:!0}},83255:function(e,t,r){e.exports={default:r(58467),__esModule:!0}},87154:function(e,t,r){e.exports={default:r(52405),__esModule:!0}},85720:function(e,t,r){e.exports={default:r(31740),__esModule:!0}},15400:function(e,t,r){e.exports={default:r(209),__esModule:!0}},74164:function(e,t,r){e.exports={default:r(10183),__esModule:!0}},89465:function(e,t){"use strict";t.__esModule=!0,t.default=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},95949:function(e,t,r){"use strict";t.__esModule=!0;var n,o=r(83255),i=(n=o)&&n.__esModule?n:{default:n};t.default=function(){function e(e,t){for(var r=0;r=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r}},16621:function(e,t,r){"use strict";t.__esModule=!0;var n,o=r(37010),i=(n=o)&&n.__esModule?n:{default:n};t.default=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!==("undefined"===typeof t?"undefined":(0,i.default)(t))&&"function"!==typeof t?e:t}},37010:function(e,t,r){"use strict";t.__esModule=!0;var n=a(r(74164)),o=a(r(15400)),i="function"===typeof o.default&&"symbol"===typeof n.default?function(e){return typeof e}:function(e){return e&&"function"===typeof o.default&&e.constructor===o.default&&e!==o.default.prototype?"symbol":typeof e};function a(e){return e&&e.__esModule?e:{default:e}}t.default="function"===typeof o.default&&"symbol"===i(n.default)?function(e){return"undefined"===typeof e?"undefined":i(e)}:function(e){return e&&"function"===typeof o.default&&e.constructor===o.default&&e!==o.default.prototype?"symbol":"undefined"===typeof e?"undefined":i(e)}},17046:function(e,t){"use strict";t.byteLength=function(e){var t=c(e),r=t[0],n=t[1];return 3*(r+n)/4-n},t.toByteArray=function(e){var t,r,i=c(e),a=i[0],p=i[1],s=new o(function(e,t,r){return 3*(t+r)/4-r}(0,a,p)),b=0,M=p>0?a-4:a;for(r=0;r>16&255,s[b++]=t>>8&255,s[b++]=255&t;2===p&&(t=n[e.charCodeAt(r)]<<2|n[e.charCodeAt(r+1)]>>4,s[b++]=255&t);1===p&&(t=n[e.charCodeAt(r)]<<10|n[e.charCodeAt(r+1)]<<4|n[e.charCodeAt(r+2)]>>2,s[b++]=t>>8&255,s[b++]=255&t);return s},t.fromByteArray=function(e){for(var t,n=e.length,o=n%3,i=[],a=16383,p=0,c=n-o;pc?c:p+a));1===o?(t=e[n-1],i.push(r[t>>2]+r[t<<4&63]+"==")):2===o&&(t=(e[n-2]<<8)+e[n-1],i.push(r[t>>10]+r[t>>4&63]+r[t<<2&63]+"="));return i.join("")};for(var r=[],n=[],o="undefined"!==typeof Uint8Array?Uint8Array:Array,i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=0,p=i.length;a0)throw new Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");return-1===r&&(r=t),[r,r===t?0:4-r%4]}function s(e,t,n){for(var o,i,a=[],p=t;p>18&63]+r[i>>12&63]+r[i>>6&63]+r[63&i]);return a.join("")}n["-".charCodeAt(0)]=62,n["_".charCodeAt(0)]=63},88372:function(e,t,r){"use strict";var n=r(86976),o=r(72071),i=r(21233),a=r(64302),p=r(40801),c=r(17046),s=r(27543),b="function"===typeof Symbol&&"function"===typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;t.Buffer=z,t.SlowBuffer=function(e){+e!=e&&(e=0);return z.alloc(+e)},t.INSPECT_MAX_BYTES=50;var M=2147483647;function u(e){if(e>M)throw new RangeError('The value "'+e+'" is invalid for option "size"');var t=new Uint8Array(e);return Object.setPrototypeOf(t,z.prototype),t}function z(e,t,r){if("number"===typeof e){if("string"===typeof t)throw new TypeError('The "string" argument must be of type string. Received type number');return f(e)}return l(e,t,r)}function l(e,t,r){if("string"===typeof e)return function(e,t){"string"===typeof t&&""!==t||(t="utf8");if(!z.isEncoding(t))throw new TypeError("Unknown encoding: "+t);var r=0|q(e,t),n=u(r),o=n.write(e,t);o!==r&&(n=n.slice(0,o));return n}(e,t);if(ArrayBuffer.isView(e))return function(e){if(ee(e,Uint8Array)){var t=new Uint8Array(e);return h(t.buffer,t.byteOffset,t.byteLength)}return d(e)}(e);if(null==e)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e);if(ee(e,ArrayBuffer)||e&&ee(e.buffer,ArrayBuffer))return h(e,t,r);if("undefined"!==typeof SharedArrayBuffer&&(ee(e,SharedArrayBuffer)||e&&ee(e.buffer,SharedArrayBuffer)))return h(e,t,r);if("number"===typeof e)throw new TypeError('The "value" argument must not be of type number. Received type number');var n=e.valueOf&&e.valueOf();if(null!=n&&n!==e)return z.from(n,t,r);var o=function(e){if(z.isBuffer(e)){var t=0|A(e.length),r=u(t);return 0===r.length||e.copy(r,0,0,t),r}if(void 0!==e.length)return"number"!==typeof e.length||te(e.length)?u(0):d(e);if("Buffer"===e.type&&Array.isArray(e.data))return d(e.data)}(e);if(o)return o;if("undefined"!==typeof Symbol&&null!=Symbol.toPrimitive&&"function"===typeof e[Symbol.toPrimitive])return z.from(e[Symbol.toPrimitive]("string"),t,r);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e)}function O(e){if("number"!==typeof e)throw new TypeError('"size" argument must be of type number');if(e<0)throw new RangeError('The value "'+e+'" is invalid for option "size"')}function f(e){return O(e),u(e<0?0:0|A(e))}function d(e){for(var t=e.length<0?0:0|A(e.length),r=u(t),n=0;n=M)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+M.toString(16)+" bytes");return 0|e}function q(e,t){if(z.isBuffer(e))return e.length;if(ArrayBuffer.isView(e)||ee(e,ArrayBuffer))return e.byteLength;if("string"!==typeof e)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof e);var r=e.length,n=arguments.length>2&&!0===arguments[2];if(!n&&0===r)return 0;for(var o=!1;;)switch(t){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":return $(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return Q(e).length;default:if(o)return n?-1:$(e).length;t=(""+t).toLowerCase(),o=!0}}function m(e,t,r){var n=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return T(this,t,r);case"utf8":case"utf-8":return B(this,t,r);case"ascii":return N(this,t,r);case"latin1":case"binary":return E(this,t,r);case"base64":return S(this,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return C(this,t,r);default:if(n)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),n=!0}}function g(e,t,r){var n=e[t];e[t]=e[r],e[r]=n}function v(e,t,r,n,o){if(0===e.length)return-1;if("string"===typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),te(r=+r)&&(r=o?0:e.length-1),r<0&&(r=e.length+r),r>=e.length){if(o)return-1;r=e.length-1}else if(r<0){if(!o)return-1;r=0}if("string"===typeof t&&(t=z.from(t,n)),z.isBuffer(t))return 0===t.length?-1:y(e,t,r,n,o);if("number"===typeof t)return t&=255,"function"===typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,r):Uint8Array.prototype.lastIndexOf.call(e,t,r):y(e,[t],r,n,o);throw new TypeError("val must be string, number or Buffer")}function y(e,t,r,n,o){var i,a=1,p=e.length,c=t.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(e.length<2||t.length<2)return-1;a=2,p/=2,c/=2,r/=2}function s(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(o){var b=-1;for(i=r;ip&&(r=p-c),i=r;i>=0;i--){for(var M=!0,u=0;uo&&(n=o):n=o;var i,a=t.length;for(n>a/2&&(n=a/2),i=0;i>8,o=r%256,i.push(o),i.push(n);return i}(t,e.length-r),e,r,n)}function S(e,t,r){return 0===t&&r===e.length?c.fromByteArray(e):c.fromByteArray(e.slice(t,r))}function B(e,t,r){r=Math.min(e.length,r);for(var n=[],o=t;o239?4:i>223?3:i>191?2:1;if(o+p<=r){var c=void 0,s=void 0,b=void 0,M=void 0;switch(p){case 1:i<128&&(a=i);break;case 2:128===(192&(c=e[o+1]))&&(M=(31&i)<<6|63&c)>127&&(a=M);break;case 3:c=e[o+1],s=e[o+2],128===(192&c)&&128===(192&s)&&(M=(15&i)<<12|(63&c)<<6|63&s)>2047&&(M<55296||M>57343)&&(a=M);break;case 4:c=e[o+1],s=e[o+2],b=e[o+3],128===(192&c)&&128===(192&s)&&128===(192&b)&&(M=(15&i)<<18|(63&c)<<12|(63&s)<<6|63&b)>65535&&M<1114112&&(a=M)}}null===a?(a=65533,p=1):a>65535&&(a-=65536,n.push(a>>>10&1023|55296),a=56320|1023&a),n.push(a),o+=p}return function(e){var t=e.length;if(t<=_)return String.fromCharCode.apply(String,e);var r="",n=0;for(;nn.length?(z.isBuffer(i)||(i=z.from(i)),i.copy(n,o)):Uint8Array.prototype.set.call(n,i,o);else{if(!z.isBuffer(i))throw new TypeError('"list" argument must be an Array of Buffers');i.copy(n,o)}o+=i.length}return n},z.byteLength=q,z.prototype._isBuffer=!0,z.prototype.swap16=function(){var e=this.length;if(e%2!==0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(var t=0;tr&&(e+=" ... "),""},b&&(z.prototype[b]=z.prototype.inspect),z.prototype.compare=function(e,t,r,n,o){if(ee(e,Uint8Array)&&(e=z.from(e,e.offset,e.byteLength)),!z.isBuffer(e))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof e);if(void 0===t&&(t=0),void 0===r&&(r=e?e.length:0),void 0===n&&(n=0),void 0===o&&(o=this.length),t<0||r>e.length||n<0||o>this.length)throw new RangeError("out of range index");if(n>=o&&t>=r)return 0;if(n>=o)return-1;if(t>=r)return 1;if(this===e)return 0;for(var i=(o>>>=0)-(n>>>=0),a=(r>>>=0)-(t>>>=0),p=Math.min(i,a),c=this.slice(n,o),s=e.slice(t,r),b=0;b>>=0,isFinite(r)?(r>>>=0,void 0===n&&(n="utf8")):(n=r,r=void 0)}var o=this.length-t;if((void 0===r||r>o)&&(r=o),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var i=!1;;)switch(n){case"hex":return W(this,e,t,r);case"utf8":case"utf-8":return R(this,e,t,r);case"ascii":case"latin1":case"binary":return L(this,e,t,r);case"base64":return w(this,e,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return x(this,e,t,r);default:if(i)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),i=!0}},z.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var _=4096;function N(e,t,r){var n="";r=Math.min(e.length,r);for(var o=t;on)&&(r=n);for(var o="",i=t;ir)throw new RangeError("Trying to access beyond buffer length")}function P(e,t,r,n,o,i){if(!z.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||te.length)throw new RangeError("Index out of range")}function D(e,t,r,n,o){H(t,n,o,e,r,7);var i=Number(t&BigInt(4294967295));e[r++]=i,i>>=8,e[r++]=i,i>>=8,e[r++]=i,i>>=8,e[r++]=i;var a=Number(t>>BigInt(32)&BigInt(4294967295));return e[r++]=a,a>>=8,e[r++]=a,a>>=8,e[r++]=a,a>>=8,e[r++]=a,r}function X(e,t,r,n,o){H(t,n,o,e,r,7);var i=Number(t&BigInt(4294967295));e[r+7]=i,i>>=8,e[r+6]=i,i>>=8,e[r+5]=i,i>>=8,e[r+4]=i;var a=Number(t>>BigInt(32)&BigInt(4294967295));return e[r+3]=a,a>>=8,e[r+2]=a,a>>=8,e[r+1]=a,a>>=8,e[r]=a,r+8}function I(e,t,r,n,o,i){if(r+n>e.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function F(e,t,r,n,o){return t=+t,r>>>=0,o||I(e,0,r,4),s.write(e,t,r,n,23,4),r+4}function j(e,t,r,n,o){return t=+t,r>>>=0,o||I(e,0,r,8),s.write(e,t,r,n,52,8),r+8}z.prototype.slice=function(e,t){var r=this.length;(e=~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),(t=void 0===t?r:~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),t>>=0,t>>>=0,r||k(e,t,this.length);for(var n=this[e],o=1,i=0;++i>>=0,t>>>=0,r||k(e,t,this.length);for(var n=this[e+--t],o=1;t>0&&(o*=256);)n+=this[e+--t]*o;return n},z.prototype.readUint8=z.prototype.readUInt8=function(e,t){return e>>>=0,t||k(e,1,this.length),this[e]},z.prototype.readUint16LE=z.prototype.readUInt16LE=function(e,t){return e>>>=0,t||k(e,2,this.length),this[e]|this[e+1]<<8},z.prototype.readUint16BE=z.prototype.readUInt16BE=function(e,t){return e>>>=0,t||k(e,2,this.length),this[e]<<8|this[e+1]},z.prototype.readUint32LE=z.prototype.readUInt32LE=function(e,t){return e>>>=0,t||k(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},z.prototype.readUint32BE=z.prototype.readUInt32BE=function(e,t){return e>>>=0,t||k(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},z.prototype.readBigUInt64LE=ne((function(e){Y(e>>>=0,"offset");var t=this[e],r=this[e+7];void 0!==t&&void 0!==r||Z(e,this.length-8);var n=t+this[++e]*Math.pow(2,8)+this[++e]*Math.pow(2,16)+this[++e]*Math.pow(2,24),o=this[++e]+this[++e]*Math.pow(2,8)+this[++e]*Math.pow(2,16)+r*Math.pow(2,24);return BigInt(n)+(BigInt(o)<>>=0,"offset");var t=this[e],r=this[e+7];void 0!==t&&void 0!==r||Z(e,this.length-8);var n=t*Math.pow(2,24)+this[++e]*Math.pow(2,16)+this[++e]*Math.pow(2,8)+this[++e],o=this[++e]*Math.pow(2,24)+this[++e]*Math.pow(2,16)+this[++e]*Math.pow(2,8)+r;return(BigInt(n)<>>=0,t>>>=0,r||k(e,t,this.length);for(var n=this[e],o=1,i=0;++i=(o*=128)&&(n-=Math.pow(2,8*t)),n},z.prototype.readIntBE=function(e,t,r){e>>>=0,t>>>=0,r||k(e,t,this.length);for(var n=t,o=1,i=this[e+--n];n>0&&(o*=256);)i+=this[e+--n]*o;return i>=(o*=128)&&(i-=Math.pow(2,8*t)),i},z.prototype.readInt8=function(e,t){return e>>>=0,t||k(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},z.prototype.readInt16LE=function(e,t){e>>>=0,t||k(e,2,this.length);var r=this[e]|this[e+1]<<8;return 32768&r?4294901760|r:r},z.prototype.readInt16BE=function(e,t){e>>>=0,t||k(e,2,this.length);var r=this[e+1]|this[e]<<8;return 32768&r?4294901760|r:r},z.prototype.readInt32LE=function(e,t){return e>>>=0,t||k(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},z.prototype.readInt32BE=function(e,t){return e>>>=0,t||k(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},z.prototype.readBigInt64LE=ne((function(e){Y(e>>>=0,"offset");var t=this[e],r=this[e+7];void 0!==t&&void 0!==r||Z(e,this.length-8);var n=this[e+4]+this[e+5]*Math.pow(2,8)+this[e+6]*Math.pow(2,16)+(r<<24);return(BigInt(n)<>>=0,"offset");var t=this[e],r=this[e+7];void 0!==t&&void 0!==r||Z(e,this.length-8);var n=(t<<24)+this[++e]*Math.pow(2,16)+this[++e]*Math.pow(2,8)+this[++e];return(BigInt(n)<>>=0,t||k(e,4,this.length),s.read(this,e,!0,23,4)},z.prototype.readFloatBE=function(e,t){return e>>>=0,t||k(e,4,this.length),s.read(this,e,!1,23,4)},z.prototype.readDoubleLE=function(e,t){return e>>>=0,t||k(e,8,this.length),s.read(this,e,!0,52,8)},z.prototype.readDoubleBE=function(e,t){return e>>>=0,t||k(e,8,this.length),s.read(this,e,!1,52,8)},z.prototype.writeUintLE=z.prototype.writeUIntLE=function(e,t,r,n){(e=+e,t>>>=0,r>>>=0,n)||P(this,e,t,r,Math.pow(2,8*r)-1,0);var o=1,i=0;for(this[t]=255&e;++i>>=0,r>>>=0,n)||P(this,e,t,r,Math.pow(2,8*r)-1,0);var o=r-1,i=1;for(this[t+o]=255&e;--o>=0&&(i*=256);)this[t+o]=e/i&255;return t+r},z.prototype.writeUint8=z.prototype.writeUInt8=function(e,t,r){return e=+e,t>>>=0,r||P(this,e,t,1,255,0),this[t]=255&e,t+1},z.prototype.writeUint16LE=z.prototype.writeUInt16LE=function(e,t,r){return e=+e,t>>>=0,r||P(this,e,t,2,65535,0),this[t]=255&e,this[t+1]=e>>>8,t+2},z.prototype.writeUint16BE=z.prototype.writeUInt16BE=function(e,t,r){return e=+e,t>>>=0,r||P(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=255&e,t+2},z.prototype.writeUint32LE=z.prototype.writeUInt32LE=function(e,t,r){return e=+e,t>>>=0,r||P(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e,t+4},z.prototype.writeUint32BE=z.prototype.writeUInt32BE=function(e,t,r){return e=+e,t>>>=0,r||P(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},z.prototype.writeBigUInt64LE=ne((function(e){return D(this,e,arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,BigInt(0),BigInt("0xffffffffffffffff"))})),z.prototype.writeBigUInt64BE=ne((function(e){return X(this,e,arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,BigInt(0),BigInt("0xffffffffffffffff"))})),z.prototype.writeIntLE=function(e,t,r,n){if(e=+e,t>>>=0,!n){var o=Math.pow(2,8*r-1);P(this,e,t,r,o-1,-o)}var i=0,a=1,p=0;for(this[t]=255&e;++i>0)-p&255;return t+r},z.prototype.writeIntBE=function(e,t,r,n){if(e=+e,t>>>=0,!n){var o=Math.pow(2,8*r-1);P(this,e,t,r,o-1,-o)}var i=r-1,a=1,p=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)e<0&&0===p&&0!==this[t+i+1]&&(p=1),this[t+i]=(e/a>>0)-p&255;return t+r},z.prototype.writeInt8=function(e,t,r){return e=+e,t>>>=0,r||P(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=255&e,t+1},z.prototype.writeInt16LE=function(e,t,r){return e=+e,t>>>=0,r||P(this,e,t,2,32767,-32768),this[t]=255&e,this[t+1]=e>>>8,t+2},z.prototype.writeInt16BE=function(e,t,r){return e=+e,t>>>=0,r||P(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=255&e,t+2},z.prototype.writeInt32LE=function(e,t,r){return e=+e,t>>>=0,r||P(this,e,t,4,2147483647,-2147483648),this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},z.prototype.writeInt32BE=function(e,t,r){return e=+e,t>>>=0,r||P(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},z.prototype.writeBigInt64LE=ne((function(e){return D(this,e,arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),z.prototype.writeBigInt64BE=ne((function(e){return X(this,e,arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),z.prototype.writeFloatLE=function(e,t,r){return F(this,e,t,!0,r)},z.prototype.writeFloatBE=function(e,t,r){return F(this,e,t,!1,r)},z.prototype.writeDoubleLE=function(e,t,r){return j(this,e,t,!0,r)},z.prototype.writeDoubleBE=function(e,t,r){return j(this,e,t,!1,r)},z.prototype.copy=function(e,t,r,n){if(!z.isBuffer(e))throw new TypeError("argument should be a Buffer");if(r||(r=0),n||0===n||(n=this.length),t>=e.length&&(t=e.length),t||(t=0),n>0&&n=this.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),e.length-t>>=0,r=void 0===r?this.length:r>>>0,e||(e=0),"number"===typeof e)for(i=t;i=n+4;r-=3)t="_".concat(e.slice(r-3,r)).concat(t);return"".concat(e.slice(0,r)).concat(t)}function H(e,t,r,n,o,i){if(e>r||e3?0===t||t===BigInt(0)?">= 0".concat(p," and < 2").concat(p," ** ").concat(8*(i+1)).concat(p):">= -(2".concat(p," ** ").concat(8*(i+1)-1).concat(p,") and < 2 ** ")+"".concat(8*(i+1)-1).concat(p):">= ".concat(t).concat(p," and <= ").concat(r).concat(p),new V.ERR_OUT_OF_RANGE("value",a,e)}!function(e,t,r){Y(t,"offset"),void 0!==e[t]&&void 0!==e[t+r]||Z(t,e.length-(r+1))}(n,o,i)}function Y(e,t){if("number"!==typeof e)throw new V.ERR_INVALID_ARG_TYPE(t,"number",e)}function Z(e,t,r){if(Math.floor(e)!==e)throw Y(e,r),new V.ERR_OUT_OF_RANGE(r||"offset","an integer",e);if(t<0)throw new V.ERR_BUFFER_OUT_OF_BOUNDS;throw new V.ERR_OUT_OF_RANGE(r||"offset",">= ".concat(r?1:0," and <= ").concat(t),e)}U("ERR_BUFFER_OUT_OF_BOUNDS",(function(e){return e?"".concat(e," is outside of buffer bounds"):"Attempt to access memory outside buffer bounds"}),RangeError),U("ERR_INVALID_ARG_TYPE",(function(e,t){return'The "'.concat(e,'" argument must be of type number. Received type ').concat(typeof t)}),TypeError),U("ERR_OUT_OF_RANGE",(function(e,t,r){var n='The value of "'.concat(e,'" is out of range.'),o=r;return Number.isInteger(r)&&Math.abs(r)>Math.pow(2,32)?o=G(String(r)):"bigint"===typeof r&&(o=String(r),(r>Math.pow(BigInt(2),BigInt(32))||r<-Math.pow(BigInt(2),BigInt(32)))&&(o=G(o)),o+="n"),n+=" It must be ".concat(t,". Received ").concat(o)}),RangeError);var K=/[^+/0-9A-Za-z-_]/g;function $(e,t){var r;t=t||1/0;for(var n=e.length,o=null,i=[],a=0;a55295&&r<57344){if(!o){if(r>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===n){(t-=3)>-1&&i.push(239,191,189);continue}o=r;continue}if(r<56320){(t-=3)>-1&&i.push(239,191,189),o=r;continue}r=65536+(o-55296<<10|r-56320)}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,r<128){if((t-=1)<0)break;i.push(r)}else if(r<2048){if((t-=2)<0)break;i.push(r>>6|192,63&r|128)}else if(r<65536){if((t-=3)<0)break;i.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;i.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return i}function Q(e){return c.toByteArray(function(e){if((e=(e=e.split("=")[0]).trim().replace(K,"")).length<2)return"";for(;e.length%4!==0;)e+="=";return e}(e))}function J(e,t,r,n){var o;for(o=0;o=t.length||o>=e.length);++o)t[o+r]=e[o];return o}function ee(e,t){return e instanceof t||null!=e&&null!=e.constructor&&null!=e.constructor.name&&e.constructor.name===t.name}function te(e){return e!==e}var re=function(){for(var e="0123456789abcdef",t=new Array(256),r=0;r<16;++r)for(var n=16*r,o=0;o<16;++o)t[n+o]=e[r]+e[o];return t}();function ne(e){return"undefined"===typeof BigInt?oe:e}function oe(){throw new Error("BigInt not supported")}},21402:function(e,t,r){r(73228),e.exports=r(18343).Date.now},22872:function(e,t,r){r(80988),r(18343).Number.isInteger},51909:function(e,t,r){r(95009),e.exports=r(18343).Object.assign},38700:function(e,t,r){r(91329);var n=r(18343).Object;e.exports=function(e,t){return n.create(e,t)}},58467:function(e,t,r){r(11015);var n=r(18343).Object;e.exports=function(e,t,r){return n.defineProperty(e,t,r)}},52405:function(e,t,r){r(9652),e.exports=r(18343).Object.getPrototypeOf},30149:function(e,t,r){r(46357),e.exports=r(18343).Object.keys},31740:function(e,t,r){r(28167),e.exports=r(18343).Object.setPrototypeOf},95526:function(e,t,r){r(19407),e.exports=r(18343).Object.values},209:function(e,t,r){r(96591),r(52805),r(41513),r(45746),e.exports=r(18343).Symbol},10183:function(e,t,r){r(62242),r(95241),e.exports=r(14319).f("iterator")},28196:function(e){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},25285:function(e){e.exports=function(){}},6300:function(e,t,r){var n=r(27830);e.exports=function(e){if(!n(e))throw TypeError(e+" is not an object!");return e}},40002:function(e,t,r){var n=r(50181),o=r(33557),i=r(52869);e.exports=function(e){return function(t,r,a){var p,c=n(t),s=o(c.length),b=i(a,s);if(e&&r!=r){for(;s>b;)if((p=c[b++])!=p)return!0}else for(;s>b;b++)if((e||b in c)&&c[b]===r)return e||b||0;return!e&&-1}}},13119:function(e){var t={}.toString;e.exports=function(e){return t.call(e).slice(8,-1)}},18343:function(e){var t=e.exports={version:"2.6.12"};"number"==typeof __e&&(__e=t)},9587:function(e,t,r){var n=r(28196);e.exports=function(e,t,r){if(n(e),void 0===t)return e;switch(r){case 1:return function(r){return e.call(t,r)};case 2:return function(r,n){return e.call(t,r,n)};case 3:return function(r,n,o){return e.call(t,r,n,o)}}return function(){return e.apply(t,arguments)}}},84754:function(e){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},44258:function(e,t,r){e.exports=!r(6718)((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},21708:function(e,t,r){var n=r(27830),o=r(90860).document,i=n(o)&&n(o.createElement);e.exports=function(e){return i?o.createElement(e):{}}},59856:function(e){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},35090:function(e,t,r){var n=r(91174),o=r(68603),i=r(5357);e.exports=function(e){var t=n(e),r=o.f;if(r)for(var a,p=r(e),c=i.f,s=0;p.length>s;)c.call(e,a=p[s++])&&t.push(a);return t}},43636:function(e,t,r){var n=r(90860),o=r(18343),i=r(9587),a=r(60136),p=r(31665),c="prototype",s=function e(t,r,s){var b,M,u,z=t&e.F,l=t&e.G,O=t&e.S,f=t&e.P,d=t&e.B,h=t&e.W,A=l?o:o[r]||(o[r]={}),q=A[c],m=l?n:O?n[r]:(n[r]||{})[c];for(b in l&&(s=r),s)(M=!z&&m&&void 0!==m[b])&&p(A,b)||(u=M?m[b]:s[b],A[b]=l&&"function"!=typeof m[b]?s[b]:d&&M?i(u,n):h&&m[b]==u?function(e){var t=function(t,r,n){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,r)}return new e(t,r,n)}return e.apply(this,arguments)};return t[c]=e[c],t}(u):f&&"function"==typeof u?i(Function.call,u):u,f&&((A.virtual||(A.virtual={}))[b]=u,t&e.R&&q&&!q[b]&&a(q,b,u)))};s.F=1,s.G=2,s.S=4,s.P=8,s.B=16,s.W=32,s.U=64,s.R=128,e.exports=s},6718:function(e){e.exports=function(e){try{return!!e()}catch(t){return!0}}},90860:function(e){var t=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=t)},31665:function(e){var t={}.hasOwnProperty;e.exports=function(e,r){return t.call(e,r)}},60136:function(e,t,r){var n=r(15231),o=r(73815);e.exports=r(44258)?function(e,t,r){return n.f(e,t,o(1,r))}:function(e,t,r){return e[t]=r,e}},95228:function(e,t,r){var n=r(90860).document;e.exports=n&&n.documentElement},78658:function(e,t,r){e.exports=!r(44258)&&!r(6718)((function(){return 7!=Object.defineProperty(r(21708)("div"),"a",{get:function(){return 7}}).a}))},18296:function(e,t,r){var n=r(13119);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==n(e)?e.split(""):Object(e)}},29211:function(e,t,r){var n=r(13119);e.exports=Array.isArray||function(e){return"Array"==n(e)}},53130:function(e,t,r){var n=r(27830),o=Math.floor;e.exports=function(e){return!n(e)&&isFinite(e)&&o(e)===e}},27830:function(e){e.exports=function(e){return"object"===typeof e?null!==e:"function"===typeof e}},61446:function(e,t,r){"use strict";var n=r(12966),o=r(73815),i=r(56359),a={};r(60136)(a,r(93267)("iterator"),(function(){return this})),e.exports=function(e,t,r){e.prototype=n(a,{next:o(1,r)}),i(e,t+" Iterator")}},66531:function(e,t,r){"use strict";var n=r(20445),o=r(43636),i=r(89292),a=r(60136),p=r(50697),c=r(61446),s=r(56359),b=r(96119),M=r(93267)("iterator"),u=!([].keys&&"next"in[].keys()),z="keys",l="values",O=function(){return this};e.exports=function(e,t,r,f,d,h,A){c(r,t,f);var q,m,g,v=function(e){if(!u&&e in L)return L[e];switch(e){case z:case l:return function(){return new r(this,e)}}return function(){return new r(this,e)}},y=t+" Iterator",W=d==l,R=!1,L=e.prototype,w=L[M]||L["@@iterator"]||d&&L[d],x=w||v(d),S=d?W?v("entries"):x:void 0,B="Array"==t&&L.entries||w;if(B&&(g=b(B.call(new e)))!==Object.prototype&&g.next&&(s(g,y,!0),n||"function"==typeof g[M]||a(g,M,O)),W&&w&&w.name!==l&&(R=!0,x=function(){return w.call(this)}),n&&!A||!u&&!R&&L[M]||a(L,M,x),p[t]=x,p[y]=O,d)if(q={values:W?x:v(l),keys:h?x:v(z),entries:S},A)for(m in q)m in L||i(L,m,q[m]);else o(o.P+o.F*(u||R),t,q);return q}},8633:function(e){e.exports=function(e,t){return{value:t,done:!!e}}},50697:function(e){e.exports={}},20445:function(e){e.exports=!0},98127:function(e,t,r){var n=r(63448)("meta"),o=r(27830),i=r(31665),a=r(15231).f,p=0,c=Object.isExtensible||function(){return!0},s=!r(6718)((function(){return c(Object.preventExtensions({}))})),b=function(e){a(e,n,{value:{i:"O"+ ++p,w:{}}})},M=e.exports={KEY:n,NEED:!1,fastKey:function(e,t){if(!o(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!i(e,n)){if(!c(e))return"F";if(!t)return"E";b(e)}return e[n].i},getWeak:function(e,t){if(!i(e,n)){if(!c(e))return!0;if(!t)return!1;b(e)}return e[n].w},onFreeze:function(e){return s&&M.NEED&&c(e)&&!i(e,n)&&b(e),e}}},23234:function(e,t,r){"use strict";var n=r(44258),o=r(91174),i=r(68603),a=r(5357),p=r(71311),c=r(18296),s=Object.assign;e.exports=!s||r(6718)((function(){var e={},t={},r=Symbol(),n="abcdefghijklmnopqrst";return e[r]=7,n.split("").forEach((function(e){t[e]=e})),7!=s({},e)[r]||Object.keys(s({},t)).join("")!=n}))?function(e,t){for(var r=p(e),s=arguments.length,b=1,M=i.f,u=a.f;s>b;)for(var z,l=c(arguments[b++]),O=M?o(l).concat(M(l)):o(l),f=O.length,d=0;f>d;)z=O[d++],n&&!u.call(l,z)||(r[z]=l[z]);return r}:s},12966:function(e,t,r){var n=r(6300),o=r(13329),i=r(59856),a=r(70709)("IE_PROTO"),p=function(){},c="prototype",s=function(){var e,t=r(21708)("iframe"),n=i.length;for(t.style.display="none",r(95228).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write("