first commit

This commit is contained in:
2025-02-24 22:33:42 +01:00
commit 737c037e85
18358 changed files with 5392983 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
<?php
namespace Essential_Addons_Elementor\Pro\Classes\License;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use Exception;
use Essential_Addons_Elementor\Pro\Classes\License\Contracts\ApiAdapter;
/**
* @property string $textdomain
* @property string $action_prefix
*/
#[\AllowDynamicProperties]
class AJAXApi extends ApiAdapter {
/**
* @throws Exception
*/
public function register() {
if ( ! isset( $this->action_prefix ) ) {
throw new Exception( "action_prefix needs to be set in ajax configuration" );
}
add_action( "wp_ajax_{$this->action_prefix}/license/activate", [ $this, 'activate' ] );
add_action( "wp_ajax_{$this->action_prefix}/license/deactivate", [ $this, 'deactivate' ] );
add_action( "wp_ajax_{$this->action_prefix}/license/submit-otp", [ $this, 'submit_otp' ] );
add_action( "wp_ajax_{$this->action_prefix}/license/resend-otp", [ $this, 'resend_otp' ] );
}
/**
* Get the API Config
* @return array
*/
public function get_api_config() {
return array_merge( parent::get_api_config(), [
'action' => $this->action_prefix,
'api_url' => esc_url( admin_url( 'admin-ajax.php' ) )
] );
}
public function error( $code, $message ) {
wp_send_json_error( [
'code' => $code,
'message' => $message
] );
}
private function nonce_permission_check( ) {
if ( ! isset( $_POST['_nonce'] ) || ! $this->verify_nonce( $_POST['_nonce'] ) ) {
$this->error( 'nonce_error', __( 'Nonce Verifications Failed.', $this->textdomain ) );
}
if ( ! $this->permission_check() ) {
$this->error( 'no_permission', __( 'You don\'t have permission to take this action.', $this->textdomain ) );
}
}
/**
* @param $request array
*
* @return void
*/
public function activate( $request = [] ) {
$this->nonce_permission_check();
$response = $this->license_manager->activate( [
'license_key' => sanitize_text_field( $_POST['license_key'] )
] );
if ( is_wp_error( $response ) ) {
$this->error( $response->get_error_code(), $response->get_error_message() );
}
wp_send_json_success( $response );
}
/**
* @param $request array
*
* @return void
*/
public function deactivate( $request = [] ) {
$response = $this->license_manager->deactivate();
if ( is_wp_error( $response ) ) {
$this->error( $response->get_error_code(), $response->get_error_message() );
}
wp_send_json_success( $response );
}
public function submit_otp( $request = [] ) {
$this->nonce_permission_check();
$args = [
'otp' => sanitize_text_field( $_POST['otp'] ),
'license_key' => sanitize_text_field( $_POST['license'] )
];
$response = $this->license_manager->submit_otp( $args );
if ( is_wp_error( $response ) ) {
$this->error( $response->get_error_code(), $response->get_error_message() );
}
wp_send_json_success( $response );
}
public function resend_otp( $request = [] ) {
$this->nonce_permission_check();
$args = [
'license_key' => sanitize_text_field( $_POST['license'] )
];
$response = $this->license_manager->resend_otp( $args );
if ( is_wp_error( $response ) ) {
$this->error( $response->get_error_code(), $response->get_error_message() );
}
wp_send_json_success( $response );
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Essential_Addons_Elementor\Pro\Classes\License\Contracts;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use Exception;
use Essential_Addons_Elementor\Pro\Classes\License\LicenseManager;
#[\AllowDynamicProperties]
abstract class ApiAdapter {
protected $config = null;
/**
* @var LicenseManager
*/
protected $license_manager;
public function __construct( $license_manager ) {
$this->license_manager = $license_manager;
$this->config = $this->license_manager->get_args( $this->license_manager->api );
$this->config['handle'] = $this->license_manager->get_args( 'scripts_handle' );
$this->config['screen_id'] = $this->license_manager->get_args( 'screen_id' );
$this->config['item_id'] = $this->license_manager->get_args( 'item_id' );
$this->register();
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ], 11 );
}
public function enqueue( $hook ) {
if ( is_array( $this->screen_id ) && ! in_array( $hook, $this->screen_id ) ) {
return;
}
if ( ! is_array( $this->screen_id ) && $this->screen_id !== $hook ) {
return;
}
wp_localize_script( $this->handle, 'wpdeveloperLicenseManagerConfig', $this->get_api_config() );
}
public function get_api_config() {
return [
'textdomain' => $this->license_manager->textdomain,
'apiType' => $this->license_manager->api,
'nonce' => wp_create_nonce( "wpdeveloper_sl_{$this->item_id}_nonce" )
];
}
/**
* @throws Exception
*/
public function __get( $name ) {
if ( isset( $this->config[ $name ] ) ) {
return $this->config[ $name ];
} elseif ( isset( $this->license_manager->{$name} ) ) {
return $this->license_manager->get_args( $name );
} else {
throw new Exception( "Please provide $name for api configuration." );
}
}
public function __isset( $name ) {
return isset( $this->config[ $name ] );
}
protected function verify_nonce( $nonce ) {
return wp_verify_nonce( $nonce, "wpdeveloper_sl_{$this->item_id}_nonce" );
}
/**
* This method is responsible for checking permissions.
* @return bool
*/
public function permission_check() {
return current_user_can( isset( $this->permission ) ? $this->permission : 'delete_users' );
}
abstract public function register();
abstract public function activate( $request );
abstract public function deactivate( $request );
}

