first commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ProLegacy addon - Unified legacy compatibility system
|
||||
*
|
||||
* Name: Duplicator PRO Legacy Support
|
||||
* Version: 1.0.0
|
||||
* Description: Provides backward compatibility for deprecated prefixes (hooks, options, temp files)
|
||||
* Author: Duplicator
|
||||
* Author URI: https://duplicator.com
|
||||
* Requires PHP: 7.4
|
||||
* Requires Duplicator min version: 4.5.24
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Addons\ProLegacy
|
||||
* @author Duplicator <support@duplicator.com>
|
||||
* @copyright 2011-2025 Snap Creek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
* @link https://duplicator.com/
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Duplicator\Addons\ProLegacy;
|
||||
|
||||
/**
|
||||
* ProLegacy Addon Class
|
||||
*
|
||||
* Provides unified legacy compatibility system:
|
||||
* - Hook forwarding: old → new hook names (40 public hooks)
|
||||
* - Options & user meta migration during upgrade
|
||||
* - Temporary files cleanup (dup_pro_* prefixes)
|
||||
* - Upgrade coordination and logging
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Addons\ProLegacy
|
||||
* @author Duplicator <support@duplicator.com>
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
* @link https://duplicator.com/
|
||||
*/
|
||||
class ProLegacy extends \Duplicator\Core\Addons\AbstractAddonCore
|
||||
{
|
||||
/**
|
||||
* Initialize addon
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init(): void
|
||||
{
|
||||
VersionMigration::init();
|
||||
BackupDirMigration::init();
|
||||
LegacyUpgrade::init();
|
||||
HookForwarding::init();
|
||||
LegacyFiles::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get addon directory path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getAddonPath(): string
|
||||
{
|
||||
return __DIR__;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get addon main file path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getAddonFile(): string
|
||||
{
|
||||
return __FILE__;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
/* Silence is golden. */
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Backup directory migration from backups-dup-pro to duplicator-backups
|
||||
*
|
||||
* @package Duplicator\Addons\ProLegacy
|
||||
* @copyright (c) 2025, Snap Creek LLC
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Duplicator\Addons\ProLegacy;
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Utils\Logging\DupLog;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Handles migration of backup directory from old to new name
|
||||
*
|
||||
* Must run BEFORE initEntities (priority 50) which calls initStorageDirectory()
|
||||
* that creates the backup directory if it doesn't exist.
|
||||
*
|
||||
* Race condition handling: During upgrade, DupLog::trace() calls from earlier
|
||||
* priority hooks (e.g., migrateOptionPrefixes at priority 12) may initialize
|
||||
* TraceLogMng which creates the new directory structure prematurely. This class
|
||||
* handles that case by moving any contents from the new directory back to the
|
||||
* old one before performing the rename.
|
||||
*/
|
||||
class BackupDirMigration
|
||||
{
|
||||
const OLD_DIR_NAME = 'backups-dup-pro';
|
||||
const NEW_DIR_NAME = 'duplicator-backups';
|
||||
|
||||
/**
|
||||
* Initialize migration hook
|
||||
*
|
||||
* Priority 45: Before initEntities (50) which calls
|
||||
* StoragesUtil::initDefaultStorage() -> initStorageDirectory()
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void
|
||||
{
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'migrate'], 45, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate backup directory from old name to new name
|
||||
*
|
||||
* If the new directory already exists (created prematurely by trace logging),
|
||||
* copies its contents to the old directory first, then performs the rename.
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function migrate($currentVersion, $newVersion): void
|
||||
{
|
||||
// Skip for new installations
|
||||
if ($currentVersion === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$contentDir = untrailingslashit(wp_normalize_path((string) realpath(WP_CONTENT_DIR)));
|
||||
$oldPath = $contentDir . '/' . self::OLD_DIR_NAME;
|
||||
$newPath = $contentDir . '/' . self::NEW_DIR_NAME;
|
||||
|
||||
if (!is_dir($oldPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_dir($newPath)) {
|
||||
// If new directory exists, copy its contents into old directory first
|
||||
if (!SnapIO::rcopy($newPath, $oldPath)) {
|
||||
DupLog::trace("LEGACY MIGRATION: Failed to copy contents from new directory to old: " . $newPath);
|
||||
return;
|
||||
}
|
||||
if (!SnapIO::rrmdir($newPath)) {
|
||||
DupLog::trace("LEGACY MIGRATION: Failed to remove new directory after merge: " . $newPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (SnapIO::rename($oldPath, $newPath)) {
|
||||
DupLog::trace("LEGACY MIGRATION: Renamed backup directory from " . self::OLD_DIR_NAME . " to " . self::NEW_DIR_NAME);
|
||||
} else {
|
||||
DupLog::trace("LEGACY MIGRATION: Failed to rename backup directory");
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
DupLog::trace("LEGACY MIGRATION: Error migrating backup directory: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Duplicator\Addons\ProLegacy;
|
||||
|
||||
/**
|
||||
* Hook Forwarding Class
|
||||
*
|
||||
* Forwards old hook names (duplicator_pro_*) to new hook names (duplicator_*)
|
||||
* for backward compatibility with third-party code
|
||||
*/
|
||||
class HookForwarding
|
||||
{
|
||||
/**
|
||||
* Version when old hooks were deprecated
|
||||
*/
|
||||
private const DEPRECATION_VERSION = '4.5.25';
|
||||
|
||||
/**
|
||||
* Registry of public hooks requiring backward compatibility
|
||||
* Maps new hook names to array with old hook name and accepted args count
|
||||
*
|
||||
* Only 4 public actions are documented and supported for backward compatibility.
|
||||
* All other hooks are internal use only.
|
||||
*/
|
||||
private const HOOKS_REGISTRY = [
|
||||
'duplicator_build_before_start' => [
|
||||
'old_hook' => 'duplicator_pro_build_before_start',
|
||||
'accepted_args' => 1,
|
||||
],
|
||||
'duplicator_build_completed' => [
|
||||
'old_hook' => 'duplicator_pro_build_completed',
|
||||
'accepted_args' => 1,
|
||||
],
|
||||
'duplicator_build_fail' => [
|
||||
'old_hook' => 'duplicator_pro_build_fail',
|
||||
'accepted_args' => 1,
|
||||
],
|
||||
'duplicator_first_login_after_install' => [
|
||||
'old_hook' => 'duplicator_pro_first_login_after_install',
|
||||
'accepted_args' => 1,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Initialize hook forwarding system
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void
|
||||
{
|
||||
self::registerForwarding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register hook forwarding for all registered hooks
|
||||
*
|
||||
* When new code fires do_action('duplicator_build_before_start', $package),
|
||||
* this callback triggers do_action_deprecated() for the old hook name,
|
||||
* which shows a deprecation warning in WP_DEBUG mode and executes callbacks.
|
||||
*
|
||||
* Priority 9999 ensures this runs last, after all other callbacks on the new hook.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function registerForwarding(): void
|
||||
{
|
||||
foreach (self::HOOKS_REGISTRY as $newHook => $hookData) {
|
||||
add_action($newHook, function (...$args) use ($newHook, $hookData) {
|
||||
do_action_deprecated(
|
||||
$hookData['old_hook'],
|
||||
$args,
|
||||
self::DEPRECATION_VERSION,
|
||||
$newHook
|
||||
);
|
||||
}, 9999, $hookData['accepted_args']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Legacy constants mapping for backward compatibility
|
||||
*
|
||||
* This file maps old DUPLICATOR_PRO_* constants to new DUPLICATOR_* constants.
|
||||
* Users who have defined old constants in wp-config.php will still have them work.
|
||||
*
|
||||
* IMPORTANT: This file must be loaded BEFORE define.php
|
||||
*
|
||||
* @package Duplicator\Addons\ProLegacy
|
||||
* @copyright (c) 2025, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Addons\ProLegacy;
|
||||
|
||||
/**
|
||||
* Maps legacy DUPLICATOR_PRO_* constants to new DUPLICATOR_* constants
|
||||
*
|
||||
* Only includes constants that were overridable in wp-config.php (defined with if (!defined()) pattern)
|
||||
*/
|
||||
class LegacyConstants
|
||||
{
|
||||
/**
|
||||
* Mapping of old constant names to new constant names
|
||||
*
|
||||
* Only constants that were user-overridable in the original define.php
|
||||
*
|
||||
* @var array<string,string>
|
||||
*/
|
||||
private const CONSTANTS_MAP = [
|
||||
'DUPLICATOR_PRO_SSDIR_NAME' => 'DUPLICATOR_SSDIR_NAME',
|
||||
'DUPLICATOR_PRO_DEBUG_TPL_OUTPUT_INVALID' => 'DUPLICATOR_DEBUG_TPL_OUTPUT_INVALID',
|
||||
'DUPLICATOR_PRO_DEBUG_TPL_DATA' => 'DUPLICATOR_DEBUG_TPL_DATA',
|
||||
'DUPLICATOR_PRO_INDEX_INCLUDE_HASH' => 'DUPLICATOR_INDEX_INCLUDE_HASH',
|
||||
'DUPLICATOR_PRO_INDEX_INCLUDE_INSTALLER_FILES' => 'DUPLICATOR_INDEX_INCLUDE_INSTALLER_FILES',
|
||||
'DUPLICATOR_PRO_DISALLOW_IMPORT' => 'DUPLICATOR_DISALLOW_IMPORT',
|
||||
'DUPLICATOR_PRO_DISALLOW_RECOVERY' => 'DUPLICATOR_DISALLOW_RECOVERY',
|
||||
'DUPLICATOR_PRO_PRIMARY_OAUTH_SERVER' => 'DUPLICATOR_PRIMARY_OAUTH_SERVER',
|
||||
'DUPLICATOR_PRO_SECONDARY_OAUTH_SERVER' => 'DUPLICATOR_SECONDARY_OAUTH_SERVER',
|
||||
];
|
||||
|
||||
/**
|
||||
* Apply legacy constant mappings
|
||||
*
|
||||
* For each legacy constant that is defined, define the new constant with the same value
|
||||
* (only if the new constant is not already defined)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function apply(): void
|
||||
{
|
||||
foreach (self::CONSTANTS_MAP as $oldName => $newName) {
|
||||
if (\defined($oldName) && !\defined($newName)) {
|
||||
\define($newName, \constant($oldName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Legacy file compatibility for backward compatibility with old installers
|
||||
*
|
||||
* @package Duplicator\Addons\ProLegacy
|
||||
* @copyright (c) 2025, Snap Creek LLC
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Duplicator\Addons\ProLegacy;
|
||||
|
||||
/**
|
||||
* Handles legacy file creation for backward compatibility
|
||||
*
|
||||
* Creates legacy-prefixed files alongside new files so that old installers
|
||||
* can still read the overwrite params files.
|
||||
*/
|
||||
class LegacyFiles
|
||||
{
|
||||
const LEGACY_OVERWRITE_PARAMS_PREFIX = 'duplicator_pro_params_overwrite';
|
||||
|
||||
/**
|
||||
* Initialize legacy file hooks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void
|
||||
{
|
||||
add_action('duplicator_after_overwrite_params_created', [__CLASS__, 'createLegacyOverwriteParams'], 10, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create legacy overwrite params file for backward compatibility with old installers
|
||||
*
|
||||
* When new plugin creates duplicator_params_overwrite_HASH.json,
|
||||
* this also creates duplicator_pro_params_overwrite_HASH.json so that
|
||||
* old installers (which look for the legacy prefix) can still find it.
|
||||
*
|
||||
* @param string $filePath Full path to the new overwrite params file
|
||||
* @param array<string, mixed> $params Parameters written to the file
|
||||
* @param string $packageHash Package hash used in filename
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function createLegacyOverwriteParams(string $filePath, array $params, string $packageHash): void
|
||||
{
|
||||
$directory = dirname($filePath);
|
||||
$legacyFilePath = $directory . '/' . self::LEGACY_OVERWRITE_PARAMS_PREFIX . '_' . $packageHash . '.json';
|
||||
|
||||
// Copy the new file to the legacy location
|
||||
if (file_exists($filePath)) {
|
||||
copy($filePath, $legacyFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,724 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Legacy upgrade functions for backward compatibility
|
||||
*
|
||||
* @package Duplicator\Addons\ProLegacy
|
||||
* @copyright (c) 2025, Snap Creek LLC
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Duplicator\Addons\ProLegacy;
|
||||
|
||||
use Duplicator\Core\CapMng;
|
||||
use Duplicator\Core\Models\AbstractEntity;
|
||||
use Duplicator\Core\UniqueId;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Models\DynamicGlobalEntity;
|
||||
use Duplicator\Models\GlobalEntity;
|
||||
use Duplicator\Models\ScheduleEntity;
|
||||
use Duplicator\Models\SecureGlobalEntity;
|
||||
use Duplicator\Models\StaticGlobal;
|
||||
use Duplicator\Models\Storages\AbstractStorageEntity;
|
||||
use Duplicator\Models\Storages\StoragesUtil;
|
||||
use Duplicator\Package\AbstractPackage;
|
||||
use Duplicator\Package\DupPackage;
|
||||
use Duplicator\Utils\Crypt\CryptBlowfish;
|
||||
use Duplicator\Utils\Logging\DupLog;
|
||||
use Duplicator\Views\AdminNotices;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Handles legacy upgrade migrations from old prefixes
|
||||
*
|
||||
* Priority scheme:
|
||||
* - 10: initTables (creates database tables)
|
||||
* - 20: initCapabilities
|
||||
* - 50: initEntities (initializes default entities - plugin semi-initialized after this)
|
||||
* - 100: initSecureKey (new installations only)
|
||||
* - 200: initUniqueId
|
||||
* - 1000: updateOptionVersion
|
||||
* - 1001: setInstallInfo
|
||||
* - 10000: resaveAllEntities (always last)
|
||||
*/
|
||||
class LegacyUpgrade
|
||||
{
|
||||
const FIRST_VERSION_WITH_NEW_TABLES = '4.5.14-beta2';
|
||||
const FIRST_VERSION_WITH_STATIC_GLOBAL_IMPROVED = '4.5.23-beta1';
|
||||
const FIRST_VERSION_DEFAULT_PURGE = '4.5.20-beta1';
|
||||
const FIRST_VERSION_WITH_PACKAGE_TYPE = '4.5.22-beta3';
|
||||
const FIRST_VERSION_WITH_DYNAMIC_GLOBAL_ENTITY = '4.5.22-beta8';
|
||||
const FIRST_VERSION_WITH_LOGS_SUBFOLDER = '4.5.23-beta2';
|
||||
const FIRST_VERSION_WITH_WEBSITE_IDENTIFIER_CORE = '4.5.23-RC3';
|
||||
const FIRST_VERSION_WITH_ACTIVITY_LOG = '4.5.25-beta1';
|
||||
const FIRST_VERSION_WITH_NEW_OPTION_PREFIX = '4.5.25-beta8';
|
||||
|
||||
const LEGACY_TRACE_LOG_ENABLED_OPT = 'duplicator_pro_trace_log_enabled';
|
||||
const LEGACY_SEND_TRACE_TO_ERROR_LOG_OPT = 'duplicator_pro_send_trace_to_error_log';
|
||||
const LEGACY_PLUGIN_DATA_OPTION_KEY = 'duplicator_pro_plugin_data_stats';
|
||||
|
||||
/**
|
||||
* List of deprecated WordPress options to be removed during upgrade
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
const DEPRECATED_OPTIONS = ['duplicator_pro_package_active'];
|
||||
|
||||
/**
|
||||
* Initialize upgrade hooks
|
||||
*
|
||||
* Core priorities: 10=initTables, 20=initCapabilities, 50=initEntities, 100+=post-init
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void
|
||||
{
|
||||
// AFTER initTables (10), BEFORE initCapabilities (20)
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'migrateOptionPrefixes'], 12, 2);
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'migrateUserMetaKeys'], 12, 2);
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'migrateCapabilities'], 18, 2);
|
||||
|
||||
// AFTER initCapabilities (20), BEFORE initEntities (50)
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'migrateStaticGlobalOptions'], 40, 2);
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'migrateEntityTypes'], 40, 2);
|
||||
|
||||
// AFTER initEntities (50), BEFORE initSecureKey (100)
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'storeDupSecureKey'], 101, 2);
|
||||
|
||||
// BEFORE initUniqueId (200)
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'migrateWebsiteIdentifier'], 150, 2);
|
||||
|
||||
// AFTER initUniqueId (200)
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'moveDataToDynamicGlobalEntity'], 250, 2);
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'updatePackageType'], 251, 2);
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'migrateLogsToSubfolder'], 252, 2);
|
||||
|
||||
// Legacy storage fixes
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'fixDoubleDefaultStorages'], 300, 2);
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'updateBackupRecordPurgeSettings'], 301, 2);
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'cleanupDeprecatedOptions'], 302, 2);
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'notifyActivityLogIntegration'], 303, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate options from old prefixes (duplicator_pro_*, duplicator_expire_) to new prefix (dupli_opt_*)
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function migrateOptionPrefixes($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion === false || version_compare($currentVersion, self::FIRST_VERSION_WITH_NEW_OPTION_PREFIX, '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
try {
|
||||
// Map of old option names to new option names
|
||||
$optionMapping = [
|
||||
'duplicator_pro_capabilities' => 'dupli_opt_capabilities',
|
||||
'duplicator_pro_notifications' => 'dupli_opt_notifications',
|
||||
'duplicator_pro_plugin_data_stats' => 'dupli_opt_plugin_data_stats',
|
||||
'duplicator_pro_recover_point' => 'dupli_opt_recover_point',
|
||||
'duplicator_pro_ui_view_state' => 'dupli_opt_ui_view_state',
|
||||
'duplicator_pro_inst_hash_notice' => 'dupli_opt_inst_hash_notice',
|
||||
'duplicator_pro_activate_plugins_after_installation' => 'dupli_opt_activate_plugins_after_installation',
|
||||
'duplicator_pro_migration_success' => 'dupli_opt_migration_success',
|
||||
'duplicator_pro_s3_contents_fetch_fail' => 'dupli_opt_s3_contents_fetch_fail',
|
||||
'duplicator_pro_quick_fix_notice' => 'dupli_opt_quick_fix_notice',
|
||||
'duplicator_pro_failed_schedule_notice' => 'dupli_opt_failed_schedule_notice',
|
||||
'duplicator_pro_failed_backup_notice' => 'dupli_opt_failed_backup_notice',
|
||||
'duplicator_pro_activity_log_upgrade_notice' => 'dupli_opt_activity_log_upgrade_notice',
|
||||
'duplicator_pro_first_login_after_install' => 'dupli_opt_first_login_after_install',
|
||||
'duplicator_pro_migration_data' => 'dupli_opt_migration_data',
|
||||
'duplicator_pro_clean_install_report' => 'dupli_opt_clean_install_report',
|
||||
'duplicator_pro_help_docs_expire' => 'dupli_opt_help_docs_expire',
|
||||
'duplicator_pro_auth_token_auto_active' => 'dupli_opt_auth_token_auto_active',
|
||||
'duplicator_pro_frotend_delay' => 'dupli_opt_frotend_delay',
|
||||
'duplicator_pro_pending_cancellations' => 'dupli_opt_pending_cancellations',
|
||||
'duplicator_pro_exe_safe_mode' => 'dupli_opt_exe_safe_mode',
|
||||
'duplicator_pro_settings' => 'dupli_opt_settings',
|
||||
// StaticGlobal options
|
||||
'duplicator_uninstall_package_option' => 'dupli_opt_uninstall_package',
|
||||
'duplicator_uninstall_settings_option' => 'dupli_opt_uninstall_settings',
|
||||
'duplicator_crypt_option' => 'dupli_opt_crypt',
|
||||
'duplicator_trace_log_enabled_option' => 'dupli_opt_trace_log_enabled',
|
||||
'duplicator_trace_to_error_log_option' => 'dupli_opt_trace_to_error_log',
|
||||
// UniqueId, TransferFailureHandler, DupCloud options
|
||||
'duplicator_unique_id' => 'dupli_opt_unique_id',
|
||||
'duplicator_failed_transfers' => 'dupli_opt_failed_transfers',
|
||||
'duplicator_dup_cloud_out_of_space_notice' => 'dupli_opt_dup_cloud_out_of_space_notice',
|
||||
];
|
||||
|
||||
$migratedCount = 0;
|
||||
foreach ($optionMapping as $oldName => $newName) {
|
||||
$oldValue = get_option($oldName);
|
||||
if ($oldValue !== false) {
|
||||
update_option($newName, $oldValue);
|
||||
delete_option($oldName);
|
||||
$migratedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($migratedCount > 0) {
|
||||
DupLog::trace("LEGACY MIGRATION: Migrated {$migratedCount} options to new prefix");
|
||||
}
|
||||
|
||||
// Migrate duplicator_expire_* options to dupli_opt_expire_*
|
||||
$expireOptions = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name LIKE %s",
|
||||
$wpdb->esc_like('duplicator_expire_') . '%'
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
$expireMigratedCount = 0;
|
||||
foreach ($expireOptions as $option) {
|
||||
$oldName = $option['option_name'];
|
||||
$newName = str_replace('duplicator_expire_', 'dupli_opt_expire_', $oldName);
|
||||
update_option($newName, $option['option_value']);
|
||||
delete_option($oldName);
|
||||
$expireMigratedCount++;
|
||||
}
|
||||
|
||||
if ($expireMigratedCount > 0) {
|
||||
DupLog::trace("LEGACY MIGRATION: Migrated {$expireMigratedCount} expire options to new prefix");
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
DupLog::trace("LEGACY MIGRATION: Error migrating option prefixes: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate user meta keys from old prefixes to new prefix (dupli_opt_*)
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function migrateUserMetaKeys($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion === false || version_compare($currentVersion, self::FIRST_VERSION_WITH_NEW_OPTION_PREFIX, '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
$userMetaMapping = [
|
||||
'duplicator_pro_created_format' => 'dupli_opt_created_format',
|
||||
'duplicator_pro_opts_per_page' => 'dupli_opt_opts_per_page',
|
||||
'duplicator_user_ui_option' => 'dupli_opt_user_ui_option',
|
||||
'dupli-import-view-mode' => 'dupli_opt_import_view_mode',
|
||||
'duplicator_recommended_plugin_dismissed' => 'dupli_opt_recommended_plugin_dismissed',
|
||||
];
|
||||
|
||||
try {
|
||||
foreach ($userMetaMapping as $oldKey => $newKey) {
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"UPDATE {$wpdb->usermeta} SET meta_key = %s WHERE meta_key = %s",
|
||||
$newKey,
|
||||
$oldKey
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Bulk migrate any remaining duplicator_pro_* user meta
|
||||
$wpdb->query(
|
||||
"UPDATE {$wpdb->usermeta} SET meta_key = REPLACE(meta_key, 'duplicator_pro_', 'dupli_opt_')
|
||||
WHERE meta_key LIKE 'duplicator\_pro\_%'"
|
||||
);
|
||||
|
||||
DupLog::trace("LEGACY MIGRATION: Migrated user meta keys to new prefix");
|
||||
} catch (Throwable $e) {
|
||||
DupLog::trace("LEGACY MIGRATION: Error migrating user meta keys: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate capabilities from old prefix (duplicator_pro_*) to new prefix (duplicator_*)
|
||||
*
|
||||
* This migration must run BEFORE CapMng::getInstance() (priority 4) because:
|
||||
* 1. CapMng reads capabilities from database option
|
||||
* 2. The option contains capability names as array keys
|
||||
* 3. If we change CAP_PREFIX without migrating, keys won't match and reset() will be called
|
||||
*
|
||||
* Migration steps:
|
||||
* 1. Read current capabilities from dupli_opt_capabilities option
|
||||
* 2. Rename array keys from duplicator_pro_* to duplicator_*
|
||||
* 3. Update WordPress roles: remove old caps, add new caps
|
||||
* 4. Update WordPress users: remove old caps, add new caps
|
||||
* 5. Save updated option
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function migrateCapabilities($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion === false || version_compare($currentVersion, self::FIRST_VERSION_WITH_NEW_OPTION_PREFIX, '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldPrefix = 'duplicator_pro_';
|
||||
$newPrefix = CapMng::CAP_PREFIX;
|
||||
|
||||
try {
|
||||
$capabilities = get_option(CapMng::OPTION_KEY, false);
|
||||
if ($capabilities === false || !is_array($capabilities)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DupLog::trace("LEGACY MIGRATION: Starting capability prefix migration");
|
||||
|
||||
// Step 1: Remove old capabilities from WordPress roles and users
|
||||
foreach ($capabilities as $oldCapName => $data) {
|
||||
if (strpos($oldCapName, $oldPrefix) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($data['roles'] as $roleName) {
|
||||
$role = get_role($roleName);
|
||||
if ($role) {
|
||||
$role->remove_cap($oldCapName);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data['users'] as $userId) {
|
||||
$user = get_user_by('id', $userId);
|
||||
if ($user) {
|
||||
$user->remove_cap($oldCapName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Build new capabilities array with renamed keys
|
||||
$newCapabilities = [];
|
||||
foreach ($capabilities as $oldCapName => $data) {
|
||||
if (strpos($oldCapName, $oldPrefix) === 0) {
|
||||
$newCapName = $newPrefix . substr($oldCapName, strlen($oldPrefix));
|
||||
} else {
|
||||
$newCapName = $oldCapName;
|
||||
}
|
||||
$newCapabilities[$newCapName] = $data;
|
||||
}
|
||||
|
||||
// Step 3: Save updated option and let CapMng sync with WordPress
|
||||
update_option(CapMng::OPTION_KEY, $newCapabilities);
|
||||
CapMng::getInstance()->update($newCapabilities);
|
||||
|
||||
$migratedCount = count($newCapabilities);
|
||||
DupLog::trace("LEGACY MIGRATION: Migrated {$migratedCount} capabilities from {$oldPrefix}* to {$newPrefix}*");
|
||||
} catch (Throwable $e) {
|
||||
DupLog::trace("LEGACY MIGRATION: Error migrating capabilities: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate entity type keys from old prefix (DUP_PRO_*) to new format
|
||||
*
|
||||
* Updates the `type` column in duplicator_entities table.
|
||||
* Runs AFTER initTables (priority 10) and BEFORE initEntities (priority 50).
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function migrateEntityTypes($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion === false || version_compare($currentVersion, self::FIRST_VERSION_WITH_NEW_OPTION_PREFIX, '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
$typeMapping = [
|
||||
'DUP_PRO_Schedule_Entity' => 'Schedule_Entity',
|
||||
'DUP_PRO_Global_Entity' => 'Global_Entity',
|
||||
'DUP_PRO_Brand_Entity' => 'Brand_Entity',
|
||||
'DUP_PRO_Package_Template_Entity' => 'Package_Template_Entity',
|
||||
'DUP_PRO_Storage_Entity' => 'Storage_Entity',
|
||||
'DUP_PRO_System_Global_Entity' => 'System_Global_Entity',
|
||||
'DUP_PRO_Secure_Global_Entity' => 'Secure_Global_Entity',
|
||||
'DUP_PRO_Staging_Entity' => 'Staging_Entity',
|
||||
];
|
||||
|
||||
try {
|
||||
$tableName = $wpdb->base_prefix . 'duplicator_entities';
|
||||
$migratedCount = 0;
|
||||
|
||||
foreach ($typeMapping as $oldType => $newType) {
|
||||
$result = $wpdb->update(
|
||||
$tableName,
|
||||
['type' => $newType],
|
||||
['type' => $oldType],
|
||||
['%s'],
|
||||
['%s']
|
||||
);
|
||||
|
||||
if ($result !== false && $result > 0) {
|
||||
$migratedCount += $result;
|
||||
}
|
||||
}
|
||||
|
||||
if ($migratedCount > 0) {
|
||||
DupLog::trace("LEGACY MIGRATION: Migrated {$migratedCount} entity type keys");
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
DupLog::trace("LEGACY MIGRATION: Error migrating entity types: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate static global options from old prefix
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function migrateStaticGlobalOptions($currentVersion, $newVersion): void
|
||||
{
|
||||
if (
|
||||
$currentVersion === false ||
|
||||
version_compare($currentVersion, self::FIRST_VERSION_WITH_STATIC_GLOBAL_IMPROVED, '>=')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$traceLogEnabled = get_option(self::LEGACY_TRACE_LOG_ENABLED_OPT, false);
|
||||
$sendTraceToErrorLog = get_option(self::LEGACY_SEND_TRACE_TO_ERROR_LOG_OPT, false);
|
||||
|
||||
if ($traceLogEnabled !== false) {
|
||||
StaticGlobal::setTraceLogEnabledOption($traceLogEnabled);
|
||||
delete_option(self::LEGACY_TRACE_LOG_ENABLED_OPT);
|
||||
DupLog::trace("LEGACY MIGRATION: Migrated trace_log_enabled option");
|
||||
}
|
||||
|
||||
if ($sendTraceToErrorLog !== false) {
|
||||
StaticGlobal::setSendTraceToErrorLogOption($sendTraceToErrorLog);
|
||||
delete_option(self::LEGACY_SEND_TRACE_TO_ERROR_LOG_OPT);
|
||||
DupLog::trace("LEGACY MIGRATION: Migrated send_trace_to_error_log option");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove deprecated WordPress options with old prefix
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function cleanupDeprecatedOptions($currentVersion, $newVersion): void
|
||||
{
|
||||
try {
|
||||
foreach (self::DEPRECATED_OPTIONS as $optionName) {
|
||||
if (delete_option($optionName)) {
|
||||
DupLog::trace("LEGACY CLEANUP: Removed deprecated option: {$optionName}");
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
DupLog::trace("LEGACY CLEANUP: Error removing deprecated options: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save DUP SECURE KEY for legacy upgrades
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function storeDupSecureKey($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion !== false && SnapUtil::versionCompare($currentVersion, '4.5.0', '<=', 3)) {
|
||||
CryptBlowfish::createWpConfigSecureKey(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate website identifier from PluginData to UniqueId core class
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function migrateWebsiteIdentifier($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion === false || version_compare($currentVersion, self::FIRST_VERSION_WITH_WEBSITE_IDENTIFIER_CORE, '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$pluginDataOption = get_option(self::LEGACY_PLUGIN_DATA_OPTION_KEY, false);
|
||||
if ($pluginDataOption === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pluginDataArray = json_decode($pluginDataOption, true);
|
||||
if (!is_array($pluginDataArray) || !isset($pluginDataArray['identifier']) || strlen($pluginDataArray['identifier']) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldIdentifier = $pluginDataArray['identifier'];
|
||||
update_option(UniqueId::OPTION_KEY, $oldIdentifier, true);
|
||||
DupLog::trace("LEGACY MIGRATION: Copied identifier to new location: " . UniqueId::OPTION_KEY);
|
||||
} catch (Throwable $e) {
|
||||
DupLog::trace("LEGACY MIGRATION: Error migrating website identifier: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update legacy package adding type column if empty
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updatePackageType($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion === false || version_compare($currentVersion, self::FIRST_VERSION_WITH_PACKAGE_TYPE, '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
$table = DupPackage::getTableName();
|
||||
$wpdb->query($wpdb->prepare("UPDATE `{$table}` SET type = %s WHERE type IS NULL OR type = ''", DupPackage::getBackupType()));
|
||||
DupLog::trace("LEGACY MIGRATION: Updated package type column");
|
||||
}
|
||||
|
||||
/**
|
||||
* Move data to dynamic global entity from secure global entity
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function moveDataToDynamicGlobalEntity($currentVersion, $newVersion): void
|
||||
{
|
||||
if (
|
||||
$currentVersion === false ||
|
||||
version_compare($currentVersion, self::FIRST_VERSION_WITH_DYNAMIC_GLOBAL_ENTITY, '>=')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sGlobal = SecureGlobalEntity::getInstance();
|
||||
$dGlobal = DynamicGlobalEntity::getInstance();
|
||||
|
||||
if (!is_null($sGlobal->lkp) && strlen($sGlobal->lkp) > 0) {
|
||||
$dGlobal->setValString('license_key_visible_pwd', $sGlobal->lkp);
|
||||
$sGlobal->lkp = null;
|
||||
}
|
||||
|
||||
if (!is_null($sGlobal->basic_auth_password) && strlen($sGlobal->basic_auth_password) > 0) {
|
||||
$dGlobal->setValString('basic_auth_password', $sGlobal->basic_auth_password);
|
||||
$sGlobal->basic_auth_password = null;
|
||||
}
|
||||
|
||||
$sGlobal->save();
|
||||
$dGlobal->save();
|
||||
DupLog::trace("LEGACY MIGRATION: Moved data to DynamicGlobalEntity");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix double default storages bug from legacy versions
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function fixDoubleDefaultStorages($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$defaultStorageId = StoragesUtil::getDefaultStorageId();
|
||||
$doubleStorageIds = StoragesUtil::removeDoubleDefaultStorages();
|
||||
|
||||
if ($doubleStorageIds === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto assign references to the correct default storage
|
||||
ScheduleEntity::listCallback(
|
||||
function (ScheduleEntity $schedule) use ($defaultStorageId, $doubleStorageIds): void {
|
||||
$save = false;
|
||||
foreach ($schedule->storage_ids as $key => $storageId) {
|
||||
if (!in_array($storageId, $doubleStorageIds)) {
|
||||
continue;
|
||||
}
|
||||
$schedule->storage_ids[$key] = $defaultStorageId;
|
||||
$save = true;
|
||||
}
|
||||
$schedule->storage_ids = array_values(array_unique($schedule->storage_ids));
|
||||
if ($save) {
|
||||
$schedule->save();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
DupPackage::dbSelectByStatusCallback(
|
||||
function (DupPackage $package) use ($defaultStorageId, $doubleStorageIds): void {
|
||||
$save = false;
|
||||
if (in_array($package->active_storage_id, $doubleStorageIds)) {
|
||||
$package->active_storage_id = $defaultStorageId;
|
||||
$save = true;
|
||||
}
|
||||
foreach ($package->upload_infos as $key => $info) {
|
||||
if (!in_array($info->getStorageId(), $doubleStorageIds)) {
|
||||
continue;
|
||||
}
|
||||
$info->setStorageId($defaultStorageId);
|
||||
$save = true;
|
||||
}
|
||||
if ($save) {
|
||||
$package->save();
|
||||
}
|
||||
},
|
||||
[
|
||||
[
|
||||
'op' => '>=',
|
||||
'status' => AbstractPackage::STATUS_COMPLETE,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
DupLog::trace("LEGACY MIGRATION: Fixed double default storages");
|
||||
} catch (Throwable $e) {
|
||||
DupLog::trace("LEGACY MIGRATION: Error fixing double default storages: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the correct backup purge setting based on previous default local storage settings
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateBackupRecordPurgeSettings($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion === false || version_compare($currentVersion, self::FIRST_VERSION_DEFAULT_PURGE, '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$global = GlobalEntity::getInstance();
|
||||
if (StoragesUtil::getDefaultStorage()->isPurgeEnabled()) {
|
||||
$global->setPurgeBackupRecords(AbstractStorageEntity::BACKUP_RECORDS_REMOVE_DEFAULT);
|
||||
} else {
|
||||
$global->setPurgeBackupRecords(AbstractStorageEntity::BACKUP_RECORDS_REMOVE_NEVER);
|
||||
}
|
||||
|
||||
$global->save();
|
||||
DupLog::trace("LEGACY MIGRATION: Updated backup record purge settings");
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate existing log files from root directory to logs subfolder
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function migrateLogsToSubfolder($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion === false || version_compare($currentVersion, self::FIRST_VERSION_WITH_LOGS_SUBFOLDER, '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
DupLog::trace("LEGACY MIGRATION: Moving log files to logs subfolder");
|
||||
|
||||
// Ensure logs directory exists
|
||||
if (!file_exists(DUPLICATOR_LOGS_PATH)) {
|
||||
SnapIO::mkdirP(DUPLICATOR_LOGS_PATH);
|
||||
SnapIO::chmod(DUPLICATOR_LOGS_PATH, 'u+rwx');
|
||||
SnapIO::createSilenceIndex(DUPLICATOR_LOGS_PATH);
|
||||
}
|
||||
|
||||
// Use SnapIO::regexGlob for more robust file discovery
|
||||
$logFiles = SnapIO::regexGlob(DUPLICATOR_SSDIR_PATH, [
|
||||
'regexFile' => [
|
||||
'/.*_log\.txt$/',
|
||||
'/.*_log_bak\.txt$/',
|
||||
'/.*\.log$/',
|
||||
],
|
||||
'regexFolder' => false,
|
||||
'recursive' => false,
|
||||
]);
|
||||
|
||||
$migratedCount = 0;
|
||||
foreach ($logFiles as $oldPath) {
|
||||
$filename = basename($oldPath);
|
||||
$newPath = DUPLICATOR_LOGS_PATH . '/' . $filename;
|
||||
|
||||
if (SnapIO::rename($oldPath, $newPath)) {
|
||||
$migratedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
DupLog::trace("LEGACY MIGRATION: Moved {$migratedCount} log files to logs subfolder");
|
||||
} catch (Throwable $e) {
|
||||
DupLog::trace("LEGACY MIGRATION: Error moving log files: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set transient for Activity Log integration upgrade notice if failed backups exist
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function notifyActivityLogIntegration($currentVersion, $newVersion): void
|
||||
{
|
||||
if ($currentVersion === false || version_compare($currentVersion, self::FIRST_VERSION_WITH_ACTIVITY_LOG, '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$count = DupPackage::countByStatus(
|
||||
[
|
||||
[
|
||||
'op' => '<',
|
||||
'status' => AbstractPackage::STATUS_PRE_PROCESS,
|
||||
],
|
||||
],
|
||||
[DupPackage::getBackupType()]
|
||||
);
|
||||
|
||||
if ($count > 0) {
|
||||
set_transient(AdminNotices::ACTIVITY_LOG_UPGRADE_NOTICE, $count, 0);
|
||||
DupLog::trace("LEGACY MIGRATION: Set Activity Log upgrade notice transient with count: " . $count);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
DupLog::trace("LEGACY MIGRATION: Error setting Activity Log upgrade notice: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Version options migration for backward compatibility
|
||||
*
|
||||
* @package Duplicator\Addons\ProLegacy
|
||||
* @copyright (c) 2025, Snap Creek LLC
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Duplicator\Addons\ProLegacy;
|
||||
|
||||
/**
|
||||
* Handles migration of version-related options from old to new prefix
|
||||
*
|
||||
* This class provides fallback filters so the core can read old option values
|
||||
* during the transition period before migration runs.
|
||||
*/
|
||||
class VersionMigration
|
||||
{
|
||||
const OLD_VERSION_OPT_KEY = 'duplicator_pro_plugin_version';
|
||||
const OLD_INSTALL_INFO_OPT_KEY = 'duplicator_pro_install_info';
|
||||
const OLD_INSTALL_TIME_OPT_KEY = 'duplicator_pro_install_time';
|
||||
|
||||
/**
|
||||
* Initialize fallback filters and migration hooks
|
||||
*
|
||||
* Must be called early in plugin boot, before upgrade checks run.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void
|
||||
{
|
||||
add_filter('duplicator_stored_version', [__CLASS__, 'fallbackVersion']);
|
||||
add_filter('duplicator_stored_install_info', [__CLASS__, 'fallbackInstallInfo']);
|
||||
|
||||
// Priority 3: run very early on duplicator_upgrade, leaving room for future additions
|
||||
add_action('duplicator_upgrade', [__CLASS__, 'migrate'], 3, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback filter for version option
|
||||
*
|
||||
* If new option doesn't exist, return value from old option.
|
||||
*
|
||||
* @param string|false $version Version from new option or false
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public static function fallbackVersion($version)
|
||||
{
|
||||
if ($version === false) {
|
||||
return get_option(self::OLD_VERSION_OPT_KEY, false);
|
||||
}
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback filter for install info option
|
||||
*
|
||||
* If new option doesn't exist, return value from old option.
|
||||
*
|
||||
* @param array<string,mixed>|false $installInfo Install info from new option or false
|
||||
*
|
||||
* @return array<string,mixed>|false
|
||||
*/
|
||||
public static function fallbackInstallInfo($installInfo)
|
||||
{
|
||||
if ($installInfo === false) {
|
||||
return get_option(self::OLD_INSTALL_INFO_OPT_KEY, false);
|
||||
}
|
||||
return $installInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate old version options to new names
|
||||
*
|
||||
* Called during upgrade process. Deletes old options after migration.
|
||||
* Idempotent - safe to run multiple times.
|
||||
*
|
||||
* @param false|string $currentVersion current Duplicator version
|
||||
* @param string $newVersion new Duplicator version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function migrate($currentVersion = false, $newVersion = ''): void
|
||||
{
|
||||
// Version option: just delete old, new will be written by updateOptionVersion()
|
||||
delete_option(self::OLD_VERSION_OPT_KEY);
|
||||
|
||||
// Install info: migrate value if needed
|
||||
$oldInstallInfo = get_option(self::OLD_INSTALL_INFO_OPT_KEY, false);
|
||||
$newInstallInfo = get_option(\Duplicator\Core\Upgrade\UpgradePlugin::DUP_INSTALL_INFO_OPT_KEY, false);
|
||||
|
||||
if ($oldInstallInfo !== false && $newInstallInfo === false) {
|
||||
update_option(
|
||||
\Duplicator\Core\Upgrade\UpgradePlugin::DUP_INSTALL_INFO_OPT_KEY,
|
||||
$oldInstallInfo,
|
||||
false
|
||||
);
|
||||
}
|
||||
delete_option(self::OLD_INSTALL_INFO_OPT_KEY);
|
||||
|
||||
// Cleanup legacy install time option
|
||||
delete_option(self::OLD_INSTALL_TIME_OPT_KEY);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user