first commit
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Base App
|
||||
*
|
||||
* Base app utility class that provides shared functionality of apps.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
abstract class App extends Module {
|
||||
|
||||
/**
|
||||
* Print config.
|
||||
*
|
||||
* Used to print the app and its components settings as a JavaScript object.
|
||||
*
|
||||
* @param string $handle Optional
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @since 2.6.0 added the `$handle` parameter
|
||||
* @access protected
|
||||
*/
|
||||
final protected function print_config( $handle = null ) {
|
||||
$name = $this->get_name();
|
||||
|
||||
$js_var = 'elementor' . str_replace( ' ', '', ucwords( str_replace( '-', ' ', $name ) ) ) . 'Config';
|
||||
|
||||
$config = $this->get_settings() + $this->get_components_config();
|
||||
|
||||
if ( ! $handle ) {
|
||||
$handle = 'elementor-' . $name;
|
||||
}
|
||||
|
||||
Utils::print_js_config( $handle, $js_var, $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get components config.
|
||||
*
|
||||
* Retrieves the app components settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access private
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_components_config() {
|
||||
$settings = [];
|
||||
|
||||
foreach ( $this->get_components() as $id => $instance ) {
|
||||
$settings[ $id ] = $instance->get_settings();
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base\BackgroundProcess;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/A5hleyRich/wp-background-processing GPL v2.0
|
||||
*
|
||||
* WP Async Request
|
||||
*
|
||||
* @package WP-Background-Processing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract WP_Async_Request class.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
abstract class WP_Async_Request {
|
||||
|
||||
/**
|
||||
* Prefix
|
||||
*
|
||||
* (default value: 'wp')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $prefix = 'wp';
|
||||
|
||||
/**
|
||||
* Action
|
||||
*
|
||||
* (default value: 'async_request')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $action = 'async_request';
|
||||
|
||||
/**
|
||||
* Identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected $identifier;
|
||||
|
||||
/**
|
||||
* Data
|
||||
*
|
||||
* (default value: array())
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Initiate new async request
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->identifier = $this->prefix . '_' . $this->action;
|
||||
|
||||
add_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) );
|
||||
add_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data used during the request
|
||||
*
|
||||
* @param array $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function data( $data ) {
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the async request
|
||||
*
|
||||
* @return array|\WP_Error
|
||||
*/
|
||||
public function dispatch() {
|
||||
$url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
|
||||
$args = $this->get_post_args();
|
||||
|
||||
return wp_remote_post( esc_url_raw( $url ), $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_query_args() {
|
||||
if ( property_exists( $this, 'query_args' ) ) {
|
||||
return $this->query_args;
|
||||
}
|
||||
|
||||
return array(
|
||||
'action' => $this->identifier,
|
||||
'nonce' => wp_create_nonce( $this->identifier ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_query_url() {
|
||||
if ( property_exists( $this, 'query_url' ) ) {
|
||||
return $this->query_url;
|
||||
}
|
||||
|
||||
return admin_url( 'admin-ajax.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_post_args() {
|
||||
if ( property_exists( $this, 'post_args' ) ) {
|
||||
return $this->post_args;
|
||||
}
|
||||
|
||||
return array(
|
||||
'timeout' => 0.01,
|
||||
'blocking' => false,
|
||||
'body' => $this->data,
|
||||
'cookies' => $_COOKIE,
|
||||
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe handle
|
||||
*
|
||||
* Check for correct nonce and pass to handler.
|
||||
*/
|
||||
public function maybe_handle() {
|
||||
// Don't lock up other requests while processing
|
||||
session_write_close();
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle
|
||||
*
|
||||
* Override this method to perform any actions required
|
||||
* during the async request.
|
||||
*/
|
||||
abstract protected function handle();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,517 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base\BackgroundProcess;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/A5hleyRich/wp-background-processing GPL v2.0
|
||||
*
|
||||
* WP Background Process
|
||||
*
|
||||
* @package WP-Background-Processing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract WP_Background_Process class.
|
||||
*
|
||||
* @abstract
|
||||
* @extends WP_Async_Request
|
||||
*/
|
||||
abstract class WP_Background_Process extends WP_Async_Request {
|
||||
|
||||
/**
|
||||
* Action
|
||||
*
|
||||
* (default value: 'background_process')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $action = 'background_process';
|
||||
|
||||
/**
|
||||
* Start time of current process.
|
||||
*
|
||||
* (default value: 0)
|
||||
*
|
||||
* @var int
|
||||
* @access protected
|
||||
*/
|
||||
protected $start_time = 0;
|
||||
|
||||
/**
|
||||
* Cron_hook_identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected $cron_hook_identifier;
|
||||
|
||||
/**
|
||||
* Cron_interval_identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected $cron_interval_identifier;
|
||||
|
||||
/**
|
||||
* Initiate new background process
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->cron_hook_identifier = $this->identifier . '_cron';
|
||||
$this->cron_interval_identifier = $this->identifier . '_cron_interval';
|
||||
|
||||
add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) );
|
||||
add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch
|
||||
*
|
||||
* @access public
|
||||
* @return array|\WP_Error
|
||||
*/
|
||||
public function dispatch() {
|
||||
// Schedule the cron healthcheck.
|
||||
$this->schedule_event();
|
||||
|
||||
// Perform remote post.
|
||||
return parent::dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Push to queue
|
||||
*
|
||||
* @param mixed $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function push_to_queue( $data ) {
|
||||
$this->data[] = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save queue
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function save() {
|
||||
$key = $this->generate_key();
|
||||
|
||||
if ( ! empty( $this->data ) ) {
|
||||
update_site_option( $key, $this->data );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update queue
|
||||
*
|
||||
* @param string $key Key.
|
||||
* @param array $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function update( $key, $data ) {
|
||||
if ( ! empty( $data ) ) {
|
||||
update_site_option( $key, $data );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete queue
|
||||
*
|
||||
* @param string $key Key.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function delete( $key ) {
|
||||
delete_site_option( $key );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate key
|
||||
*
|
||||
* Generates a unique key based on microtime. Queue items are
|
||||
* given a unique key so that they can be merged upon save.
|
||||
*
|
||||
* @param int $length Length.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_key( $length = 64 ) {
|
||||
$unique = md5( microtime() . rand() );
|
||||
$prepend = $this->identifier . '_batch_';
|
||||
|
||||
return substr( $prepend . $unique, 0, $length );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe process queue
|
||||
*
|
||||
* Checks whether data exists within the queue and that
|
||||
* the process is not already running.
|
||||
*/
|
||||
public function maybe_handle() {
|
||||
// Don't lock up other requests while processing
|
||||
session_write_close();
|
||||
|
||||
if ( $this->is_process_running() ) {
|
||||
// Background process already running.
|
||||
wp_die();
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
wp_die();
|
||||
}
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is queue empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_queue_empty() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$table = $wpdb->sitemeta;
|
||||
$column = 'meta_key';
|
||||
}
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
// Can't use placeholders for table/column names, it will be wrapped by a single quote (') instead of a backquote (`).
|
||||
$count = $wpdb->get_var( $wpdb->prepare( "
|
||||
SELECT COUNT(*)
|
||||
FROM {$table}
|
||||
WHERE {$column} LIKE %s
|
||||
", $key ) );
|
||||
// phpcs:enable
|
||||
|
||||
return ( $count > 0 ) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is process running
|
||||
*
|
||||
* Check whether the current process is already running
|
||||
* in a background process.
|
||||
*/
|
||||
protected function is_process_running() {
|
||||
if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
|
||||
// Process already running.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock process
|
||||
*
|
||||
* Lock the process so that multiple instances can't run simultaneously.
|
||||
* Override if applicable, but the duration should be greater than that
|
||||
* defined in the time_exceeded() method.
|
||||
*/
|
||||
protected function lock_process() {
|
||||
$this->start_time = time(); // Set start time of current process.
|
||||
|
||||
$lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute
|
||||
$lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
|
||||
|
||||
set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock process
|
||||
*
|
||||
* Unlock the process so that other instances can spawn.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function unlock_process() {
|
||||
delete_site_transient( $this->identifier . '_process_lock' );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get batch
|
||||
*
|
||||
* @return \stdClass Return the first batch from the queue
|
||||
*/
|
||||
protected function get_batch() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
$key_column = 'option_id';
|
||||
$value_column = 'option_value';
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$table = $wpdb->sitemeta;
|
||||
$column = 'meta_key';
|
||||
$key_column = 'meta_id';
|
||||
$value_column = 'meta_value';
|
||||
}
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
// Can't use placeholders for table/column names, it will be wrapped by a single quote (') instead of a backquote (`).
|
||||
$query = $wpdb->get_row( $wpdb->prepare( "
|
||||
SELECT *
|
||||
FROM {$table}
|
||||
WHERE {$column} LIKE %s
|
||||
ORDER BY {$key_column} ASC
|
||||
LIMIT 1
|
||||
", $key ) );
|
||||
// phpcs:enable
|
||||
|
||||
$batch = new \stdClass();
|
||||
$batch->key = $query->$column;
|
||||
$batch->data = maybe_unserialize( $query->$value_column );
|
||||
|
||||
return $batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle
|
||||
*
|
||||
* Pass each queue item to the task handler, while remaining
|
||||
* within server memory and time limit constraints.
|
||||
*/
|
||||
protected function handle() {
|
||||
$this->lock_process();
|
||||
|
||||
do {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
foreach ( $batch->data as $key => $value ) {
|
||||
$task = $this->task( $value );
|
||||
|
||||
if ( false !== $task ) {
|
||||
$batch->data[ $key ] = $task;
|
||||
} else {
|
||||
unset( $batch->data[ $key ] );
|
||||
}
|
||||
|
||||
if ( $this->time_exceeded() || $this->memory_exceeded() ) {
|
||||
// Batch limits reached.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update or delete current batch.
|
||||
if ( ! empty( $batch->data ) ) {
|
||||
$this->update( $batch->key, $batch->data );
|
||||
} else {
|
||||
$this->delete( $batch->key );
|
||||
}
|
||||
} while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
|
||||
|
||||
$this->unlock_process();
|
||||
|
||||
// Start next batch or complete process.
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$this->dispatch();
|
||||
} else {
|
||||
$this->complete();
|
||||
}
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory exceeded
|
||||
*
|
||||
* Ensures the batch process never exceeds 90%
|
||||
* of the maximum WordPress memory.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function memory_exceeded() {
|
||||
$memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory
|
||||
$current_memory = memory_get_usage( true );
|
||||
$return = false;
|
||||
|
||||
if ( $current_memory >= $memory_limit ) {
|
||||
$return = true;
|
||||
}
|
||||
|
||||
return apply_filters( $this->identifier . '_memory_exceeded', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory limit
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get_memory_limit() {
|
||||
if ( function_exists( 'ini_get' ) ) {
|
||||
$memory_limit = ini_get( 'memory_limit' );
|
||||
} else {
|
||||
// Sensible default.
|
||||
$memory_limit = '128M';
|
||||
}
|
||||
|
||||
if ( ! $memory_limit || -1 === intval( $memory_limit ) ) {
|
||||
// Unlimited, set to 32GB.
|
||||
$memory_limit = '32000M';
|
||||
}
|
||||
|
||||
return intval( $memory_limit ) * 1024 * 1024;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time exceeded.
|
||||
*
|
||||
* Ensures the batch never exceeds a sensible time limit.
|
||||
* A timeout limit of 30s is common on shared hosting.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function time_exceeded() {
|
||||
$finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds
|
||||
$return = false;
|
||||
|
||||
if ( time() >= $finish ) {
|
||||
$return = true;
|
||||
}
|
||||
|
||||
return apply_filters( $this->identifier . '_time_exceeded', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete.
|
||||
*
|
||||
* Override if applicable, but ensure that the below actions are
|
||||
* performed, or, call parent::complete().
|
||||
*/
|
||||
protected function complete() {
|
||||
// Unschedule the cron healthcheck.
|
||||
$this->clear_scheduled_event();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule cron healthcheck
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $schedules Schedules.
|
||||
* @return mixed
|
||||
*/
|
||||
public function schedule_cron_healthcheck( $schedules ) {
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', 5 );
|
||||
|
||||
if ( property_exists( $this, 'cron_interval' ) ) {
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval );
|
||||
}
|
||||
|
||||
// Adds every 5 minutes to the existing schedules.
|
||||
$schedules[ $this->identifier . '_cron_interval' ] = array(
|
||||
'interval' => MINUTE_IN_SECONDS * $interval,
|
||||
'display' => sprintf( __( 'Every %d Minutes', 'elementor' ), $interval ),
|
||||
);
|
||||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cron healthcheck
|
||||
*
|
||||
* Restart the background process if not already running
|
||||
* and data exists in the queue.
|
||||
*/
|
||||
public function handle_cron_healthcheck() {
|
||||
if ( $this->is_process_running() ) {
|
||||
// Background process already running.
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
$this->clear_scheduled_event();
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->handle();
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule event
|
||||
*/
|
||||
protected function schedule_event() {
|
||||
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
|
||||
wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear scheduled event
|
||||
*/
|
||||
protected function clear_scheduled_event() {
|
||||
$timestamp = wp_next_scheduled( $this->cron_hook_identifier );
|
||||
|
||||
if ( $timestamp ) {
|
||||
wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel Process
|
||||
*
|
||||
* Stop processing queue items, clear cronjob and delete batch.
|
||||
*
|
||||
*/
|
||||
public function cancel_process() {
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
$this->delete( $batch->key );
|
||||
|
||||
wp_clear_scheduled_hook( $this->cron_hook_identifier );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Task
|
||||
*
|
||||
* Override this method to perform any actions required on each
|
||||
* queue item. Return the modified item for further processing
|
||||
* in the next pass through. Or, return false to remove the
|
||||
* item from the queue.
|
||||
*
|
||||
* @param mixed $item Queue item to iterate over.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function task( $item );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Background_Task_Manager extends BaseModule {
|
||||
/**
|
||||
* @var Background_Task
|
||||
*/
|
||||
protected $task_runner;
|
||||
|
||||
abstract public function get_action();
|
||||
abstract public function get_plugin_name();
|
||||
abstract public function get_plugin_label();
|
||||
abstract public function get_task_runner_class();
|
||||
abstract public function get_query_limit();
|
||||
|
||||
abstract protected function start_run();
|
||||
|
||||
public function on_runner_start() {
|
||||
$logger = Plugin::$instance->logger->get_logger();
|
||||
$logger->info( $this->get_plugin_name() . '::' . $this->get_action() . ' Started' );
|
||||
}
|
||||
|
||||
public function on_runner_complete( $did_tasks = false ) {
|
||||
$logger = Plugin::$instance->logger->get_logger();
|
||||
$logger->info( $this->get_plugin_name() . '::' . $this->get_action() . ' Completed' );
|
||||
}
|
||||
|
||||
public function get_task_runner() {
|
||||
if ( empty( $this->task_runner ) ) {
|
||||
$class_name = $this->get_task_runner_class();
|
||||
$this->task_runner = new $class_name( $this );
|
||||
}
|
||||
|
||||
return $this->task_runner;
|
||||
}
|
||||
|
||||
// TODO: Replace with a db settings system.
|
||||
protected function add_flag( $flag ) {
|
||||
add_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag, 1 );
|
||||
}
|
||||
|
||||
protected function get_flag( $flag ) {
|
||||
return get_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag );
|
||||
}
|
||||
|
||||
protected function delete_flag( $flag ) {
|
||||
delete_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag );
|
||||
}
|
||||
|
||||
protected function get_start_action_url() {
|
||||
return wp_nonce_url( add_query_arg( $this->get_action(), 'run' ), $this->get_action() . 'run' );
|
||||
}
|
||||
|
||||
protected function get_continue_action_url() {
|
||||
return wp_nonce_url( add_query_arg( $this->get_action(), 'continue' ), $this->get_action() . 'continue' );
|
||||
}
|
||||
|
||||
private function continue_run() {
|
||||
$runner = $this->get_task_runner();
|
||||
$runner->continue_run();
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
if ( empty( $_GET[ $this->get_action() ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Plugin::$instance->init_common();
|
||||
|
||||
if ( 'run' === $_GET[ $this->get_action() ] && check_admin_referer( $this->get_action() . 'run' ) ) {
|
||||
$this->start_run();
|
||||
}
|
||||
|
||||
if ( 'continue' === $_GET[ $this->get_action() ] && check_admin_referer( $this->get_action() . 'continue' ) ) {
|
||||
$this->continue_run();
|
||||
}
|
||||
|
||||
wp_safe_redirect( remove_query_arg( [ $this->get_action(), '_wpnonce' ] ) );
|
||||
die;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Core\Base\BackgroundProcess\WP_Background_Process;
|
||||
|
||||
/**
|
||||
* Based on https://github.com/woocommerce/woocommerce/blob/master/includes/abstracts/class-wc-background-process.php
|
||||
* & https://github.com/woocommerce/woocommerce/blob/master/includes/class-wc-background-updater.php
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* WC_Background_Process class.
|
||||
*/
|
||||
abstract class Background_Task extends WP_Background_Process {
|
||||
protected $current_item;
|
||||
|
||||
/**
|
||||
* Dispatch updater.
|
||||
*
|
||||
* Updater will still run via cron job if this fails for any reason.
|
||||
*/
|
||||
public function dispatch() {
|
||||
$dispatched = parent::dispatch();
|
||||
|
||||
if ( is_wp_error( $dispatched ) ) {
|
||||
wp_die( $dispatched );
|
||||
}
|
||||
}
|
||||
|
||||
public function query_col( $sql ) {
|
||||
global $wpdb;
|
||||
|
||||
// Add Calc.
|
||||
$item = $this->get_current_item();
|
||||
if ( empty( $item['total'] ) ) {
|
||||
$sql = preg_replace( '/^SELECT/', 'SELECT SQL_CALC_FOUND_ROWS', $sql );
|
||||
}
|
||||
|
||||
// Add offset & limit.
|
||||
$sql = preg_replace( '/;$/', '', $sql );
|
||||
$sql .= ' LIMIT %d, %d;';
|
||||
|
||||
$results = $wpdb->get_col( $wpdb->prepare( $sql, $this->get_current_offset(), $this->get_limit() ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
if ( ! empty( $results ) ) {
|
||||
$this->set_total();
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function should_run_again( $updated_rows ) {
|
||||
return count( $updated_rows ) === $this->get_limit();
|
||||
}
|
||||
|
||||
public function get_current_offset() {
|
||||
$limit = $this->get_limit();
|
||||
return ( $this->current_item['iterate_num'] - 1 ) * $limit;
|
||||
}
|
||||
|
||||
public function get_limit() {
|
||||
return $this->manager->get_query_limit();
|
||||
}
|
||||
|
||||
public function set_total() {
|
||||
global $wpdb;
|
||||
|
||||
if ( empty( $this->current_item['total'] ) ) {
|
||||
$total_rows = $wpdb->get_var( 'SELECT FOUND_ROWS();' );
|
||||
$total_iterates = ceil( $total_rows / $this->get_limit() );
|
||||
$this->current_item['total'] = $total_iterates;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete
|
||||
*
|
||||
* Override if applicable, but ensure that the below actions are
|
||||
* performed, or, call parent::complete().
|
||||
*/
|
||||
protected function complete() {
|
||||
$this->manager->on_runner_complete( true );
|
||||
|
||||
parent::complete();
|
||||
}
|
||||
|
||||
public function continue_run() {
|
||||
// Used to fire an action added in WP_Background_Process::_construct() that calls WP_Background_Process::handle_cron_healthcheck().
|
||||
// This method will make sure the database updates are executed even if cron is disabled. Nothing will happen if the updates are already running.
|
||||
do_action( $this->cron_hook_identifier );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_current_item() {
|
||||
return $this->current_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get batch.
|
||||
*
|
||||
* @return \stdClass Return the first batch from the queue.
|
||||
*/
|
||||
protected function get_batch() {
|
||||
$batch = parent::get_batch();
|
||||
$batch->data = array_filter( (array) $batch->data );
|
||||
|
||||
return $batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cron healthcheck
|
||||
*
|
||||
* Restart the background process if not already running
|
||||
* and data exists in the queue.
|
||||
*/
|
||||
public function handle_cron_healthcheck() {
|
||||
if ( $this->is_process_running() ) {
|
||||
// Background process already running.
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
$this->clear_scheduled_event();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule fallback event.
|
||||
*/
|
||||
protected function schedule_event() {
|
||||
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
|
||||
wp_schedule_event( time() + 10, $this->cron_interval_identifier, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the updater running?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_running() {
|
||||
return false === $this->is_queue_empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* See if the batch limit has been exceeded.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function batch_limit_exceeded() {
|
||||
return $this->time_exceeded() || $this->memory_exceeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle.
|
||||
*
|
||||
* Pass each queue item to the task handler, while remaining
|
||||
* within server memory and time limit constraints.
|
||||
*/
|
||||
protected function handle() {
|
||||
$this->manager->on_runner_start();
|
||||
|
||||
$this->lock_process();
|
||||
|
||||
do {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
foreach ( $batch->data as $key => $value ) {
|
||||
$task = $this->task( $value );
|
||||
|
||||
if ( false !== $task ) {
|
||||
$batch->data[ $key ] = $task;
|
||||
} else {
|
||||
unset( $batch->data[ $key ] );
|
||||
}
|
||||
|
||||
if ( $this->batch_limit_exceeded() ) {
|
||||
// Batch limits reached.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update or delete current batch.
|
||||
if ( ! empty( $batch->data ) ) {
|
||||
$this->update( $batch->key, $batch->data );
|
||||
} else {
|
||||
$this->delete( $batch->key );
|
||||
}
|
||||
} while ( ! $this->batch_limit_exceeded() && ! $this->is_queue_empty() );
|
||||
|
||||
$this->unlock_process();
|
||||
|
||||
// Start next batch or complete process.
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$this->dispatch();
|
||||
} else {
|
||||
$this->complete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the protected `is_process_running` method as a public method.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_process_locked() {
|
||||
return $this->is_process_running();
|
||||
}
|
||||
|
||||
public function handle_immediately( $callbacks ) {
|
||||
$this->manager->on_runner_start();
|
||||
|
||||
$this->lock_process();
|
||||
|
||||
foreach ( $callbacks as $callback ) {
|
||||
$item = [
|
||||
'callback' => $callback,
|
||||
];
|
||||
|
||||
do {
|
||||
$item = $this->task( $item );
|
||||
} while ( $item );
|
||||
}
|
||||
|
||||
$this->unlock_process();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task
|
||||
*
|
||||
* Override this method to perform any actions required on each
|
||||
* queue item. Return the modified item for further processing
|
||||
* in the next pass through. Or, return false to remove the
|
||||
* item from the queue.
|
||||
*
|
||||
* @param array $item
|
||||
*
|
||||
* @return array|bool
|
||||
*/
|
||||
protected function task( $item ) {
|
||||
$result = false;
|
||||
|
||||
if ( ! isset( $item['iterate_num'] ) ) {
|
||||
$item['iterate_num'] = 1;
|
||||
}
|
||||
|
||||
$logger = Plugin::$instance->logger->get_logger();
|
||||
$callback = $this->format_callback_log( $item );
|
||||
|
||||
if ( is_callable( $item['callback'] ) ) {
|
||||
$progress = '';
|
||||
|
||||
if ( 1 < $item['iterate_num'] ) {
|
||||
if ( empty( $item['total'] ) ) {
|
||||
$progress = sprintf( '(x%s)', $item['iterate_num'] );
|
||||
} else {
|
||||
$percent = ceil( $item['iterate_num'] / ( $item['total'] / 100 ) );
|
||||
$progress = sprintf( '(%s of %s, %s%%)', $item['iterate_num'], $item['total'], $percent );
|
||||
}
|
||||
}
|
||||
|
||||
$logger->info( sprintf( '%s Start %s', $callback, $progress ) );
|
||||
|
||||
$this->current_item = $item;
|
||||
|
||||
$result = (bool) call_user_func( $item['callback'], $this );
|
||||
|
||||
// get back the updated item.
|
||||
$item = $this->current_item;
|
||||
$this->current_item = null;
|
||||
|
||||
if ( $result ) {
|
||||
if ( empty( $item['total'] ) ) {
|
||||
$logger->info( sprintf( '%s callback needs to run again', $callback ) );
|
||||
} elseif ( 1 === $item['iterate_num'] ) {
|
||||
$logger->info( sprintf( '%s callback needs to run more %d times', $callback, $item['total'] - $item['iterate_num'] ) );
|
||||
}
|
||||
|
||||
$item['iterate_num']++;
|
||||
} else {
|
||||
$logger->info( sprintf( '%s Finished', $callback ) );
|
||||
}
|
||||
} else {
|
||||
$logger->notice( sprintf( 'Could not find %s callback', $callback ) );
|
||||
}
|
||||
|
||||
return $result ? $item : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule cron healthcheck.
|
||||
*
|
||||
* @param array $schedules Schedules.
|
||||
* @return array
|
||||
*/
|
||||
public function schedule_cron_healthcheck( $schedules ) {
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', 5 );
|
||||
|
||||
// Adds every 5 minutes to the existing schedules.
|
||||
$schedules[ $this->identifier . '_cron_interval' ] = array(
|
||||
'interval' => MINUTE_IN_SECONDS * $interval,
|
||||
/* translators: %d: interval */
|
||||
'display' => sprintf( __( 'Every %d minutes', 'elementor' ), $interval ),
|
||||
);
|
||||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* See if the batch limit has been exceeded.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_memory_exceeded() {
|
||||
return $this->memory_exceeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all batches.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function delete_all_batches() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$table = $wpdb->sitemeta;
|
||||
$column = 'meta_key';
|
||||
}
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
$wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE {$column} LIKE %s", $key ) ); // @codingStandardsIgnoreLine.
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill process.
|
||||
*
|
||||
* Stop processing queue items, clear cronjob and delete all batches.
|
||||
*/
|
||||
public function kill_process() {
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$this->delete_all_batches();
|
||||
wp_clear_scheduled_hook( $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
public function set_current_item( $item ) {
|
||||
$this->current_item = $item;
|
||||
}
|
||||
|
||||
protected function format_callback_log( $item ) {
|
||||
return implode( '::', (array) $item['callback'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @var \Elementor\Core\Base\Background_Task_Manager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
public function __construct( $manager ) {
|
||||
$this->manager = $manager;
|
||||
// Uses unique prefix per blog so each blog has separate queue.
|
||||
$this->prefix = 'elementor_' . get_current_blog_id();
|
||||
$this->action = $this->manager->get_action();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Base Object
|
||||
*
|
||||
* Base class that provides basic settings handling functionality.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
class Base_Object {
|
||||
|
||||
/**
|
||||
* Settings.
|
||||
*
|
||||
* Holds the object settings.
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* Get Settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $setting Optional. The key of the requested setting. Default is null.
|
||||
*
|
||||
* @return mixed An array of all settings, or a single value if `$setting` was specified.
|
||||
*/
|
||||
final public function get_settings( $setting = null ) {
|
||||
$this->ensure_settings();
|
||||
|
||||
return self::get_items( $this->settings, $setting );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array|string $key If key is an array, the settings are overwritten by that array. Otherwise, the
|
||||
* settings of the key will be set to the given `$value` param.
|
||||
*
|
||||
* @param mixed $value Optional. Default is null.
|
||||
*/
|
||||
final public function set_settings( $key, $value = null ) {
|
||||
$this->ensure_settings();
|
||||
|
||||
if ( is_array( $key ) ) {
|
||||
$this->settings = $key;
|
||||
} else {
|
||||
$this->settings[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete setting.
|
||||
*
|
||||
* Deletes the settings array or a specific key of the settings array if `$key` is specified.
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $key Optional. Default is null.
|
||||
*/
|
||||
public function delete_setting( $key = null ) {
|
||||
if ( $key ) {
|
||||
unset( $this->settings[ $key ] );
|
||||
} else {
|
||||
$this->settings = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items.
|
||||
*
|
||||
* Utility method that receives an array with a needle and returns all the
|
||||
* items that match the needle. If needle is not defined the entire haystack
|
||||
* will be returned.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
* @static
|
||||
*
|
||||
* @param array $haystack An array of items.
|
||||
* @param string $needle Optional. Needle. Default is null.
|
||||
*
|
||||
* @return mixed The whole haystack or the needle from the haystack when requested.
|
||||
*/
|
||||
final protected static function get_items( array $haystack, $needle = null ) {
|
||||
if ( $needle ) {
|
||||
return isset( $haystack[ $needle ] ) ? $haystack[ $needle ] : null;
|
||||
}
|
||||
|
||||
return $haystack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get init settings.
|
||||
*
|
||||
* Used to define the default/initial settings of the object. Inheriting classes may implement this method to define
|
||||
* their own default/initial settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_init_settings() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure settings.
|
||||
*
|
||||
* Ensures that the `$settings` member is initialized
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access private
|
||||
*/
|
||||
private function ensure_settings() {
|
||||
if ( null === $this->settings ) {
|
||||
$this->settings = $this->get_init_settings();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class DB_Upgrades_Manager extends Background_Task_Manager {
|
||||
protected $current_version = null;
|
||||
protected $query_limit = 100;
|
||||
|
||||
abstract public function get_new_version();
|
||||
abstract public function get_version_option_name();
|
||||
abstract public function get_upgrades_class();
|
||||
abstract public function get_updater_label();
|
||||
|
||||
public function get_task_runner_class() {
|
||||
return 'Elementor\Core\Upgrade\Updater';
|
||||
}
|
||||
|
||||
public function get_query_limit() {
|
||||
return $this->query_limit;
|
||||
}
|
||||
|
||||
public function set_query_limit( $limit ) {
|
||||
$this->query_limit = $limit;
|
||||
}
|
||||
|
||||
public function get_current_version() {
|
||||
if ( null === $this->current_version ) {
|
||||
$this->current_version = get_option( $this->get_version_option_name() );
|
||||
}
|
||||
|
||||
return $this->current_version;
|
||||
}
|
||||
|
||||
public function should_upgrade() {
|
||||
$current_version = $this->get_current_version();
|
||||
|
||||
// It's a new install.
|
||||
if ( ! $current_version ) {
|
||||
$this->update_db_version();
|
||||
return false;
|
||||
}
|
||||
|
||||
return version_compare( $this->get_new_version(), $current_version, '>' );
|
||||
}
|
||||
|
||||
public function on_runner_start() {
|
||||
parent::on_runner_start();
|
||||
|
||||
define( 'IS_ELEMENTOR_UPGRADE', true );
|
||||
}
|
||||
|
||||
public function on_runner_complete( $did_tasks = false ) {
|
||||
$logger = Plugin::$instance->logger->get_logger();
|
||||
|
||||
$logger->info( 'Elementor data updater process has been completed.', [
|
||||
'meta' => [
|
||||
'plugin' => $this->get_plugin_label(),
|
||||
'from' => $this->current_version,
|
||||
'to' => $this->get_new_version(),
|
||||
],
|
||||
] );
|
||||
|
||||
Plugin::$instance->files_manager->clear_cache();
|
||||
|
||||
$this->update_db_version();
|
||||
|
||||
if ( $did_tasks ) {
|
||||
$this->add_flag( 'completed' );
|
||||
}
|
||||
}
|
||||
|
||||
public function admin_notice_start_upgrade() {
|
||||
$upgrade_link = $this->get_start_action_url();
|
||||
$message = '<p>' . sprintf( __( '%s Your site database needs to be updated to the latest version.', 'elementor' ), $this->get_updater_label() ) . '</p>';
|
||||
$message .= '<p>' . sprintf( '<a href="%s" class="button-primary">%s</a>', $upgrade_link, __( 'Update Now', 'elementor' ) ) . '</p>';
|
||||
|
||||
echo '<div class="notice notice-error is-dismissible">' . $message . '</div>';
|
||||
}
|
||||
|
||||
public function admin_notice_upgrade_is_running() {
|
||||
$upgrade_link = $this->get_continue_action_url();
|
||||
$message = '<p>' . sprintf( __( '%s Database update process is running in the background.', 'elementor' ), $this->get_updater_label() ) . '</p>';
|
||||
$message .= '<p>' . __( 'Taking a while?', 'elementor' ) . ' <a href="' . $upgrade_link . '" class="button-primary">' . __( 'Click here to run it now', 'elementor' ) . '</a></p>';
|
||||
|
||||
echo '<div class="notice notice-warning is-dismissible">' . $message . '</div>';
|
||||
}
|
||||
|
||||
public function admin_notice_upgrade_is_completed() {
|
||||
$this->delete_flag( 'completed' );
|
||||
|
||||
$message = '<p>' . sprintf( __( '%s The database update process is now complete. Thank you for updating to the latest version!', 'elementor' ), $this->get_updater_label() ) . '</p>';
|
||||
|
||||
echo '<div class="notice notice-success is-dismissible">' . $message . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @access protected
|
||||
*/
|
||||
protected function start_run() {
|
||||
$updater = $this->get_task_runner();
|
||||
|
||||
if ( $updater->is_running() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$upgrade_callbacks = $this->get_upgrade_callbacks();
|
||||
|
||||
if ( empty( $upgrade_callbacks ) ) {
|
||||
$this->on_runner_complete();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $upgrade_callbacks as $callback ) {
|
||||
$updater->push_to_queue( [
|
||||
'callback' => $callback,
|
||||
] );
|
||||
}
|
||||
|
||||
$updater->save()->dispatch();
|
||||
|
||||
Plugin::$instance->logger->get_logger()->info( 'Elementor data updater process has been queued.', [
|
||||
'meta' => [
|
||||
'plugin' => $this->get_plugin_label(),
|
||||
'from' => $this->current_version,
|
||||
'to' => $this->get_new_version(),
|
||||
],
|
||||
] );
|
||||
}
|
||||
|
||||
protected function update_db_version() {
|
||||
update_option( $this->get_version_option_name(), $this->get_new_version() );
|
||||
}
|
||||
|
||||
public function get_upgrade_callbacks() {
|
||||
$prefix = '_v_';
|
||||
$upgrades_class = $this->get_upgrades_class();
|
||||
$upgrades_reflection = new \ReflectionClass( $upgrades_class );
|
||||
|
||||
$callbacks = [];
|
||||
|
||||
foreach ( $upgrades_reflection->getMethods() as $method ) {
|
||||
$method_name = $method->getName();
|
||||
if ( false === strpos( $method_name, $prefix ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! preg_match_all( "/$prefix(\d+_\d+_\d+)/", $method_name, $matches ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$method_version = str_replace( '_', '.', $matches[1][0] );
|
||||
|
||||
if ( ! version_compare( $method_version, $this->current_version, '>' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$callbacks[] = [ $upgrades_class, $method_name ];
|
||||
}
|
||||
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
// If upgrade is completed - show the notice only for admins.
|
||||
// Note: in this case `should_upgrade` returns false, because it's already upgraded.
|
||||
if ( is_admin() && current_user_can( 'update_plugins' ) && $this->get_flag( 'completed' ) ) {
|
||||
add_action( 'admin_notices', [ $this, 'admin_notice_upgrade_is_completed' ] );
|
||||
}
|
||||
|
||||
if ( ! $this->should_upgrade() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$updater = $this->get_task_runner();
|
||||
|
||||
$this->start_run();
|
||||
|
||||
if ( $updater->is_running() && current_user_can( 'update_plugins' ) ) {
|
||||
add_action( 'admin_notices', [ $this, 'admin_notice_upgrade_is_running' ] );
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor module.
|
||||
*
|
||||
* An abstract class that provides the needed properties and methods to
|
||||
* manage and handle modules in inheriting classes.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @abstract
|
||||
*/
|
||||
abstract class Module extends Base_Object {
|
||||
|
||||
/**
|
||||
* Module class reflection.
|
||||
*
|
||||
* Holds the information about a class.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access private
|
||||
*
|
||||
* @var \ReflectionClass
|
||||
*/
|
||||
private $reflection;
|
||||
|
||||
/**
|
||||
* Module components.
|
||||
*
|
||||
* Holds the module components.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access private
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $components = [];
|
||||
|
||||
/**
|
||||
* Module instance.
|
||||
*
|
||||
* Holds the module instance.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access protected
|
||||
*
|
||||
* @var Module
|
||||
*/
|
||||
protected static $_instances = [];
|
||||
|
||||
/**
|
||||
* Get module name.
|
||||
*
|
||||
* Retrieve the module name.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
* @abstract
|
||||
*
|
||||
* @return string Module name.
|
||||
*/
|
||||
abstract public function get_name();
|
||||
|
||||
/**
|
||||
* Instance.
|
||||
*
|
||||
* Ensures only one instance of the module class is loaded or can be loaded.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
* @static
|
||||
*
|
||||
* @return Module An instance of the class.
|
||||
*/
|
||||
public static function instance() {
|
||||
$class_name = static::class_name();
|
||||
|
||||
if ( empty( static::$_instances[ $class_name ] ) ) {
|
||||
static::$_instances[ $class_name ] = new static();
|
||||
}
|
||||
|
||||
return static::$_instances[ $class_name ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function is_active() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class name.
|
||||
*
|
||||
* Retrieve the name of the class.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function class_name() {
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone.
|
||||
*
|
||||
* Disable class cloning and throw an error on object clone.
|
||||
*
|
||||
* The whole idea of the singleton design pattern is that there is a single
|
||||
* object. Therefore, we don't want the object to be cloned.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*/
|
||||
public function __clone() {
|
||||
// Cloning instances of the class is forbidden
|
||||
_doing_it_wrong( __FUNCTION__, esc_html__( 'Something went wrong.', 'elementor' ), '1.0.0' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wakeup.
|
||||
*
|
||||
* Disable unserializing of the class.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*/
|
||||
public function __wakeup() {
|
||||
// Unserializing instances of the class is forbidden
|
||||
_doing_it_wrong( __FUNCTION__, esc_html__( 'Something went wrong.', 'elementor' ), '1.0.0' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_reflection() {
|
||||
if ( null === $this->reflection ) {
|
||||
$this->reflection = new \ReflectionClass( $this );
|
||||
}
|
||||
|
||||
return $this->reflection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add module component.
|
||||
*
|
||||
* Add new component to the current module.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $id Component ID.
|
||||
* @param mixed $instance An instance of the component.
|
||||
*/
|
||||
public function add_component( $id, $instance ) {
|
||||
$this->components[ $id ] = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
* @return Module[]
|
||||
*/
|
||||
public function get_components() {
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module component.
|
||||
*
|
||||
* Retrieve the module component.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $id Component ID.
|
||||
*
|
||||
* @return mixed An instance of the component, or `false` if the component
|
||||
* doesn't exist.
|
||||
*/
|
||||
public function get_component( $id ) {
|
||||
if ( isset( $this->components[ $id ] ) ) {
|
||||
return $this->components[ $id ];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assets url.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @param string $file_name
|
||||
* @param string $file_extension
|
||||
* @param string $relative_url Optional. Default is null.
|
||||
* @param string $add_min_suffix Optional. Default is 'default'.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final protected function get_assets_url( $file_name, $file_extension, $relative_url = null, $add_min_suffix = 'default' ) {
|
||||
static $is_test_mode = null;
|
||||
|
||||
if ( null === $is_test_mode ) {
|
||||
$is_test_mode = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || defined( 'ELEMENTOR_TESTS' ) && ELEMENTOR_TESTS;
|
||||
}
|
||||
|
||||
if ( ! $relative_url ) {
|
||||
$relative_url = $this->get_assets_relative_url() . $file_extension . '/';
|
||||
}
|
||||
|
||||
$url = $this->get_assets_base_url() . $relative_url . $file_name;
|
||||
|
||||
if ( 'default' === $add_min_suffix ) {
|
||||
$add_min_suffix = ! $is_test_mode;
|
||||
}
|
||||
|
||||
if ( $add_min_suffix ) {
|
||||
$url .= '.min';
|
||||
}
|
||||
|
||||
return $url . '.' . $file_extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get js assets url
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @param string $file_name
|
||||
* @param string $relative_url Optional. Default is null.
|
||||
* @param string $add_min_suffix Optional. Default is 'default'.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final protected function get_js_assets_url( $file_name, $relative_url = null, $add_min_suffix = 'default' ) {
|
||||
return $this->get_assets_url( $file_name, 'js', $relative_url, $add_min_suffix );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get css assets url
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @param string $file_name
|
||||
* @param string $relative_url Optional. Default is null.
|
||||
* @param string $add_min_suffix Optional. Default is 'default'.
|
||||
* @param bool $add_direction_suffix Optional. Default is `false`
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final protected function get_css_assets_url( $file_name, $relative_url = null, $add_min_suffix = 'default', $add_direction_suffix = false ) {
|
||||
static $direction_suffix = null;
|
||||
|
||||
if ( ! $direction_suffix ) {
|
||||
$direction_suffix = is_rtl() ? '-rtl' : '';
|
||||
}
|
||||
|
||||
if ( $add_direction_suffix ) {
|
||||
$file_name .= $direction_suffix;
|
||||
}
|
||||
|
||||
return $this->get_assets_url( $file_name, 'css', $relative_url, $add_min_suffix );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assets base url
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @access protected
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_assets_base_url() {
|
||||
return ELEMENTOR_URL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assets relative url
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_assets_relative_url() {
|
||||
return 'assets/';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user