first commit

This commit is contained in:
Roman Pyrih
2026-04-21 15:48:41 +02:00
commit 7483681901
10216 changed files with 3236626 additions and 0 deletions

View File

@@ -0,0 +1,170 @@
<?php
/**
* Version Pro Base addon class
*
* Name: Duplicator PRO base
* Version: 1
* Author: Duplicator
* Author URI: http://snapcreek.com
*
* PHP version 7.4
*
* @category Duplicator
* @package Plugin
* @author Duplicator
* @copyright 2011-2021 Snapcreek LLC
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
* @version GIT: $Id$
* @link https://duplicator.com/
*/
namespace Duplicator\Addons\ProBase;
// phpcs:disable
require_once __DIR__ . '/vendor/edd/EDD_SL_Plugin_Updater.php';
// phpcs:enable
use Duplicator\Controllers\SchedulePageController;
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Addons\ProBase\License\LicenseNotices;
use Duplicator\Addons\ProBase\Models\LicenseData;
use Duplicator\Core\Controllers\AbstractMenuPageController;
use Duplicator\Core\MigrationMng;
use Duplicator\Installer\Models\MigrateData;
/**
* Version Pro Base addon class
*
* @category Duplicator
* @package Plugin
* @author Snapcreek <admin@snapcreek.com>
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
* @link http://snapcreek.com
*/
class ProBase extends \Duplicator\Core\Addons\AbstractAddonCore
{
/**
* @return void
*/
public function init(): void
{
add_action('init', [$this, 'hookInit']);
add_filter('duplicator_main_menu_label', fn(): string => 'Duplicator Pro');
add_filter('duplicator_menu_pages', [$this, 'addScheduleMenuField']);
add_action(MigrationMng::HOOK_FIRST_LOGIN_AFTER_INSTALL, function (MigrateData $migrationData): void {
License::clearVersionCache(true);
$licenseData = LicenseData::getInstance();
if ($licenseData->getStatus() !== LicenseData::STATUS_VALID) {
$licenseData->activate();
}
});
add_action('duplicator_after_activation', [$this, 'onUpgradePlugin'], 10, 2);
add_action('duplicator_before_update_crypt_setting', [self::class, 'beforeCryptUpdateSettings']);
add_action('duplicator_after_update_crypt_setting', [self::class, 'afterCryptUpdateSettings']);
add_filter('duplicator_dynamic_data_skip_reset', function (array $skipResetData): array {
$skipResetData[] = 'license_key_visible';
$skipResetData[] = 'license_key_visible_pwd';
return $skipResetData;
});
add_filter('duplicator_dynamic_skip_data_export', function (array $skipExportData): array {
$skipExportData[] = 'license_key_visible';
$skipExportData[] = 'license_key_visible_pwd';
return $skipExportData;
});
LicenseNotices::init();
LicensingController::init();
}
/**
* Add schedule menu page
*
* @param array<string, AbstractMenuPageController> $basicMenuPages menu pages
*
* @return array<string, AbstractMenuPageController>
*/
public function addScheduleMenuField($basicMenuPages)
{
$page = SchedulePageController::getInstance();
$basicMenuPages[$page->getSlug()] = $page;
return $basicMenuPages;
}
/**
* Function calle on duplicator_addons_loaded hook
*
* @return void
*/
public function hookInit(): void
{
License::check();
}
/**
* Function called on plugin upgrade
*
* @param false|string $currentVersion current version
* @param string $newVersion new version
*
* @return void
*/
public function onUpgradePlugin($currentVersion, $newVersion): void
{
if ($currentVersion !== false && version_compare($currentVersion, '4.5.16-beta1', '<')) {
$legacyKey = get_option(LicenseData::LICENSE_OLD_KEY_OPTION_NAME, '');
if (!empty($legacyKey)) {
LicenseData::getInstance()->setKey($legacyKey);
}
delete_option(LicenseData::LICENSE_OLD_KEY_OPTION_NAME);
}
License::clearVersionCache(false);
}
/**
* Before crypt update settings
*
* @return void
*/
public static function beforeCryptUpdateSettings(): void
{
// make sure the license date si reade before the settings are updated
LicenseData::getInstance();
}
/**
* After crypt update settings
*
* @return void
*/
public static function afterCryptUpdateSettings(): void
{
LicenseData::getInstance()->save();
}
/**
*
* @return string
*/
public static function getAddonPath(): string
{
return __DIR__;
}
/**
*
* @return string
*/
public static function getAddonFile(): string
{
return __FILE__;
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Version Pro Base Installer addon class
*
* Name: Duplicator PRO base
* Version: 1
* Author: Snap Creek
* Author URI: http://snapcreek.com
*
* @category Duplicator
* @package Installer
* @author Snapcreek <admin@snapcreek.com>
* @copyright 2011-2021 Snapcreek LLC
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
* @version GIT: $Id$
* @link http://snapcreek.com
*/
namespace Duplicator\Installer\Addons\ProBase;
use Duplicator\Installer\Core\Hooks\HooksMng;
use Duplicator\Installer\Core\Params\Items\ParamItem;
/**
* Version Pro Base Installer addon class
*
* @category Duplicator
* @package Installer
* @author Snapcreek <admin@snapcreek.com>
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
* @link http://snapcreek.com
*/
class ProBase extends \Duplicator\Installer\Core\Addons\InstAbstractAddonCore
{
/**
* Main init addon
*
* @return void
*/
public function init(): void
{
HooksMng::getInstance()->addFilter(
'dupx_main_header',
fn($value): string => 'Duplicator PRO'
);
HooksMng::getInstance()->addFilter('installer_get_init_params', [self::class, 'getInitParams']);
HooksMng::getInstance()->addAction(
'after_params_overwrite',
[
AdvancedParams::class,
'updateParamsAfterOverwrite',
]
);
}
/**
* getInitParams
*
* @param ParamItem[] $params params list
*
* @return ParamItem[]
*/
public static function getInitParams($params): array
{
$advParams = [];
AdvancedParams::init($advParams);
return array_merge($params, $advParams);
}
/**
* Get addon main folder
*
* @return string
*/
public static function getAddonPath(): string
{
return __DIR__;
}
/**
* Get addon main file
*
* @return string
*/
public static function getAddonFile(): string
{
return __FILE__;
}
}

View File

@@ -0,0 +1,292 @@
<?php
/**
*
* @package Duplicator
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Installer\Addons\ProBase;
use Exception;
abstract class AbstractLicense
{
const TYPE_UNKNOWN = -1;
const TYPE_UNLICENSED = 0;
const TYPE_PERSONAL = 1;
const TYPE_FREELANCER = 2;
const TYPE_BUSINESS = 3;
const TYPE_PERSONAL_AUTO = 4;
const TYPE_FREELANCER_AUTO = 5;
const TYPE_BUSINESS_AUTO = 6;
const TYPE_GOLD = 7;
const TYPE_BASIC = 8;
const TYPE_PLUS = 9;
const TYPE_PRO = 10;
const TYPE_ELITE = 11;
const CAPABILITY_BRAND = 1000;
const CAPABILITY_IMPORT_SETTINGS = 1001;
const CAPABILITY_SHEDULE_HOURLY = 1002;
const CAPABILITY_MULTISITE = 1003;
const CAPABILITY_MULTISITE_PLUS = 1004;
const CAPABILITY_POWER_TOOLS = 1005;
const CAPABILITY_CHANGE_TABLE_PREFIX = 1006;
const CAPABILITY_UPDATE_AUTH = 1007;
const CAPABILITY_CAPABILITIES_MNG = 1008;
const CAPABILITY_CAPABILITIES_MNG_PLUS = 1009;
const CAPABILITY_BASE_ADVANCED = 1010;
const CAPABILITY_PACKAGE_COMPONENTS_PLUS = 1011;
const CAPABILITY_IMPORT = 1012;
const CAPABILITY_SCHEDULE = 1013;
const CAPABILITY_STORAGE = 1014;
const CAPABILITY_TEMPLATE = 1015;
const CAPABILITY_STAGING = 1016;
/**
* Return true if multisite is enabled
*
* @return bool
*/
protected static function isMultisite()
{
// This is to avoid warnings in PHP 5.6 because isn't possibile declare an abstract static method.
throw new Exception('This method must be extended');
}
/**
* Returns the license type this installer file is made of.
*
* @return int Returns an enum type of License
*/
public static function getType()
{
// This is to avoid warnings in PHP 5.6 because isn't possibile declare an abstract static method.
throw new Exception('This method must be extended');
}
/**
* Return license limit
*
* @return int<0, max>
*/
public static function getLimit()
{
// This is to avoid warnings in PHP 5.6 because isn't possibile declare an abstract static method.
throw new Exception('This method must be extended');
}
/**
* Return upsell URL
*
* @return string
*/
public static function getUpsellURL()
{
// This is to avoid warnings in PHP 5.6 because isn't possibile declare an abstract static method.
throw new Exception('This method must be extended');
}
/**
* Return true if license is unlimited
*
* @return bool
*/
public static function isUnlimited()
{
return in_array(
static::getType(),
[
self::TYPE_BUSINESS,
self::TYPE_BUSINESS_AUTO,
self::TYPE_GOLD,
]
);
}
/**
* Return true if license have the capability
*
* @param int $capability capability key
* @param ?int $license ENUM license type, if null Get currnt licnse type
*
* @return bool
*/
public static function can($capability, $license = null)
{
if (is_null($license)) {
$license = static::getType();
}
$basicMultisite = in_array(
$license,
[
self::TYPE_PERSONAL,
self::TYPE_FREELANCER,
self::TYPE_FREELANCER_AUTO,
self::TYPE_BUSINESS,
self::TYPE_BUSINESS_AUTO,
self::TYPE_GOLD,
self::TYPE_PRO,
self::TYPE_ELITE,
]
);
switch ($capability) {
case self::CAPABILITY_BASE_ADVANCED:
return true;
case self::CAPABILITY_MULTISITE:
return $basicMultisite;
case self::CAPABILITY_IMPORT:
case self::CAPABILITY_SCHEDULE:
case self::CAPABILITY_STORAGE:
case self::CAPABILITY_TEMPLATE:
if (static::isMultisite()) {
return $basicMultisite;
} else {
return $license > 0;
}
case self::CAPABILITY_BRAND:
case self::CAPABILITY_IMPORT_SETTINGS:
case self::CAPABILITY_SHEDULE_HOURLY:
case self::CAPABILITY_POWER_TOOLS:
case self::CAPABILITY_UPDATE_AUTH:
case self::CAPABILITY_CHANGE_TABLE_PREFIX:
return in_array(
$license,
[
self::TYPE_FREELANCER,
self::TYPE_FREELANCER_AUTO,
self::TYPE_BUSINESS,
self::TYPE_BUSINESS_AUTO,
self::TYPE_GOLD,
self::TYPE_PLUS,
self::TYPE_PRO,
self::TYPE_ELITE,
]
);
case self::CAPABILITY_MULTISITE_PLUS:
case self::CAPABILITY_CAPABILITIES_MNG:
case self::CAPABILITY_CAPABILITIES_MNG_PLUS:
case self::CAPABILITY_PACKAGE_COMPONENTS_PLUS:
return in_array(
$license,
[
self::TYPE_BUSINESS,
self::TYPE_BUSINESS_AUTO,
self::TYPE_GOLD,
self::TYPE_PRO,
self::TYPE_ELITE,
]
);
case self::CAPABILITY_STAGING:
// Staging is available for Pro and Elite licenses
return in_array(
$license,
[
self::TYPE_PRO,
self::TYPE_ELITE,
]
);
default:
return false;
}
}
/**
* Return true if license can be upgraded
*
* @param ?int $license ENUM license type, if null Get currnt licnse type
*
* @return bool
*/
public static function canBeUpgraded($license = null)
{
if (is_null($license)) {
$license = static::getType();
}
return !in_array($license, [
self::TYPE_BUSINESS,
self::TYPE_BUSINESS_AUTO,
self::TYPE_GOLD,
self::TYPE_ELITE,
]);
}
/**
* Return license description
*
* @param ?int $license ENUM license type, if null Get currnt licnse type
* @param bool $article if true add article before description
*
* @return string
*/
public static function getLicenseToString($license = null, $article = false)
{
if (is_null($license)) {
$license = static::getType();
}
switch ($license) {
case self::TYPE_UNLICENSED:
return ($article ? 'an ' : '') . 'Unlicensed';
case self::TYPE_PERSONAL:
case self::TYPE_PERSONAL_AUTO:
return ($article ? 'a ' : '') . 'Personal';
case self::TYPE_FREELANCER:
case self::TYPE_FREELANCER_AUTO:
return ($article ? 'a ' : '') . 'Freelancer';
case self::TYPE_BUSINESS:
case self::TYPE_BUSINESS_AUTO:
return ($article ? 'a ' : '') . 'Business';
case self::TYPE_GOLD:
return ($article ? 'a ' : '') . 'Gold';
case self::TYPE_BASIC:
return ($article ? 'a ' : '') . 'Basic';
case self::TYPE_PLUS:
return ($article ? 'a ' : '') . 'Plus';
case self::TYPE_PRO:
return ($article ? 'a ' : '') . 'Pro';
case self::TYPE_ELITE:
return ($article ? 'a ' : '') . 'Elite';
default:
return ($article ? 'an ' : '') . 'Unknown Type';
}
}
/**
* Get best license from two given
*
* @param int $l1 ENUM license
* @param int $l2 ENUM license
*
* @return int ENUM license
*/
protected static function getBestLicense($l1, $l2)
{
$l1Weight = 0;
$l2Weight = 0;
$wheigts = [
self::TYPE_UNLICENSED => -1,
self::TYPE_BASIC => 0,
self::TYPE_PERSONAL => 1,
self::TYPE_PERSONAL_AUTO => 1,
self::TYPE_PLUS => 2,
self::TYPE_FREELANCER => 3,
self::TYPE_FREELANCER_AUTO => 3,
self::TYPE_PRO => 4,
self::TYPE_ELITE => 5,
self::TYPE_BUSINESS => 6,
self::TYPE_BUSINESS_AUTO => 6,
self::TYPE_GOLD => 7,
];
$l1Weight = ($wheigts[$l1] ?? -1);
$l2Weight = ($wheigts[$l2] ?? -1);
return ($l1Weight >= $l2Weight ? $l1 : $l2);
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* License class
*
* @category Duplicator
* @package Installer
* @author Snapcreek <admin@snapcreek.com>
* @copyright 2011-2021 Snapcreek LLC
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
*/
namespace Duplicator\Installer\Addons\ProBase;
use Duplicator\Installer\Core\Params\Descriptors\DescriptorInterface;
use Duplicator\Installer\Core\Params\Items\ParamForm;
use Duplicator\Installer\Core\Params\PrmMng;
use DUPX_U;
class AdvancedParams implements DescriptorInterface
{
/**
* Init advanced params
*
* @param \Duplicator\Installer\Core\Params\Items\ParamItem[] $params params list
*
* @return void
*/
public static function init(&$params): void
{
$params[PrmMng::PARAM_GEN_WP_AUTH_KEY] = new ParamForm(
PrmMng::PARAM_GEN_WP_AUTH_KEY,
ParamForm::TYPE_BOOL,
ParamForm::FORM_TYPE_CHECKBOX,
['default' => false],
[
'label' => 'Auth Keys:',
'checkboxLabel' => 'Generate New Unique Authentication Keys and Salts',
'status' => (License::can(License::CAPABILITY_UPDATE_AUTH) ? ParamForm::STATUS_ENABLED : ParamForm::STATUS_DISABLED),
'subNote' => (License::can(License::CAPABILITY_UPDATE_AUTH) ? '' : License::getLicenseUpdateText()),
]
);
}
/**
* Update params after overwrite
*
* @param \Duplicator\Installer\Core\Params\Items\ParamItem[] $params params list
*
* @return void
*/
public static function updateParamsAfterOverwrite($params)
{
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
*
* @package Duplicator
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Installer\Addons\ProBase;
use Duplicator\Installer\Core\Params\PrmMng;
use DUPX_ArchiveConfig;
use DUPX_U;
class License extends AbstractLicense
{
/**
* Returns the license type this installer file is made of.
*
* @return int Returns an enum type of License
*/
public static function getType()
{
return self::getBestLicense(self::getImporterLicense(), self::getInstallerLicense());
}
/**
* Return license limit
*
* @return int<0, max>
*/
public static function getLimit(): int
{
return (int) max(0, (int) \DUPX_ArchiveConfig::getInstance()->license_limit);
}
/**
* Return upsell URL
*
* @return string
*/
public static function getUpsellURL(): string
{
return 'https://duplicator.com/my-account/';
}
/**
* Return true if multisite is enabled
*
* @return bool
*/
protected static function isMultisite(): bool
{
return DUPX_ArchiveConfig::getInstance()->isNetwork();
}
/**
* Get license on installer from package data
*
* @return int Returns an enum type of License
*/
protected static function getInstallerLicense()
{
return DUPX_ArchiveConfig::getInstance()->license_type;
}
/**
* Get importer license from params data
*
* @return int Returns an enum type of License
*/
protected static function getImporterLicense()
{
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
return $overwriteData['dupLicense'] ?? self::TYPE_UNLICENSED;
}
/**
* Return license required note
*
* @return string
*/
public static function getLicenseUpdateText(): string
{
return 'This option isn\'t available at the <b>' . static::getLicenseToString() . '</b> license level.' .
'To enable this option ' .
'<a href="' . DUPX_U::esc_url(static::getUpsellURL()) . '" target="_blank">' . 'upgrade' . '</a>' .
' the License.';
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
*
* @package Duplicator
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Addons\ProBase;
use Duplicator\Models\ScheduleEntity;
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Addons\ProBase\Models\LicenseData;
class DrmHandler
{
const SCHEDULE_DRM_DELAY_DAYS = 14;
/**
* Check if the license has a schedule delay to DRM.
*
* This method determines if the current license type allows for a schedule delay
* to DRM (Digital Rights Management). It returns true if the license type is not
* one of the restricted types that do not allow for a schedule delay.
*
* @return bool True if there is a schedule delay, false otherwise.
*/
protected static function haveLicenseScheduleDelayToDRM(): bool
{
$licenseType = LicenseData::getInstance()->getLicenseType();
if (is_multisite()) {
$noScheduleDRMTypes = [
License::TYPE_UNKNOWN,
License::TYPE_UNLICENSED,
License::TYPE_BASIC,
License::TYPE_PLUS,
];
} else {
$noScheduleDRMTypes = [
License::TYPE_UNKNOWN,
License::TYPE_UNLICENSED,
];
}
return !in_array($licenseType, $noScheduleDRMTypes);
}
/**
* Get the number of days until the scheduled DRM activation.
*
* @return int Returns the number of days left until the scheduled DRM activation, or -1 if there are no days left.
*/
public static function getDaysTillScheduleDRM()
{
if (count(ScheduleEntity::getActive()) == 0) {
// No active schedules, no need to check DRM
return -1;
}
$status = LicenseData::getInstance()->getStatus();
if (!in_array($status, [LicenseData::STATUS_VALID, LicenseData::STATUS_EXPIRED])) {
return -1;
}
if (!self::haveLicenseScheduleDelayToDRM()) {
return -1;
}
if (($expiresDays = LicenseData::getInstance()->getExpirationDays()) === false) {
return -1;
}
return self::SCHEDULE_DRM_DELAY_DAYS + $expiresDays;
}
}

View File

@@ -0,0 +1,295 @@
<?php
/**
*
* @package Duplicator
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Addons\ProBase\License;
use Duplicator\Utils\Logging\DupLog;
use Duplicator\Addons\ProBase\Vendor\EDD\EDD_SL_Plugin_Updater;
use Duplicator\Addons\ProBase\DrmHandler;
use Duplicator\Addons\ProBase\Models\LicenseData;
use Duplicator\Core\CapMng;
use Duplicator\Installer\Addons\ProBase\AbstractLicense;
use Duplicator\Utils\ExpireOptions;
final class License extends AbstractLicense
{
/**
* GENERAL SETTINGS
*/
const EDD_DUP_ITEM_NAME = 'Duplicator Pro';
const FRONTEND_CHECK_DELAY = 61; // Seconds, different fromgeneral frontend check to unsync
const FRONTEND_CHECK_DELAY_OPTION_KEY = 'license_check';
const VISIBILITY_INFO = 0;
const VISIBILITY_ALL = 1;
const VISIBILITY_NONE = 2;
/** @var ?EDD_SL_Plugin_Updater */
private static $edd_updater;
/**
* License check
*
* @return void
*/
public static function check(): void
{
if (
!is_admin() &&
!(defined('WP_CLI') && WP_CLI) &&
ExpireOptions::getUpdate(
self::FRONTEND_CHECK_DELAY_OPTION_KEY,
true,
self::FRONTEND_CHECK_DELAY
) !== false
) {
return;
}
if (
in_array(
LicenseData::getInstance()->getStatus(),
[
LicenseData::STATUS_INVALID,
LicenseData::STATUS_UNKNOWN,
]
)
) {
return;
}
self::initEddUpdater();
}
/**
* Return true if multisite is enabled
*
* @return bool
*/
protected static function isMultisite()
{
return is_multisite();
}
/**
* Return true if license have the capability
*
* @param int $capability capability key
* @param ?int $license ENUM license type, if null Get currnt licnse type
*
* @return bool
*/
public static function can($capability, $license = null)
{
if (is_null($license)) {
$license = static::getType();
}
switch ($capability) {
case self::CAPABILITY_SCHEDULE:
if (parent::can($capability, $license)) {
return true;
}
if (DrmHandler::getDaysTillScheduleDRM() >= 0) {
return true;
}
return false;
default:
return parent::can($capability, $license);
}
}
/**
* Force upgrade check
*
* @return void
*/
public static function forceUpgradeCheck(): void
{
if (
in_array(
LicenseData::getInstance()->getStatus(),
[
LicenseData::STATUS_INVALID,
LicenseData::STATUS_UNKNOWN,
]
)
) {
return;
}
self::clearVersionCache(true);
}
/**
* Return latest version of the plugin
*
* @return string|false
*/
public static function getLatestVersion()
{
$version_info = null;
$edd_updater = self::getEddUpdater();
/** @var false|object */
$version_info = $edd_updater->get_cached_version_info();
if (is_object($version_info) && isset($version_info->new_version)) {
return $version_info->new_version;
} else {
return false;
}
}
/**
* Clear version cache
*
* @param bool $clearLicenseData Clear license data cache
*
* @return void
*/
public static function clearVersionCache(bool $clearLicenseData): void
{
DupLog::trace("CLEAR EDD VERSION CACHE");
if ($clearLicenseData) {
LicenseData::getInstance()->clearCache();
}
self::getEddUpdater()->set_version_info_cache(false);
}
/**
* Return license key
*
* @return string
*/
public static function getLicenseKey()
{
return LicenseData::getInstance()->getKey();
}
/**
* Get license status
*
* @return int
*/
public static function getLicenseStatus()
{
return LicenseData::getInstance()->getStatus();
}
/**
* Return license type
*
* @return int ENUM AbstractLicense::TYPE_*
*/
public static function getType()
{
return (
LicenseData::getInstance()->getStatus() == LicenseData::STATUS_VALID ?
LicenseData::getInstance()->getLicenseType() :
self::TYPE_UNLICENSED
);
}
/**
* Initialize the EDD Updater
*
* @return void
*/
private static function initEddUpdater(): void
{
if (self::$edd_updater !== null) {
return;
}
$eddOpts = [
'version' => DUPLICATOR_VERSION,
'license' => self::getLicenseKey(),
'item_name' => self::EDD_DUP_ITEM_NAME,
'author' => 'Snap Creek Software',
'wp_override' => true,
];
self::$edd_updater = new EDD_SL_Plugin_Updater(
DUPLICATOR_STORE_URL,
DUPLICATOR____FILE,
$eddOpts
);
}
/**
* Accessor that returns the EDD Updater singleton
*
* @return EDD_SL_Plugin_Updater
*/
private static function getEddUpdater()
{
if (self::$edd_updater === null) {
self::initEddUpdater();
}
return self::$edd_updater;
}
/**
* Return upsell URL
*
* @return string
*/
public static function getUpsellURL(): string
{
return DUPLICATOR_BLOG_URL . 'my-account/';
}
/**
* Return EDD license upgrade checkout page URL
*
* @return string
*/
public static function getLicenseCheckoutURL(): string
{
if (
!CapMng::can(CapMng::CAP_LICENSE, false) ||
empty(self::getLicenseKey())
) {
return DUPLICATOR_BLOG_URL . 'my-account/';
} else {
return DUPLICATOR_BLOG_URL . 'checkout?edd_license_key=' . self::getLicenseKey();
}
}
/**
* Return no activation left message
*
* @return string
*/
public static function getNoActivationLeftMessage()
{
if (self::isUnlimited()) {
$result = sprintf(__('%1$s site licenses are granted in batches of 500.', 'duplicator-pro'), License::getLicenseToString());
$result .= ' ';
$result .= sprintf(
_x(
'Please submit a %1$sticket request%2$s and we will grant you another batch.',
'%1$s and %2$s represents the opening and closing HTML tags for an anchor or link',
'duplicator-pro'
),
'<a href="' . DUPLICATOR_BLOG_URL . 'my-account/support/" target="_blank">',
'</a>'
);
$result .= '<br>';
return $result . __('This process helps to ensure that licenses are not stolen or abused for users.', 'duplicator-pro');
} else {
return __(
'Use the link above to login to your duplicator.com dashboard to manage your licenses or upgrade to a higher license.',
'duplicator-pro'
);
}
}
}

View File

@@ -0,0 +1,305 @@
<?php
/**
*
* @package Duplicator
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Addons\ProBase\License;
use Duplicator\Addons\ProBase\DrmHandler;
use Duplicator\Addons\ProBase\Models\LicenseData;
use Duplicator\Controllers\SettingsPageController;
use Duplicator\Core\CapMng;
use Duplicator\Core\Controllers\ControllersManager;
use Duplicator\Core\Views\TplMng;
use Duplicator\Views\AdminNotices;
class LicenseNotices
{
/**
* Init notice actions
*
* @return void
*/
public static function init(): void
{
add_action('admin_init', [self::class, 'adminInit']);
$path = plugin_basename(DUPLICATOR____FILE);
// Important to make this priority 11 or greater to ensure the version cache is up to date by EDD
add_action("after_plugin_row_{$path}", [self::class, 'noLicenseDisplay'], 11, 2);
}
/**
* Function called on hook admin_init
*
* @return void
*/
public static function adminInit(): void
{
$action = is_multisite() ? 'network_admin_notices' : 'admin_notices';
add_action($action, [self::class, 'licenseAlertCheck']);
}
/**
* Function called on hook admin_init
*
* @param string $file Path to the plugin file relative to the plugins directory
* @param array<string, mixed> $plugin An array of plugin data
*
* @return void
*/
public static function noLicenseDisplay($file, $plugin): void
{
$latest_version = License::getLatestVersion();
// Only display this message when there is no update message
if (($latest_version === false) || version_compare(DUPLICATOR_VERSION, $latest_version, '>=')) {
$error_string = null;
$licenseSettingsUrl = SettingsPageController::getInstance()->getMenuLink(
SettingsPageController::L2_SLUG_GENERAL
);
switch (LicenseData::getInstance()->getStatus()) {
case LicenseData::STATUS_INVALID:
case LicenseData::STATUS_SITE_INACTIVE:
$error_string = sprintf(
esc_html_x(
'Your Duplicator Pro license key is invalid so you aren\'t getting important updates!
%1$sActivate your license%2$s or %3$spurchase a license%4$s.',
'1,3: <a> tag, 2,4: </a> tag',
'duplicator-pro'
),
'<a href="' . esc_url($licenseSettingsUrl) . '">',
'</a>',
'<a target="_blank" href="' . esc_url(DUPLICATOR_BLOG_URL . 'pricing/') . '">',
'</a>'
);
break;
case LicenseData::STATUS_EXPIRED:
$error_string = sprintf(
__(
'Your Duplicator Pro license key has expired so you aren\'t getting important updates! %1$sRenew your license now%2$s',
'duplicator-pro'
),
'<a target="_blank" href="' . esc_url(License::getLicenseCheckoutURL()) . '">',
'</a>'
);
break;
default:
break;
}
if ($error_string == null) {
return;
} elseif (!CapMng::can(CapMng::CAP_LICENSE, false)) {
// Overwrite error message if the user does not have the license capability
$error_string = esc_html__(
'Duplicator Pro license key is not activated. Please contact the Duplicator license manager to activate it.',
'duplicator-pro'
);
}
ob_start();
?>
<script>
jQuery("[data-slug=\'duplicator-pro\']").addClass("update");
</script>
<tr style="border-top-color:black" class="plugin-update-tr active">
<td colspan="4" class="plugin-update colspanchange">
<div class="update-message notice inline notice-error notice-alt">
<p>
<?php
echo wp_kses(
$error_string,
[
'a' => [
'href' => [],
'target' => [],
],
]
);
?>
</p>
</div>
</td>
</tr>
<?php
ob_end_flush();
}
}
/**
* Used by the WP action hook to detect the state of the endpoint license
* which calls the various show* methods for which alert to display
*
* @return void
*/
public static function licenseAlertCheck(): void
{
if (
!CapMng::can(CapMng::CAP_BASIC, false) ||
ControllersManager::isCurrentPage(
ControllersManager::SETTINGS_SUBMENU_SLUG,
SettingsPageController::L2_SLUG_GENERAL
)
) {
return;
}
if (file_exists(DUPLICATOR_SSDIR_PATH . "/ovr.dup")) {
return;
}
$licenseData = LicenseData::getInstance();
switch ($licenseData->getStatus()) {
case LicenseData::STATUS_VALID:
if (is_multisite() && !License::can(License::CAPABILITY_MULTISITE)) {
self::showMultisiteDRMAlert();
}
break;
case LicenseData::STATUS_EXPIRED:
self::showExpired();
break;
case LicenseData::STATUS_UNKNOWN:
case LicenseData::STATUS_INVALID:
case LicenseData::STATUS_INACTIVE:
case LicenseData::STATUS_DISABLED:
case LicenseData::STATUS_SITE_INACTIVE:
default:
if ($licenseData->haveNoActivationsLeft()) {
self::showNoActivationsLeft();
} else {
self::showInvalidStandardNag();
}
break;
}
}
/**
* Shows the smaller standard nag screen
*
* @return void
*/
private static function showInvalidStandardNag(): void
{
$problem_text = esc_html__('missing', 'duplicator-pro');
$htmlMsg = TplMng::getInstance()->render(
'licensing/notices/inactive_message',
['problem' => $problem_text],
false
);
AdminNotices::displayGeneralAdminNotice(
$htmlMsg,
AdminNotices::GEN_ERROR_NOTICE,
false,
['dup-license-message'],
[],
true
);
}
/**
* Shows the license count used up alert
*
* @return void
*/
private static function showNoActivationsLeft(): void
{
$htmlMsg = TplMng::getInstance()->render(
'licensing/notices/no_activation_left',
[],
false
);
AdminNotices::displayGeneralAdminNotice(
$htmlMsg,
AdminNotices::GEN_ERROR_NOTICE,
false,
['dup-license-message'],
[],
true
);
}
/**
* Shows the expired message alert
*
* @return void
*/
private static function showExpired(): void
{
$renewal_url = License::getLicenseCheckoutURL();
$htmlMsg = TplMng::getInstance()->render(
'licensing/notices/expired',
[
'renewal_url' => $renewal_url,
'schedule_disalbe_days_left' => DrmHandler::getDaysTillScheduleDRM(),
],
false
);
AdminNotices::displayGeneralAdminNotice(
$htmlMsg,
AdminNotices::GEN_ERROR_NOTICE,
false,
['dup-license-message'],
[],
true
);
}
/**
* Shows the multisite DRM alert
*
* @return void
*/
private static function showMultisiteDRMAlert(): void
{
$problem_text = esc_html__('missing', 'duplicator-pro');
$htmlMsg = TplMng::getInstance()->render(
'licensing/notices/drm_multisite_msg',
[],
false
);
AdminNotices::displayGeneralAdminNotice(
$htmlMsg,
AdminNotices::GEN_ERROR_NOTICE,
false,
['dup-license-message'],
[],
true
);
}
/**
* Gets the upgrade link
*
* @param string $label The label of the link
* @param bool $echo Whether to echo the link or return it
*
* @return string
*/
public static function getUpsellLinkHTML($label = 'Upgrade', $echo = true)
{
ob_start();
?>
<a class="dup-upgrade-license-link primary-color" href="<?php echo esc_url(License::getUpsellURL()); ?>" target="_blank">
<?php echo esc_html($label); ?>
</a>
<?php
if ($echo) {
ob_end_flush();
return '';
} else {
return ob_get_clean();
}
}
}

View File

@@ -0,0 +1,13 @@
<?php
/**
*
* @package Duplicator
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Addons\ProBase\License;
class LicenseUtils
{
}

View File

@@ -0,0 +1,649 @@
<?php
/**
* Version Pro Base functionalities
*
* Name: Duplicator PRO base
* Version: 1
* Author: Snap Creek
* Author URI: http://snapcreek.com
*
* @package Duplicator
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Addons\ProBase;
use Duplicator\Utils\Logging\DupLog;
use Duplicator\Core\Controllers\ControllersManager;
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Addons\ProBase\Models\LicenseData;
use Duplicator\Controllers\SettingsPageController;
use Duplicator\Core\CapMng;
use Duplicator\Core\Controllers\PageAction;
use Duplicator\Core\Views\TplMng;
use Duplicator\Libs\Snap\SnapUtil;
use Duplicator\Libs\Snap\SnapWP;
use Duplicator\Models\ActivityLog\LogEventLicenseActivation;
use Duplicator\Models\ActivityLog\LogEventLicenseDeactivation;
use Duplicator\Models\ActivityLog\LogEventLicenseKeyCleared;
use Duplicator\Models\ActivityLog\LogEventLicenseVisibilityChanged;
use Duplicator\Models\ActivityLog\LogEventLicenseStatusChanged;
use Duplicator\Models\DynamicGlobalEntity;
use Duplicator\Views\AdminNotices;
use Exception;
class LicensingController
{
//License actions
const ACTION_ACTIVATE_LICENSE = 'activate_license';
const ACTION_DEACTIVATE_LICENSE = 'deactivate_license';
const ACTION_CHANGE_VISIBILITY = 'change_visibility';
const ACTION_CLEAR_KEY = 'clear_key';
const ACTION_FORCE_REFRESH = 'force_refresh';
// Legacy license key option
const LEGACY_LICENSE_KEY_OPTION_AUTO_ACTIVE = 'duplicator_pro_license_auto_active';
// New license key option
const LICENSE_KEY_OPTION_AUTO_ACTIVE = 'dupli_opt_auth_token_auto_active';
/**
* License controller init
*
* @return void
*/
public static function init(): void
{
add_action('admin_init', [self::class, 'licenseAutoActive']);
add_action('admin_init', [self::class, 'forceUpgradeCheckAction']);
add_action('duplicator_settings_general_before', [self::class, 'renderLicenseContent'], 10);
add_action('duplicator_settings_general_before', [self::class, 'renderLicenseVisibility'], 100);
add_filter('duplicator_page_actions_' . ControllersManager::SETTINGS_SUBMENU_SLUG, [self::class, 'pageActions']);
add_filter('duplicator_template_file', [self::class, 'getTemplateFile'], 10, 2);
}
/**
* Method call on admin_init hook
*
* @return void
*/
public static function licenseAutoActive(): void
{
// Get license key option
$license_key_option = self::getLicenseKeyOption();
// Auto activation don't require capabilities check because is a background process
if (($lKey = get_option($license_key_option, false)) === false) {
return;
}
if (($action = SettingsPageController::getInstance()->getActionByKey(self::ACTION_ACTIVATE_LICENSE)) == false) {
return;
}
delete_option($license_key_option);
$redirect = $action->getUrl(['_license_key' => $lKey]);
DupLog::trace("CONTROLLER LICENSE AUTO ACTIVE: Redirecting to " . $action->getUrl());
if (wp_safe_redirect($redirect)) {
exit;
} else {
throw new Exception(__('Error redirecting to license activation page', 'duplicator-pro'));
}
}
/**
* Get license key option
*
* @return string
*/
public static function getLicenseKeyOption(): string
{
$license_key_option = self::LICENSE_KEY_OPTION_AUTO_ACTIVE;
if (get_option(self::LEGACY_LICENSE_KEY_OPTION_AUTO_ACTIVE, false) !== false) {
$license_key_option = self::LEGACY_LICENSE_KEY_OPTION_AUTO_ACTIVE;
}
return $license_key_option;
}
/**
* Return force upgrade check URL
*
* @return string
*/
public static function getForceUpgradeCheckURL(): string
{
return SnapWP::adminUrl('update-core.php', ['force-check' => 1]);
}
/**
* Force upgrade check action
*
* @return void
*/
public static function forceUpgradeCheckAction(): void
{
global $pagenow;
if ($pagenow !== 'update-core.php') {
return;
}
if (!SnapUtil::sanitizeBoolInput(SnapUtil::INPUT_REQUEST, 'force-check')) {
return;
}
License::forceUpgradeCheck();
}
/**
* Define actions related to the license
*
* @param PageAction[] $actions Page actions array from filter
*
* @return PageAction[] Updated page actions array
*/
public static function pageActions($actions)
{
$actions[] = new PageAction(
self::ACTION_ACTIVATE_LICENSE,
[
self::class,
'activateLicense',
],
[ControllersManager::SETTINGS_SUBMENU_SLUG]
);
$actions[] = new PageAction(
self::ACTION_DEACTIVATE_LICENSE,
[
self::class,
'deactivateLicense',
],
[ControllersManager::SETTINGS_SUBMENU_SLUG]
);
$actions[] = new PageAction(
self::ACTION_CLEAR_KEY,
[
self::class,
'clearLicenseKeyAction',
],
[ControllersManager::SETTINGS_SUBMENU_SLUG]
);
$actions[] = new PageAction(
self::ACTION_CHANGE_VISIBILITY,
[
self::class,
'changeLicenseVisibility',
],
[ControllersManager::SETTINGS_SUBMENU_SLUG]
);
$actions[] = new PageAction(
self::ACTION_FORCE_REFRESH,
[
self::class,
'forceRefresh',
],
[ControllersManager::SETTINGS_SUBMENU_SLUG]
);
return $actions;
}
/**
* Action that changes the license visibility
*
* @return array<string, mixed>
*/
public static function changeLicenseVisibility(): array
{
DupLog::trace("CONTROLLER CHANGE LICENSE VISIBILITY ACTION: Changing license visibility");
$result = [
'license_success' => false,
'license_message' => '',
];
$dGlobal = DynamicGlobalEntity::getInstance();
$oldVisibility = $dGlobal->getValInt('license_key_visible', LIcense::VISIBILITY_ALL);
$newVisibility = filter_input(INPUT_POST, 'license_key_visible', FILTER_VALIDATE_INT);
$newPassword = SnapUtil::sanitizeInput(INPUT_POST, '_key_password', '');
if ($oldVisibility === $newVisibility) {
return $result;
}
switch ($newVisibility) {
case License::VISIBILITY_ALL:
if ($dGlobal->getValString('license_key_visible_pwd') !== $newPassword) {
$result['license_message'] = __("Wrong password entered. Please enter the correct password.", 'duplicator-pro');
return $result;
}
$newPassword = ''; // reset password
break;
case License::VISIBILITY_NONE:
case License::VISIBILITY_INFO:
if ($oldVisibility == License::VISIBILITY_ALL) {
$password_confirmation = SnapUtil::sanitizeInput(INPUT_POST, '_key_password_confirmation', '');
if (strlen($newPassword) === 0) {
$result['license_message'] = __('Password cannot be empty.', 'duplicator-pro');
return $result;
}
if ($newPassword !== $password_confirmation) {
$result['license_message'] = __("Passwords don't match.", 'duplicator-pro');
return $result;
}
} else {
if ($dGlobal->getValString('license_key_visible_pwd') !== $newPassword) {
$result['license_message'] = __("Wrong password entered. Please enter the correct password.", 'duplicator-pro');
return $result;
}
}
break;
default:
throw new Exception(__('Invalid license visibility value.', 'duplicator-pro'));
}
$dGlobal->setValInt('license_key_visible', $newVisibility);
$dGlobal->setValString('license_key_visible_pwd', $newPassword);
if ($dGlobal->save()) {
LogEventLicenseVisibilityChanged::create($oldVisibility, $newVisibility);
return [
'license_success' => true,
'license_message' => __("License visibility changed", 'duplicator-pro'),
];
} else {
LogEventLicenseVisibilityChanged::create($oldVisibility, $newVisibility, LogEventLicenseVisibilityChanged::SUB_TYPE_ERROR);
return [
'license_success' => false,
'license_message' => __("Couldn't change license visibility.", 'duplicator-pro'),
];
}
}
/**
* Action that clears the license key
*
* @return array<string, mixed>
*/
public static function clearLicenseKeyAction()
{
DupLog::trace("CONTROLLER CLEAR LICENSE KEY ACTION: Clearing license key");
LicenseData::resetLastRequestFailure();
$result = self::clearLicenseKey();
LicenseData::resetLastRequestFailure();
return $result;
}
/**
* Action that clears the license key
*
* @param bool $logEvent Whether to log the event
*
* @return array<string, mixed>
*/
protected static function clearLicenseKey($logEvent = true): array
{
$dGlobal = DynamicGlobalEntity::getInstance();
LicenseData::getInstance()->setKey('');
License::clearVersionCache(true);
$dGlobal->setValInt('license_key_visible', License::VISIBILITY_ALL);
$dGlobal->setValString('license_key_visible_pwd', '');
$result = [];
if ($dGlobal->save()) {
$result['license_success'] = true;
$result['license_message'] = __("License key cleared", 'duplicator-pro');
} else {
$result['license_success'] = false;
$result['license_message'] = __("Couldn't save changes", 'duplicator-pro');
}
if ($logEvent) {
$eventSubType = $result['license_success'] ? LogEventLicenseKeyCleared::SUB_TYPE_SUCCESS : LogEventLicenseKeyCleared::SUB_TYPE_ERROR;
LogEventLicenseKeyCleared::create($eventSubType);
}
return $result;
}
/**
* Action that deactivates the license
*
* @return array<string, mixed>
*/
public static function deactivateLicense(): array
{
$result = [
'license_success' => true,
'license_message' => '',
];
$lData = LicenseData::getInstance();
try {
DupLog::trace("CONTROLLER DEACTIVATE LICENSE ACTION: Deactivating license");
if ($lData->getStatus() !== LicenseData::STATUS_VALID) {
return [
'license_success' => true,
'license_message' => __('License already deactivated.', 'duplicator-pro'),
];
}
$licenseKey = $lData->getKey();
switch ($lData->deactivate()) {
case LicenseData::ACTIVATION_RESPONSE_OK:
$result['license_message'] = sprintf(
_x(
'License %1$s %2$sDeactivated%3$s',
'%1$s is the license key, %2$s and %3$s are opening and closing HTML tags',
'duplicator-pro'
),
LicenseData::maskLicenseKey($licenseKey),
'<b class="alert-color">',
'</b>'
);
LogEventLicenseDeactivation::create(
LogEventLicenseDeactivation::SUB_TYPE_SUCCESS,
LicenseData::maskLicenseKey($licenseKey),
$lData->getStatus(),
);
break;
case LicenseData::ACTIVATION_RESPONSE_INVALID:
throw new Exception(__('Invalid license key.', 'duplicator-pro'));
case LicenseData::ACTIVATION_REQUEST_ERROR:
$result['license_request_error'] = $lData->getLastRequestError();
throw new Exception(self::getRequestErrorMessage());
default:
throw new Exception(__('Error deactivating license.', 'duplicator-pro'));
}
} catch (Exception $e) {
LogEventLicenseDeactivation::create(
LogEventLicenseDeactivation::SUB_TYPE_ERROR,
LicenseData::maskLicenseKey($lData->getKey()),
$lData->getStatus(),
$e->getMessage()
);
$result['license_success'] = false;
$result['license_message'] = $e->getMessage();
}
return $result;
}
/**
* Return template file path
*
* @param string $path path to the template file
* @param string $slugTpl slug of the template
*
* @return string
*/
public static function getTemplateFile($path, $slugTpl)
{
if (strpos($slugTpl, 'licensing/') === 0) {
return ProBase::getAddonPath() . '/template/' . $slugTpl . '.php';
}
return $path;
}
/**
* Action that activates the license
*
* @return array<string, mixed>
*/
public static function activateLicense(): array
{
$result = [
'license_success' => true,
'license_message' => '',
];
$lData = LicenseData::getInstance();
try {
if (($licenseKey = SnapUtil::sanitizeDefaultInput(SnapUtil::INPUT_REQUEST, '_license_key')) === false) {
throw new Exception(__('Please enter a valid key. Key should be 32 characters long.', 'duplicator-pro'));
}
if (!preg_match('/^[a-f0-9]{32}$/i', $licenseKey)) {
throw new Exception(__('Please enter a valid key. Key should be 32 characters long.', 'duplicator-pro'));
}
DupLog::trace("CONTROLLER ACTIVATE LICENSE ACTION: Setting license key to " . LicenseData::maskLicenseKey($licenseKey));
// make sure reset old license key if exists (without logging)
self::clearLicenseKey(false);
$lData->setKey($licenseKey);
switch ($lData->activate()) {
case LicenseData::ACTIVATION_RESPONSE_OK:
$result['license_message'] = sprintf(
_x(
'License %1$s %2$sActivated%3$s',
'%1$s is the license key, %2$s and %3$s are opening and closing HTML tags',
'duplicator-pro'
),
LicenseData::maskLicenseKey($licenseKey),
'<b class="green-color">',
'</b>'
);
// Important to manage frontend license post activation actions
$result['license_action_activation_success'] = true;
LogEventLicenseActivation::create(
LogEventLicenseActivation::SUB_TYPE_SUCCESS,
LicenseData::maskLicenseKey($licenseKey),
$lData->getExpirationDate(),
$lData->getExpirationDays(),
$lData->getSiteCount(),
$lData->getLicenseLimit(),
$lData->getStatus(),
);
break;
case LicenseData::ACTIVATION_RESPONSE_INVALID:
throw new Exception(__('Invalid license key.', 'duplicator-pro'));
case LicenseData::ACTIVATION_REQUEST_ERROR:
$result['license_request_error'] = $lData->getLastRequestError();
DupLog::traceObject('License request error', $result['license_request_error']);
throw new Exception(self::getRequestErrorMessage());
default:
throw new Exception(__('Error activating license.', 'duplicator-pro'));
}
} catch (Exception $e) {
$result['license_success'] = false;
$result['license_message'] = $e->getMessage();
LogEventLicenseActivation::create(
LogEventLicenseActivation::SUB_TYPE_ERROR,
LicenseData::maskLicenseKey($lData->getKey()),
$lData->getExpirationDate(),
$lData->getExpirationDays(),
$lData->getSiteCount(),
$lData->getLicenseLimit(),
$lData->getStatus(),
$e->getMessage()
);
}
return $result;
}
/**
* Force a refresh of the license data action
*
* @return array<string,mixed>
*/
public static function forceRefresh(): array
{
DupLog::trace("CONTROLLER FORCE REFRESH ACTION: Force refreshing license data");
$result = [
'license_success' => true,
'license_message' => __("License data reloaded.", 'duplicator-pro'),
];
$lData = LicenseData::getInstance();
try {
$oldStatus = $lData->getStatus();
$licenseKey = $lData->getKey();
$lData->resetLastRequestFailure();
$lData->getLicenseData(true);
$newStatus = $lData->getStatus();
// Log the force refresh action and any status changes
if ($oldStatus !== $newStatus) {
// Status changed during refresh - determine the appropriate subtype
$subType = LogEventLicenseStatusChanged::SUB_TYPE_INVALID; // default
switch ($newStatus) {
case LicenseData::STATUS_EXPIRED:
$subType = LogEventLicenseStatusChanged::SUB_TYPE_EXPIRED;
break;
case LicenseData::STATUS_DISABLED:
$subType = LogEventLicenseStatusChanged::SUB_TYPE_DISABLED;
break;
case LicenseData::STATUS_INACTIVE:
case LicenseData::STATUS_SITE_INACTIVE:
$subType = LogEventLicenseStatusChanged::SUB_TYPE_INACTIVE;
break;
case LicenseData::STATUS_INVALID:
$subType = LogEventLicenseStatusChanged::SUB_TYPE_INVALID;
break;
case LicenseData::STATUS_VALID:
$subType = LogEventLicenseStatusChanged::SUB_TYPE_RESTORED;
break;
}
LogEventLicenseStatusChanged::create(
$subType,
LicenseData::maskLicenseKey($licenseKey),
$lData->getStatus(),
$lData->getExpirationDate(),
$lData->getExpirationDays(),
$lData->getSiteCount(),
$lData->getLicenseLimit()
);
}
} catch (Exception $e) {
$result['license_success'] = false;
$result['license_message'] = $e->getMessage();
LogEventLicenseStatusChanged::create(
LogEventLicenseStatusChanged::SUB_TYPE_ERROR,
LicenseData::maskLicenseKey($lData->getKey()),
$lData->getStatus(),
$lData->getExpirationDate(),
$lData->getExpirationDays(),
$lData->getSiteCount(),
$lData->getLicenseLimit(),
$e->getMessage()
);
}
return $result;
}
/**
* Render page content
*
* @return void
*/
public static function renderLicenseContent(): void
{
if (!CapMng::getInstance()->can(CapMng::CAP_LICENSE, false)) {
return;
}
self::renderLicenseMessage();
TplMng::getInstance()->render('licensing/main');
}
/**
* Render page content
*
* @return void
*/
public static function renderLicenseVisibility(): void
{
if (!CapMng::getInstance()->can(CapMng::CAP_LICENSE, false)) {
return;
}
TplMng::getInstance()->render('licensing/visibility');
}
/**
* Return true avter license activation action
*
* @return bool
*/
public static function isActivationLicenseRender(): bool
{
return TplMng::getInstance()->getDataValueBool('license_action_activation_success', false);
}
/**
* Render activation/deactivation license message
*
* @return void
*/
protected static function renderLicenseMessage()
{
if (!CapMng::getInstance()->can(CapMng::CAP_LICENSE, false)) {
return;
}
$tplData = TplMng::getInstance()->getGlobalData();
if (empty($tplData['license_message'])) {
return;
}
$success = (isset($tplData['license_success']) && $tplData['license_success'] === true);
AdminNotices::displayGeneralAdminNotice(
TplMng::getInstance()->render('licensing/notices/activation_message', [], false),
($success ? AdminNotices::GEN_SUCCESS_NOTICE : AdminNotices::GEN_ERROR_NOTICE),
false,
[],
[],
true
);
}
/**
* Returns the communication error message
*
* @return string
*/
private static function getRequestErrorMessage(): string
{
$result = '<b>' . __('License data request failed.', 'duplicator-pro') . '</b>';
$result .= '<br>';
return $result . sprintf(
_x(
'Please see %1$sthis FAQ entry%2$s for possible causes and resolutions.',
'%1$s and %2$s represents the opening and closing HTML tags for an anchor or link',
'duplicator-pro'
),
'<a href="' . DUPLICATOR_DUPLICATOR_DOCS_URL . 'how-to-resolve-license-activation-issues/" target="_blank">',
'</a>'
);
}
}

View File

@@ -0,0 +1,821 @@
<?php
/**
*
* @package Duplicator
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Addons\ProBase\Models;
use DateTime;
use Duplicator\Utils\Logging\DupLog;
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Models\StaticGlobal;
use Duplicator\Core\Models\AbstractEntity;
use Duplicator\Core\Models\TraitEntitySerializationEncryption;
use Duplicator\Core\Models\TraitGenericModelSingleton;
use Duplicator\Installer\Addons\ProBase\AbstractLicense;
use Duplicator\Libs\Snap\SnapUtil;
use Duplicator\Utils\Crypt\CryptBlowfish;
use Duplicator\Utils\ExpireOptions;
use Error;
use Exception;
use Throwable;
use VendorDuplicator\Amk\JsonSerialize\JsonSerialize;
use WP_Error;
class LicenseData extends AbstractEntity
{
use TraitGenericModelSingleton;
use TraitEntitySerializationEncryption;
/**
* Encrypted properties for license data
*
* @var string[]
*/
protected static array $encryptedProperties = [
'licenseKey',
'status',
'type',
'data',
];
/**
* GENERAL SETTINGS
*/
const LICENSE_CACHE_TIME = 7 * DAY_IN_SECONDS;
const LICENSE_FAILURE_DELAY_TIME = 60 * MINUTE_IN_SECONDS;
const LICENSE_FAILURE_OPT_KEY = 'license_failure_request';
const FAILURE_SKIP_EXCEPTION_CODE = 100;
const LICENSE_OLD_KEY_OPTION_NAME = 'duplicator_pro_license_key';
/**
* LICENSE STATUS
*/
const STATUS_UNKNOWN = -1;
const STATUS_VALID = 0;
const STATUS_INVALID = 1;
const STATUS_INACTIVE = 2;
const STATUS_DISABLED = 3;
const STATUS_SITE_INACTIVE = 4;
const STATUS_EXPIRED = 5;
/**
* ACTIVATION REPONSE
*/
const ACTIVATION_RESPONSE_OK = 0;
const ACTIVATION_REQUEST_ERROR = -1;
const ACTIVATION_RESPONSE_INVALID = -2;
const DEFAULT_LICENSE_DATA = [
'success' => false,
'license' => 'invalid',
'item_id' => false,
'item_name' => '',
'checksum' => '',
'expires' => '',
'payment_id' => -1,
'customer_name' => '',
'customer_email' => '',
'license_limit' => -1,
'site_count' => -1,
'activations_left' => -1,
'price_id' => AbstractLicense::TYPE_UNLICENSED,
'activeSubscription' => false,
];
const VALID_RESPONSE_REQUIRED_KEYS = [
'success',
'license',
'item_id',
'item_name',
'checksum',
];
/** @var string */
protected $licenseKey = '';
/** @var int */
protected $status = self::STATUS_INVALID;
/** @var int */
protected $type = AbstractLicense::TYPE_UNKNOWN;
/** @var array<string,scalar> License remote data */
protected $data = self::DEFAULT_LICENSE_DATA;
/** @var string timestamp YYYY-MM-DD HH:MM:SS UTC */
protected $lastRemoteUpdate = '';
/** @var string timestamp YYYY-MM-DD HH:MM:SS UTC */
protected $lastFailureTime = '';
/**
* Last error request
*
* @var array{code:int, message: string, details: string, requestDetails: string}
*/
protected $lastRequestError = [
'code' => 0,
'message' => '',
'details' => '',
'requestDetails' => '',
];
/**
* Return entity type identifier
*
* @return string
*/
public static function getType(): string
{
return 'LicenseDataEntity';
}
/**
* Will be called, automatically, when Serialize
*
* @return array<string, mixed>
*/
public function __serialize(): array
{
$data = JsonSerialize::serializeToData($this, JsonSerialize::JSON_SKIP_MAGIC_METHODS | JsonSerialize::JSON_SKIP_CLASS_NAME);
// Encrypt properties using trait
$data = $this->encryptSerializedProperties($data);
unset($data['lastRequestError']);
return $data;
}
/**
* Unserialize the entity from serialized data
*
* @param array<string,mixed> $data Serialized data
*
* @return void
*/
public function __unserialize(array $data): void
{
try {
// Decrypt properties
$data = $this->decryptSerializedProperties($data);
// Verify that the data is an array
if (!isset($data['data']) || !is_array($data['data'])) {
throw new Exception('Unserialized license data is not an array');
}
// Assign properties
foreach ($data as $pName => $val) {
if (!property_exists($this, $pName)) {
continue;
}
$this->$pName = $val;
}
} catch (Throwable $e) {
DupLog::trace('ERROR UNSERIALIZE LICENSE DATA: ' . $e->getMessage());
// In case of any error during unserialization, reset to default values
if (isset($data['licenseKey']) && !self::isValidLicenseFormat($data['licenseKey'])) {
// Reset license only if is invalid format.
$this->licenseKey = '';
} elseif (isset($data['licenseKey'])) {
$this->licenseKey = $data['licenseKey'];
}
// Reset cache and try to reload corrupted dat but don't reset last failure to avoid too many requests
$this->clearCache(false, false);
}
}
/**
* Legacy decryption for old license data format
*
* @param array<string,mixed> $data Serialized data
*
* @return array<string,mixed> Data with legacy format decrypted
*/
protected function legacyDecryptProperties(array $data): array
{
// Old format detection: data is a string (encrypted JSON)
if (isset($data['data']) && is_string($data['data'])) {
// Data is encrypted in old format, decrypt all fields
$data['licenseKey'] = (string) CryptBlowfish::decryptIfAvaiable((string) ($data['licenseKey'] ?? ''), null, true);
$data['status'] = (int) CryptBlowfish::decryptIfAvaiable((string) ($data['status'] ?? 0), null, true);
$data['type'] = (int) CryptBlowfish::decryptIfAvaiable((string) ($data['type'] ?? 0), null, true);
// Decrypt and unserialize the data property
$decryptedData = CryptBlowfish::decryptIfAvaiable($data['data'], null, true);
if (strlen($decryptedData)) {
$data['data'] = JsonSerialize::unserialize($decryptedData);
} else {
// Decryption failed, set to empty array to avoid errors
$data['data'] = null;
}
}
return $data;
}
/**
* Set license key
*
* @param string $licenseKey License key, if empty the license key will be removed
*
* @return bool return true if license key is valid and set
*/
public function setKey($licenseKey)
{
if ($this->licenseKey === $licenseKey) {
return true;
}
DupLog::trace("UPDATE LICENSE KEY FROM " . self::maskLicenseKey($this->licenseKey) . " TO " . self::maskLicenseKey($licenseKey));
if ($this->getStatus() === self::STATUS_VALID) {
// Deactivate old license
$this->deactivate();
}
$this->licenseKey = self::isValidLicenseFormat($licenseKey) ? $licenseKey : '';
return $this->clearCache();
}
/**
* Chdeck is key is a valid licenze format
*
* @param string $key License Key
*
* @return bool
*/
protected static function isValidLicenseFormat(string $key)
{
return preg_match('/^[a-f0-9]{32}$/i', $key) === 1;
}
/**
* Get license key
*
* @return string
*/
public function getKey()
{
return $this->licenseKey;
}
/**
* Reset license data cache
*
* @param bool $save if true save the entity
* @param bool $resetFailure if true reset last remote failure
*
* @return bool return true if license data cache is reset
*/
public function clearCache($save = true, $resetFailure = true): bool
{
DupLog::trace("LICENSE DATA CLEAR CACHE");
$this->data = self::DEFAULT_LICENSE_DATA;
$this->status = self::STATUS_INVALID;
$this->type = AbstractLicense::TYPE_UNKNOWN;
$this->lastRemoteUpdate = '';
if ($resetFailure) {
$this->lastFailureTime = '';
}
return ($save ? $this->save() : true);
}
/**
* Get original home URL before filter it, prevent other plugins conflics (Like WPML).
*
* @return string
*/
private static function getOriginalHomeURL(): string
{
$originalUrl = '';
if (is_multisite()) {
$callback = function ($url, $path, $orig_scheme) use (&$originalUrl) {
$originalUrl = $url;
return $url;
};
add_filter('network_home_url', $callback, PHP_INT_MIN, 3);
network_home_url();
remove_filter('network_home_url', $callback, PHP_INT_MIN);
} else {
$callback = function ($url, $path, $orig_scheme, $blog_id) use (&$originalUrl) {
$originalUrl = $url;
return $url;
};
add_filter('home_url', $callback, PHP_INT_MIN, 4);
home_url();
remove_filter('home_url', $callback, PHP_INT_MIN);
}
return $originalUrl;
}
/**
* Get license data.
* This function manage the license data cache.
*
* @param bool $force if true reset data cache and force a new request
*
* @return array<string,scalar> License data
*/
public function getLicenseData($force = false)
{
$this->data = [
'success' => true,
'license' => 'valid',
'item_id' => 31,
'item_name' => '',
'checksum' => '',
'expires' => 'lifetime',
'payment_id' => -1,
'customer_name' => '',
'customer_email' => '',
'license_limit' => 100,
'site_count' => 1,
'activations_left' => 99,
'price_id' => AbstractLicense::TYPE_ELITE,
'activeSubscription' => false,
];
$this->status = self::STATUS_VALID;
$this->type = AbstractLicense::TYPE_ELITE;
$this->save();
return $this->data;
if ($this->licenseKey === '') {
return $this->data;
}
if ($force) {
DupLog::trace("Force license data update resetting license data cache");
$this->clearCache(false);
}
$currentTime = (int) strtotime(gmdate("Y-m-d H:i:s"));
$lastFailureTime = (int) ($this->lastFailureTime === '' ? 0 : strtotime($this->lastFailureTime));
if (($currentTime - $lastFailureTime) < self::LICENSE_FAILURE_DELAY_TIME) {
return $this->data;
}
$updatedTime = (int) ($this->lastRemoteUpdate === '' ? 0 : strtotime($this->lastRemoteUpdate));
if ($this->data['license'] == 'valid') {
if ($this->data['expires'] === 'lifetime') {
$expireTime = PHP_INT_MAX;
} elseif (empty($this->data['expires'])) {
$expireTime = 0;
} else {
$expireTime = (int) strtotime($this->data['expires']);
}
// Recheck expired if the license is expired with more than 1 day to avoid unnecessary requests
$recheckExpired = ($expireTime < ($currentTime - DAY_IN_SECONDS));
} else {
$recheckExpired = false;
}
if (
($currentTime - $updatedTime) > self::LICENSE_CACHE_TIME ||
$recheckExpired
) {
try {
$licenseEddStatus = $this->data['license'];
$api_params = [
'edd_action' => 'check_license',
'license' => $this->licenseKey,
'item_name' => urlencode(License::EDD_DUP_ITEM_NAME),
'url' => self::getOriginalHomeURL(),
];
$api_params = apply_filters('duplicator_license_request_params', $api_params);
$requestObj = $api_params;
$requestObj['license'] = self::maskLicenseKey($this->licenseKey);
if (($remoteData = $this->request($api_params, true)) === false) {
DupLog::trace("LICENSE DATA GET: request error for " . self::maskLicenseKey($this->licenseKey) . " so leaving status alone");
DupLog::traceObject("Request Data", $requestObj);
$this->lastFailureTime = gmdate("Y-m-d H:i:s");
} elseif (count(array_diff(self::VALID_RESPONSE_REQUIRED_KEYS, array_keys(get_object_vars($remoteData)))) > 0) {
// If the response is not valid, do not update the license data
DupLog::trace("LICENSE DATA GET: invalid response for " . self::maskLicenseKey($this->licenseKey) . " so leaving status alone");
DupLog::traceObject("Request Data", $requestObj);
DupLog::traceObject("Response Data", $remoteData);
$this->lastFailureTime = gmdate("Y-m-d H:i:s");
} else {
// Update license data only if the request is successful
if ($licenseEddStatus !== $remoteData->license) {
DupLog::trace(
"LICENSE DATA GET: License " .
self::maskLicenseKey($this->licenseKey) .
" status changed from $licenseEddStatus to $remoteData->license"
);
DupLog::traceObject("Request Data", $requestObj);
DupLog::traceObject("Response Data", $remoteData);
}
$this->clearCache(false);
foreach (self::DEFAULT_LICENSE_DATA as $key => $value) {
if (isset($remoteData->{$key})) {
$this->data[$key] = $remoteData->{$key};
}
}
$this->status = self::getStatusFromEDDStatus($remoteData->license);
$this->type = (int) (property_exists($remoteData, 'price_id') ? $remoteData->price_id : AbstractLicense::TYPE_UNLICENSED);
$this->lastRemoteUpdate = gmdate("Y-m-d H:i:s");
DupLog::trace("NEW LICENSE DATA UPDATED STATUS: " . self::getStatusLabel($this->status));
do_action('duplicator_license_check_remote_data_success', $this, $remoteData);
}
} finally {
$this->save();
}
}
return $this->data;
}
/**
* Activate license key
*
* @return int license status
*/
public function activate(): int
{
if (strlen($this->licenseKey) == 0) {
return self::ACTIVATION_REQUEST_ERROR;
}
$api_params = [
'edd_action' => 'activate_license',
'license' => $this->licenseKey,
'item_name' => urlencode(License::EDD_DUP_ITEM_NAME), // the name of our product in EDD,
'url' => self::getOriginalHomeURL(),
];
$this->clearCache();
if (($responseData = $this->request($api_params, true)) === false) {
return self::ACTIVATION_REQUEST_ERROR;
}
if ($responseData->license !== 'valid') {
DupLog::traceObject("Problem activating license " . self::maskLicenseKey($this->licenseKey), $responseData);
return self::ACTIVATION_RESPONSE_INVALID;
}
$this->getLicenseData(true);
DupLog::trace("License Activated " . self::maskLicenseKey($this->licenseKey));
return self::ACTIVATION_RESPONSE_OK;
}
/**
* Get license status
*
* @return int ENUM self::STATUS_*
*/
public function getStatus()
{
$this->getLicenseData();
return $this->status;
}
/**
* Get license type
*
* @return int ENUM AbstractLicense::TYPE_*
*/
public function getLicenseType()
{
$this->getLicenseData();
return $this->type;
}
/**
* Get license websites limit
*
* @return int<0, max>
*/
public function getLicenseLimit(): int
{
$this->getLicenseData();
return (int) max(0, (int) $this->data['license_limit']);
}
/**
* Get site count
*
* @return int<-1, max>
*/
public function getSiteCount(): int
{
$this->getLicenseData();
return (int) max(-1, (int) $this->data['site_count']);
}
/**
* Deactivate license key
*
* @param string $url URL to deactivate for. If empty, uses current site URL and refreshes local license data.
*
* @return int license status
*/
public function deactivate(string $url = ''): int
{
if (strlen($this->licenseKey) == 0) {
return self::ACTIVATION_RESPONSE_OK;
}
$isRemoteDeactivation = !empty($url);
if (empty($url)) {
$url = self::getOriginalHomeURL();
}
DupLog::trace("DEACTIVATE LICENSE: " . self::maskLicenseKey($this->licenseKey) . " for URL: " . $url);
$api_params = [
'edd_action' => 'deactivate_license',
'license' => $this->licenseKey,
'item_name' => urlencode(License::EDD_DUP_ITEM_NAME), // the name of our product in EDD,
'url' => $url,
];
if (!$isRemoteDeactivation) {
$this->clearCache();
}
if (($responseData = $this->request($api_params, true)) === false) {
return self::ACTIVATION_REQUEST_ERROR;
}
if ($responseData->license !== 'deactivated') {
DupLog::traceObject("Problems deactivating license " . $this->licenseKey, $responseData);
return self::ACTIVATION_RESPONSE_INVALID;
}
if (!$isRemoteDeactivation) {
$this->getLicenseData(true);
}
DupLog::trace("Deactivated license " . $this->licenseKey);
return self::ACTIVATION_RESPONSE_OK;
}
/**
* Get expiration date format
*
* @param string $format date format
*
* @return string return expirtation date formatted, Unknown if license data is not available or Lifetime if license is lifetime
*/
public function getExpirationDate($format = 'Y-m-d')
{
$this->getLicenseData();
if ($this->data['expires'] === 'lifetime') {
return 'Lifetime';
}
if (empty($this->data['expires'])) {
return 'Unknown';
}
$expirationDate = new DateTime($this->data['expires']);
return date_i18n($format, $expirationDate->getTimestamp());
}
/**
* Return expiration license days, if is expired a negative number is returned
*
* @return false|int reutrn false on fail or number of days to expire, PHP_INT_MAX is filetime
*/
public function getExpirationDays()
{
$this->getLicenseData();
if ($this->data['expires'] === 'lifetime') {
return PHP_INT_MAX;
}
if (empty($this->data['expires'])) {
return false;
}
$expirationDate = new DateTime($this->data['expires']);
return (-1 * intval($expirationDate->diff(new DateTime())->format('%r%a')));
}
/**
* check is have no activations left
*
* @return bool
*/
public function haveNoActivationsLeft(): bool
{
return ($this->getStatus() === self::STATUS_SITE_INACTIVE && $this->data['activations_left'] === 0);
}
/**
* Return true if have active subscription
*
* @return bool
*/
public function haveActiveSubscription()
{
$this->getLicenseData();
return $this->data['activeSubscription'];
}
/**
* Reset last request failure delay time.
* Important: Do not use this function indiscriminately if you are not sure what you are doing.
* Using this function incorrectly overrides the logic that prevents too many requests to the server.
*
* @return void
*/
public static function resetLastRequestFailure(): void
{
// Reset request failure
ExpireOptions::delete(self::LICENSE_FAILURE_OPT_KEY);
}
/**
* Get a license rquest
*
* @param mixed[] $params request params
* @param bool $nocache if true add a dynamic parameter to avoid remote cache
*
* @return false|object
*/
public function request($params, $nocache = false)
{
DupLog::trace('LICENSE REMOTE REQUEST FUNCTION CMD: ' . $params['edd_action']);
if (!is_array($params)) {
$params = [];
}
if ($nocache) {
$params['cachetime'] = microtime(true); // To avoid remote cache
}
$requestParams = [
'timeout' => 60,
'sslverify' => false,
'user-agent' => 'WordPress/' . get_bloginfo('version') . '; ' . get_bloginfo('url'),
'body' => $params,
];
$requestDetails = JsonSerialize::serialize([
'url' => DUPLICATOR_STORE_URL,
'curlEnabled' => SnapUtil::isCurlEnabled(),
'params' => $requestParams,
], JSON_PRETTY_PRINT);
$this->lastRequestError['code'] = 0;
$this->lastRequestError['message'] = '';
$this->lastRequestError['details'] = '';
$this->lastRequestError['requestDetails'] = '';
try {
if (ExpireOptions::get(self::LICENSE_FAILURE_OPT_KEY, false)) {
$endTime = human_time_diff(time(), ExpireOptions::getExpireTime(self::LICENSE_FAILURE_OPT_KEY));
// Wait before try again
$this->lastRequestError['code'] = 1;
$this->lastRequestError['message'] = sprintf(__('License request failed recently. Wait %s and try again.', 'duplicator-pro'), $endTime);
$this->lastRequestError['details'] = '';
$this->lastRequestError['requestDetails'] = '';
throw new Exception($this->lastRequestError['message'], self::FAILURE_SKIP_EXCEPTION_CODE);
} else {
DupLog::trace('LICENSE REMOTE GET CMD: ' . $params['edd_action']);
$response = wp_remote_get(DUPLICATOR_STORE_URL, $requestParams);
}
if (is_wp_error($response)) {
/** @var WP_Error $response */
$this->lastRequestError['code'] = $response->get_error_code();
$this->lastRequestError['message'] = $response->get_error_message();
$this->lastRequestError['details'] = JsonSerialize::serialize($response->get_error_data(), JSON_PRETTY_PRINT);
$this->lastRequestError['requestDetails'] = $requestDetails;
throw new Exception($this->lastRequestError['message']);
} elseif ($response['response']['code'] < 200 || $response['response']['code'] >= 300) {
$this->lastRequestError['code'] = $response['response']['code'];
$this->lastRequestError['message'] = $response['response']['message'];
$this->lastRequestError['details'] = JsonSerialize::serialize($response, JSON_PRETTY_PRINT);
$this->lastRequestError['requestDetails'] = $requestDetails;
throw new Exception($this->lastRequestError['message']);
}
$data = json_decode(wp_remote_retrieve_body($response));
if (!is_object($data) || !property_exists($data, 'license')) {
$this->lastRequestError['code'] = -1;
$this->lastRequestError['message'] = __('Invalid license data.', 'duplicator-pro');
$this->lastRequestError['details'] = 'Response: ' . wp_remote_retrieve_body($response);
$this->lastRequestError['requestDetails'] = $requestDetails;
throw new Exception($this->lastRequestError['message']);
}
} catch (Exception $e) {
if ($e->getCode() !== self::FAILURE_SKIP_EXCEPTION_CODE) {
ExpireOptions::set(self::LICENSE_FAILURE_OPT_KEY, true, self::LICENSE_FAILURE_DELAY_TIME);
}
DupLog::trace('LICENSE REQUEST FAILED: ' . $e->getMessage());
DupLog::traceObject('** REQUEST DETAILS', $this->lastRequestError);
return false;
} catch (Error $e) {
ExpireOptions::set(self::LICENSE_FAILURE_OPT_KEY, true, self::LICENSE_FAILURE_DELAY_TIME);
DupLog::trace('LICENSE REQUEST FAILED: ' . $e->getMessage());
DupLog::traceObject('** REQUEST DETAILS', $this->lastRequestError);
return false;
}
return $data;
}
/**
* Get last error request
*
* @return array{code:int, message: string, details: string}
*/
public function getLastRequestError()
{
return $this->lastRequestError;
}
/**
* Get license status from status by string
*
* @param string $eddStatus license status string
*
* @return int
*/
private static function getStatusFromEDDStatus($eddStatus): int
{
switch ($eddStatus) {
case 'valid':
return self::STATUS_VALID;
case 'invalid':
return self::STATUS_INVALID;
case 'expired':
return self::STATUS_EXPIRED;
case 'disabled':
return self::STATUS_DISABLED;
case 'site_inactive':
return self::STATUS_SITE_INACTIVE;
case 'inactive':
return self::STATUS_INACTIVE;
default:
return self::STATUS_UNKNOWN;
}
}
/**
* Return license statu string by status
*
* @return string
*/
public function getLicenseStatusString()
{
switch ($this->getStatus()) {
case self::STATUS_VALID:
return __('Valid', 'duplicator-pro');
case self::STATUS_INVALID:
return __('Invalid', 'duplicator-pro');
case self::STATUS_EXPIRED:
return __('Expired', 'duplicator-pro');
case self::STATUS_DISABLED:
return __('Disabled', 'duplicator-pro');
case self::STATUS_SITE_INACTIVE:
return __('Site Inactive', 'duplicator-pro');
case self::STATUS_EXPIRED:
return __('Expired', 'duplicator-pro');
default:
return __('Unknown', 'duplicator-pro');
}
}
/**
* Get status label
*
* @param int $status status ENUM self::STATUS_*
*
* @return string
*/
public static function getStatusLabel(int $status): string
{
switch ($status) {
case self::STATUS_VALID:
return __('Valid', 'duplicator-pro');
case self::STATUS_INVALID:
return __('Invalid', 'duplicator-pro');
case self::STATUS_EXPIRED:
return __('Expired', 'duplicator-pro');
case self::STATUS_DISABLED:
return __('Disabled', 'duplicator-pro');
case self::STATUS_SITE_INACTIVE:
return __('Site Inactive', 'duplicator-pro');
case self::STATUS_INACTIVE:
return __('Inactive', 'duplicator-pro');
default:
return __('Unknown', 'duplicator-pro');
}
}
/**
* Mask license key for security (show first 5 chars + ***)
*
* @param string $licenseKey License key to mask
*
* @return string Masked license key (e.g., ABCDE***)
*/
public static function maskLicenseKey(string $licenseKey): string
{
if (empty($licenseKey)) {
return 'No License Key';
}
return substr($licenseKey, 0, 5) . '***';
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Models\DynamicGlobalEntity;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
$visibility = DynamicGlobalEntity::getInstance()->getValInt('license_key_visible', License::VISIBILITY_ALL);
?>
<h3 class="title">
<?php esc_html_e('License', 'duplicator-pro') ?>
</h3>
<hr size="1">
<p>
<?php esc_html_e('Your license key provides access to updates and addons.', 'duplicator-pro') ?>
</p>
<label class="lbl-larger">
<?php esc_html_e("License Key", 'duplicator-pro'); ?>
</label>
<div class="margin-bottom-1">
<?php $tplMng->render('licensing/license_key_actions'); ?>
<?php if ($visibility !== License::VISIBILITY_NONE) { ?>
<div id="dup-tr-license-type" >
<?php $tplMng->render('licensing/license_info'); ?>
<?php $tplMng->render('licensing/activation_issue_message'); ?>
</div>
<?php } ?>
</div>

View File

@@ -0,0 +1,59 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Addons\ProBase\License\LicenseNotices;
use Duplicator\Addons\ProBase\Models\LicenseData;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
switch (LicenseData::getInstance()->getStatus()) {
case LicenseData::STATUS_VALID:
case LicenseData::STATUS_INACTIVE:
case LicenseData::STATUS_SITE_INACTIVE:
case LicenseData::STATUS_EXPIRED:
return;
default:
break;
}
esc_html_e('If license activation fails please wait ~ one hour and retry.', 'duplicator-pro');
?>
<div class="dup-license-status-notes ">
<?php
printf(
esc_html_x(
'- Failure to activate after several attempts please review %1$sfaq activation steps%2$s.',
'1 and 2 represent opening and closing anchor tags (<a> and </a>)',
'duplicator-pro'
),
'<a target="_blank" href="' . esc_url(DUPLICATOR_DUPLICATOR_DOCS_URL . 'how-to-resolve-license-activation-issues/') . '">',
'</a>'
);
?>
<br/>
<?php
printf(
esc_html_x(
'- To upgrade or renew your license visit %1$sduplicator.com%2$s.',
'1 and 2 represent opening and closing anchor tags (<a> and </a>)',
'duplicator-pro'
),
'<a target="_blank" href="' . esc_url(DUPLICATOR_BLOG_URL) . '">',
'</a>'
);
?>
<br/>
<?php esc_html_e('- A valid key is needed for plugin updates but not for functionality.', 'duplicator-pro'); ?>
</div>

View File

@@ -0,0 +1,198 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Addons\ProBase\LicensingController;
use Duplicator\Addons\ProBase\Models\LicenseData;
use Duplicator\Core\Controllers\PageAction;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
* @var Duplicator\Core\Controllers\PageAction $refreshAction
*/
$refreshAction = $tplData['actions'][LicensingController::ACTION_FORCE_REFRESH];
?>
<p>
<i>
<?php
printf(
esc_html_x(
'If your license has been updated or is incorrect, then please %1$sForce a Refresh%2$s.',
'1: <a> tag, 2: </a> tag',
'duplicator-pro'
),
'<a href="' . esc_url($refreshAction->getUrl()) . '">',
'</a>'
);
?>
</i>
</p>
<div id="dup-license-info">
<?php
switch (LicenseData::getInstance()->getStatus()) {
case LicenseData::STATUS_VALID:
$statusClass = "";
$statusText = '<b>' . License::getLicenseToString() . '</b> ' . __('License expires on', 'duplicator-pro') . ' ';
$statusText .= '<b>' . LicenseData::getInstance()->getExpirationDate(get_option('date_format')) . '</b> ';
$expDays = LicenseData::getInstance()->getExpirationDays();
$statusText .= ' (';
if ($expDays === false) {
$statusText .= __('no data', 'duplicator-pro');
} elseif ($expDays <= 0) {
$statusText .= __('expired', 'duplicator-pro');
} elseif ($expDays == PHP_INT_MAX) {
$statusText .= __('no expiration', 'duplicator-pro');
} else {
$statusText .= sprintf(__('%d days left', 'duplicator-pro'), $expDays);
}
$statusText .= ')';
break;
case LicenseData::STATUS_INACTIVE:
$statusClass = "alert-color";
$statusText = __('License Inactive', 'duplicator-pro');
break;
case LicenseData::STATUS_SITE_INACTIVE:
$statusClass = "alert-color";
if (LicenseData::getInstance()->haveNoActivationsLeft()) {
$statusText = __('License Inactive (out of site licenses).', 'duplicator-pro') . '<br>' . License::getNoActivationLeftMessage();
} else {
$statusText = __('License Inactive', 'duplicator-pro');
}
break;
case LicenseData::STATUS_EXPIRED:
$statusClass = "alert-color";
$statusText = sprintf(
_x(
'Your Duplicator Pro license key has expired so you aren\'t getting important updates! %1$sRenew your license now%2$s',
'1: <a> tag, 2: </a> tag',
'duplicator-pro'
),
'<a target="_blank" href="' . esc_url(License::getLicenseCheckoutURL()) . '">',
'</a>'
);
break;
case LicenseData::STATUS_INVALID:
case LicenseData::STATUS_UNKNOWN:
default:
// https://duplicator.com/knowledge-base/how-to-resolve-license-activation-issues/
$statusClass = "alert-color";
$statusText = sprintf(
_x(
'License %1$s',
'1: License status (Invalid, Unknown)',
'duplicator-pro'
),
'</b>' . LicenseData::getStatusLabel(LicenseData::getInstance()->getStatus()) . '<br/>'
);
break;
}
?>
<div class="<?php echo esc_attr($statusClass); ?>">
<?php
echo wp_kses(
$statusText,
[
'a' => [
'href' => [],
'target' => [],
],
'b' => [],
'br' => [],
]
);
?>
</div>
<ul class="dup-license-type-info no-bullet">
<li>
<?php
$checkedClass = (LicenseData::getInstance()->getStatus() == LicenseData::STATUS_VALID ? 'far fa-check-circle' : 'far fa-circle');
$sitesLimit = (License::isUnlimited() ? __('Unlimited', 'duplicator-pro') : LicenseData::getInstance()->getLicenseLimit());
$sitesCount = (LicenseData::getInstance()->getSiteCount() < 0 ? '?' : LicenseData::getInstance()->getSiteCount());
?>
<i class="<?php echo esc_attr($checkedClass); ?>"></i>
<?php
printf(
esc_html_x(
'Site Licenses: %1$s of %2$s',
'1 = (string) Site Licenses, 2 = (number) Active sites, 3 = (number) License limit',
'duplicator-pro'
),
esc_html($sitesCount),
esc_html($sitesLimit)
);
$tipContent = __(
'Indicates the number of sites the plugin can be active on at any one time.
At any point you may deactivate/uninstall the plugin to free up the license and use the plugin elsewhere if needed.',
'duplicator-pro'
);
?>
<i
class='fa-solid fa-question-circle fa-sm dark-gray-color'
data-tooltip-title='<?php esc_attr_e('Site Licenses', 'duplicator-pro') ?>'
data-tooltip='<?php echo esc_attr($tipContent) ?>'>
</i>
</li>
<li>
<?php
$checkedClass = License::can(License::CAPABILITY_POWER_TOOLS) ? 'far fa-check-circle' : 'far fa-circle';
$tipContent = __(
'Enhanced features that greatly improve the productivity of serious users. Include hourly schedules,
installer branding, salt & key replacement, priority support and more.',
'duplicator-pro'
);
?>
<i class="<?php echo esc_attr($checkedClass); ?>"></i>
<?php esc_html_e('Powertools', 'duplicator-pro') ?>
<i
class='fa-solid fa-question-circle fa-sm dark-gray-color'
data-tooltip-title='<?php echo esc_attr_e('Powertools', 'duplicator-pro') ?>'
data-tooltip='<?php echo esc_attr($tipContent) ?>'>
</i>
</li>
<li>
<?php
$checkedClass = License::can(License::CAPABILITY_MULTISITE_PLUS) ? 'far fa-check-circle' : 'far fa-circle';
$tipContent = __(
'Adds the ability to install a subsite as a standalone site,
insert a standalone site into a multisite, or insert a subsite from the same/different multisite into a multisite.',
'duplicator-pro'
);
?>
<i class="<?php echo esc_attr($checkedClass) ?>"></i>
<?php esc_html_e('Multisite Plus+', 'duplicator-pro') ?>
<i
class='fa-solid fa-question-circle fa-sm dark-gray-color'
data-tooltip-title='<?php echo esc_attr_e('Multisite Plus+', 'duplicator-pro') ?>'
data-tooltip='<?php echo esc_attr($tipContent) ?>'>
</i>
</li>
<?php if (License::canBeUpgraded()) { ?>
<li>
<div class="margin-top-1">
<?php
printf(
esc_html_x(
'🔥 To unlock more features see our %1$sUpgrade Offers%2$s.',
'1: <a> tag, 2: </a> tag',
'duplicator-pro'
),
'<a class="dup-upgrade-license-link primary-color" href="' . esc_url(License::getUpsellURL()) . '" target="_blank">',
'</a>'
);
?>
</div>
</li>
<?php } ?>
</ul>
</div>

View File

@@ -0,0 +1,66 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Addons\ProBase\Models\LicenseData;
use Duplicator\Models\DynamicGlobalEntity;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
$dGlobal = DynamicGlobalEntity::getInstance();
$licenseVisibility = $dGlobal->getValInt('license_key_visible', License::VISIBILITY_ALL);
if ($licenseVisibility === License::VISIBILITY_ALL) {
?>
<div id="dup-tr-license-key-and-description" class="inline-display">
<input
type="text"
class="dup-license-key-input width-large inline-display margin-0"
name="_license_key"
id="_license_key"
value="<?php echo esc_attr(License::getLicenseKey()); ?>">
<span class="dup-license-check-icon">
<?php if (LicenseData::getInstance()->getStatus() === LicenseData::STATUS_VALID) { ?>
<i class="fa-solid fa-circle-check fa-lg success-color"></i>
<?php } else { ?>
<i class="fa-solid fa-circle-xmark fa-lg alert-color"></i>
<?php } ?>
</span>
&nbsp;
</div>
<?php } ?>
<div class="dup-license-key-btns inline-display">
<?php
if (LicenseData::getInstance()->getStatus() === LicenseData::STATUS_VALID) {
$echostring = 'false';
$buttonText = __('Deactivate', 'duplicator-pro');
} else {
$echostring = 'true';
$buttonText = __('Activate', 'duplicator-pro');
}
?>
<button
id="dup-license-activation-btn"
class="button secondary hollow small margin-0"
onclick="DupliJs.Licensing.ChangeActivationStatus(<?php echo esc_js($echostring); ?>);return false;">
<?php echo esc_html($buttonText); ?>
</button>
&nbsp;
<button
id="dup-license-clear-btn"
class="button secondary hollow small margin-0"
onclick="DupliJs.Licensing.ClearActivationStatus();return false;">
<?php esc_html_e('Clear Key', 'duplicator-pro') ?>
</button>
</div>

View File

@@ -0,0 +1,80 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Addons\ProBase\LicensingController;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
?>
<div class="dup-settings-wrapper" >
<?php $tplMng->render('licensing/activation'); ?>
</div>
<script>
jQuery(document).ready(function($) {
DupliJs.Licensing = new Object();
DupliJs.Licensing.VISIBILITY_ALL = <?php echo (int) License::VISIBILITY_ALL;?>;
DupliJs.Licensing.VISIBILITY_INFO = <?php echo (int) License::VISIBILITY_INFO;?>;
DupliJs.Licensing.VISIBILITY_NONE = <?php echo (int) License::VISIBILITY_NONE;?>;
$("#_key_password, #_key_password_confirmation").keyup(function(event) {
if (event.keyCode == 13) {
$("#show_hide").click();
}
});
DupliJs.Licensing.ChangeActivationStatus = function(activate) {
if (activate) {
let licenseKey = $('.dup-license-key-input').val();
window.location.href =
<?php echo json_encode($tplData['actions'][LicensingController::ACTION_ACTIVATE_LICENSE]->getUrl()); ?> +
'&_license_key=' + encodeURIComponent(licenseKey);
} else {
window.location.href = <?php echo json_encode($tplData['actions'][LicensingController::ACTION_DEACTIVATE_LICENSE]->getUrl()); ?>;
}
return false;
}
DupliJs.Licensing.ClearActivationStatus = function() {
window.location.href = <?php echo json_encode($tplData['actions'][LicensingController::ACTION_CLEAR_KEY]->getUrl()); ?>;
}
DupliJs.Licensing.ChangeKeyVisibility = function(show) {
$('#dup-license-visibility-form').submit();
}
DupliJs.Licensing.VisibilityTemporary = function(visibility) {
switch (visibility) {
case DupliJs.Licensing.VISIBILITY_ALL:
$("#dup-tr-license-dashboard").show();
$("#dup-tr-license-type").show();
$("#dup-tr-license-key-and-description").show();
break;
case DupliJs.Licensing.VISIBILITY_INFO:
$("#dup-tr-license-dashboard").show();
$("#dup-tr-license-type").show();
$("#dup-tr-license-key-and-description").hide();
break;
case DupliJs.Licensing.VISIBILITY_NONE:
$("#dup-tr-license-dashboard").hide();
$("#dup-tr-license-type").hide();
$("#dup-tr-license-key-and-description").hide();
break;
default:
alert("Unexpected visibility value!");
}
}
});
</script>

View File

@@ -0,0 +1,90 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Libs\Snap\SnapUtil;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
if (empty($tplData['license_message'])) {
return;
}
$details = "";
if (isset($tplData['license_request_error'])) {
$details = 'Message: ' . $tplData['license_request_error']['message'] . "\n" .
'Error code: ' . $tplData['license_request_error']['code'];
if (strlen($tplData['license_request_error']['requestDetails'])) {
$details .= "\n\n" . 'Request Details' . "\n" . $tplData['license_request_error']['requestDetails'];
}
if (strlen($tplData['license_request_error']['details'])) {
$details .= "\n" . 'Response Details' . "\n" . $tplData['license_request_error']['details'];
}
}
?>
<p>
<?php if (!$tplData['license_success']) { ?>
<i class="fa fa-exclamation-triangle"></i>&nbsp;
<?php
}
echo wp_kses(
$tplData['license_message'],
[
'a' => [
'href' => [],
'target' => [],
],
'span' => [
'class' => [],
],
'br' => [],
'b' => [
'class' => [],
],
]
);
?>
</p>
<?php if (isset($tplData['license_request_error'])) { ?>
<p>
<?php echo esc_html__('Error:', 'duplicator-pro') ?> <b><?php echo esc_html($tplData['license_request_error']['message']); ?></b>
</p>
<b>Details:</b>
<textarea class="dup-error-message-textarea" disabled ><?php echo esc_textarea($details); ?></textarea>
<button
data-dup-copy-value="<?php echo esc_attr($details); ?>"
data-dup-copy-title="<?php echo esc_attr("Copy Error Message to clipboard"); ?>"
data-dup-copied-title="<?php echo esc_attr("Error Message copied to clipboard"); ?>"
class="button dup-btn-copy-error-message">
<?php esc_html_e('Copy error details', 'duplicator-pro'); ?>
</button>
<?php if (!SnapUtil::isCurlEnabled()) {
$tplMng->render('licensing/notices/curl_message');
} ?>
<p>
<?php
printf(
wp_kses(
__("If the error persists please open a ticket <a href=\"%s\">here</a> and attach the errors details.", 'duplicator-pro'),
[
'a' => [
'href' => [],
],
]
),
esc_url(DUPLICATOR_BLOG_URL . 'my-account/support/')
);
?>
</p>
<?php } ?>

View File

@@ -0,0 +1,47 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Views\ViewHelper;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
?>
<p>
<?php
echo wp_kses(
__(
'<b>CURL isn\'t enabled.</b> This module is far more reliable for remote communication.',
'duplicator-pro'
),
ViewHelper::GEN_KSES_TAGS
);
?>
</br>
<?php esc_html_e('A possible solution to the problem could be to activate it.', 'duplicator-pro'); ?>
</br>
<?php
printf(
wp_kses(
_x(
'For detailed steps on how to enable cURL please see <b>Solution 3, Issue A</b> in %1$sthis FAQ Entry%2$s.',
'%1$s and %2$s represents the opening and closing HTML tags for an anchor or link',
'duplicator-pro'
),
ViewHelper::GEN_KSES_TAGS
),
'<a href="' . esc_url(DUPLICATOR_DUPLICATOR_DOCS_URL . 'how-to-resolve-license-activation-issues/') . '" target="_blank">',
'</a>'
);
?>
<br/>
</p>

View File

@@ -0,0 +1,54 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Core\CapMng;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
$upgradeUrl = License::getUpsellURL();
?>
<span class='dashicons dashicons-warning'></span>
<div class="dup-sub-content">
<h3>
<?php esc_html_e('Your license does not support multisite functionality', 'duplicator-pro'); ?>
</h3>
<p>
<?php echo esc_html(
sprintf(
_x(
'By upgrading to the %1$s or %2$s plans you will unlock the ability to create
backups and do advanced migrations on multi-site installations!',
'1: name of pro plan, 2: name of elite plan',
'duplicator-pro'
),
License::getLicenseToString(License::TYPE_PRO),
License::getLicenseToString(License::TYPE_ELITE)
)
); ?>
</p>
<br>
<?php if (CapMng::can(CapMng::CAP_LICENSE, false)) { ?>
<a class="button primary small margin-bottom-0" target="_blank" href="<?php echo esc_url($upgradeUrl); ?>">
<?php esc_html_e('Upgrade Now!', 'duplicator-pro'); ?>
</a>
<?php } else { ?>
<?php
echo '<b>' . esc_html__(
'Please contact the Duplicator license manager to update it.',
'duplicator-pro'
) . '</b>';
?>
<?php } ?>
</div>

View File

@@ -0,0 +1,60 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Addons\ProBase\License\License;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
$daysLeft = $tplData['schedule_disalbe_days_left'];
if ($daysLeft === false) {
return;
}
if ($daysLeft >= 0) {
?>
<u>
<?php
if (License::can(License::CAPABILITY_SCHEDULE)) {
$message = sprintf(
_n(
'Scheduled Backups are going to be disabled <b><em>in %d day</em></b>.',
'Scheduled Backups are going to be disabled <b><em>in %d days</em></b>.',
$daysLeft,
'duplicator-pro'
),
$daysLeft
);
$message .= __(' Please renew your license to assure your backups are not interrupted.', 'duplicator-pro');
echo wp_kses(
$message,
[
'b' => [],
'em' => [],
]
);
} else {
esc_html_e(
'All automatic backups have been disabeld. Please renew your license to re-enable them.',
'duplicator-pro'
);
}
?>
</u>
<?php
} else {
esc_html_e(
'Scheduled Backups.',
'duplicator-pro'
);
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Core\CapMng;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
$renewal_url = $tplData['renewal_url'];
?>
<span class='dashicons dashicons-warning'></span>
<div class="dup-sub-content">
<h3>
<?php esc_html_e('Warning! Your Duplicator Pro license has expired...', 'duplicator-pro');?>
</h3>
<?php esc_html_e('You\'re currently missing:', 'duplicator-pro'); ?>
<ul class="dupli-simple-style-disc" >
<li><?php esc_html_e('Access to Advanced Features', 'duplicator-pro'); ?></li>
<li><?php $tplMng->render('licensing/notices/drm_schedules_msg'); ?></li>
<li><?php esc_html_e('Storages Management', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Templates Management', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('New Features', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Important Updates for Security Patches', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Bug Fixes', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Support Requests', 'duplicator-pro'); ?></li>
</ul>
<?php if (CapMng::can(CapMng::CAP_LICENSE, false)) { ?>
<a class="button primary small" target="_blank" href="<?php echo esc_url($renewal_url); ?>">
<?php esc_html_e('Renew Now!', 'duplicator-pro'); ?>
</a>
<?php } else { ?>
<?php
echo '<b>' . esc_html__(
'Please contact the Duplicator license manager to activate it.',
'duplicator-pro'
) . '</b>';
?>
<?php } ?>
</div>

View File

@@ -0,0 +1,74 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Controllers\SettingsPageController;
use Duplicator\Core\CapMng;
use Duplicator\Core\Controllers\ControllersManager;
use Duplicator\Views\ViewHelper;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
$img_url = plugins_url('duplicator-pro/assets/img/warning.png');
$problem_text = $tplData['problem'];
$licensing_tab_url = ControllersManager::getMenuLink(
ControllersManager::SETTINGS_SUBMENU_SLUG,
SettingsPageController::L2_SLUG_GENERAL
);
?>
<span class='dashicons dashicons-warning'></span>
<div class="dup-sub-content">
<h3>
<?php
printf(
esc_html_x('Your Duplicator Pro license key is %1$s ...', '%1$s represent the license status', 'duplicator-pro'),
esc_html($tplData['problem'])
);
?>
</h3>
<?php esc_html_e('You\'re currently missing:', 'duplicator-pro'); ?>
<ul class="dupli-simple-style-disc" >
<li><?php esc_html_e('Access to Advanced Features', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Scheduled Backups', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Storages Management', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Templates Management', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('New Features', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Important Updates for Security Patches', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Bug Fixes', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Support Requests', 'duplicator-pro'); ?></li>
</ul>
<?php
if (CapMng::can(CapMng::CAP_LICENSE, false)) {
printf(
wp_kses(
_x(
'<b>Please %1$sActivate Your License%2$s</b>. If you do not have a license key go to %3$sduplicator.com%4$s to get it.',
'1 and 2 are opening and 3 and 4 are closing anchor tags (<a> and </a>)',
'duplicator-pro'
),
ViewHelper::GEN_KSES_TAGS
),
'<a href="' . esc_url($licensing_tab_url) . '">',
'</a>',
'<a target="_blank" href="' . esc_url(DUPLICATOR_BLOG_URL . 'my-account') . '">',
'</a>'
);
} else {
echo '<b>' . esc_html__(
'Please contact the Duplicator license manager to activate it.',
'duplicator-pro'
) . '</b>';
}
?>
</div>

View File

@@ -0,0 +1,72 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Addons\ProBase\LicensingController;
use Duplicator\Controllers\SettingsPageController;
use Duplicator\Core\CapMng;
use Duplicator\Core\Controllers\ControllersManager;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
$licensing_tab_url = ControllersManager::getMenuLink(
ControllersManager::SETTINGS_SUBMENU_SLUG,
SettingsPageController::L2_SLUG_GENERAL
);
$dashboard_url = DUPLICATOR_BLOG_URL . 'my-account';
$img_url = plugins_url('duplicator-pro/assets/img/warning.png');
?>
<span class='dashicons dashicons-warning'></span>
<div class="dup-sub-content">
<h3>
<?php esc_html_e('Duplicator Pro\'s license is deactivated because you\'re out of site activations.', 'duplicator-pro'); ?>
</h3>
<?php esc_html_e('You\'re currently missing:', 'duplicator-pro'); ?>
<ul class="dupli-simple-style-disc" >
<li><?php esc_html_e('Access to Advanced Features', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Scheduled Backups', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Storages Management', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Templates Management', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('New Features', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Important Updates for Security Patches', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Bug Fixes', 'duplicator-pro'); ?></li>
<li><?php esc_html_e('Support Requests', 'duplicator-pro'); ?></li>
</ul>
<?php
if (CapMng::can(CapMng::CAP_LICENSE, false)) {
printf(
wp_kses(
_x(
'Upgrade your license using the %1$sDuplicator Dashboard%2$s or deactivate plugin on old sites.<br/>
After making necessary changes %3$srefresh the license status%4$s.',
'1 and 2 are opening and 3 and 4 are closing anchor tags (<a> and </a>)',
'duplicator-pro'
),
[
'br' => [],
]
),
'<a href="' . esc_url($dashboard_url) . '" target="_blank">',
'</a>',
'<a href="' . esc_url($licensing_tab_url) . '">',
'</a>'
);
} else {
echo '<b>' . esc_html__(
'Please contact the Duplicator license manager to activate it.',
'duplicator-pro'
) . '</b>';
}
?>
</div>

View File

@@ -0,0 +1,125 @@
<?php
/**
* @package Duplicator
*/
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Addons\ProBase\LicensingController;
use Duplicator\Core\Controllers\ControllersManager;
use Duplicator\Models\DynamicGlobalEntity;
defined("ABSPATH") or die("");
/**
* Variables
*
* @var Duplicator\Core\Controllers\ControllersManager $ctrlMng
* @var Duplicator\Core\Views\TplMng $tplMng
* @var array<string, mixed> $tplData
*/
$dGlobal = DynamicGlobalEntity::getInstance();
$licenseVisibility = $dGlobal->getValInt('license_key_visible', License::VISIBILITY_ALL);
?>
<div class="dup-settings-wrapper" >
<div class="dup-accordion-wrapper display-separators close">
<div class="accordion-header">
<h3 class="title">
<?php esc_html_e("License Key Visibility", 'duplicator-pro') ?>
</h3>
</div>
<div class="accordion-content">
<p>
<?php
esc_html_e(
"This is an optional setting that prevents the 'License Key' from being copied.
Select the desired visibility mode, enter a password and hit the 'Change Visibility' button.",
'duplicator-pro'
);
?>
</p>
<form
id="dup-license-visibility-form"
action="<?php echo esc_url(ControllersManager::getCurrentLink()); ?>"
method="post"
data-parsley-validate>
<?php $tplData['actions'][LicensingController::ACTION_CHANGE_VISIBILITY]->getActionNonceFileds(); ?>
<label class="lbl-larger">
<?php esc_html_e("Visibility", 'duplicator-pro'); ?>
</label>
<div class="margin-bottom-1">
<label class="inline-display margin-right-1">
<input
type="radio"
name="license_key_visible"
value="<?php echo (int) License::VISIBILITY_ALL; ?>"
class="margin-0"
onclick="DupliJs.Licensing.VisibilityTemporary(<?php echo (int) License::VISIBILITY_ALL; ?>);"
<?php checked($licenseVisibility, License::VISIBILITY_ALL); ?>>
<?php esc_html_e("License Visible", 'duplicator-pro'); ?>
</label>
<label class="inline-display margin-right-1">
<input
type="radio"
name="license_key_visible"
value="<?php echo (int) License::VISIBILITY_INFO; ?>"
class="margin-0"
onclick="DupliJs.Licensing.VisibilityTemporary(<?php echo (int) License::VISIBILITY_INFO; ?>);"
<?php checked($licenseVisibility, License::VISIBILITY_INFO); ?>>
<?php esc_html_e("Info Only", 'duplicator-pro'); ?>
</label>
<label class="inline-display">
<input
type="radio"
name="license_key_visible"
value="<?php echo (int) License::VISIBILITY_NONE; ?>"
class="margin-0"
onclick="DupliJs.Licensing.VisibilityTemporary(<?php echo (int) License::VISIBILITY_NONE; ?>);"
<?php checked($licenseVisibility, License::VISIBILITY_NONE); ?>>
<?php esc_html_e("License Invisible", 'duplicator-pro'); ?>
</label>
</div>
<label class="lbl-larger">
<?php esc_html_e("Password", 'duplicator-pro'); ?>
</label>
<div class="margin-bottom-1">
<input
type="password"
class="dup-wide-input"
name="_key_password"
id="_key_password" size="50">
</div>
<?php if ($licenseVisibility == License::VISIBILITY_ALL) { ?>
<label class="lbl-larger">
<?php esc_html_e("Retype Password", 'duplicator-pro'); ?>
</label>
<div class="margin-bottom-1">
<input
type="password"
class="dup-wide-input"
name="_key_password_confirmation"
id="_key_password_confirmation"
data-parsley-equalto="#_key_password"
size="50">
</div>
<?php } ?>
<label class="lbl-larger">
&nbsp;
</label>
<div class="margin-bottom-1">
<button
class="button secondary hollow small margin-0"
id="show_hide"
onclick="DupliJs.Licensing.ChangeKeyVisibility(); return false;">
<?php esc_html_e('Change Visibility', 'duplicator-pro'); ?>
</button>
</div>
</form>
</div>
</div>
</div>