update
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* Abstract Class PPWP Background Task Manager
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class PPW_Background_Task_Manager extends PPW_Module {
|
||||
|
||||
/**
|
||||
* @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 __construct() {
|
||||
if ( empty( $_GET[ $this->get_action() ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
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(
|
||||
array(
|
||||
$this->get_action(),
|
||||
'_wpnonce',
|
||||
)
|
||||
)
|
||||
);
|
||||
die;
|
||||
}
|
||||
|
||||
public function on_runner_start() {
|
||||
// Implement logger here
|
||||
}
|
||||
|
||||
public function on_runner_complete( $did_tasks = false ) {
|
||||
// Implement logger here
|
||||
$this->add_flag( '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;
|
||||
}
|
||||
|
||||
protected function add_flag( $flag ) {
|
||||
update_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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
/**
|
||||
* Abstract Class PPWP Background Task
|
||||
*/
|
||||
defined ( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! class_exists( 'WP_Async_Request', false ) ) {
|
||||
include_once PPW_DIR_PATH . '/includes/libs/wp-background-process/wp-async-request.php';
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'WP_Background_Process', false ) ) {
|
||||
include_once PPW_DIR_PATH . '/includes/libs/wp-background-process/wp-background-process.php';
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'PPW_Pro_Background_Task' ) ) {
|
||||
/**
|
||||
* Class PPW_Background_Task
|
||||
*/
|
||||
abstract class PPW_Background_Task extends WP_Background_Process {
|
||||
protected $current_item;
|
||||
|
||||
protected $manager;
|
||||
|
||||
public function __construct( $manager ) {
|
||||
$this->manager = $manager;
|
||||
$this->prefix = 'ppw_' . get_current_blog_id();
|
||||
$this->action = $this->manager->get_action();
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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( ppw_escape_wp_error( $dispatched ) ); // phpcs:ignores -- we already escape the wp_error
|
||||
}
|
||||
}
|
||||
|
||||
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 -- WPCS: unprepared SQL OK.
|
||||
|
||||
if ( ! empty( $results ) ) {
|
||||
$this->set_total();
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_current_item() {
|
||||
error_log( 'Current item: ' . wp_json_encode( $this->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();
|
||||
|
||||
if (is_object($batch) && isset($batch->data)) {
|
||||
$batch->data = array_filter((array) $batch->data);
|
||||
} else {
|
||||
$batch = new stdClass();
|
||||
}
|
||||
|
||||
return $batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
// }
|
||||
|
||||
|
||||
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();' ); // phpcs:ignore -- WPCS: db call ok. cache ok.
|
||||
$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 );
|
||||
}
|
||||
|
||||
protected function task( $item ) {
|
||||
$result = false;
|
||||
|
||||
if ( is_callable( $item['callback'] ) ) {
|
||||
$result = (bool) call_user_func( $item['callback'], $this );
|
||||
}
|
||||
|
||||
return $result ? $item : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is running?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_running() {
|
||||
return false === $this->is_queue_empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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', 'password-protect-page' ), $interval ),
|
||||
);
|
||||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cron healthcheck
|
||||
*
|
||||
* Restart the background process if not already running
|
||||
* and data exists in the queue.
|
||||
*/
|
||||
public function handle_cron_healthcheck() {
|
||||
error_log( 'Checking health-check: ' . wp_json_encode( 'Hello World' ) );
|
||||
if ( $this->is_process_running() ) {
|
||||
error_log( 'PPWP - Background process is running' );
|
||||
// Background process already running.
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
$this->clear_scheduled_event();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedele Event
|
||||
*/
|
||||
protected function schedule_event() {
|
||||
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
|
||||
wp_schedule_event( time() + 20, $this->cron_interval_identifier, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/**
|
||||
* Abstract Class PPWP Migration Manager
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'PPW_Migration_Manager' ) ) {
|
||||
abstract class PPW_Migration_Manager extends PPW_Background_Task_Manager {
|
||||
|
||||
abstract public function get_migrations_class();
|
||||
|
||||
abstract public function get_migration_label();
|
||||
|
||||
abstract public function get_success_message();
|
||||
|
||||
public function __construct() {
|
||||
$this->handle_admin_notices();
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin Notice Handling
|
||||
*/
|
||||
protected function handle_admin_notices() {
|
||||
$action = 'admin_notices';
|
||||
|
||||
if ( is_admin() && $this->get_flag( 'completed' ) ) {
|
||||
add_action( $action, array( $this, 'admin_notice_upgrade_is_completed' ) );
|
||||
}
|
||||
|
||||
$migration = $this->get_task_runner();
|
||||
|
||||
if ( $migration->is_running() ) {
|
||||
add_action( $action, array( $this, 'admin_notice_upgrade_is_running' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PPW_Migration
|
||||
*/
|
||||
public function get_task_runner_class() {
|
||||
return 'PPW_Migration';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Query Limit
|
||||
*/
|
||||
public function get_query_limit() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Boolian value of Migration event
|
||||
*/
|
||||
public function should_migrate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runner Complete
|
||||
* @param boolian $did_task Deafult is False
|
||||
*/
|
||||
public function on_runner_complete( $did_tasks = false ) {
|
||||
// Implement log here
|
||||
if ( $did_tasks ) {
|
||||
$this->add_flag( 'completed' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Run
|
||||
*/
|
||||
public function start_run() {
|
||||
$updater = $this->get_task_runner();
|
||||
|
||||
if ( $updater->is_running() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$upgrade_callbacks = $this->get_migration_callbacks();
|
||||
|
||||
if ( empty( $upgrade_callbacks ) ) {
|
||||
$this->on_runner_complete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $upgrade_callbacks as $callback ) {
|
||||
$updater->push_to_queue(
|
||||
array(
|
||||
'callback' => $callback,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$updater->save()->dispatch();
|
||||
|
||||
// Use log here
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration Callback
|
||||
* @return array $callbacks Callbacks
|
||||
*/
|
||||
public function get_migration_callbacks() {
|
||||
$prefix = 'migrate_v_';
|
||||
$migrations_class = $this->get_migrations_class();
|
||||
$migration_reflection = new ReflectionClass( $migrations_class );
|
||||
$callbacks = array();
|
||||
|
||||
$methods = $migration_reflection->getMethods();
|
||||
foreach ( $methods 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, PPW_VERSION, '>' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$callbacks[] = array( $migrations_class, $method_name );
|
||||
}
|
||||
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin Notice
|
||||
*/
|
||||
public function admin_notice_upgrade_is_running() {
|
||||
$upgrade_link = $this->get_continue_action_url();
|
||||
$message = '<p>' . sprintf( __( '%s To keep password protecting your private content, we have to <a href="https://passwordprotectwp.com/password-migration/" target="_blank" rel="noopener noreferrer">migrate your passwords</a> to our plugin. The migration process is running in the background.', 'password-protect-page' ), $this->get_updater_label() ) . '</p>';
|
||||
$message .= '<p>' . sprintf( 'Taking a while? <a href="%s" class="button-primary">Click here to run it now</a>', $upgrade_link ) . '</p>';
|
||||
echo '<div class="notice notice-warning">' . $message . '</div>'; // phpcs:ignore -- we don’t need to escape for this
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin Notice success message
|
||||
*/
|
||||
public function admin_notice_upgrade_is_completed() {
|
||||
$this->delete_flag( 'completed' );
|
||||
$message = $this->get_success_message();
|
||||
if ( ! empty( $message ) ) {
|
||||
echo '<div class="notice notice-success">' . $message . '</div>'; // phpcs:ignore -- we don’t need to escape for this
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Abstract Class for PPWP Module
|
||||
*/
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'PPW_Module' ) ) {
|
||||
abstract class PPW_Module {
|
||||
/**
|
||||
* 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 ];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user