View File

@@ -0,0 +1,358 @@
<?php
namespace Essential_Addons_Elementor\Pro\Classes\License;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use Exception;
use WP_Error;
/**
* @property int $item_id
* @property string $version
* @property string $storeURL
* @property string $db_prefix
* @property string $textdomain
* @property string $item_name
*/
#[\AllowDynamicProperties]
class LicenseManager {
private static $_instance = null;
protected $license = '';
protected $license_data = null;
protected $args = [
'version' => '',
// 'author' => '',
// 'beta' => '',
'plugin_file' => '',
'item_id' => 0,
'item_name' => '',
'item_slug' => '',
'storeURL' => '',
'textdomain' => '',
'db_prefix' => '',
'scripts_handle' => '',
'screen_id' => '',
'page_slug' => '',
'api' => ''
];
public static function get_instance( $args ) {
if ( self::$_instance === null ) {
self::$_instance = new self( $args );
}
return self::$_instance;
}
/**
* @throws Exception
*/
public function __construct( $args ) {
foreach ( $this->args as $property => $value ) {
if ( ! array_key_exists( $property, $args ) ) {
throw new Exception( "$property is missing in licensing." );
}
}
$this->args = wp_parse_args( $args, $this->args );
$this->license_data = $this->get_license_data();
if ( ( empty( $this->license_data ) ) && current_user_can( 'activate_plugins' ) ) {
add_action( 'admin_notices', [ $this, 'admin_notices' ] );
add_action( 'eael_admin_notices', [ $this, 'admin_notices' ] );
}
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ], 11 );
if ( isset( $this->args['api'] ) ) {
switch ( strtolower( $this->args['api'] ) ) {
case 'rest':
if ( ! isset( $this->args['rest'] ) ) {
throw new Exception( "rest is missing in licensing." );
}
new RESTApi( $this );
break;
case 'ajax':
if ( ! isset( $this->args['ajax'] ) ) {
throw new Exception( "ajax is missing in licensing." );
}
new AJAXApi( $this );
break;
}
}
add_action( 'init', [ $this, 'plugin_updater' ] );
add_action( 'eael_licensing', array( $this, 'render_licenses_page' ) );
}
public function admin_notices() {
$message = sprintf( __( 'Please %1$sactivate your license%2$s key to enable updates for %3$s.', $this->textdomain ), '<a style="text-decoration: none;" href="' . admin_url( 'admin.php?page=' . $this->page_slug ) . '">', '</a>', '<strong>' . $this->item_name . '</strong>' );
$notice = sprintf( '<div style="padding: 10px;" class="%1$s-notice wpdeveloper-licensing-notice notice notice-error">%2$s</div>', $this->textdomain, $message );
echo wp_kses_post( $notice );
}
public function plugin_updater() {
$_license = get_option( "{$this->db_prefix}-license-key" );
new PluginUpdater( $this->storeURL, $this->plugin_file, [
'version' => $this->version, // current version number
'license' => $_license, // license key (used get_option above to retrieve from DB)
'item_id' => $this->item_id, // ID of the product
'author' => empty( $this->author ) ? 'WPDeveloper' : $this->author, // author of this plugin
'beta' => isset( $this->beta ) ? $this->beta : false
] );
}
public function get_args( $name = '' ) {
return empty( $name ) ? $this->args : $this->args[ $name ];
}
public function enqueue( $hook ) {
if ( is_array( $this->screen_id ) && ! in_array( $hook, $this->screen_id ) ) {
return;
}
if ( ! is_array( $this->screen_id ) && $this->screen_id !== $hook ) {
return;
}
wp_localize_script( $this->scripts_handle, 'wpdeveloperLicenseData', $this->get_license_data() );
}
public function get_license_data() {
$_license = get_option( "{$this->db_prefix}-license-key" );
$_license_status = get_option( "{$this->db_prefix}-license-status" );
$_license_data = get_transient( "{$this->db_prefix}-license_data" );
if ( $_license_data !== false ) {
$_license_data = (array) $_license_data;
}
if ( $_license_data == false || empty( $_license_data ) ) {
$response = $this->check();
if ( is_wp_error( $response ) ) {
return [];
}
$_license_data = (array) $response;
}
return array_merge( [
'license_key' => $_license,
'hidden_license_key' => $this->hide_license_key( $_license ),
'license_status' => $_license_status
], $_license_data );
}
public function hide_license_key( $_license ) {
$length = mb_strlen( $_license ) - 10;
$_license = substr_replace( $_license, mb_substr( preg_replace( '/\S/', '*', $_license ), 5, $length ), 5, $length );
return $_license;
}
public function activate( $args = [] ) {
$this->license = sanitize_text_field( isset( $args['license_key'] ) ? trim( $args['license_key'] ) : '' );
$response = $this->remote_post( 'activate_license' );
if ( is_wp_error( $response ) ) {
return $response;
}
/**
* Return if license required OTP to activate.
*/
if ( isset( $response->license ) && $response->license == 'required_otp' ) {
return $response;
}
update_option( "{$this->db_prefix}-license-key", $this->license, 'no' );
update_option( "{$this->db_prefix}-license-status", $response->license, 'no' );
set_transient( "{$this->db_prefix}-license_data", $response, MONTH_IN_SECONDS * 3 );
return $response;
}
public function deactivate( $args = [] ) {
$this->license = get_option( "{$this->db_prefix}-license-key", '' );
$response = $this->remote_post( 'deactivate_license' );
if ( is_wp_error( $response ) ) {
return $response;
}
delete_option( "{$this->db_prefix}-license-key" );
delete_option( "{$this->db_prefix}-license-status" );
delete_transient( "{$this->db_prefix}-license_data" );
return $response;
}
public function submit_otp( $args = [] ) {
$this->license = sanitize_text_field( isset( $args['license_key'] ) ? trim( $args['license_key'] ) : '' );
$response = $this->remote_post( 'activate_license_by_otp', $args );
if ( is_wp_error( $response ) ) {
return $response;
}
update_option( "{$this->db_prefix}-license-key", $this->license, 'no' );
update_option( "{$this->db_prefix}-license-status", $response->license, 'no' );
set_transient( "{$this->db_prefix}-license_data", $response, MONTH_IN_SECONDS * 3 );
return $response;
}
public function resend_otp( $args ) {
$this->license = sanitize_text_field( isset( $args['license_key'] ) ? trim( $args['license_key'] ) : '' );
return $this->remote_post( 'resend_otp_for_license', $args );
}
public function check( $args = [] ) {
$this->license = get_option( "{$this->db_prefix}-license-key", '' );
$_license_data = get_transient( "{$this->db_prefix}-license_data" );
if ( $_license_data !== false ) {
$_license_data = (array) $_license_data;
}
if ( ! empty( $_license_data ) ) {
return $_license_data;
}
$response = $this->remote_post( 'check_license' );
if ( is_wp_error( $response ) ) {
delete_transient( "{$this->db_prefix}-license_data" );
return $response;
}
set_transient( "{$this->db_prefix}-license_data", $response, MONTH_IN_SECONDS * 3 );
return $response;
}
/**
* 'activate_license'
*
* @param mixed $args
*
* @return mixed
*/
public function remote_post( $action, $args = [] ) {
if ( empty( $this->license ) ) {
return new WP_Error( 'empty_license', __( 'Please provide a valid license.', $this->textdomain ) );
}
$defaults = [
'edd_action' => $action,
'license' => $this->license,
'item_id' => $this->item_id,
'item_name' => rawurlencode( $this->item_name ), // the name of our product in EDD
'url' => home_url(),
'version' => $this->version,
'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production'
];
$args = wp_parse_args( $args, $defaults );
$response = wp_safe_remote_post( $this->storeURL, [
'timeout' => 15,
'sslverify' => false,
'body' => $args
] );
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
if ( is_wp_error( $response ) ) {
return $response;
}
return new WP_Error( 'unknown', __( 'An error occurred, please try again.', $this->textdomain ) );
}
$license_data = $this->maybe_error( json_decode( wp_remote_retrieve_body( $response ) ) );
if ( ! is_wp_error( $license_data ) ) {
$license_data->license_key = $this->hide_license_key( $this->license );
}
return $license_data;
}
private function maybe_error( $license_data ) {
if ( false === $license_data->success ) {
$message = '';
$error_code = isset($license_data->error) ? $license_data->error : 'unknown';
switch ( $error_code ) {
case 'expired':
$message = sprintf( /* translators: the license key expiration date */ __( 'Your license key expired on %s.', $this->textdomain ), date_i18n( get_option( 'date_format' ), $license_data->expires ) );
break;
case 'invalid_otp':
$message = __( 'Your license confirmation code is invalid.', $this->textdomain );
break;
case 'expired_otp':
$message = __( 'Your license confirmation code has been expired.', $this->textdomain );
break;
case 'disabled':
case 'revoked':
$message = __( 'Your license key has been disabled.', $this->textdomain );
break;
case 'missing':
$message = __( 'Invalid license.', $this->textdomain );
break;
case 'invalid':
case 'site_inactive':
$message = __( 'Your license is not active for this URL.', $this->textdomain );
break;
case 'item_name_mismatch':
/* translators: the plugin name */ $message = sprintf( __( 'This appears to be an invalid license key for %s.', $this->textdomain ), $this->item_name );
break;
case 'no_activations_left':
$message = __( 'Your license key has reached its activation limit.', $this->textdomain );
break;
default:
$message = __( 'An error occurred, please try again.', $this->textdomain );
break;
}
return new WP_Error( $error_code, wp_kses( $message, 'post' ) );
}
return $license_data;
}
public function __get( $name ) {
if ( isset( $this->args[ $name ] ) ) {
return $this->args[ $name ];
}
return null;
}
public function render_licenses_page() {
$hidden_license_key = $this->license_data['hidden_license_key'] ?? '';
$status = $this->license_data['license_status'] ?? '';
$title = sprintf( __( '%s License', $this->text_domain ), $this->product_name );
// if ( $status !== 'valid' ) {
// $this->set_license_key( '' );
// }
include_once __DIR__ . '/views/settings.php';
}
}

View File

@@ -0,0 +1,671 @@
<?php
namespace Essential_Addons_Elementor\Pro\Classes\License;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use stdClass;
/**
* Allows plugins to use their own update API.
*
* @author Easy Digital Downloads
* @version 1.9.2
*/
#[\AllowDynamicProperties]
class PluginUpdater {
private $api_url = '';
private $api_data = [];
private $plugin_file = '';
private $name = '';
private $slug = '';
private $version = '';
private $wp_override = false;
private $beta = false;
private $failed_request_cache_key;
/**
* Class constructor.
*
* @uses plugin_basename()
* @uses hook()
*
* @param string $_api_url The URL pointing to the custom API endpoint.
* @param string $_plugin_file Path to the plugin file.
* @param array $_api_data Optional data to send with API calls.
*/
public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
global $edd_plugin_data;
$this->api_url = trailingslashit( $_api_url );
$this->api_data = $_api_data;
$this->plugin_file = $_plugin_file;
$this->name = plugin_basename( $_plugin_file );
$this->slug = basename( $_plugin_file, '.php' );
$this->version = $_api_data['version'];
$this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
$this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
$this->failed_request_cache_key = 'edd_sl_failed_http_' . md5( $this->api_url );
$edd_plugin_data[$this->slug] = $this->api_data;
/**
* Fires after the $edd_plugin_data is setup.
*
* @since x.x.x
*
* @param array $edd_plugin_data Array of EDD SL plugin data.
*/
do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
// Set up hooks.
$this->init();
}
/**
* Set up WordPress filters to hook into WP's update process.
*
* @uses add_filter()
*
* @return void
*/
public function init() {
add_filter( 'pre_set_site_transient_update_plugins', [$this, 'check_update'] );
add_filter( 'plugins_api', [$this, 'plugins_api_filter'], 10, 3 );
add_action( 'after_plugin_row', [$this, 'show_update_notification'], 10, 2 );
add_action( 'admin_init', [$this, 'show_changelog'] );
}
/**
* Check for Updates at the defined API endpoint and modify the update array.
*
* This function dives into the update API just when WordPress creates its update array,
* then adds a custom API call and injects the custom plugin data retrieved from the API.
* It is reassembled from parts of the native WordPress plugin update code.
* See wp-includes/update.php line 121 for the original wp_update_plugins() function.
*
* @uses api_request()
*
* @param array $_transient_data Update array build by WordPress.
* @return array|object Modified update array with custom plugin data.
*/
public function check_update( $_transient_data ) {
global $pagenow;
if ( ! is_object( $_transient_data ) ) {
$_transient_data = new stdClass();
}
if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[$this->name] ) && false === $this->wp_override ) {
return $_transient_data;
}
$current = $this->get_repo_api_data();
if ( false !== $current && is_object( $current ) && isset( $current->new_version ) ) {
if ( version_compare( $this->version, $current->new_version, '<' ) ) {
$_transient_data->response[$this->name] = $current;
} else {
// Populating the no_update information is required to support auto-updates in WordPress 5.5.
$_transient_data->no_update[$this->name] = $current;
}
}
$_transient_data->last_checked = time();
$_transient_data->checked[$this->name] = $this->version;
return $_transient_data;
}
/**
* Get repo API data from store.
* Save to cache.
*
* @return stdClass|bool
*/
public function get_repo_api_data() {
$version_info = $this->get_cached_version_info();
if ( false === $version_info ) {
$version_info = $this->api_request(
'plugin_latest_version',
[
'slug' => $this->slug,
'beta' => $this->beta
]
);
if ( ! $version_info ) {
return false;
}
// This is required for your plugin to support auto-updates in WordPress 5.5.
$version_info->plugin = $this->name;
$version_info->id = $this->name;
$version_info->tested = $this->get_tested_version( $version_info );
$this->set_version_info_cache( $version_info );
}
return $version_info;
}
/**
* Gets the plugin's tested version.
*
* @since 1.9.2
* @param object $version_info
* @return null|string
*/
private function get_tested_version( $version_info ) {
// There is no tested version.
if ( empty( $version_info->tested ) ) {
return null;
}
// Strip off extra version data so the result is x.y or x.y.z.
list( $current_wp_version ) = explode( '-', get_bloginfo( 'version' ) );
// The tested version is greater than or equal to the current WP version, no need to do anything.
if ( version_compare( $version_info->tested, $current_wp_version, '>=' ) ) {
return $version_info->tested;
}
$current_version_parts = explode( '.', $current_wp_version );
$tested_parts = explode( '.', $version_info->tested );
// The current WordPress version is x.y.z, so update the tested version to match it.
if ( isset( $current_version_parts[2] ) && $current_version_parts[0] === $tested_parts[0] && $current_version_parts[1] === $tested_parts[1] ) {
$tested_parts[2] = $current_version_parts[2];
}
return implode( '.', $tested_parts );
}
/**
* Show the update notification on multisite subsites.
*
* @param string $file
* @param array $plugin
*/
public function show_update_notification( $file, $plugin ) {
// Return early if in the network admin, or if this is not a multisite install.
if ( is_network_admin() || ! is_multisite() ) {
return;
}
// Allow single site admins to see that an update is available.
if ( ! current_user_can( 'activate_plugins' ) ) {
return;
}
if ( $this->name !== $file ) {
return;
}
// Do not print any message if update does not exist.
$update_cache = get_site_transient( 'update_plugins' );
if ( ! isset( $update_cache->response[$this->name] ) ) {
if ( ! is_object( $update_cache ) ) {
$update_cache = new stdClass();
}
$update_cache->response[$this->name] = $this->get_repo_api_data();
}
// Return early if this plugin isn't in the transient->response or if the site is running the current or newer version of the plugin.
if ( empty( $update_cache->response[$this->name] ) || version_compare( $this->version, $update_cache->response[$this->name]->new_version, '>=' ) ) {
return;
}
printf(
'<tr class="plugin-update-tr %3$s" id="%1$s-update" data-slug="%1$s" data-plugin="%2$s">',
$this->slug,
$file,
in_array( $this->name, $this->get_active_plugins(), true ) ? 'active' : 'inactive'
);
echo '<td colspan="3" class="plugin-update colspanchange">';
echo '<div class="update-message notice inline notice-warning notice-alt"><p>';
$changelog_link = '';
if ( ! empty( $update_cache->response[$this->name]->sections->changelog ) ) {
$changelog_link = add_query_arg(
[
'edd_sl_action' => 'view_plugin_changelog',
'plugin' => urlencode( $this->name ),
'slug' => urlencode( $this->slug ),
'TB_iframe' => 'true',
'width' => 77,
'height' => 911
],
self_admin_url( 'index.php' )
);
}
$update_link = add_query_arg(
[
'action' => 'upgrade-plugin',
'plugin' => urlencode( $this->name )
],
self_admin_url( 'update.php' )
);
printf(
/* translators: the plugin name. */
esc_html__( 'There is a new version of %1$s available.', 'easy-digital-downloads' ),
esc_html( $plugin['Name'] )
);
if ( ! current_user_can( 'update_plugins' ) ) {
echo ' ';
esc_html_e( 'Contact your network administrator to install the update.', 'easy-digital-downloads' );
} elseif ( empty( $update_cache->response[$this->name]->package ) && ! empty( $changelog_link ) ) {
echo ' ';
printf(
/* translators: 1. opening anchor tag, do not translate 2. the new plugin version 3. closing anchor tag, do not translate. */
__( '%1$sView version %2$s details%3$s.', 'easy-digital-downloads' ),
'<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
esc_html( $update_cache->response[$this->name]->new_version ),
'</a>'
);
} elseif ( ! empty( $changelog_link ) ) {
echo ' ';
printf(
__( '%1$sView version %2$s details%3$s or %4$supdate now%5$s.', 'easy-digital-downloads' ),
'<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
esc_html( $update_cache->response[$this->name]->new_version ),
'</a>',
'<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
'</a>'
);
} else {
printf(
' %1$s%2$s%3$s',
'<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
esc_html__( 'Update now.', 'easy-digital-downloads' ),
'</a>'
);
}
do_action( "in_plugin_update_message-{$file}", $plugin, $plugin );
echo '</p></div></td></tr>';
}
/**
* Gets the plugins active in a multisite network.
*
* @return array
*/
private function get_active_plugins() {
$active_plugins = (array) get_option( 'active_plugins' );
$active_network_plugins = (array) get_site_option( 'active_sitewide_plugins' );
return array_merge( $active_plugins, array_keys( $active_network_plugins ) );
}
/**
* Updates information on the "View version x.x details" page with custom data.
*
* @uses api_request()
*
* @param mixed $_data
* @param string $_action
* @param object $_args
* @return object $_data
*/
public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
if ( 'plugin_information' !== $_action ) {
return $_data;
}
if ( ! isset( $_args->slug ) || ( $_args->slug !== $this->slug ) ) {
return $_data;
}
$to_send = [
'slug' => $this->slug,
'is_ssl' => is_ssl(),
'fields' => [
'banners' => [],
'reviews' => false,
'icons' => []
]
];
// Get the transient where we store the api request for this plugin for 24 hours
$edd_api_request_transient = $this->get_cached_version_info();
//If we have no transient-saved value, run the API, set a fresh transient with the API value, and return that value too right now.
if ( empty( $edd_api_request_transient ) ) {
$api_response = $this->api_request( 'plugin_information', $to_send );
// Expires in 3 hours
$this->set_version_info_cache( $api_response );
if ( false !== $api_response ) {
$_data = $api_response;
}
} else {
$_data = $edd_api_request_transient;
}
// Convert sections into an associative array, since we're getting an object, but Core expects an array.
if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
$_data->sections = $this->convert_object_to_array( $_data->sections );
}
// Convert banners into an associative array, since we're getting an object, but Core expects an array.
if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
$_data->banners = $this->convert_object_to_array( $_data->banners );
}
// Convert icons into an associative array, since we're getting an object, but Core expects an array.
if ( isset( $_data->icons ) && ! is_array( $_data->icons ) ) {
$_data->icons = $this->convert_object_to_array( $_data->icons );
}
// Convert contributors into an associative array, since we're getting an object, but Core expects an array.
if ( isset( $_data->contributors ) && ! is_array( $_data->contributors ) ) {
$_data->contributors = $this->convert_object_to_array( $_data->contributors );
}
if ( ! isset( $_data->plugin ) ) {
$_data->plugin = $this->name;
}
return $_data;
}
/**
* Convert some objects to arrays when injecting data into the update API
*
* Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
* decoding, they are objects. This method allows us to pass in the object and return an associative array.
*
* @since 3.6.5
*
* @param stdClass $data
*
* @return array
*/
private function convert_object_to_array( $data ) {
if ( ! is_array( $data ) && ! is_object( $data ) ) {
return [];
}
$new_data = [];
foreach ( $data as $key => $value ) {
$new_data[$key] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
}
return $new_data;
}
/**
* Disable SSL verification in order to prevent download update failures
*
* @param array $args
* @param string $url
* @return array $array
*/
public function http_request_args( $args, $url ) {
if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
$args['sslverify'] = $this->verify_ssl();
}
return $args;
}
/**
* Calls the API and, if successfull, returns the object delivered by the API.
*
* @uses get_bloginfo()
* @uses wp_remote_post()
* @uses is_wp_error()
*
* @param string $_action The requested action.
* @param array $_data Parameters for the API action.
* @return false|object|void
*/
private function api_request( $_action, $_data ) {
$data = array_merge( $this->api_data, $_data );
if ( $data['slug'] !== $this->slug ) {
return;
}
// Don't allow a plugin to ping itself
if ( trailingslashit( home_url() ) === $this->api_url ) {
return false;
}
if ( $this->request_recently_failed() ) {
return false;
}
return $this->get_version_from_remote();
}
/**
* Determines if a request has recently failed.
*
* @since 1.9.1
*
* @return bool
*/
private function request_recently_failed() {
$failed_request_details = get_option( $this->failed_request_cache_key );
// Request has never failed.
if ( empty( $failed_request_details ) || ! is_numeric( $failed_request_details ) ) {
return false;
}
/*
* Request previously failed, but the timeout has expired.
* This means we're allowed to try again.
*/
if ( time() > $failed_request_details ) {
delete_option( $this->failed_request_cache_key );
return false;
}
return true;
}
/**
* Logs a failed HTTP request for this API URL.
* We set a timestamp for 1 hour from now. This prevents future API requests from being
* made to this domain for 1 hour. Once the timestamp is in the past, API requests
* will be allowed again. This way if the site is down for some reason we don't bombard
* it with failed API requests.
*
* @see EDD_SL_Plugin_Updater::request_recently_failed
*
* @since 1.9.1
*/
private function log_failed_request() {
update_option( $this->failed_request_cache_key, strtotime( '+1 hour' ) );
}
/**
* If available, show the changelog for sites in a multisite install.
*/
public function show_changelog() {
if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' !== $_REQUEST['edd_sl_action'] ) {
return;
}
if ( empty( $_REQUEST['plugin'] ) ) {
return;
}
if ( empty( $_REQUEST['slug'] ) || $this->slug !== $_REQUEST['slug'] ) {
return;
}
if ( ! current_user_can( 'update_plugins' ) ) {
wp_die( esc_html__( 'You do not have permission to install plugin updates', 'easy-digital-downloads' ), esc_html__( 'Error', 'easy-digital-downloads' ), ['response' => 403] );
}
$version_info = $this->get_repo_api_data();
if ( isset( $version_info->sections ) ) {
$sections = $this->convert_object_to_array( $version_info->sections );
if ( ! empty( $sections['changelog'] ) ) {
echo '<div style="background:#fff;padding:10px;">' . wp_kses_post( $sections['changelog'] ) . '</div>';
}
}
exit;
}
/**
* Gets the current version information from the remote site.
*
* @return array|false
*/
private function get_version_from_remote() {
$api_params = [
'edd_action' => 'get_version',
'license' => ! empty( $this->api_data['license'] ) ? $this->api_data['license'] : '',
'item_name' => isset( $this->api_data['item_name'] ) ? $this->api_data['item_name'] : false,
'item_id' => isset( $this->api_data['item_id'] ) ? $this->api_data['item_id'] : false,
'version' => isset( $this->api_data['version'] ) ? $this->api_data['version'] : false,
'slug' => $this->slug,
'author' => $this->api_data['author'],
'url' => home_url(),
'beta' => $this->beta,
'php_version' => phpversion(),
'wp_version' => get_bloginfo( 'version' )
];
/**
* Filters the parameters sent in the API request.
*
* @param array $api_params The array of data sent in the request.
* @param array $this->api_data The array of data set up in the class constructor.
* @param string $this->plugin_file The full path and filename of the file.
*/
$api_params = apply_filters( 'edd_sl_plugin_updater_api_params', $api_params, $this->api_data, $this->plugin_file );
$request = wp_remote_post(
$this->api_url,
[
'timeout' => 15,
'sslverify' => $this->verify_ssl(),
'body' => $api_params
]
);
if ( is_wp_error( $request ) || ( 200 !== wp_remote_retrieve_response_code( $request ) ) ) {
$this->log_failed_request();
return false;
}
$request = json_decode( wp_remote_retrieve_body( $request ) );
if ( $request && isset( $request->sections ) ) {
$request->sections = maybe_unserialize( $request->sections );
} else {
$request = false;
}
if ( $request && isset( $request->banners ) ) {
$request->banners = maybe_unserialize( $request->banners );
}
if ( $request && isset( $request->icons ) ) {
$request->icons = maybe_unserialize( $request->icons );
}
if ( ! empty( $request->sections ) ) {
foreach ( $request->sections as $key => $section ) {
$request->$key = (array) $section;
}
}
return $request;
}
/**
* Get the version info from the cache, if it exists.
*
* @param string $cache_key
* @return mixed
*/
public function get_cached_version_info( $cache_key = '' ) {
if ( empty( $cache_key ) ) {
$cache_key = $this->get_cache_key();
}
$cache = get_option( $cache_key );
// Cache is expired
if ( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
return false;
}
// We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
$cache['value'] = json_decode( $cache['value'] );
if ( ! empty( $cache['value']->icons ) ) {
$cache['value']->icons = (array) $cache['value']->icons;
}
return $cache['value'];
}
/**
* Adds the plugin version information to the database.
*
* @param string $value
* @param string $cache_key
*/
public function set_version_info_cache( $value = '', $cache_key = '' ) {
if ( empty( $cache_key ) ) {
$cache_key = $this->get_cache_key();
}
$data = [
'timeout' => strtotime( '+3 hours', time() ),
'value' => wp_json_encode( $value )
];
update_option( $cache_key, $data, 'no' );
// Delete the duplicate option
delete_option( 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) ) );
}
/**
* Returns if the SSL of the store should be verified.
*
* @since 1.6.13
* @return bool
*/
private function verify_ssl() {
return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
}
/**
* Gets the unique key (option name) for a plugin.
*
* @since 1.9.0
* @return string
*/
private function get_cache_key() {
$string = $this->slug . $this->api_data['license'] . $this->beta;
return 'edd_sl_' . md5( serialize( $string ) );
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace Essential_Addons_Elementor\Pro\Classes\License;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use Exception;
use WP_REST_Server;
use Essential_Addons_Elementor\Pro\Classes\License\Contracts\ApiAdapter;
#[\AllowDynamicProperties]
class RESTApi extends ApiAdapter {
private $version = 'v1';
public function register() {
if ( ! isset( $this->namespace ) ) {
throw new Exception( "namespace is missing in your rest configuration." );
}
add_action( 'rest_api_init', [$this, 'routes'] );
}
public function get_api_config() {
return array_merge( parent::get_api_config(), [
'api_url' => esc_url( trailingslashit( rest_url( $this->get_namespace() ) ) )
] );
}
public function routes() {
$this->route( '/license/activate', [$this, 'activate'], $this->args() );
$this->route( '/license/deactivate', [$this, 'deactivate'] );
$this->route( '/license/submit-otp', [$this, 'submit_otp'], $this->args([
'otp' => [
'required' => true,
'validate_callback' => function ( $param, $request, $key ) {
return is_string( $param ) && ! empty( $param );
}
]
]) );
$this->route( '/license/resend-otp', [$this, 'resend_otp'], $this->args() );
$this->route( '/license/get-license', [$this, 'get_license'] );
}
public function activate( $request ) {
return $this->license_manager->activate( [
'license_key' => sanitize_text_field( $request->get_param( 'license_key' ) )
] );
}
public function deactivate( $request ) {
return $this->license_manager->deactivate();
}
/**
* Handles OTP submission request.
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_Error|WP_REST_Response
*/
public function submit_otp( $request ) {
$args = [
'otp' => sanitize_text_field( $request->get_param( 'otp') ),
'license_key' => sanitize_text_field( $request->get_param( 'license_key') )
];
return $this->license_manager->submit_otp( $args );
}
/**
* Handles OTP resend request.
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_Error|WP_REST_Response
*/
public function resend_otp( $request ) {
$args = [
'license_key' => sanitize_text_field( $request->get_param( 'license_key') )
];
return $this->license_manager->resend_otp( $args );
}
/**
* Retrieves the license details.
*
* This method uses the LicenseManager to get the license data, hide the license key, and format the title.
* It then returns an array with the title, hidden license key, and license status.
*
* @return array An array containing the title, hidden license key, and license status.
*/
public function get_license(){
$license_data = $this->license_manager->get_license_data();
$license_key = $this->license_manager->hide_license_key($license_data['license_key']);
$status = $license_data['license_status'];
$title = sprintf(__('%s License', $this->license_manager->textdomain), $this->license_manager->item_name);
return ['title' => $title, 'key' => $license_key, 'status' => $status];
}
protected function args($args = []) {
return wp_parse_args($args, [
'license_key' => [
'required' => true,
'validate_callback' => function ( $param, $request, $key ) {
return is_string( $param ) && ! empty( $param );
}
]
]);
}
private function get_namespace() {
return $this->namespace . '/' . $this->version;
}
protected function route( $endpoint, $callback, $args = [] ) {
return register_rest_route( $this->get_namespace(), $endpoint, [
'methods' => WP_REST_Server::CREATABLE,
'callback' => $callback,
'permission_callback' => [$this, 'permission_check'],
'args' => $args
] );
}
}

View File

@@ -0,0 +1,83 @@
<div class="eael-block p45 eael-activate__license__block --activation-form" style="display: <?php echo ( $status === false || $status !== 'valid' ) ? 'block' : 'none'; ?>">
<div class="eael__flex eael__flex--wrap align__center mb30">
<h3>Just one more step to go!</h3>
<img src="<?php echo esc_url( EAEL_PLUGIN_URL . 'assets/admin/images/steps.svg' ); ?>" alt="">
</div>
<p><?php _e( 'Enter your license key here, to activate <strong>Essential Addons for Elementor</strong>, and get automatic updates and premium support.', $this->text_domain ); ?></p>
<p><?php printf( __( 'Visit the <a href="%s" target="_blank">Validation Guide</a> for help.', $this->text_domain ), 'https://essential-addons.com/elementor/docs/getting-started/validating-license/' ); ?></p>
<ol>
<li>
<p><?php printf( __( 'Log in to <a href="%s" target="_blank">your account</a> to get your license key.', $this->text_domain ), 'https://wpdeveloper.com/account/' ); ?></p>
</li>
<li>
<p><?php printf( __( 'If you don\'t yet have a license key, get <a href="%s" target="_blank">Essential Addons for Elementor now</a>.', $this->text_domain ), 'https://wpdeveloper.com/in/upgrade-essential-addons-elementor' ); ?></p>
</li>
<li><?php _e( __( 'Copy the license key from your account and paste it below.', $this->text_domain ) ); ?></li>
<li><?php _e( __( 'Click on <strong>"Activate License"</strong> button.', $this->text_domain ) ); ?></li>
</ol>
<div class="license__form__block">
<div class="eael-license-form-block">
<form method="post" action="#">
<?php wp_nonce_field( $this->args['item_slug'] . '_license_nonce', $this->args['item_slug'] . '_license_nonce' ); ?>
<input id="<?php echo $this->args['item_slug']; ?>-license-key" type="text" class="eael-form__control" placeholder="Place Your License Key & Activate">
<button type="submit" class="eael-button button__themeColor" name="license_activate">Activate</button>
</form>
</div>
</div>
<div class="eael-verification-msg" style="display: none;">
<p>License Verification code has been sent to this <span class="eael-customer-email"></span>. Please check your email for the code &amp; insert it below 👇</p>
<div class="short-description">
<b style="font-weight: 700;">Note: </b> Check out this <a href="https://essential-addons.com/docs/verify-essential-addons-pro-license-key/" target="_blank">guide</a> to
verify your license key. If you need any assistance with retrieving your License Verification Key, please <a href="https://wpdeveloper.com/support/"
target="_blank">contact support</a>.
</div>
<div class="eael-verification-input-container license__form__block">
<div class="eael-license-form-block">
<input type="text" id="<?php echo $this->args['item_slug']; ?>-license-otp" class="eael-form__control" placeholder="Enter Your Verification Code">
<button type="submit" class="eael-button button__themeColor">Verify</button>
</div>
<p>Havent received an email? Please hit this <a href="#" class="eael-otp-resend">"Resend"</a> to retry. Please note that this verification code will
expire after 15 minutes.</p>
</div>
</div>
<p class="eael-license-error-msg error-message" style="display: none;"></p>
</div>
<div class="eael-block p45 eael-activate__license__block --deactivation-form" style="display: <?php echo ( $status !== false && $status === 'valid' ) ? 'block' : 'none'; ?>">
<div class="eael-grid">
<div class="eael-col-md-6">
<ul class="eael-feature__list ls-none">
<li class="feature__item">
<span class="icon">
<img src="<?php echo EAEL_PRO_PLUGIN_URL . 'assets/admin/images/icon-auto-update.svg'; ?>" alt="essential-addons-auto-update">
</span>
<div class="content">
<h4>Premium Support</h4>
<p>Supported by professional and courteous staff.</p>
</div>
</li>
<li class="feature__item">
<span class="icon">
<img src="<?php echo EAEL_PRO_PLUGIN_URL . 'assets/admin/images/icon-auto-update.svg'; ?>" alt="essential-addons-auto-update">
</span>
<div class="content">
<h4>Auto Update</h4>
<p>Update the plugin right from your WordPress Dashboard.</p>
</div>
</li>
</ul>
</div>
<div class="eael-col-md-6">
<div class="license__form__block">
<div class="eael-license-form-block">
<form method="post" action="#">
<input class="eael-form__control regular-text" disabled type="text" value="<?php echo esc_attr( $hidden_license_key ); ?>"
placeholder="Place Your License Key and Activate"/>
<button type="submit" class="eael-button button__danger" name="license_deactivate">Deactivate</button>
</form>
</div>
</div>
</div>
</div>
</div>