Plugin Updates
This commit is contained in:
@@ -122,7 +122,7 @@ class Main extends \Imagify_AS3CF_Deprecated {
|
||||
* WebP images to display with a <picture> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see \Imagify\Webp\Picture\Display->process_image()
|
||||
* @see \Imagify\Picture\Display->process_image()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $data An array of data for this image.
|
||||
@@ -443,7 +443,7 @@ class Main extends \Imagify_AS3CF_Deprecated {
|
||||
}
|
||||
|
||||
foreach ( $paths as $size_name => $file_path ) {
|
||||
if ( 'thumb' === $size_name || 'backup' === $size_name || $process->is_size_webp( $size_name ) ) {
|
||||
if ( 'thumb' === $size_name || 'backup' === $size_name || $process->is_size_next_gen( $size_name ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,10 +100,11 @@ class NGG extends AbstractBulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ids of all optimized media without WebP versions.
|
||||
* Get ids of all optimized media without next-gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.5 The method doesn't return the IDs directly anymore.
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $format Format we are looking for. (webp|avif).
|
||||
*
|
||||
* @return array {
|
||||
* @type array $ids A list of media IDs.
|
||||
@@ -113,7 +114,7 @@ class NGG extends AbstractBulk {
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimized_media_ids_without_webp() {
|
||||
public function get_optimized_media_ids_without_format( $format ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->set_no_time_limit();
|
||||
@@ -121,7 +122,12 @@ class NGG extends AbstractBulk {
|
||||
$storage = C_Gallery_Storage::get_instance();
|
||||
$ngg_table = $wpdb->prefix . 'ngg_pictures';
|
||||
$data_table = DB::get_instance()->get_table_name();
|
||||
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::WEBP_SUFFIX' );
|
||||
$suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::WEBP_SUFFIX' );
|
||||
|
||||
if ( get_imagify_option( 'convert_to_avif' ) ) {
|
||||
$suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::AVIF_SUFFIX' );
|
||||
}
|
||||
|
||||
$files = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok.
|
||||
"
|
||||
SELECT ngg.pid
|
||||
@@ -132,7 +138,7 @@ class NGG extends AbstractBulk {
|
||||
( data.status = 'success' OR data.status = 'already_optimized' )
|
||||
AND data.data NOT LIKE %s
|
||||
ORDER BY ngg.pid DESC",
|
||||
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
'%' . $wpdb->esc_like( $suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
) );
|
||||
|
||||
$wpdb->flush();
|
||||
@@ -175,18 +181,22 @@ class NGG extends AbstractBulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if there are optimized media without WebP versions.
|
||||
* Tell if there are optimized media without next-gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 2.2
|
||||
*
|
||||
* @return int The number of media.
|
||||
*/
|
||||
public function has_optimized_media_without_webp() {
|
||||
public function has_optimized_media_without_nextgen() {
|
||||
global $wpdb;
|
||||
|
||||
$ngg_table = $wpdb->prefix . 'ngg_pictures';
|
||||
$data_table = DB::get_instance()->get_table_name();
|
||||
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::WEBP_SUFFIX' );
|
||||
$suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::WEBP_SUFFIX' );
|
||||
|
||||
if ( get_imagify_option( 'convert_to_avif' ) ) {
|
||||
$suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::AVIF_SUFFIX' );
|
||||
}
|
||||
|
||||
return (int) $wpdb->get_var( $wpdb->prepare( // WPCS: unprepared SQL ok.
|
||||
"
|
||||
@@ -197,7 +207,7 @@ class NGG extends AbstractBulk {
|
||||
WHERE
|
||||
( data.status = 'success' OR data.status = 'already_optimized' )
|
||||
AND data.data NOT LIKE %s",
|
||||
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
'%' . $wpdb->esc_like( $suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
) );
|
||||
}
|
||||
|
||||
|
||||
@@ -284,7 +284,8 @@ function imagify_ngg_cleanup_after_media_deletion( $image_id, $image ) {
|
||||
* The backup file has already been deleted by NGG.
|
||||
* Delete the WebP versions and the optimization data.
|
||||
*/
|
||||
$process->delete_webp_files();
|
||||
$process->delete_nextgen_files();
|
||||
|
||||
$process->get_data()->delete_optimization_data();
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ class Main extends \Imagify_Regenerate_Thumbnails_Deprecated {
|
||||
|
||||
if ( ! empty( $optimization_data['sizes'] ) ) {
|
||||
foreach ( $optimization_data['sizes'] as $size_name => $size_data ) {
|
||||
$non_webp_size_name = $process->is_size_webp( $size_name );
|
||||
$non_webp_size_name = $process->is_size_next_gen( $size_name );
|
||||
|
||||
if ( ! $non_webp_size_name || ! isset( $sizes[ $non_webp_size_name ] ) ) {
|
||||
continue;
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
* Description: A robust scheduling library for use in WordPress plugins.
|
||||
* Author: Automattic
|
||||
* Author URI: https://automattic.com/
|
||||
* Version: 3.5.4
|
||||
* Version: 3.7.1
|
||||
* License: GPLv3
|
||||
* Requires at least: 6.2
|
||||
* Tested up to: 6.4
|
||||
* Requires PHP: 5.6
|
||||
*
|
||||
* Copyright 2019 Automattic, Inc. (https://automattic.com/contact/)
|
||||
*
|
||||
@@ -26,27 +29,29 @@
|
||||
* @package ActionScheduler
|
||||
*/
|
||||
|
||||
if ( ! function_exists( 'action_scheduler_register_3_dot_5_dot_4' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
|
||||
if ( ! function_exists( 'action_scheduler_register_3_dot_7_dot_1' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
|
||||
|
||||
if ( ! class_exists( 'ActionScheduler_Versions', false ) ) {
|
||||
require_once __DIR__ . '/classes/ActionScheduler_Versions.php';
|
||||
add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
|
||||
}
|
||||
|
||||
add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_5_dot_4', 0, 0 ); // WRCS: DEFINED_VERSION.
|
||||
add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_7_dot_1', 0, 0 ); // WRCS: DEFINED_VERSION.
|
||||
|
||||
// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
|
||||
/**
|
||||
* Registers this version of Action Scheduler.
|
||||
*/
|
||||
function action_scheduler_register_3_dot_5_dot_4() { // WRCS: DEFINED_VERSION.
|
||||
function action_scheduler_register_3_dot_7_dot_1() { // WRCS: DEFINED_VERSION.
|
||||
$versions = ActionScheduler_Versions::instance();
|
||||
$versions->register( '3.5.4', 'action_scheduler_initialize_3_dot_5_dot_4' ); // WRCS: DEFINED_VERSION.
|
||||
$versions->register( '3.7.1', 'action_scheduler_initialize_3_dot_7_dot_1' ); // WRCS: DEFINED_VERSION.
|
||||
}
|
||||
|
||||
// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
|
||||
/**
|
||||
* Initializes this version of Action Scheduler.
|
||||
*/
|
||||
function action_scheduler_initialize_3_dot_5_dot_4() { // WRCS: DEFINED_VERSION.
|
||||
function action_scheduler_initialize_3_dot_7_dot_1() { // WRCS: DEFINED_VERSION.
|
||||
// A final safety check is required even here, because historic versions of Action Scheduler
|
||||
// followed a different pattern (in some unusual cases, we could reach this point and the
|
||||
// ActionScheduler class is already defined—so we need to guard against that).
|
||||
@@ -58,7 +63,7 @@ if ( ! function_exists( 'action_scheduler_register_3_dot_5_dot_4' ) && function_
|
||||
|
||||
// Support usage in themes - load this version if no plugin has loaded a version yet.
|
||||
if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) {
|
||||
action_scheduler_initialize_3_dot_5_dot_4(); // WRCS: DEFINED_VERSION.
|
||||
action_scheduler_initialize_3_dot_7_dot_1(); // WRCS: DEFINED_VERSION.
|
||||
do_action( 'action_scheduler_pre_theme_init' );
|
||||
ActionScheduler_Versions::initialize_latest_version();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,65 @@
|
||||
*** Changelog ***
|
||||
|
||||
= 3.7.1 - 2023-12-13 =
|
||||
* Release/3.7.0.
|
||||
* Tweak - WP 6.4 compatibility.
|
||||
* update semver to 5.7.2 because of a security vulnerability in 5.7.1.
|
||||
|
||||
= 3.7.0 - 2023-11-20 =
|
||||
* Important: starting with this release, Action Scheduler follows an L-2 version policy (WordPress, and consequently PHP).
|
||||
* Add extended indexes for hook_status_scheduled_date_gmt and status_sheduled_date_gmt.
|
||||
* Catch and log exceptions thrown when actions can't be created, e.g. under a corrupt database schema.
|
||||
* Release/3.6.4.
|
||||
* Tweak - WP 6.4 compatibility.
|
||||
* Update unit tests for upcoming dependency version policy.
|
||||
* make sure hook action_scheduler_failed_execution can access original exception object.
|
||||
* mention dependency version policy in usage.md.
|
||||
|
||||
= 3.6.4 - 2023-10-11 =
|
||||
* Performance improvements when bulk cancelling actions.
|
||||
* Dev-related fixes.
|
||||
|
||||
= 3.6.3 - 2023-09-13 =
|
||||
* Use `_doing_it_wrong` in initialization check.
|
||||
|
||||
= 3.6.2 - 2023-08-09 =
|
||||
* Add guidance about passing arguments.
|
||||
* Atomic option locking.
|
||||
* Improve bulk delete handling.
|
||||
* Include database error in the exception message.
|
||||
* Tweak - WP 6.3 compatibility.
|
||||
|
||||
= 3.6.1 - 2023-06-14 =
|
||||
* Document new optional `$priority` arg for various API functions.
|
||||
* Document the new `--exclude-groups` WP CLI option.
|
||||
* Document the new `action_scheduler_init` hook.
|
||||
* Ensure actions within each claim are executed in the expected order.
|
||||
* Fix incorrect text domain.
|
||||
* Remove SHOW TABLES usage when checking if tables exist.
|
||||
|
||||
= 3.6.0 - 2023-05-10 =
|
||||
* Add $unique parameter to function signatures.
|
||||
* Add a cast-to-int for extra safety before forming new DateTime object.
|
||||
* Add a hook allowing exceptions for consistently failing recurring actions.
|
||||
* Add action priorities.
|
||||
* Add init hook.
|
||||
* Always raise the time limit.
|
||||
* Bump minimatch from 3.0.4 to 3.0.8.
|
||||
* Bump yaml from 2.2.1 to 2.2.2.
|
||||
* Defensive coding relating to gaps in declared schedule types.
|
||||
* Do not process an action if it cannot be set to `in-progress`.
|
||||
* Filter view labels (status names) should be translatable | #919.
|
||||
* Fix WPCLI progress messages.
|
||||
* Improve data-store initialization flow.
|
||||
* Improve error handling across all supported PHP versions.
|
||||
* Improve logic for flushing the runtime cache.
|
||||
* Support exclusion of multiple groups.
|
||||
* Update lint-staged and Node/NPM requirements.
|
||||
* add CLI clean command.
|
||||
* add CLI exclude-group filter.
|
||||
* exclude past-due from list table all filter count.
|
||||
* throwing an exception if as_schedule_recurring_action interval param is not of type integer.
|
||||
|
||||
= 3.5.4 - 2023-01-17 =
|
||||
* Add pre filters during action registration.
|
||||
* Async scheduling.
|
||||
|
||||
@@ -13,10 +13,15 @@ class ActionScheduler_ActionFactory {
|
||||
* @param array $args Args to pass to callbacks when the hook is triggered.
|
||||
* @param ActionScheduler_Schedule $schedule The action's schedule.
|
||||
* @param string $group A group to put the action in.
|
||||
* phpcs:ignore Squiz.Commenting.FunctionComment.ExtraParamComment
|
||||
* @param int $priority The action priority.
|
||||
*
|
||||
* @return ActionScheduler_Action An instance of the stored action.
|
||||
*/
|
||||
public function get_stored_action( $status, $hook, array $args = array(), ActionScheduler_Schedule $schedule = null, $group = '' ) {
|
||||
// The 6th parameter ($priority) is not formally declared in the method signature to maintain compatibility with
|
||||
// third-party subclasses created before this param was added.
|
||||
$priority = func_num_args() >= 6 ? (int) func_get_arg( 5 ) : 10;
|
||||
|
||||
switch ( $status ) {
|
||||
case ActionScheduler_Store::STATUS_PENDING:
|
||||
@@ -36,17 +41,19 @@ class ActionScheduler_ActionFactory {
|
||||
$action_class = apply_filters( 'action_scheduler_stored_action_class', $action_class, $status, $hook, $args, $schedule, $group );
|
||||
|
||||
$action = new $action_class( $hook, $args, $schedule, $group );
|
||||
$action->set_priority( $priority );
|
||||
|
||||
/**
|
||||
* Allow 3rd party code to change the instantiated action for a given hook, args, schedule and group.
|
||||
*
|
||||
* @param ActionScheduler_Action $action The instantiated action.
|
||||
* @param string $hook The instantiated action's hook.
|
||||
* @param array $args The instantiated action's args.
|
||||
* @param ActionScheduler_Action $action The instantiated action.
|
||||
* @param string $hook The instantiated action's hook.
|
||||
* @param array $args The instantiated action's args.
|
||||
* @param ActionScheduler_Schedule $schedule The instantiated action's schedule.
|
||||
* @param string $group The instantiated action's group.
|
||||
* @param string $group The instantiated action's group.
|
||||
* @param int $priority The action priority.
|
||||
*/
|
||||
return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group );
|
||||
return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group, $priority );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,9 +236,100 @@ class ActionScheduler_ActionFactory {
|
||||
$schedule_class = get_class( $schedule );
|
||||
$new_schedule = new $schedule( $next, $schedule->get_recurrence(), $schedule->get_first_date() );
|
||||
$new_action = new ActionScheduler_Action( $action->get_hook(), $action->get_args(), $new_schedule, $action->get_group() );
|
||||
$new_action->set_priority( $action->get_priority() );
|
||||
return $this->store( $new_action );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scheduled action.
|
||||
*
|
||||
* This general purpose method can be used in place of specific methods such as async(),
|
||||
* async_unique(), single() or single_unique(), etc.
|
||||
*
|
||||
* @internal Not intended for public use, should not be overriden by subclasses.
|
||||
*
|
||||
* @param array $options {
|
||||
* Describes the action we wish to schedule.
|
||||
*
|
||||
* @type string $type Must be one of 'async', 'cron', 'recurring', or 'single'.
|
||||
* @type string $hook The hook to be executed.
|
||||
* @type array $arguments Arguments to be passed to the callback.
|
||||
* @type string $group The action group.
|
||||
* @type bool $unique If the action should be unique.
|
||||
* @type int $when Timestamp. Indicates when the action, or first instance of the action in the case
|
||||
* of recurring or cron actions, becomes due.
|
||||
* @type int|string $pattern Recurrence pattern. This is either an interval in seconds for recurring actions
|
||||
* or a cron expression for cron actions.
|
||||
* @type int $priority Lower values means higher priority. Should be in the range 0-255.
|
||||
* }
|
||||
*
|
||||
* @return int The action ID. Zero if there was an error scheduling the action.
|
||||
*/
|
||||
public function create( array $options = array() ) {
|
||||
$defaults = array(
|
||||
'type' => 'single',
|
||||
'hook' => '',
|
||||
'arguments' => array(),
|
||||
'group' => '',
|
||||
'unique' => false,
|
||||
'when' => time(),
|
||||
'pattern' => null,
|
||||
'priority' => 10,
|
||||
);
|
||||
|
||||
$options = array_merge( $defaults, $options );
|
||||
|
||||
// Cron/recurring actions without a pattern are treated as single actions (this gives calling code the ability
|
||||
// to use functions like as_schedule_recurring_action() to schedule recurring as well as single actions).
|
||||
if ( ( 'cron' === $options['type'] || 'recurring' === $options['type'] ) && empty( $options['pattern'] ) ) {
|
||||
$options['type'] = 'single';
|
||||
}
|
||||
|
||||
switch ( $options['type'] ) {
|
||||
case 'async':
|
||||
$schedule = new ActionScheduler_NullSchedule();
|
||||
break;
|
||||
|
||||
case 'cron':
|
||||
$date = as_get_datetime_object( $options['when'] );
|
||||
$cron = CronExpression::factory( $options['pattern'] );
|
||||
$schedule = new ActionScheduler_CronSchedule( $date, $cron );
|
||||
break;
|
||||
|
||||
case 'recurring':
|
||||
$date = as_get_datetime_object( $options['when'] );
|
||||
$schedule = new ActionScheduler_IntervalSchedule( $date, $options['pattern'] );
|
||||
break;
|
||||
|
||||
case 'single':
|
||||
$date = as_get_datetime_object( $options['when'] );
|
||||
$schedule = new ActionScheduler_SimpleSchedule( $date );
|
||||
break;
|
||||
|
||||
default:
|
||||
error_log( "Unknown action type '{$options['type']}' specified when trying to create an action for '{$options['hook']}'." );
|
||||
return 0;
|
||||
}
|
||||
|
||||
$action = new ActionScheduler_Action( $options['hook'], $options['arguments'], $schedule, $options['group'] );
|
||||
$action->set_priority( $options['priority'] );
|
||||
|
||||
$action_id = 0;
|
||||
try {
|
||||
$action_id = $options['unique'] ? $this->store_unique_action( $action ) : $this->store( $action );
|
||||
} catch ( Exception $e ) {
|
||||
error_log(
|
||||
sprintf(
|
||||
/* translators: %1$s is the name of the hook to be enqueued, %2$s is the exception message. */
|
||||
__( 'Caught exception while enqueuing action "%1$s": %2$s', 'action-scheduler' ),
|
||||
$options['hook'],
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
return $action_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save action to database.
|
||||
*
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* Class ActionScheduler_Compatibility
|
||||
*/
|
||||
class ActionScheduler_Compatibility {
|
||||
|
||||
/**
|
||||
* Converts a shorthand byte value to an integer byte value.
|
||||
*
|
||||
@@ -89,21 +88,18 @@ class ActionScheduler_Compatibility {
|
||||
$limit = (int) $limit;
|
||||
$max_execution_time = (int) ini_get( 'max_execution_time' );
|
||||
|
||||
/*
|
||||
* If the max execution time is already unlimited (zero), or if it exceeds or is equal to the proposed
|
||||
* limit, there is no reason for us to make further changes (we never want to lower it).
|
||||
*/
|
||||
if (
|
||||
0 === $max_execution_time
|
||||
|| ( $max_execution_time >= $limit && $limit !== 0 )
|
||||
) {
|
||||
// If the max execution time is already set to zero (unlimited), there is no reason to make a further change.
|
||||
if ( 0 === $max_execution_time ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Whichever of $max_execution_time or $limit is higher is the amount by which we raise the time limit.
|
||||
$raise_by = 0 === $limit || $limit > $max_execution_time ? $limit : $max_execution_time;
|
||||
|
||||
if ( function_exists( 'wc_set_time_limit' ) ) {
|
||||
wc_set_time_limit( $limit );
|
||||
wc_set_time_limit( $raise_by );
|
||||
} elseif ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
|
||||
@set_time_limit( $limit ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
@set_time_limit( $raise_by ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
|
||||
*/
|
||||
protected function get_recurrence( $action ) {
|
||||
$schedule = $action->get_schedule();
|
||||
if ( $schedule->is_recurring() ) {
|
||||
if ( $schedule->is_recurring() && method_exists( $schedule, 'get_recurrence' ) ) {
|
||||
$recurrence = $schedule->get_recurrence();
|
||||
|
||||
if ( is_numeric( $recurrence ) ) {
|
||||
@@ -471,7 +471,7 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
|
||||
return __( 'async', 'action-scheduler' );
|
||||
}
|
||||
|
||||
if ( ! $schedule->get_date() ) {
|
||||
if ( ! method_exists( $schedule, 'get_date' ) || ! $schedule->get_date() ) {
|
||||
return '0000-00-00 00:00:00';
|
||||
}
|
||||
|
||||
@@ -502,7 +502,20 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
|
||||
*/
|
||||
protected function bulk_delete( array $ids, $ids_sql ) {
|
||||
foreach ( $ids as $id ) {
|
||||
$this->store->delete_action( $id );
|
||||
try {
|
||||
$this->store->delete_action( $id );
|
||||
} catch ( Exception $e ) {
|
||||
// A possible reason for an exception would include a scenario where the same action is deleted by a
|
||||
// concurrent request.
|
||||
error_log(
|
||||
sprintf(
|
||||
/* translators: 1: action ID 2: exception message. */
|
||||
__( 'Action Scheduler was unable to delete action %1$d. Reason: %2$s', 'action-scheduler' ),
|
||||
$id,
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,37 @@ class ActionScheduler_OptionLock extends ActionScheduler_Lock {
|
||||
* @bool True if lock value has changed, false if not or if set failed.
|
||||
*/
|
||||
public function set( $lock_type ) {
|
||||
return update_option( $this->get_key( $lock_type ), time() + $this->get_duration( $lock_type ) );
|
||||
global $wpdb;
|
||||
|
||||
$lock_key = $this->get_key( $lock_type );
|
||||
$existing_lock_value = $this->get_existing_lock( $lock_type );
|
||||
$new_lock_value = $this->new_lock_value( $lock_type );
|
||||
|
||||
// The lock may not exist yet, or may have been deleted.
|
||||
if ( empty( $existing_lock_value ) ) {
|
||||
return (bool) $wpdb->insert(
|
||||
$wpdb->options,
|
||||
array(
|
||||
'option_name' => $lock_key,
|
||||
'option_value' => $new_lock_value,
|
||||
'autoload' => 'no',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( $this->get_expiration_from( $existing_lock_value ) >= time() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, try to obtain the lock.
|
||||
return (bool) $wpdb->update(
|
||||
$wpdb->options,
|
||||
array( 'option_value' => $new_lock_value ),
|
||||
array(
|
||||
'option_name' => $lock_key,
|
||||
'option_value' => $existing_lock_value,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,7 +64,30 @@ class ActionScheduler_OptionLock extends ActionScheduler_Lock {
|
||||
* @return bool|int False if no lock is set, otherwise the timestamp for when the lock is set to expire.
|
||||
*/
|
||||
public function get_expiration( $lock_type ) {
|
||||
return get_option( $this->get_key( $lock_type ) );
|
||||
return $this->get_expiration_from( $this->get_existing_lock( $lock_type ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the lock string, derives the lock expiration timestamp (or false if it cannot be determined).
|
||||
*
|
||||
* @param string $lock_value String containing a timestamp, or pipe-separated combination of unique value and timestamp.
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
private function get_expiration_from( $lock_value ) {
|
||||
$lock_string = explode( '|', $lock_value );
|
||||
|
||||
// Old style lock?
|
||||
if ( count( $lock_string ) === 1 && is_numeric( $lock_string[0] ) ) {
|
||||
return (int) $lock_string[0];
|
||||
}
|
||||
|
||||
// New style lock?
|
||||
if ( count( $lock_string ) === 2 && is_numeric( $lock_string[1] ) ) {
|
||||
return (int) $lock_string[1];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,4 +99,37 @@ class ActionScheduler_OptionLock extends ActionScheduler_Lock {
|
||||
protected function get_key( $lock_type ) {
|
||||
return sprintf( 'action_scheduler_lock_%s', $lock_type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies the existing lock value, or an empty string if not set.
|
||||
*
|
||||
* @param string $lock_type A string to identify different lock types.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_existing_lock( $lock_type ) {
|
||||
global $wpdb;
|
||||
|
||||
// Now grab the existing lock value, if there is one.
|
||||
return (string) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT option_value FROM $wpdb->options WHERE option_name = %s",
|
||||
$this->get_key( $lock_type )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies a lock value consisting of a unique value and the current timestamp, which are separated by a pipe
|
||||
* character.
|
||||
*
|
||||
* Example: (string) "649de012e6b262.09774912|1688068114"
|
||||
*
|
||||
* @param string $lock_type A string to identify different lock types.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function new_lock_value( $lock_type ) {
|
||||
return uniqid( '', true ) . '|' . ( time() + $this->get_duration( $lock_type ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,14 @@ class ActionScheduler_QueueCleaner {
|
||||
*/
|
||||
private $month_in_seconds = 2678400;
|
||||
|
||||
/**
|
||||
* @var string[] Default list of statuses purged by the cleaner process.
|
||||
*/
|
||||
private $default_statuses_to_purge = [
|
||||
ActionScheduler_Store::STATUS_COMPLETE,
|
||||
ActionScheduler_Store::STATUS_CANCELED,
|
||||
];
|
||||
|
||||
/**
|
||||
* ActionScheduler_QueueCleaner constructor.
|
||||
*
|
||||
@@ -29,46 +37,113 @@ class ActionScheduler_QueueCleaner {
|
||||
$this->batch_size = $batch_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default queue cleaner process used by queue runner.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function delete_old_actions() {
|
||||
/**
|
||||
* Filter the minimum scheduled date age for action deletion.
|
||||
*
|
||||
* @param int $retention_period Minimum scheduled age in seconds of the actions to be deleted.
|
||||
*/
|
||||
$lifespan = apply_filters( 'action_scheduler_retention_period', $this->month_in_seconds );
|
||||
$cutoff = as_get_datetime_object($lifespan.' seconds ago');
|
||||
|
||||
$statuses_to_purge = array(
|
||||
ActionScheduler_Store::STATUS_COMPLETE,
|
||||
ActionScheduler_Store::STATUS_CANCELED,
|
||||
);
|
||||
try {
|
||||
$cutoff = as_get_datetime_object( $lifespan . ' seconds ago' );
|
||||
} catch ( Exception $e ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
sprintf(
|
||||
/* Translators: %s is the exception message. */
|
||||
esc_html__( 'It was not possible to determine a valid cut-off time: %s.', 'action-scheduler' ),
|
||||
esc_html( $e->getMessage() )
|
||||
),
|
||||
'3.5.5'
|
||||
);
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the statuses when cleaning the queue.
|
||||
*
|
||||
* @param string[] $default_statuses_to_purge Action statuses to clean.
|
||||
*/
|
||||
$statuses_to_purge = (array) apply_filters( 'action_scheduler_default_cleaner_statuses', $this->default_statuses_to_purge );
|
||||
|
||||
return $this->clean_actions( $statuses_to_purge, $cutoff, $this->get_batch_size() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete selected actions limited by status and date.
|
||||
*
|
||||
* @param string[] $statuses_to_purge List of action statuses to purge. Defaults to canceled, complete.
|
||||
* @param DateTime $cutoff_date Date limit for selecting actions. Defaults to 31 days ago.
|
||||
* @param int|null $batch_size Maximum number of actions per status to delete. Defaults to 20.
|
||||
* @param string $context Calling process context. Defaults to `old`.
|
||||
* @return array Actions deleted.
|
||||
*/
|
||||
public function clean_actions( array $statuses_to_purge, DateTime $cutoff_date, $batch_size = null, $context = 'old' ) {
|
||||
$batch_size = $batch_size !== null ? $batch_size : $this->batch_size;
|
||||
$cutoff = $cutoff_date !== null ? $cutoff_date : as_get_datetime_object( $this->month_in_seconds . ' seconds ago' );
|
||||
$lifespan = time() - $cutoff->getTimestamp();
|
||||
if ( empty( $statuses_to_purge ) ) {
|
||||
$statuses_to_purge = $this->default_statuses_to_purge;
|
||||
}
|
||||
|
||||
$deleted_actions = [];
|
||||
foreach ( $statuses_to_purge as $status ) {
|
||||
$actions_to_delete = $this->store->query_actions( array(
|
||||
'status' => $status,
|
||||
'modified' => $cutoff,
|
||||
'modified_compare' => '<=',
|
||||
'per_page' => $this->get_batch_size(),
|
||||
'per_page' => $batch_size,
|
||||
'orderby' => 'none',
|
||||
) );
|
||||
|
||||
foreach ( $actions_to_delete as $action_id ) {
|
||||
try {
|
||||
$this->store->delete_action( $action_id );
|
||||
} catch ( Exception $e ) {
|
||||
$deleted_actions = array_merge( $deleted_actions, $this->delete_actions( $actions_to_delete, $lifespan, $context ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify 3rd party code of exceptions when deleting a completed action older than the retention period
|
||||
*
|
||||
* This hook provides a way for 3rd party code to log or otherwise handle exceptions relating to their
|
||||
* actions.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param int $action_id The scheduled actions ID in the data store
|
||||
* @param Exception $e The exception thrown when attempting to delete the action from the data store
|
||||
* @param int $lifespan The retention period, in seconds, for old actions
|
||||
* @param int $count_of_actions_to_delete The number of old actions being deleted in this batch
|
||||
*/
|
||||
do_action( 'action_scheduler_failed_old_action_deletion', $action_id, $e, $lifespan, count( $actions_to_delete ) );
|
||||
}
|
||||
return $deleted_actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $actions_to_delete List of action IDs to delete.
|
||||
* @param int $lifespan Minimum scheduled age in seconds of the actions being deleted.
|
||||
* @param string $context Context of the delete request.
|
||||
* @return array Deleted action IDs.
|
||||
*/
|
||||
private function delete_actions( array $actions_to_delete, $lifespan = null, $context = 'old' ) {
|
||||
$deleted_actions = [];
|
||||
if ( $lifespan === null ) {
|
||||
$lifespan = $this->month_in_seconds;
|
||||
}
|
||||
|
||||
foreach ( $actions_to_delete as $action_id ) {
|
||||
try {
|
||||
$this->store->delete_action( $action_id );
|
||||
$deleted_actions[] = $action_id;
|
||||
} catch ( Exception $e ) {
|
||||
/**
|
||||
* Notify 3rd party code of exceptions when deleting a completed action older than the retention period
|
||||
*
|
||||
* This hook provides a way for 3rd party code to log or otherwise handle exceptions relating to their
|
||||
* actions.
|
||||
*
|
||||
* @param int $action_id The scheduled actions ID in the data store
|
||||
* @param Exception $e The exception thrown when attempting to delete the action from the data store
|
||||
* @param int $lifespan The retention period, in seconds, for old actions
|
||||
* @param int $count_of_actions_to_delete The number of old actions being deleted in this batch
|
||||
* @since 2.0.0
|
||||
*
|
||||
*/
|
||||
do_action( "action_scheduler_failed_{$context}_action_deletion", $action_id, $e, $lifespan, count( $actions_to_delete ) );
|
||||
}
|
||||
}
|
||||
return $deleted_actions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -103,9 +103,12 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {
|
||||
* should dispatch a request to process pending actions.
|
||||
*/
|
||||
public function maybe_dispatch_async_request() {
|
||||
if ( is_admin() && ! ActionScheduler::lock()->is_locked( 'async-request-runner' ) ) {
|
||||
// Only start an async queue at most once every 60 seconds
|
||||
ActionScheduler::lock()->set( 'async-request-runner' );
|
||||
// Only start an async queue at most once every 60 seconds.
|
||||
if (
|
||||
is_admin()
|
||||
&& ! ActionScheduler::lock()->is_locked( 'async-request-runner' )
|
||||
&& ActionScheduler::lock()->set( 'async-request-runner' )
|
||||
) {
|
||||
$this->async_request->maybe_dispatch();
|
||||
}
|
||||
}
|
||||
@@ -185,9 +188,15 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {
|
||||
protected function clear_caches() {
|
||||
/*
|
||||
* Calling wp_cache_flush_runtime() lets us clear the runtime cache without invalidating the external object
|
||||
* cache, so we will always prefer this when it is available (but it was only introduced in WordPress 6.0).
|
||||
* cache, so we will always prefer this method (as compared to calling wp_cache_flush()) when it is available.
|
||||
*
|
||||
* However, this function was only introduced in WordPress 6.0. Additionally, the preferred way of detecting if
|
||||
* it is supported changed in WordPress 6.1 so we use two different methods to decide if we should utilize it.
|
||||
*/
|
||||
if ( function_exists( 'wp_cache_flush_runtime' ) ) {
|
||||
$flushing_runtime_cache_explicitly_supported = function_exists( 'wp_cache_supports' ) && wp_cache_supports( 'flush_runtime' );
|
||||
$flushing_runtime_cache_implicitly_supported = ! function_exists( 'wp_cache_supports' ) && function_exists( 'wp_cache_flush_runtime' );
|
||||
|
||||
if ( $flushing_runtime_cache_explicitly_supported || $flushing_runtime_cache_implicitly_supported ) {
|
||||
wp_cache_flush_runtime();
|
||||
} elseif (
|
||||
! wp_using_ext_object_cache()
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Commands for Action Scheduler.
|
||||
*/
|
||||
class ActionScheduler_WPCLI_Clean_Command extends WP_CLI_Command {
|
||||
/**
|
||||
* Run the Action Scheduler Queue Cleaner
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [--batch-size=<size>]
|
||||
* : The maximum number of actions to delete per batch. Defaults to 20.
|
||||
*
|
||||
* [--batches=<size>]
|
||||
* : Limit execution to a number of batches. Defaults to 0, meaning batches will continue all eligible actions are deleted.
|
||||
*
|
||||
* [--status=<status>]
|
||||
* : Only clean actions with the specified status. Defaults to Canceled, Completed. Define multiple statuses as a comma separated string (without spaces), e.g. `--status=complete,failed,canceled`
|
||||
*
|
||||
* [--before=<datestring>]
|
||||
* : Only delete actions with scheduled date older than this. Defaults to 31 days. e.g `--before='7 days ago'`, `--before='02-Feb-2020 20:20:20'`
|
||||
*
|
||||
* [--pause=<seconds>]
|
||||
* : The number of seconds to pause between batches. Default no pause.
|
||||
*
|
||||
* @param array $args Positional arguments.
|
||||
* @param array $assoc_args Keyed arguments.
|
||||
* @throws \WP_CLI\ExitException When an error occurs.
|
||||
*
|
||||
* @subcommand clean
|
||||
*/
|
||||
public function clean( $args, $assoc_args ) {
|
||||
// Handle passed arguments.
|
||||
$batch = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 20 ) );
|
||||
$batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) );
|
||||
$status = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'status', '' ) );
|
||||
$status = array_filter( array_map( 'trim', $status ) );
|
||||
$before = \WP_CLI\Utils\get_flag_value( $assoc_args, 'before', '' );
|
||||
$sleep = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 );
|
||||
|
||||
$batches_completed = 0;
|
||||
$actions_deleted = 0;
|
||||
$unlimited = $batches === 0;
|
||||
try {
|
||||
$lifespan = as_get_datetime_object( $before );
|
||||
} catch ( Exception $e ) {
|
||||
$lifespan = null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Custom queue cleaner instance.
|
||||
$cleaner = new ActionScheduler_QueueCleaner( null, $batch );
|
||||
|
||||
// Clean actions for as long as possible.
|
||||
while ( $unlimited || $batches_completed < $batches ) {
|
||||
if ( $sleep && $batches_completed > 0 ) {
|
||||
sleep( $sleep );
|
||||
}
|
||||
|
||||
$deleted = count( $cleaner->clean_actions( $status, $lifespan, null,'CLI' ) );
|
||||
if ( $deleted <= 0 ) {
|
||||
break;
|
||||
}
|
||||
$actions_deleted += $deleted;
|
||||
$batches_completed++;
|
||||
$this->print_success( $deleted );
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$this->print_error( $e );
|
||||
}
|
||||
|
||||
$this->print_total_batches( $batches_completed );
|
||||
if ( $batches_completed > 1 ) {
|
||||
$this->print_success( $actions_deleted );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print WP CLI message about how many batches of actions were processed.
|
||||
*
|
||||
* @param int $batches_processed
|
||||
*/
|
||||
protected function print_total_batches( int $batches_processed ) {
|
||||
WP_CLI::log(
|
||||
sprintf(
|
||||
/* translators: %d refers to the total number of batches processed */
|
||||
_n( '%d batch processed.', '%d batches processed.', $batches_processed, 'action-scheduler' ),
|
||||
$batches_processed
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an exception into a WP CLI error.
|
||||
*
|
||||
* @param Exception $e The error object.
|
||||
*
|
||||
* @throws \WP_CLI\ExitException
|
||||
*/
|
||||
protected function print_error( Exception $e ) {
|
||||
WP_CLI::error(
|
||||
sprintf(
|
||||
/* translators: %s refers to the exception error message */
|
||||
__( 'There was an error deleting an action: %s', 'action-scheduler' ),
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a success message with the number of completed actions.
|
||||
*
|
||||
* @param int $actions_deleted
|
||||
*/
|
||||
protected function print_success( int $actions_deleted ) {
|
||||
WP_CLI::success(
|
||||
sprintf(
|
||||
/* translators: %d refers to the total number of actions deleted */
|
||||
_n( '%d action deleted.', '%d actions deleted.', $actions_deleted, 'action-scheduler' ),
|
||||
$actions_deleted
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class ActionScheduler_WPCLI_QueueRunner extends ActionScheduler_Abstract_QueueRu
|
||||
$count = count( $this->actions );
|
||||
$this->progress_bar = new ProgressBar(
|
||||
/* translators: %d: amount of actions */
|
||||
sprintf( _n( 'Running %d action', 'Running %d actions', $count, 'action-scheduler' ), number_format_i18n( $count ) ),
|
||||
sprintf( _n( 'Running %d action', 'Running %d actions', $count, 'action-scheduler' ), $count ),
|
||||
$count
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,9 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
* [--group=<group>]
|
||||
* : Only run actions from the specified group. Omitting this option runs actions from all groups.
|
||||
*
|
||||
* [--exclude-groups=<groups>]
|
||||
* : Run actions from all groups except the specified group(s). Define multiple groups as a comma separated string (without spaces), e.g. '--group_a,group_b'. This option is ignored when `--group` is used.
|
||||
*
|
||||
* [--free-memory-on=<count>]
|
||||
* : The number of actions to process between freeing memory. 0 disables freeing memory. Default 50.
|
||||
*
|
||||
@@ -72,15 +75,16 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
*/
|
||||
public function run( $args, $assoc_args ) {
|
||||
// Handle passed arguments.
|
||||
$batch = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 100 ) );
|
||||
$batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) );
|
||||
$clean = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'cleanup-batch-size', $batch ) );
|
||||
$hooks = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'hooks', '' ) );
|
||||
$hooks = array_filter( array_map( 'trim', $hooks ) );
|
||||
$group = \WP_CLI\Utils\get_flag_value( $assoc_args, 'group', '' );
|
||||
$free_on = \WP_CLI\Utils\get_flag_value( $assoc_args, 'free-memory-on', 50 );
|
||||
$sleep = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 );
|
||||
$force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false );
|
||||
$batch = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 100 ) );
|
||||
$batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) );
|
||||
$clean = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'cleanup-batch-size', $batch ) );
|
||||
$hooks = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'hooks', '' ) );
|
||||
$hooks = array_filter( array_map( 'trim', $hooks ) );
|
||||
$group = \WP_CLI\Utils\get_flag_value( $assoc_args, 'group', '' );
|
||||
$exclude_groups = \WP_CLI\Utils\get_flag_value( $assoc_args, 'exclude-groups', '' );
|
||||
$free_on = \WP_CLI\Utils\get_flag_value( $assoc_args, 'free-memory-on', 50 );
|
||||
$sleep = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 );
|
||||
$force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false );
|
||||
|
||||
ActionScheduler_DataController::set_free_ticks( $free_on );
|
||||
ActionScheduler_DataController::set_sleep_time( $sleep );
|
||||
@@ -88,6 +92,13 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
$batches_completed = 0;
|
||||
$actions_completed = 0;
|
||||
$unlimited = $batches === 0;
|
||||
if ( is_callable( [ ActionScheduler::store(), 'set_claim_filter' ] ) ) {
|
||||
$exclude_groups = $this->parse_comma_separated_string( $exclude_groups );
|
||||
|
||||
if ( ! empty( $exclude_groups ) ) {
|
||||
ActionScheduler::store()->set_claim_filter('exclude-groups', $exclude_groups );
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Custom queue cleaner instance.
|
||||
@@ -116,6 +127,17 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
$this->print_success( $actions_completed );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string of comma-separated values into an array of those same values.
|
||||
*
|
||||
* @param string $string The string of one or more comma separated values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function parse_comma_separated_string( $string ): array {
|
||||
return array_filter( str_getcsv( $string ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Print WP CLI message about how many actions are about to be processed.
|
||||
*
|
||||
@@ -126,9 +148,9 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
protected function print_total_actions( $total ) {
|
||||
WP_CLI::log(
|
||||
sprintf(
|
||||
/* translators: %d refers to how many scheduled taks were found to run */
|
||||
/* translators: %d refers to how many scheduled tasks were found to run */
|
||||
_n( 'Found %d scheduled task', 'Found %d scheduled tasks', $total, 'action-scheduler' ),
|
||||
number_format_i18n( $total )
|
||||
$total
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -145,7 +167,7 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
sprintf(
|
||||
/* translators: %d refers to the total number of batches executed */
|
||||
_n( '%d batch executed.', '%d batches executed.', $batches_completed, 'action-scheduler' ),
|
||||
number_format_i18n( $batches_completed )
|
||||
$batches_completed
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -179,9 +201,9 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
protected function print_success( $actions_completed ) {
|
||||
WP_CLI::success(
|
||||
sprintf(
|
||||
/* translators: %d refers to the total number of taskes completed */
|
||||
/* translators: %d refers to the total number of tasks completed */
|
||||
_n( '%d scheduled task completed.', '%d scheduled tasks completed.', $actions_completed, 'action-scheduler' ),
|
||||
number_format_i18n( $actions_completed )
|
||||
$actions_completed
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -153,11 +153,41 @@ abstract class ActionScheduler {
|
||||
add_action( 'init', array( $store, 'init' ), 1, 0 );
|
||||
add_action( 'init', array( $logger, 'init' ), 1, 0 );
|
||||
add_action( 'init', array( $runner, 'init' ), 1, 0 );
|
||||
|
||||
add_action(
|
||||
'init',
|
||||
/**
|
||||
* Runs after the active store's init() method has been called.
|
||||
*
|
||||
* It would probably be preferable to have $store->init() (or it's parent method) set this itself,
|
||||
* once it has initialized, however that would cause problems in cases where a custom data store is in
|
||||
* use and it has not yet been updated to follow that same logic.
|
||||
*/
|
||||
function () {
|
||||
self::$data_store_initialized = true;
|
||||
|
||||
/**
|
||||
* Fires when Action Scheduler is ready: it is safe to use the procedural API after this point.
|
||||
*
|
||||
* @since 3.5.5
|
||||
*/
|
||||
do_action( 'action_scheduler_init' );
|
||||
},
|
||||
1
|
||||
);
|
||||
} else {
|
||||
$admin_view->init();
|
||||
$store->init();
|
||||
$logger->init();
|
||||
$runner->init();
|
||||
self::$data_store_initialized = true;
|
||||
|
||||
/**
|
||||
* Fires when Action Scheduler is ready: it is safe to use the procedural API after this point.
|
||||
*
|
||||
* @since 3.5.5
|
||||
*/
|
||||
do_action( 'action_scheduler_init' );
|
||||
}
|
||||
|
||||
if ( apply_filters( 'action_scheduler_load_deprecated_functions', true ) ) {
|
||||
@@ -166,14 +196,13 @@ abstract class ActionScheduler {
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
WP_CLI::add_command( 'action-scheduler', 'ActionScheduler_WPCLI_Scheduler_command' );
|
||||
WP_CLI::add_command( 'action-scheduler', 'ActionScheduler_WPCLI_Clean_Command' );
|
||||
if ( ! ActionScheduler_DataController::is_migration_complete() && Controller::instance()->allow_migration() ) {
|
||||
$command = new Migration_Command();
|
||||
$command->register();
|
||||
}
|
||||
}
|
||||
|
||||
self::$data_store_initialized = true;
|
||||
|
||||
/**
|
||||
* Handle WP comment cleanup after migration.
|
||||
*/
|
||||
@@ -192,8 +221,12 @@ abstract class ActionScheduler {
|
||||
*/
|
||||
public static function is_initialized( $function_name = null ) {
|
||||
if ( ! self::$data_store_initialized && ! empty( $function_name ) ) {
|
||||
$message = sprintf( __( '%s() was called before the Action Scheduler data store was initialized', 'action-scheduler' ), esc_attr( $function_name ) );
|
||||
error_log( $message, E_WARNING );
|
||||
$message = sprintf(
|
||||
/* translators: %s function name. */
|
||||
__( '%s() was called before the Action Scheduler data store was initialized', 'action-scheduler' ),
|
||||
esc_attr( $function_name )
|
||||
);
|
||||
_doing_it_wrong( $function_name, $message, '3.1.6' );
|
||||
}
|
||||
|
||||
return self::$data_store_initialized;
|
||||
|
||||
@@ -673,24 +673,34 @@ abstract class ActionScheduler_Abstract_ListTable extends WP_List_Table {
|
||||
|
||||
// Helper to set 'all' filter when not set on status counts passed in.
|
||||
if ( ! isset( $this->status_counts['all'] ) ) {
|
||||
$this->status_counts = array( 'all' => array_sum( $this->status_counts ) ) + $this->status_counts;
|
||||
$all_count = array_sum( $this->status_counts );
|
||||
if ( isset( $this->status_counts['past-due'] ) ) {
|
||||
$all_count -= $this->status_counts['past-due'];
|
||||
}
|
||||
$this->status_counts = array( 'all' => $all_count ) + $this->status_counts;
|
||||
}
|
||||
|
||||
foreach ( $this->status_counts as $status_name => $count ) {
|
||||
// Translated status labels.
|
||||
$status_labels = ActionScheduler_Store::instance()->get_status_labels();
|
||||
$status_labels['all'] = _x( 'All', 'status labels', 'action-scheduler' );
|
||||
$status_labels['past-due'] = _x( 'Past-due', 'status labels', 'action-scheduler' );
|
||||
|
||||
foreach ( $this->status_counts as $status_slug => $count ) {
|
||||
|
||||
if ( 0 === $count ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $status_name === $request_status || ( empty( $request_status ) && 'all' === $status_name ) ) {
|
||||
if ( $status_slug === $request_status || ( empty( $request_status ) && 'all' === $status_slug ) ) {
|
||||
$status_list_item = '<li class="%1$s"><a href="%2$s" class="current">%3$s</a> (%4$d)</li>';
|
||||
} else {
|
||||
$status_list_item = '<li class="%1$s"><a href="%2$s">%3$s</a> (%4$d)</li>';
|
||||
}
|
||||
|
||||
$status_filter_url = ( 'all' === $status_name ) ? remove_query_arg( 'status' ) : add_query_arg( 'status', $status_name );
|
||||
$status_name = isset( $status_labels[ $status_slug ] ) ? $status_labels[ $status_slug ] : ucfirst( $status_slug );
|
||||
$status_filter_url = ( 'all' === $status_slug ) ? remove_query_arg( 'status' ) : add_query_arg( 'status', $status_slug );
|
||||
$status_filter_url = remove_query_arg( array( 'paged', 's' ), $status_filter_url );
|
||||
$status_list_items[] = sprintf( $status_list_item, esc_attr( $status_name ), esc_url( $status_filter_url ), esc_html( ucfirst( $status_name ) ), absint( $count ) );
|
||||
$status_list_items[] = sprintf( $status_list_item, esc_attr( $status_slug ), esc_url( $status_filter_url ), esc_html( $status_name ), absint( $count ) );
|
||||
}
|
||||
|
||||
if ( $status_list_items ) {
|
||||
|
||||
@@ -48,30 +48,56 @@ abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abst
|
||||
* Generally, this should be capitalised and not localised as it's a proper noun.
|
||||
*/
|
||||
public function process_action( $action_id, $context = '' ) {
|
||||
// Temporarily override the error handler while we process the current action.
|
||||
set_error_handler(
|
||||
/**
|
||||
* Temporary error handler which can catch errors and convert them into exceptions. This faciliates more
|
||||
* robust error handling across all supported PHP versions.
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @param int $type Error level expressed as an integer.
|
||||
* @param string $message Error message.
|
||||
*/
|
||||
function ( $type, $message ) {
|
||||
throw new Exception( $message );
|
||||
},
|
||||
E_USER_ERROR | E_RECOVERABLE_ERROR
|
||||
);
|
||||
|
||||
/*
|
||||
* The nested try/catch structure is required because we potentially need to convert thrown errors into
|
||||
* exceptions (and an exception thrown from a catch block cannot be caught by a later catch block in the *same*
|
||||
* structure).
|
||||
*/
|
||||
try {
|
||||
$valid_action = false;
|
||||
do_action( 'action_scheduler_before_execute', $action_id, $context );
|
||||
try {
|
||||
$valid_action = false;
|
||||
do_action( 'action_scheduler_before_execute', $action_id, $context );
|
||||
|
||||
if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) {
|
||||
do_action( 'action_scheduler_execution_ignored', $action_id, $context );
|
||||
return;
|
||||
if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) {
|
||||
do_action( 'action_scheduler_execution_ignored', $action_id, $context );
|
||||
return;
|
||||
}
|
||||
|
||||
$valid_action = true;
|
||||
do_action( 'action_scheduler_begin_execute', $action_id, $context );
|
||||
|
||||
$action = $this->store->fetch_action( $action_id );
|
||||
$this->store->log_execution( $action_id );
|
||||
$action->execute();
|
||||
do_action( 'action_scheduler_after_execute', $action_id, $action, $context );
|
||||
$this->store->mark_complete( $action_id );
|
||||
} catch ( Throwable $e ) {
|
||||
// Throwable is defined when executing under PHP 7.0 and up. We convert it to an exception, for
|
||||
// compatibility with ActionScheduler_Logger.
|
||||
throw new Exception( $e->getMessage(), $e->getCode(), $e );
|
||||
}
|
||||
|
||||
$valid_action = true;
|
||||
do_action( 'action_scheduler_begin_execute', $action_id, $context );
|
||||
|
||||
$action = $this->store->fetch_action( $action_id );
|
||||
$this->store->log_execution( $action_id );
|
||||
$action->execute();
|
||||
do_action( 'action_scheduler_after_execute', $action_id, $action, $context );
|
||||
$this->store->mark_complete( $action_id );
|
||||
} catch ( Exception $e ) {
|
||||
if ( $valid_action ) {
|
||||
$this->store->mark_failure( $action_id );
|
||||
do_action( 'action_scheduler_failed_execution', $action_id, $e, $context );
|
||||
} else {
|
||||
do_action( 'action_scheduler_failed_validation', $action_id, $e, $context );
|
||||
}
|
||||
// This catch block exists for compatibility with PHP 5.6.
|
||||
$this->handle_action_error( $action_id, $e, $context, $valid_action );
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
if ( isset( $action ) && is_a( $action, 'ActionScheduler_Action' ) && $action->get_schedule()->is_recurring() ) {
|
||||
@@ -79,6 +105,39 @@ abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abst
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks actions as either having failed execution or failed validation, as appropriate.
|
||||
*
|
||||
* @param int $action_id Action ID.
|
||||
* @param Exception $e Exception instance.
|
||||
* @param string $context Execution context.
|
||||
* @param bool $valid_action If the action is valid.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handle_action_error( $action_id, $e, $context, $valid_action ) {
|
||||
if ( $valid_action ) {
|
||||
$this->store->mark_failure( $action_id );
|
||||
/**
|
||||
* Runs when action execution fails.
|
||||
*
|
||||
* @param int $action_id Action ID.
|
||||
* @param Exception $e Exception instance.
|
||||
* @param string $context Execution context.
|
||||
*/
|
||||
do_action( 'action_scheduler_failed_execution', $action_id, $e, $context );
|
||||
} else {
|
||||
/**
|
||||
* Runs when action validation fails.
|
||||
*
|
||||
* @param int $action_id Action ID.
|
||||
* @param Exception $e Exception instance.
|
||||
* @param string $context Execution context.
|
||||
*/
|
||||
do_action( 'action_scheduler_failed_validation', $action_id, $e, $context );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the next instance of the action if necessary.
|
||||
*
|
||||
@@ -143,12 +202,22 @@ abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abst
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now let's fetch the first action (having the same hook) of *any status*ithin the same window.
|
||||
// Now let's fetch the first action (having the same hook) of *any status* within the same window.
|
||||
unset( $query_args['status'] );
|
||||
$first_action_id_with_the_same_hook = $this->store->query_actions( $query_args );
|
||||
|
||||
// If the IDs match, then actions for this hook must be consistently failing.
|
||||
return $first_action_id_with_the_same_hook === $first_failing_action_id;
|
||||
/**
|
||||
* If a recurring action is assessed as consistently failing, it will not be rescheduled. This hook provides a
|
||||
* way to observe and optionally override that assessment.
|
||||
*
|
||||
* @param bool $is_consistently_failing If the action is considered to be consistently failing.
|
||||
* @param ActionScheduler_Action $action The action being assessed.
|
||||
*/
|
||||
return (bool) apply_filters(
|
||||
'action_scheduler_recurring_action_is_consistently_failing',
|
||||
$first_action_id_with_the_same_hook === $first_failing_action_id,
|
||||
$action
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ abstract class ActionScheduler_Abstract_Schema {
|
||||
/**
|
||||
* @var array Names of tables that will be registered by this class.
|
||||
*/
|
||||
protected $tables = [];
|
||||
protected $tables = array();
|
||||
|
||||
/**
|
||||
* Can optionally be used by concrete classes to carry out additional initialization work
|
||||
@@ -90,10 +90,10 @@ abstract class ActionScheduler_Abstract_Schema {
|
||||
$plugin_option_name = 'schema-';
|
||||
|
||||
switch ( static::class ) {
|
||||
case 'ActionScheduler_StoreSchema' :
|
||||
case 'ActionScheduler_StoreSchema':
|
||||
$plugin_option_name .= 'Action_Scheduler\Custom_Tables\DB_Store_Table_Maker';
|
||||
break;
|
||||
case 'ActionScheduler_LoggerSchema' :
|
||||
case 'ActionScheduler_LoggerSchema':
|
||||
$plugin_option_name .= 'Action_Scheduler\Custom_Tables\DB_Logger_Table_Maker';
|
||||
break;
|
||||
}
|
||||
@@ -129,7 +129,7 @@ abstract class ActionScheduler_Abstract_Schema {
|
||||
* @return void
|
||||
*/
|
||||
private function update_table( $table ) {
|
||||
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
$definition = $this->get_table_definition( $table );
|
||||
if ( $definition ) {
|
||||
$updated = dbDelta( $definition );
|
||||
@@ -148,7 +148,7 @@ abstract class ActionScheduler_Abstract_Schema {
|
||||
* table prefix for the current blog
|
||||
*/
|
||||
protected function get_full_table_name( $table ) {
|
||||
return $GLOBALS[ 'wpdb' ]->prefix . $table;
|
||||
return $GLOBALS['wpdb']->prefix . $table;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,14 +159,19 @@ abstract class ActionScheduler_Abstract_Schema {
|
||||
public function tables_exist() {
|
||||
global $wpdb;
|
||||
|
||||
$existing_tables = $wpdb->get_col( 'SHOW TABLES' );
|
||||
$expected_tables = array_map(
|
||||
function ( $table_name ) use ( $wpdb ) {
|
||||
return $wpdb->prefix . $table_name;
|
||||
},
|
||||
$this->tables
|
||||
);
|
||||
$tables_exist = true;
|
||||
|
||||
return count( array_intersect( $existing_tables, $expected_tables ) ) === count( $expected_tables );
|
||||
foreach ( $this->tables as $table_name ) {
|
||||
$table_name = $wpdb->prefix . $table_name;
|
||||
$pattern = str_replace( '_', '\\_', $table_name );
|
||||
$existing_table = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $pattern ) );
|
||||
|
||||
if ( $existing_table !== $table_name ) {
|
||||
$tables_exist = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $tables_exist;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ abstract class ActionScheduler_Lock {
|
||||
/**
|
||||
* Set a lock.
|
||||
*
|
||||
* To prevent race conditions, implementations should avoid setting the lock if the lock is already held.
|
||||
*
|
||||
* @param string $lock_type A string to identify different lock types.
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
@@ -347,7 +347,7 @@ abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated {
|
||||
'hook' => $hook,
|
||||
'status' => self::STATUS_PENDING,
|
||||
'per_page' => 1000,
|
||||
'orderby' => 'action_id',
|
||||
'orderby' => 'none',
|
||||
)
|
||||
);
|
||||
|
||||
@@ -372,7 +372,7 @@ abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated {
|
||||
'group' => $group,
|
||||
'status' => self::STATUS_PENDING,
|
||||
'per_page' => 1000,
|
||||
'orderby' => 'action_id',
|
||||
'orderby' => 'none',
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -10,6 +10,19 @@ class ActionScheduler_Action {
|
||||
protected $schedule = NULL;
|
||||
protected $group = '';
|
||||
|
||||
/**
|
||||
* Priorities are conceptually similar to those used for regular WordPress actions.
|
||||
* Like those, a lower priority takes precedence over a higher priority and the default
|
||||
* is 10.
|
||||
*
|
||||
* Unlike regular WordPress actions, the priority of a scheduled action is strictly an
|
||||
* integer and should be kept within the bounds 0-255 (anything outside the bounds will
|
||||
* be brought back into the acceptable range).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $priority = 10;
|
||||
|
||||
public function __construct( $hook, array $args = array(), ActionScheduler_Schedule $schedule = NULL, $group = '' ) {
|
||||
$schedule = empty( $schedule ) ? new ActionScheduler_NullSchedule() : $schedule;
|
||||
$this->set_hook($hook);
|
||||
@@ -93,4 +106,30 @@ class ActionScheduler_Action {
|
||||
public function is_finished() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the priority of the action.
|
||||
*
|
||||
* @param int $priority Priority level (lower is higher priority). Should be in the range 0-255.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_priority( $priority ) {
|
||||
if ( $priority < 0 ) {
|
||||
$priority = 0;
|
||||
} elseif ( $priority > 255 ) {
|
||||
$priority = 255;
|
||||
}
|
||||
|
||||
$this->priority = (int) $priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the action priority.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_priority() {
|
||||
return $this->priority;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class ActionScheduler_DBLogger extends ActionScheduler_Logger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the an action's log entries from the database.
|
||||
* Retrieve an action's log entries from the database.
|
||||
*
|
||||
* @param int $action_id Action ID.
|
||||
*
|
||||
|
||||
@@ -25,6 +25,13 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
|
||||
/** @var int */
|
||||
protected static $max_index_length = 191;
|
||||
|
||||
/** @var array List of claim filters. */
|
||||
protected $claim_filters = [
|
||||
'group' => '',
|
||||
'hooks' => '',
|
||||
'exclude-groups' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* Initialize the data store
|
||||
*
|
||||
@@ -84,7 +91,8 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
|
||||
'scheduled_date_gmt' => $this->get_scheduled_date_string( $action, $date ),
|
||||
'scheduled_date_local' => $this->get_scheduled_date_string_local( $action, $date ),
|
||||
'schedule' => serialize( $action->get_schedule() ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
|
||||
'group_id' => $this->get_group_id( $action->get_group() ),
|
||||
'group_id' => current( $this->get_group_ids( $action->get_group() ) ),
|
||||
'priority' => $action->get_priority(),
|
||||
);
|
||||
|
||||
$args = wp_json_encode( $action->get_args() );
|
||||
@@ -172,6 +180,7 @@ WHERE ( $where_clause ) IS NULL",
|
||||
ActionScheduler_Store::STATUS_RUNNING,
|
||||
);
|
||||
$pending_status_placeholders = implode( ', ', array_fill( 0, count( $pending_statuses ), '%s' ) );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $pending_status_placeholders is hardcoded.
|
||||
$where_clause = $wpdb->prepare(
|
||||
"
|
||||
@@ -242,23 +251,35 @@ AND `group_id` = %d
|
||||
/**
|
||||
* Get a group's ID based on its name/slug.
|
||||
*
|
||||
* @param string $slug The string name of a group.
|
||||
* @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group.
|
||||
* @param string|array $slugs The string name of a group, or names for several groups.
|
||||
* @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group.
|
||||
*
|
||||
* @return int The group's ID, if it exists or is created, or 0 if it does not exist and is not created.
|
||||
* @return array The group IDs, if they exist or were successfully created. May be empty.
|
||||
*/
|
||||
protected function get_group_id( $slug, $create_if_not_exists = true ) {
|
||||
if ( empty( $slug ) ) {
|
||||
return 0;
|
||||
}
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
$group_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT group_id FROM {$wpdb->actionscheduler_groups} WHERE slug=%s", $slug ) );
|
||||
if ( empty( $group_id ) && $create_if_not_exists ) {
|
||||
$group_id = $this->create_group( $slug );
|
||||
protected function get_group_ids( $slugs, $create_if_not_exists = true ) {
|
||||
$slugs = (array) $slugs;
|
||||
$group_ids = array();
|
||||
|
||||
if ( empty( $slugs ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $group_id;
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
foreach ( $slugs as $slug ) {
|
||||
$group_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT group_id FROM {$wpdb->actionscheduler_groups} WHERE slug=%s", $slug ) );
|
||||
|
||||
if ( empty( $group_id ) && $create_if_not_exists ) {
|
||||
$group_id = $this->create_group( $slug );
|
||||
}
|
||||
|
||||
if ( $group_id ) {
|
||||
$group_ids[] = $group_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $group_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,7 +376,7 @@ AND `group_id` = %d
|
||||
}
|
||||
$group = $data->group ? $data->group : '';
|
||||
|
||||
return ActionScheduler::factory()->get_stored_action( $data->status, $data->hook, $args, $schedule, $group );
|
||||
return ActionScheduler::factory()->get_stored_action( $data->status, $data->hook, $args, $schedule, $group, $data->priority );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -684,7 +705,7 @@ AND `group_id` = %d
|
||||
array(
|
||||
'per_page' => 1000,
|
||||
'status' => self::STATUS_PENDING,
|
||||
'orderby' => 'action_id',
|
||||
'orderby' => 'none',
|
||||
)
|
||||
);
|
||||
|
||||
@@ -796,6 +817,33 @@ AND `group_id` = %d
|
||||
return $wpdb->insert_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a claim filter.
|
||||
*
|
||||
* @param string $filter_name Claim filter name.
|
||||
* @param mixed $filter_values Values to filter.
|
||||
* @return void
|
||||
*/
|
||||
public function set_claim_filter( $filter_name, $filter_values ) {
|
||||
if ( isset( $this->claim_filters[ $filter_name ] ) ) {
|
||||
$this->claim_filters[ $filter_name ] = $filter_values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the claim filter value.
|
||||
*
|
||||
* @param string $filter_name Claim filter name.
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_claim_filter( $filter_name ) {
|
||||
if ( isset( $this->claim_filters[ $filter_name ] ) ) {
|
||||
return $this->claim_filters[ $filter_name ];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark actions claimed.
|
||||
*
|
||||
@@ -813,9 +861,8 @@ AND `group_id` = %d
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
$now = as_get_datetime_object();
|
||||
$date = is_null( $before_date ) ? $now : clone $before_date;
|
||||
|
||||
$now = as_get_datetime_object();
|
||||
$date = is_null( $before_date ) ? $now : clone $before_date;
|
||||
// can't use $wpdb->update() because of the <= condition.
|
||||
$update = "UPDATE {$wpdb->actionscheduler_actions} SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s";
|
||||
$params = array(
|
||||
@@ -824,6 +871,18 @@ AND `group_id` = %d
|
||||
current_time( 'mysql' ),
|
||||
);
|
||||
|
||||
// Set claim filters.
|
||||
if ( ! empty( $hooks ) ) {
|
||||
$this->set_claim_filter( 'hooks', $hooks );
|
||||
} else {
|
||||
$hooks = $this->get_claim_filter( 'hooks' );
|
||||
}
|
||||
if ( ! empty( $group ) ) {
|
||||
$this->set_claim_filter( 'group', $group );
|
||||
} else {
|
||||
$group = $this->get_claim_filter( 'group' );
|
||||
}
|
||||
|
||||
$where = 'WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s';
|
||||
$params[] = $date->format( 'Y-m-d H:i:s' );
|
||||
$params[] = self::STATUS_PENDING;
|
||||
@@ -834,18 +893,33 @@ AND `group_id` = %d
|
||||
$params = array_merge( $params, array_values( $hooks ) );
|
||||
}
|
||||
|
||||
$group_operator = 'IN';
|
||||
if ( empty( $group ) ) {
|
||||
$group = $this->get_claim_filter( 'exclude-groups' );
|
||||
$group_operator = 'NOT IN';
|
||||
}
|
||||
|
||||
if ( ! empty( $group ) ) {
|
||||
$group_ids = $this->get_group_ids( $group, false );
|
||||
|
||||
$group_id = $this->get_group_id( $group, false );
|
||||
|
||||
// throw exception if no matching group found, this matches ActionScheduler_wpPostStore's behaviour.
|
||||
if ( empty( $group_id ) ) {
|
||||
/* translators: %s: group name */
|
||||
throw new InvalidArgumentException( sprintf( __( 'The group "%s" does not exist.', 'action-scheduler' ), $group ) );
|
||||
// throw exception if no matching group(s) found, this matches ActionScheduler_wpPostStore's behaviour.
|
||||
if ( empty( $group_ids ) ) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
/* translators: %s: group name(s) */
|
||||
_n(
|
||||
'The group "%s" does not exist.',
|
||||
'The groups "%s" do not exist.',
|
||||
is_array( $group ) ? count( $group ) : 1,
|
||||
'action-scheduler'
|
||||
),
|
||||
$group
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$where .= ' AND group_id = %d';
|
||||
$params[] = $group_id;
|
||||
$id_list = implode( ',', array_map( 'intval', $group_ids ) );
|
||||
$where .= " AND group_id {$group_operator} ( $id_list )";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -855,13 +929,23 @@ AND `group_id` = %d
|
||||
*
|
||||
* @param string $order_by_sql
|
||||
*/
|
||||
$order = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY attempts ASC, scheduled_date_gmt ASC, action_id ASC' );
|
||||
$order = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC' );
|
||||
$params[] = $limit;
|
||||
|
||||
$sql = $wpdb->prepare( "{$update} {$where} {$order} LIMIT %d", $params ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders
|
||||
$rows_affected = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
if ( false === $rows_affected ) {
|
||||
throw new \RuntimeException( __( 'Unable to claim actions. Database error.', 'action-scheduler' ) );
|
||||
$error = empty( $wpdb->last_error )
|
||||
? _x( 'unknown', 'database error', 'action-scheduler' )
|
||||
: $wpdb->last_error;
|
||||
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
/* translators: %s database error. */
|
||||
__( 'Unable to claim actions. Database error: %s.', 'action-scheduler' ),
|
||||
$error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (int) $rows_affected;
|
||||
@@ -912,7 +996,7 @@ AND `group_id` = %d
|
||||
$cut_off = $before_date->format( 'Y-m-d H:i:s' );
|
||||
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT action_id, scheduled_date_gmt FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d",
|
||||
"SELECT action_id, scheduled_date_gmt FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC",
|
||||
$claim_id
|
||||
);
|
||||
|
||||
@@ -955,7 +1039,7 @@ AND `group_id` = %d
|
||||
if ( $row_updates < count( $action_ids ) ) {
|
||||
throw new RuntimeException(
|
||||
sprintf(
|
||||
__( 'Unable to release actions from claim id %d.', 'woocommerce' ),
|
||||
__( 'Unable to release actions from claim id %d.', 'action-scheduler' ),
|
||||
$claim->get_id()
|
||||
)
|
||||
);
|
||||
@@ -1005,6 +1089,8 @@ AND `group_id` = %d
|
||||
/**
|
||||
* Add execution message to action log.
|
||||
*
|
||||
* @throws Exception If the action status cannot be updated to self::STATUS_RUNNING ('in-progress').
|
||||
*
|
||||
* @param int $action_id Action ID.
|
||||
*
|
||||
* @return void
|
||||
@@ -1015,7 +1101,20 @@ AND `group_id` = %d
|
||||
|
||||
$sql = "UPDATE {$wpdb->actionscheduler_actions} SET attempts = attempts+1, status=%s, last_attempt_gmt = %s, last_attempt_local = %s WHERE action_id = %d";
|
||||
$sql = $wpdb->prepare( $sql, self::STATUS_RUNNING, current_time( 'mysql', true ), current_time( 'mysql' ), $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$status_updated = $wpdb->query( $sql );
|
||||
|
||||
if ( ! $status_updated ) {
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
/* translators: 1: action ID. 2: status slug. */
|
||||
__( 'Unable to update the status of action %1$d to %2$s.', 'action-scheduler' ),
|
||||
$action_id,
|
||||
self::STATUS_RUNNING
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -936,6 +936,8 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
/**
|
||||
* Log Execution.
|
||||
*
|
||||
* @throws Exception If the action status cannot be updated to self::STATUS_RUNNING ('in-progress').
|
||||
*
|
||||
* @param string $action_id Action ID.
|
||||
*/
|
||||
public function log_execution( $action_id ) {
|
||||
@@ -947,7 +949,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
global $wpdb;
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
$wpdb->query(
|
||||
$status_updated = $wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"UPDATE {$wpdb->posts} SET menu_order = menu_order+1, post_status=%s, post_modified_gmt = %s, post_modified = %s WHERE ID = %d AND post_type = %s",
|
||||
self::STATUS_RUNNING,
|
||||
@@ -957,6 +959,17 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
self::POST_TYPE
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! $status_updated ) {
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
/* translators: 1: action ID. 2: status slug. */
|
||||
__( 'Unable to update the status of action %1$d to %2$s.', 'action-scheduler' ),
|
||||
$action_id,
|
||||
self::STATUS_RUNNING
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,7 +79,7 @@ class Runner {
|
||||
|
||||
if ( $this->progress_bar ) {
|
||||
/* translators: %d: amount of actions */
|
||||
$this->progress_bar->set_message( sprintf( _n( 'Migrating %d action', 'Migrating %d actions', $batch_size, 'action-scheduler' ), number_format_i18n( $batch_size ) ) );
|
||||
$this->progress_bar->set_message( sprintf( _n( 'Migrating %d action', 'Migrating %d actions', $batch_size, 'action-scheduler' ), $batch_size ) );
|
||||
$this->progress_bar->set_count( $batch_size );
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema {
|
||||
/**
|
||||
* @var int Increment this value to trigger a schema update.
|
||||
*/
|
||||
protected $schema_version = 6;
|
||||
protected $schema_version = 7;
|
||||
|
||||
public function __construct() {
|
||||
$this->tables = [
|
||||
@@ -38,6 +38,7 @@ class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema {
|
||||
$table_name = $wpdb->$table;
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
$max_index_length = 191; // @see wp_get_db_schema()
|
||||
$hook_status_scheduled_date_gmt_max_index_length = $max_index_length - 20 - 8; // - status, - scheduled_date_gmt
|
||||
$default_date = self::DEFAULT_DATE;
|
||||
switch ( $table ) {
|
||||
|
||||
@@ -49,6 +50,7 @@ class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema {
|
||||
status varchar(20) NOT NULL,
|
||||
scheduled_date_gmt datetime NULL default '{$default_date}',
|
||||
scheduled_date_local datetime NULL default '{$default_date}',
|
||||
priority tinyint unsigned NOT NULL default '10',
|
||||
args varchar($max_index_length),
|
||||
schedule longtext,
|
||||
group_id bigint(20) unsigned NOT NULL default '0',
|
||||
@@ -58,8 +60,8 @@ class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema {
|
||||
claim_id bigint(20) unsigned NOT NULL default '0',
|
||||
extended_args varchar(8000) DEFAULT NULL,
|
||||
PRIMARY KEY (action_id),
|
||||
KEY hook (hook($max_index_length)),
|
||||
KEY status (status),
|
||||
KEY hook_status_scheduled_date_gmt (hook($hook_status_scheduled_date_gmt_max_index_length), status, scheduled_date_gmt),
|
||||
KEY status_scheduled_date_gmt (status, scheduled_date_gmt),
|
||||
KEY scheduled_date_gmt (scheduled_date_gmt),
|
||||
KEY args (args($max_index_length)),
|
||||
KEY group_id (group_id),
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
* @param array $args Arguments to pass when the hook triggers.
|
||||
* @param string $group The group to assign this job to.
|
||||
* @param bool $unique Whether the action should be unique.
|
||||
* @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255.
|
||||
*
|
||||
* @return int The action ID.
|
||||
* @return int The action ID. Zero if there was an error scheduling the action.
|
||||
*/
|
||||
function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = false ) {
|
||||
function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) {
|
||||
if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
|
||||
return 0;
|
||||
}
|
||||
@@ -33,13 +34,23 @@ function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique =
|
||||
* @param string $hook Action hook.
|
||||
* @param array $args Action arguments.
|
||||
* @param string $group Action group.
|
||||
* @param int $priority Action priority.
|
||||
*/
|
||||
$pre = apply_filters( 'pre_as_enqueue_async_action', null, $hook, $args, $group );
|
||||
$pre = apply_filters( 'pre_as_enqueue_async_action', null, $hook, $args, $group, $priority );
|
||||
if ( null !== $pre ) {
|
||||
return is_int( $pre ) ? $pre : 0;
|
||||
}
|
||||
|
||||
return ActionScheduler::factory()->async_unique( $hook, $args, $group, $unique );
|
||||
return ActionScheduler::factory()->create(
|
||||
array(
|
||||
'type' => 'async',
|
||||
'hook' => $hook,
|
||||
'arguments' => $args,
|
||||
'group' => $group,
|
||||
'unique' => $unique,
|
||||
'priority' => $priority,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,10 +61,11 @@ function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique =
|
||||
* @param array $args Arguments to pass when the hook triggers.
|
||||
* @param string $group The group to assign this job to.
|
||||
* @param bool $unique Whether the action should be unique.
|
||||
* @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255.
|
||||
*
|
||||
* @return int The action ID.
|
||||
* @return int The action ID. Zero if there was an error scheduling the action.
|
||||
*/
|
||||
function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '', $unique = false ) {
|
||||
function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) {
|
||||
if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
|
||||
return 0;
|
||||
}
|
||||
@@ -72,13 +84,24 @@ function as_schedule_single_action( $timestamp, $hook, $args = array(), $group =
|
||||
* @param string $hook Action hook.
|
||||
* @param array $args Action arguments.
|
||||
* @param string $group Action group.
|
||||
* @param int $priorities Action priority.
|
||||
*/
|
||||
$pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group );
|
||||
$pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group, $priority );
|
||||
if ( null !== $pre ) {
|
||||
return is_int( $pre ) ? $pre : 0;
|
||||
}
|
||||
|
||||
return ActionScheduler::factory()->single_unique( $hook, $args, $timestamp, $group, $unique );
|
||||
return ActionScheduler::factory()->create(
|
||||
array(
|
||||
'type' => 'single',
|
||||
'hook' => $hook,
|
||||
'arguments' => $args,
|
||||
'when' => $timestamp,
|
||||
'group' => $group,
|
||||
'unique' => $unique,
|
||||
'priority' => $priority,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,14 +113,34 @@ function as_schedule_single_action( $timestamp, $hook, $args = array(), $group =
|
||||
* @param array $args Arguments to pass when the hook triggers.
|
||||
* @param string $group The group to assign this job to.
|
||||
* @param bool $unique Whether the action should be unique.
|
||||
* @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255.
|
||||
*
|
||||
* @return int The action ID.
|
||||
* @return int The action ID. Zero if there was an error scheduling the action.
|
||||
*/
|
||||
function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '', $unique = false ) {
|
||||
function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) {
|
||||
if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$interval = (int) $interval_in_seconds;
|
||||
|
||||
// We expect an integer and allow it to be passed using float and string types, but otherwise
|
||||
// should reject unexpected values.
|
||||
if ( ! is_numeric( $interval_in_seconds ) || $interval_in_seconds != $interval ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
sprintf(
|
||||
/* translators: 1: provided value 2: provided type. */
|
||||
esc_html__( 'An integer was expected but "%1$s" (%2$s) was received.', 'action-scheduler' ),
|
||||
esc_html( $interval_in_seconds ),
|
||||
esc_html( gettype( $interval_in_seconds ) )
|
||||
),
|
||||
'3.6.0'
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an opportunity to short-circuit the default process for enqueuing recurring
|
||||
* actions.
|
||||
@@ -113,13 +156,25 @@ function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook,
|
||||
* @param string $hook Action hook.
|
||||
* @param array $args Action arguments.
|
||||
* @param string $group Action group.
|
||||
* @param int $priority Action priority.
|
||||
*/
|
||||
$pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group );
|
||||
$pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group, $priority );
|
||||
if ( null !== $pre ) {
|
||||
return is_int( $pre ) ? $pre : 0;
|
||||
}
|
||||
|
||||
return ActionScheduler::factory()->recurring_unique( $hook, $args, $timestamp, $interval_in_seconds, $group, $unique );
|
||||
return ActionScheduler::factory()->create(
|
||||
array(
|
||||
'type' => 'recurring',
|
||||
'hook' => $hook,
|
||||
'arguments' => $args,
|
||||
'when' => $timestamp,
|
||||
'pattern' => $interval_in_seconds,
|
||||
'group' => $group,
|
||||
'unique' => $unique,
|
||||
'priority' => $priority,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,10 +198,11 @@ function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook,
|
||||
* @param array $args Arguments to pass when the hook triggers.
|
||||
* @param string $group The group to assign this job to.
|
||||
* @param bool $unique Whether the action should be unique.
|
||||
* @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255.
|
||||
*
|
||||
* @return int The action ID.
|
||||
* @return int The action ID. Zero if there was an error scheduling the action.
|
||||
*/
|
||||
function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '', $unique = false ) {
|
||||
function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) {
|
||||
if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
|
||||
return 0;
|
||||
}
|
||||
@@ -166,13 +222,25 @@ function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(),
|
||||
* @param string $hook Action hook.
|
||||
* @param array $args Action arguments.
|
||||
* @param string $group Action group.
|
||||
* @param int $priority Action priority.
|
||||
*/
|
||||
$pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group );
|
||||
$pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group, $priority );
|
||||
if ( null !== $pre ) {
|
||||
return is_int( $pre ) ? $pre : 0;
|
||||
}
|
||||
|
||||
return ActionScheduler::factory()->cron_unique( $hook, $args, $timestamp, $schedule, $group, $unique );
|
||||
return ActionScheduler::factory()->create(
|
||||
array(
|
||||
'type' => 'cron',
|
||||
'hook' => $hook,
|
||||
'arguments' => $args,
|
||||
'when' => $timestamp,
|
||||
'pattern' => $schedule,
|
||||
'group' => $group,
|
||||
'unique' => $unique,
|
||||
'priority' => $priority,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,9 +283,10 @@ function as_unschedule_action( $hook, $args = array(), $group = '' ) {
|
||||
ActionScheduler::logger()->log(
|
||||
$action_id,
|
||||
sprintf(
|
||||
/* translators: %s is the name of the hook to be cancelled. */
|
||||
__( 'Caught exception while cancelling action: %s', 'action-scheduler' ),
|
||||
esc_attr( $hook )
|
||||
/* translators: %1$s is the name of the hook to be cancelled, %2$s is the exception message. */
|
||||
__( 'Caught exception while cancelling action "%1$s": %2$s', 'action-scheduler' ),
|
||||
$hook,
|
||||
$exception->getMessage()
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
=== Action Scheduler ===
|
||||
Contributors: Automattic, wpmuguru, claudiosanches, peterfabian1000, vedjain, jamosova, obliviousharmony, konamiman, sadowski, royho, barryhughes-1
|
||||
Tags: scheduler, cron
|
||||
Requires at least: 5.2
|
||||
Tested up to: 6.0
|
||||
Stable tag: 3.5.4
|
||||
Stable tag: 3.7.1
|
||||
License: GPLv3
|
||||
Requires at least: 6.2
|
||||
Tested up to: 6.4
|
||||
Requires PHP: 5.6
|
||||
|
||||
Action Scheduler - Job Queue for WordPress
|
||||
@@ -47,6 +47,66 @@ Collaboration is cool. We'd love to work with you to improve Action Scheduler. [
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 3.7.1 - 2023-12-13 =
|
||||
* Release/3.7.0.
|
||||
* Tweak - WP 6.4 compatibility.
|
||||
* update semver to 5.7.2 because of a security vulnerability in 5.7.1.
|
||||
|
||||
= 3.7.0 - 2023-11-20 =
|
||||
* Important: starting with this release, Action Scheduler follows an L-2 version policy (WordPress, and consequently PHP).
|
||||
* Add extended indexes for hook_status_scheduled_date_gmt and status_sheduled_date_gmt.
|
||||
* Catch and log exceptions thrown when actions can't be created, e.g. under a corrupt database schema.
|
||||
* Release/3.6.4.
|
||||
* Tweak - WP 6.4 compatibility.
|
||||
* Update unit tests for upcoming dependency version policy.
|
||||
* make sure hook action_scheduler_failed_execution can access original exception object.
|
||||
* mention dependency version policy in usage.md.
|
||||
|
||||
= 3.6.4 - 2023-10-11 =
|
||||
* Performance improvements when bulk cancelling actions.
|
||||
* Dev-related fixes.
|
||||
|
||||
= 3.6.3 - 2023-09-13 =
|
||||
* Use `_doing_it_wrong` in initialization check.
|
||||
|
||||
= 3.6.2 - 2023-08-09 =
|
||||
* Add guidance about passing arguments.
|
||||
* Atomic option locking.
|
||||
* Improve bulk delete handling.
|
||||
* Include database error in the exception message.
|
||||
* Tweak - WP 6.3 compatibility.
|
||||
|
||||
= 3.6.1 - 2023-06-14 =
|
||||
* Document new optional `$priority` arg for various API functions.
|
||||
* Document the new `--exclude-groups` WP CLI option.
|
||||
* Document the new `action_scheduler_init` hook.
|
||||
* Ensure actions within each claim are executed in the expected order.
|
||||
* Fix incorrect text domain.
|
||||
* Remove SHOW TABLES usage when checking if tables exist.
|
||||
|
||||
= 3.6.0 - 2023-05-10 =
|
||||
* Add $unique parameter to function signatures.
|
||||
* Add a cast-to-int for extra safety before forming new DateTime object.
|
||||
* Add a hook allowing exceptions for consistently failing recurring actions.
|
||||
* Add action priorities.
|
||||
* Add init hook.
|
||||
* Always raise the time limit.
|
||||
* Bump minimatch from 3.0.4 to 3.0.8.
|
||||
* Bump yaml from 2.2.1 to 2.2.2.
|
||||
* Defensive coding relating to gaps in declared schedule types.
|
||||
* Do not process an action if it cannot be set to `in-progress`.
|
||||
* Filter view labels (status names) should be translatable | #919.
|
||||
* Fix WPCLI progress messages.
|
||||
* Improve data-store initialization flow.
|
||||
* Improve error handling across all supported PHP versions.
|
||||
* Improve logic for flushing the runtime cache.
|
||||
* Support exclusion of multiple groups.
|
||||
* Update lint-staged and Node/NPM requirements.
|
||||
* add CLI clean command.
|
||||
* add CLI exclude-group filter.
|
||||
* exclude past-due from list table all filter count.
|
||||
* throwing an exception if as_schedule_recurring_action interval param is not of type integer.
|
||||
|
||||
= 3.5.4 - 2023-01-17 =
|
||||
* Add pre filters during action registration.
|
||||
* Async scheduling.
|
||||
|
||||
@@ -296,7 +296,7 @@ function _imagify_new_upgrade( $network_version, $site_version ) {
|
||||
|
||||
// 1.9.6
|
||||
if ( version_compare( $site_version, '1.9.6' ) < 0 ) {
|
||||
\Imagify\Stats\OptimizedMediaWithoutWebp::get_instance()->clear_cache();
|
||||
\Imagify\Stats\OptimizedMediaWithoutNextGen::get_instance()->clear_cache();
|
||||
}
|
||||
|
||||
// 1.9.11
|
||||
@@ -307,6 +307,11 @@ function _imagify_new_upgrade( $network_version, $site_version ) {
|
||||
if ( version_compare( $site_version, '2.0' ) < 0 ) {
|
||||
Imagify_Options::get_instance()->set( 'optimization_level', 2 );
|
||||
}
|
||||
|
||||
if ( version_compare( $site_version, '2.2' ) < 0 ) {
|
||||
Imagify_Options::get_instance()->set( 'display_nextgen', Imagify_Options::get_instance()->get( 'display_webp', 0 ) );
|
||||
Imagify_Options::get_instance()->set( 'display_nextgen_method', Imagify_Options::get_instance()->get( 'display_webp_method' ) );
|
||||
}
|
||||
}
|
||||
add_action( 'imagify_upgrade', '_imagify_new_upgrade', 10, 2 );
|
||||
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
* @package WP-Background-Processing
|
||||
*/
|
||||
|
||||
// phpcs:disable Generic.Commenting.DocComment.MissingShort
|
||||
/** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
/** @noinspection AutoloadingIssuesInspection */
|
||||
// phpcs:disable Generic.Commenting.DocComment.MissingShort
|
||||
|
||||
/**
|
||||
* Abstract Imagify_WP_Async_Request class.
|
||||
*
|
||||
@@ -51,7 +56,7 @@ abstract class Imagify_WP_Async_Request {
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Initiate new async request
|
||||
* Initiate new async request.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->identifier = $this->prefix . '_' . $this->action;
|
||||
@@ -61,7 +66,7 @@ abstract class Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data used during the request
|
||||
* Set data used during the request.
|
||||
*
|
||||
* @param array $data Data.
|
||||
*
|
||||
@@ -74,9 +79,9 @@ abstract class Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the async request
|
||||
* Dispatch the async request.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
* @return array|WP_Error|false HTTP Response array, WP_Error on failure, or false if not attempted.
|
||||
*/
|
||||
public function dispatch() {
|
||||
$url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
|
||||
@@ -86,7 +91,7 @@ abstract class Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query args
|
||||
* Get query args.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@@ -109,7 +114,7 @@ abstract class Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query URL
|
||||
* Get query URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -129,7 +134,7 @@ abstract class Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post args
|
||||
* Get post args.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@@ -139,11 +144,11 @@ abstract class Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'timeout' => 0.01,
|
||||
'timeout' => 5,
|
||||
'blocking' => false,
|
||||
'body' => $this->data,
|
||||
'cookies' => $_COOKIE,
|
||||
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
|
||||
'cookies' => $_COOKIE, // Passing cookies ensures request is performed as initiating user.
|
||||
'sslverify' => apply_filters( 'https_local_ssl_verify', false ), // Local requests, fine to pass false.
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -155,27 +160,49 @@ abstract class Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe handle
|
||||
* Maybe handle a dispatched request.
|
||||
*
|
||||
* Check for correct nonce and pass to handler.
|
||||
*
|
||||
* @return void|mixed
|
||||
*/
|
||||
public function maybe_handle() {
|
||||
// Don't lock up other requests while processing
|
||||
// Don't lock up other requests while processing.
|
||||
session_write_close();
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
wp_die();
|
||||
return $this->maybe_wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle
|
||||
* Should the process exit with wp_die?
|
||||
*
|
||||
* @param mixed $return What to return if filter says don't die, default is null.
|
||||
*
|
||||
* @return void|mixed
|
||||
* @noinspection ForgottenDebugOutputInspection
|
||||
*/
|
||||
protected function maybe_wp_die( $return = null ) {
|
||||
/**
|
||||
* Should wp_die be used?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
if ( apply_filters( $this->identifier . '_wp_die', true ) ) {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a dispatched request.
|
||||
*
|
||||
* Override this method to perform any actions required
|
||||
* during the async request.
|
||||
*/
|
||||
abstract protected function handle();
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
* @package WP-Background-Processing
|
||||
*/
|
||||
|
||||
// phpcs:disable Generic.Commenting.DocComment.MissingShort
|
||||
/** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
/** @noinspection AutoloadingIssuesInspection */
|
||||
// phpcs:disable Generic.Commenting.DocComment.MissingShort
|
||||
|
||||
/**
|
||||
* Abstract Imagify_WP_Background_Process class.
|
||||
*
|
||||
@@ -36,7 +41,7 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
/**
|
||||
* Cron_hook_identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $cron_hook_identifier;
|
||||
@@ -44,13 +49,27 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
/**
|
||||
* Cron_interval_identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $cron_interval_identifier;
|
||||
|
||||
/**
|
||||
* Initiate new background process
|
||||
* The status set when process is cancelling.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const STATUS_CANCELLED = 1;
|
||||
|
||||
/**
|
||||
* The status set when process is paused or pausing.
|
||||
*
|
||||
* @var int;
|
||||
*/
|
||||
const STATUS_PAUSED = 2;
|
||||
|
||||
/**
|
||||
* Initiate new background process.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
@@ -59,16 +78,22 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
$this->cron_interval_identifier = $this->identifier . '_cron_interval';
|
||||
|
||||
add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) );
|
||||
// phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
|
||||
add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch
|
||||
* Schedule the cron healthcheck and dispatch an async request to start processing the queue.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
* @return array|WP_Error|false HTTP Response array, WP_Error on failure, or false if not attempted.
|
||||
*/
|
||||
public function dispatch() {
|
||||
if ( $this->is_processing() ) {
|
||||
// Process already running.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Schedule the cron healthcheck.
|
||||
$this->schedule_event();
|
||||
|
||||
@@ -77,7 +102,9 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Push to queue
|
||||
* Push to the queue.
|
||||
*
|
||||
* Note, save must be called in order to persist queued items to a batch for processing.
|
||||
*
|
||||
* @param mixed $data Data.
|
||||
*
|
||||
@@ -90,7 +117,7 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Save queue
|
||||
* Save the queued items for future processing.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
@@ -101,11 +128,14 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
update_site_option( $key, $this->data );
|
||||
}
|
||||
|
||||
// Clean out data so that new data isn't prepended with closed session's data.
|
||||
$this->data = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update queue
|
||||
* Update a batch's queued items.
|
||||
*
|
||||
* @param string $key Key.
|
||||
* @param array $data Data.
|
||||
@@ -121,7 +151,7 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete queue
|
||||
* Delete a batch of queued items.
|
||||
*
|
||||
* @param string $key Key.
|
||||
*
|
||||
@@ -134,83 +164,209 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate key
|
||||
* Delete entire job queue.
|
||||
*/
|
||||
public function delete_all() {
|
||||
$batches = $this->get_batches();
|
||||
|
||||
foreach ( $batches as $batch ) {
|
||||
$this->delete( $batch->key );
|
||||
}
|
||||
|
||||
delete_site_option( $this->get_status_key() );
|
||||
|
||||
$this->cancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel job on next batch.
|
||||
*/
|
||||
public function cancel() {
|
||||
update_site_option( $this->get_status_key(), self::STATUS_CANCELLED );
|
||||
|
||||
// Just in case the job was paused at the time.
|
||||
$this->dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the process been cancelled?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_cancelled() {
|
||||
$status = get_site_option( $this->get_status_key(), 0 );
|
||||
|
||||
return absint( $status ) === self::STATUS_CANCELLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when background process has been cancelled.
|
||||
*/
|
||||
protected function cancelled() {
|
||||
do_action( $this->identifier . '_cancelled' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause job on next batch.
|
||||
*/
|
||||
public function pause() {
|
||||
update_site_option( $this->get_status_key(), self::STATUS_PAUSED );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the job paused?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_paused() {
|
||||
$status = get_site_option( $this->get_status_key(), 0 );
|
||||
|
||||
return absint( $status ) === self::STATUS_PAUSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when background process has been paused.
|
||||
*/
|
||||
protected function paused() {
|
||||
do_action( $this->identifier . '_paused' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume job.
|
||||
*/
|
||||
public function resume() {
|
||||
delete_site_option( $this->get_status_key() );
|
||||
|
||||
$this->schedule_event();
|
||||
$this->dispatch();
|
||||
$this->resumed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when background process has been resumed.
|
||||
*/
|
||||
protected function resumed() {
|
||||
do_action( $this->identifier . '_resumed' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is queued?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_queued() {
|
||||
return ! $this->is_queue_empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the tool currently active, e.g. starting, working, paused or cleaning up?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_active() {
|
||||
return $this->is_queued() || $this->is_processing() || $this->is_paused() || $this->is_cancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate key for a batch.
|
||||
*
|
||||
* 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.
|
||||
* @param int $length Optional max length to trim key to, defaults to 64 characters.
|
||||
* @param string $key Optional string to append to identifier before hash, defaults to "batch".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_key( $length = 64 ) {
|
||||
$unique = md5( microtime() . rand() );
|
||||
$prepend = $this->identifier . '_batch_';
|
||||
protected function generate_key( $length = 64, $key = 'batch' ) {
|
||||
$unique = md5( microtime() . wp_rand() );
|
||||
$prepend = $this->identifier . '_' . $key . '_';
|
||||
|
||||
return substr( $prepend . $unique, 0, $length );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe process queue
|
||||
* Get the status key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_status_key() {
|
||||
return $this->identifier . '_status';
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe process a batch of queued items.
|
||||
*
|
||||
* 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
|
||||
// Don't lock up other requests while processing.
|
||||
session_write_close();
|
||||
|
||||
if ( $this->is_process_running() ) {
|
||||
if ( $this->is_processing() ) {
|
||||
// Background process already running.
|
||||
wp_die();
|
||||
return $this->maybe_wp_die();
|
||||
}
|
||||
|
||||
if ( $this->is_cancelled() ) {
|
||||
$this->clear_scheduled_event();
|
||||
$this->delete_all();
|
||||
|
||||
return $this->maybe_wp_die();
|
||||
}
|
||||
|
||||
if ( $this->is_paused() ) {
|
||||
$this->clear_scheduled_event();
|
||||
$this->paused();
|
||||
|
||||
return $this->maybe_wp_die();
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
wp_die();
|
||||
return $this->maybe_wp_die();
|
||||
}
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
wp_die();
|
||||
return $this->maybe_wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is queue empty
|
||||
* Is queue empty?
|
||||
*
|
||||
* @return bool
|
||||
* @noinspection IsEmptyFunctionUsageInspection
|
||||
*/
|
||||
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_' ) . '%';
|
||||
|
||||
$count = $wpdb->get_var( $wpdb->prepare( "
|
||||
SELECT COUNT(*)
|
||||
FROM {$table}
|
||||
WHERE {$column} LIKE %s
|
||||
", $key ) );
|
||||
|
||||
return ( $count > 0 ) ? false : true;
|
||||
return empty( $this->get_batch() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is process running
|
||||
* Is process running?
|
||||
*
|
||||
* Check whether the current process is already running
|
||||
* in a background process.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @deprecated 1.1.0 Superseded.
|
||||
* @see is_processing()
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
protected function is_process_running() {
|
||||
return $this->is_processing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the background process currently running?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_processing() {
|
||||
if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
|
||||
// Process already running.
|
||||
return true;
|
||||
@@ -220,7 +376,7 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock process
|
||||
* Lock process.
|
||||
*
|
||||
* Lock the process so that multiple instances can't run simultaneously.
|
||||
* Override if applicable, but the duration should be greater than that
|
||||
@@ -236,7 +392,7 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock process
|
||||
* Unlock process.
|
||||
*
|
||||
* Unlock the process so that other instances can spawn.
|
||||
*
|
||||
@@ -249,13 +405,34 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get batch
|
||||
* Get batch.
|
||||
*
|
||||
* @return stdClass Return the first batch from the queue
|
||||
* @return stdClass Return the first batch of queued items.
|
||||
*/
|
||||
protected function get_batch() {
|
||||
return array_reduce(
|
||||
$this->get_batches( 1 ),
|
||||
static function ( $carry, $batch ) {
|
||||
return $batch;
|
||||
},
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get batches.
|
||||
*
|
||||
* @param int $limit Number of batches to return, defaults to all.
|
||||
*
|
||||
* @return array of stdClass
|
||||
*/
|
||||
public function get_batches( $limit = 0 ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( empty( $limit ) || ! is_int( $limit ) ) {
|
||||
$limit = 0;
|
||||
}
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
$key_column = 'option_id';
|
||||
@@ -270,30 +447,68 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
$query = $wpdb->get_row( $wpdb->prepare( "
|
||||
$sql = '
|
||||
SELECT *
|
||||
FROM {$table}
|
||||
WHERE {$column} LIKE %s
|
||||
ORDER BY {$key_column} ASC
|
||||
LIMIT 1
|
||||
", $key ) );
|
||||
FROM ' . $table . '
|
||||
WHERE ' . $column . ' LIKE %s
|
||||
ORDER BY ' . $key_column . '
|
||||
';
|
||||
|
||||
$batch = new stdClass();
|
||||
$batch->key = $query->$column;
|
||||
$batch->data = maybe_unserialize( $query->$value_column );
|
||||
$args = array( $key );
|
||||
|
||||
return $batch;
|
||||
if ( ! empty( $limit ) ) {
|
||||
$sql .= ' LIMIT %d';
|
||||
|
||||
$args[] = $limit;
|
||||
}
|
||||
|
||||
$items = $wpdb->get_results( $wpdb->prepare( $sql, $args ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
$batches = array();
|
||||
|
||||
if ( ! empty( $items ) ) {
|
||||
$batches = array_map(
|
||||
static function ( $item ) use ( $column, $value_column ) {
|
||||
$batch = new stdClass();
|
||||
$batch->key = $item->{$column};
|
||||
$batch->data = maybe_unserialize( $item->{$value_column} );
|
||||
|
||||
return $batch;
|
||||
},
|
||||
$items
|
||||
);
|
||||
}
|
||||
|
||||
return $batches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle
|
||||
* Handle a dispatched request.
|
||||
*
|
||||
* Pass each queue item to the task handler, while remaining
|
||||
* within server memory and time limit constraints.
|
||||
*
|
||||
* @noinspection DisconnectedForeachInstructionInspection
|
||||
*/
|
||||
protected function handle() {
|
||||
$this->lock_process();
|
||||
|
||||
/**
|
||||
* Number of seconds to sleep between batches. Defaults to 0 seconds, minimum 0.
|
||||
*
|
||||
* @param int $seconds
|
||||
*/
|
||||
$throttle_seconds = max(
|
||||
0,
|
||||
apply_filters(
|
||||
$this->identifier . '_seconds_between_batches',
|
||||
apply_filters(
|
||||
$this->prefix . '_seconds_between_batches',
|
||||
0
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
do {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
@@ -306,19 +521,25 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
unset( $batch->data[ $key ] );
|
||||
}
|
||||
|
||||
if ( $this->time_exceeded() || $this->memory_exceeded() ) {
|
||||
// Batch limits reached.
|
||||
// Keep the batch up to date while processing it.
|
||||
if ( ! empty( $batch->data ) ) {
|
||||
$this->update( $batch->key, $batch->data );
|
||||
}
|
||||
|
||||
// Let the server breathe a little.
|
||||
sleep( $throttle_seconds );
|
||||
|
||||
// Batch limits reached, or pause or cancel request.
|
||||
if ( $this->time_exceeded() || $this->memory_exceeded() || $this->is_paused() || $this->is_cancelled() ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update or delete current batch.
|
||||
if ( ! empty( $batch->data ) ) {
|
||||
$this->update( $batch->key, $batch->data );
|
||||
} else {
|
||||
// Delete current batch if fully processed.
|
||||
if ( empty( $batch->data ) ) {
|
||||
$this->delete( $batch->key );
|
||||
}
|
||||
} while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
|
||||
} while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() && ! $this->is_paused() && ! $this->is_cancelled() );
|
||||
|
||||
$this->unlock_process();
|
||||
|
||||
@@ -329,11 +550,11 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
$this->complete();
|
||||
}
|
||||
|
||||
wp_die();
|
||||
return $this->maybe_wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory exceeded
|
||||
* Memory exceeded?
|
||||
*
|
||||
* Ensures the batch process never exceeds 90%
|
||||
* of the maximum WordPress memory.
|
||||
@@ -353,7 +574,7 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory limit
|
||||
* Get memory limit in bytes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -365,7 +586,7 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
$memory_limit = '128M';
|
||||
}
|
||||
|
||||
if ( ! $memory_limit || - 1 === intval( $memory_limit ) ) {
|
||||
if ( ! $memory_limit || -1 === (int) $memory_limit ) {
|
||||
// Unlimited, set to 32GB.
|
||||
$memory_limit = '32000M';
|
||||
}
|
||||
@@ -374,7 +595,7 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Time exceeded.
|
||||
* Time limit exceeded?
|
||||
*
|
||||
* Ensures the batch never exceeds a sensible time limit.
|
||||
* A timeout limit of 30s is common on shared hosting.
|
||||
@@ -385,7 +606,10 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
$finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds
|
||||
$return = false;
|
||||
|
||||
if ( time() >= $finish ) {
|
||||
if (
|
||||
! ( defined( 'WP_CLI' ) && WP_CLI ) &&
|
||||
time() >= $finish
|
||||
) {
|
||||
$return = true;
|
||||
}
|
||||
|
||||
@@ -393,18 +617,29 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete.
|
||||
* Complete processing.
|
||||
*
|
||||
* Override if applicable, but ensure that the below actions are
|
||||
* performed, or, call parent::complete().
|
||||
*/
|
||||
protected function complete() {
|
||||
// Unschedule the cron healthcheck.
|
||||
delete_site_option( $this->get_status_key() );
|
||||
|
||||
// Remove the cron healthcheck job from the cron schedule.
|
||||
$this->clear_scheduled_event();
|
||||
|
||||
$this->completed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule cron healthcheck
|
||||
* Called when background process has completed.
|
||||
*/
|
||||
protected function completed() {
|
||||
do_action( $this->identifier . '_completed' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the cron healthcheck job.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
@@ -413,29 +648,35 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
* @return mixed
|
||||
*/
|
||||
public function schedule_cron_healthcheck( $schedules ) {
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', 5 );
|
||||
$interval = apply_filters( $this->cron_interval_identifier, 5 );
|
||||
|
||||
if ( property_exists( $this, 'cron_interval' ) ) {
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval );
|
||||
$interval = apply_filters( $this->cron_interval_identifier, $this->cron_interval );
|
||||
}
|
||||
|
||||
// Adds every 5 minutes to the existing schedules.
|
||||
$schedules[ $this->identifier . '_cron_interval' ] = array(
|
||||
if ( 1 === $interval ) {
|
||||
$display = __( 'Every Minute' );
|
||||
} else {
|
||||
$display = sprintf( __( 'Every %d Minutes' ), $interval );
|
||||
}
|
||||
|
||||
// Adds an "Every NNN Minute(s)" schedule to the existing cron schedules.
|
||||
$schedules[ $this->cron_interval_identifier ] = array(
|
||||
'interval' => MINUTE_IN_SECONDS * $interval,
|
||||
'display' => sprintf( __( 'Every %d Minutes' ), $interval ),
|
||||
'display' => $display,
|
||||
);
|
||||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cron healthcheck
|
||||
* Handle cron healthcheck event.
|
||||
*
|
||||
* Restart the background process if not already running
|
||||
* and data exists in the queue.
|
||||
*/
|
||||
public function handle_cron_healthcheck() {
|
||||
if ( $this->is_process_running() ) {
|
||||
if ( $this->is_processing() ) {
|
||||
// Background process already running.
|
||||
exit;
|
||||
}
|
||||
@@ -446,13 +687,11 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->handle();
|
||||
|
||||
exit;
|
||||
$this->dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule event
|
||||
* Schedule the cron healthcheck event.
|
||||
*/
|
||||
protected function schedule_event() {
|
||||
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
|
||||
@@ -461,7 +700,7 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear scheduled event
|
||||
* Clear scheduled cron healthcheck event.
|
||||
*/
|
||||
protected function clear_scheduled_event() {
|
||||
$timestamp = wp_next_scheduled( $this->cron_hook_identifier );
|
||||
@@ -472,24 +711,20 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel Process
|
||||
* Cancel the background process.
|
||||
*
|
||||
* Stop processing queue items, clear cronjob and delete batch.
|
||||
* Stop processing queue items, clear cron job and delete batch.
|
||||
*
|
||||
* @deprecated 1.1.0 Superseded.
|
||||
* @see cancel()
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
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 );
|
||||
}
|
||||
|
||||
$this->cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task
|
||||
* Perform task with queued item.
|
||||
*
|
||||
* Override this method to perform any actions required on each
|
||||
* queue item. Return the modified item for further processing
|
||||
@@ -501,5 +736,4 @@ abstract class Imagify_WP_Background_Process extends Imagify_WP_Async_Request {
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function task( $item );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
namespace Imagify\EventManagement;
|
||||
|
||||
/**
|
||||
* The event manager manages events using the WordPress plugin API.
|
||||
*
|
||||
* @since 3.1
|
||||
* @author Carl Alexander <contact@carlalexander.ca>
|
||||
*/
|
||||
class EventManager {
|
||||
/**
|
||||
* Adds a callback to a specific hook of the WordPress plugin API.
|
||||
*
|
||||
* @uses add_filter()
|
||||
*
|
||||
* @param string $hook_name Name of the hook.
|
||||
* @param callable $callback Callback function.
|
||||
* @param int $priority Priority.
|
||||
* @param int $accepted_args Number of arguments.
|
||||
*/
|
||||
public function add_callback( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) {
|
||||
add_filter( $hook_name, $callback, $priority, $accepted_args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event subscriber.
|
||||
*
|
||||
* The event manager registers all the hooks that the given subscriber
|
||||
* wants to register with the WordPress Plugin API.
|
||||
*
|
||||
* @param SubscriberInterface $subscriber SubscriberInterface implementation.
|
||||
*/
|
||||
public function add_subscriber( SubscriberInterface $subscriber ) {
|
||||
if ( $subscriber instanceof EventManagerAwareSubscriberInterface ) {
|
||||
$subscriber->set_event_manager( $this );
|
||||
}
|
||||
|
||||
$events = $subscriber->get_subscribed_events();
|
||||
|
||||
if ( empty( $events ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $subscriber->get_subscribed_events() as $hook_name => $parameters ) {
|
||||
$this->add_subscriber_callback( $subscriber, $hook_name, $parameters );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the WordPress plugin API to see if the given hook has
|
||||
* the given callback. The priority of the callback will be returned
|
||||
* or false. If no callback is given will return true or false if
|
||||
* there's any callbacks registered to the hook.
|
||||
*
|
||||
* @uses has_filter()
|
||||
*
|
||||
* @param string $hook_name Hook name.
|
||||
* @param mixed $callback Callback.
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
public function has_callback( $hook_name, $callback = false ) {
|
||||
return has_filter( $hook_name, $callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given callback from the given hook. The WordPress plugin API only
|
||||
* removes the hook if the callback and priority match a registered hook.
|
||||
*
|
||||
* @uses remove_filter()
|
||||
*
|
||||
* @param string $hook_name Hook name.
|
||||
* @param callable $callback Callback.
|
||||
* @param int $priority Priority.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function remove_callback( $hook_name, $callback, $priority = 10 ) {
|
||||
return remove_filter( $hook_name, $callback, $priority );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an event subscriber.
|
||||
*
|
||||
* The event manager removes all the hooks that the given subscriber
|
||||
* wants to register with the WordPress Plugin API.
|
||||
*
|
||||
* @param SubscriberInterface $subscriber SubscriberInterface implementation.
|
||||
*/
|
||||
public function remove_subscriber( SubscriberInterface $subscriber ) {
|
||||
foreach ( $subscriber->get_subscribed_events() as $hook_name => $parameters ) {
|
||||
$this->remove_subscriber_callback( $subscriber, $hook_name, $parameters );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given subscriber's callback to a specific hook
|
||||
* of the WordPress plugin API.
|
||||
*
|
||||
* @param SubscriberInterface $subscriber SubscriberInterface implementation.
|
||||
* @param string $hook_name Hook name.
|
||||
* @param mixed $parameters Parameters, can be a string, an array or a multidimensional array.
|
||||
*/
|
||||
private function add_subscriber_callback( SubscriberInterface $subscriber, $hook_name, $parameters ) {
|
||||
if ( is_string( $parameters ) ) {
|
||||
$this->add_callback( $hook_name, [ $subscriber, $parameters ] );
|
||||
} elseif ( is_array( $parameters ) && count( $parameters ) !== count( $parameters, COUNT_RECURSIVE ) ) {
|
||||
foreach ( $parameters as $parameter ) {
|
||||
$this->add_subscriber_callback( $subscriber, $hook_name, $parameter );
|
||||
}
|
||||
} elseif ( is_array( $parameters ) && isset( $parameters[0] ) ) {
|
||||
$this->add_callback( $hook_name, [ $subscriber, $parameters[0] ], isset( $parameters[1] ) ? $parameters[1] : 10, isset( $parameters[2] ) ? $parameters[2] : 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given subscriber's callback to a specific hook
|
||||
* of the WordPress plugin API.
|
||||
*
|
||||
* @param SubscriberInterface $subscriber SubscriberInterface implementation.
|
||||
* @param string $hook_name Hook name.
|
||||
* @param mixed $parameters Parameters, can be a string, an array or a multidimensional array.
|
||||
*/
|
||||
private function remove_subscriber_callback( SubscriberInterface $subscriber, $hook_name, $parameters ) {
|
||||
if ( is_string( $parameters ) ) {
|
||||
$this->remove_callback( $hook_name, [ $subscriber, $parameters ] );
|
||||
} elseif ( is_array( $parameters ) && count( $parameters ) !== count( $parameters, COUNT_RECURSIVE ) ) {
|
||||
foreach ( $parameters as $parameter ) {
|
||||
$this->remove_subscriber_callback( $subscriber, $hook_name, $parameter );
|
||||
}
|
||||
} elseif ( is_array( $parameters ) && isset( $parameters[0] ) ) {
|
||||
$this->remove_callback( $hook_name, [ $subscriber, $parameters[0] ], isset( $parameters[1] ) ? $parameters[1] : 10 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace Imagify\EventManagement;
|
||||
|
||||
interface EventManagerAwareSubscriberInterface extends SubscriberInterface {
|
||||
/**
|
||||
* Set the WordPress event manager for the subscriber.
|
||||
*
|
||||
* @since 3.1
|
||||
* @author Remy Perona
|
||||
*
|
||||
* @param EventManager $event_manager EventManager instance.
|
||||
*/
|
||||
public function set_event_manager( EventManager $event_manager );
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Imagify\EventManagement;
|
||||
|
||||
/**
|
||||
* A Subscriber knows what specific WordPress events it wants to listen to.
|
||||
*
|
||||
* When an EventManager adds a Subscriber, it gets all the WordPress events that
|
||||
* it wants to listen to. It then adds the subscriber as a listener for each of them.
|
||||
*
|
||||
* @author Carl Alexander <contact@carlalexander.ca>
|
||||
*/
|
||||
interface SubscriberInterface {
|
||||
/**
|
||||
* Returns an array of events that this subscriber wants to listen to.
|
||||
*
|
||||
* The array key is the event name. The value can be:
|
||||
*
|
||||
* * The method name
|
||||
* * An array with the method name and priority
|
||||
* * An array with the method name, priority and number of accepted arguments
|
||||
*
|
||||
* For instance:
|
||||
*
|
||||
* * array('hook_name' => 'method_name')
|
||||
* * array('hook_name' => array('method_name', $priority))
|
||||
* * array('hook_name' => array('method_name', $priority, $accepted_args))
|
||||
* * array('hook_name' => array(array('method_name_1', $priority_1, $accepted_args_1)), array('method_name_2', $priority_2, $accepted_args_2)))
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_subscribed_events();
|
||||
}
|
||||
@@ -27,8 +27,8 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
|
||||
'imagify_manual_optimize',
|
||||
'imagify_manual_reoptimize',
|
||||
'imagify_optimize_missing_sizes',
|
||||
'imagify_generate_webp_versions',
|
||||
'imagify_delete_webp_versions',
|
||||
'imagify_generate_nextgen_versions',
|
||||
'imagify_delete_nextgen_versions',
|
||||
'imagify_restore',
|
||||
// Custom folders optimization.
|
||||
'imagify_optimize_file',
|
||||
@@ -194,7 +194,7 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate WebP images if they are missing.
|
||||
* Generate next-gen images if they are missing.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
@@ -202,12 +202,12 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
|
||||
* @param string $context The context.
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
protected function generate_webp_versions( $media_id, $context ) {
|
||||
return imagify_get_optimization_process( $media_id, $context )->generate_webp_versions();
|
||||
protected function generate_nextgen_versions( $media_id, $context ) {
|
||||
return imagify_get_optimization_process( $media_id, $context )->generate_nextgen_versions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete WebP images for media that are "already_optimize".
|
||||
* Delete Next gen images for media that are "already_optimize".
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
@@ -215,7 +215,7 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
|
||||
* @param string $context The context.
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
protected function delete_webp_versions( $media_id, $context ) {
|
||||
protected function delete_nextgen_versions( $media_id, $context ) {
|
||||
$process = imagify_get_optimization_process( $media_id, $context );
|
||||
|
||||
if ( ! $process->is_valid() ) {
|
||||
@@ -228,15 +228,15 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
|
||||
return new WP_Error( 'not_already_optimized', __( 'This media does not have the right optimization status.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! $process->has_webp() ) {
|
||||
if ( ! $process->has_next_gen() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$data->delete_optimization_data();
|
||||
$deleted = $process->delete_webp_files();
|
||||
$deleted = $process->delete_nextgen_files( false, true );
|
||||
|
||||
if ( is_wp_error( $deleted ) ) {
|
||||
return new WP_Error( 'webp_not_deleted', __( 'Previous WebP files could not be deleted.', 'imagify' ) );
|
||||
return new WP_Error( 'nextgen_not_deleted', __( 'Previous next-gen files could not be deleted.', 'imagify' ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -359,11 +359,11 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate WebP images if they are missing.
|
||||
* Generate next-gen images if they are missing.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function imagify_generate_webp_versions_callback() {
|
||||
public function imagify_generate_nextgen_versions_callback() {
|
||||
$context = $this->get_context();
|
||||
$media_id = $this->get_media_id();
|
||||
|
||||
@@ -371,13 +371,13 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
|
||||
imagify_die( __( 'Invalid request', 'imagify' ) );
|
||||
}
|
||||
|
||||
imagify_check_nonce( 'imagify-generate-webp-versions-' . $media_id . '-' . $context );
|
||||
imagify_check_nonce( 'imagify-generate-nextgen-versions-' . $media_id . '-' . $context );
|
||||
|
||||
if ( ! imagify_get_context( $context )->current_user_can( 'manual-optimize', $media_id ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
|
||||
$result = $this->generate_webp_versions( $media_id, $context );
|
||||
$result = $this->generate_nextgen_versions( $media_id, $context );
|
||||
|
||||
imagify_maybe_redirect( is_wp_error( $result ) ? $result : false );
|
||||
|
||||
@@ -392,11 +392,11 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate WebP images if they are missing.
|
||||
* Generate next-gen images if they are missing.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
public function imagify_delete_webp_versions_callback() {
|
||||
public function imagify_delete_nextgen_versions_callback() {
|
||||
$context = $this->get_context();
|
||||
$media_id = $this->get_media_id();
|
||||
|
||||
@@ -404,13 +404,13 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
|
||||
imagify_die( __( 'Invalid request', 'imagify' ) );
|
||||
}
|
||||
|
||||
imagify_check_nonce( 'imagify-delete-webp-versions-' . $media_id . '-' . $context );
|
||||
imagify_check_nonce( 'imagify-delete-nextgen-versions-' . $media_id . '-' . $context );
|
||||
|
||||
if ( ! imagify_get_context( $context )->current_user_can( 'manual-restore', $media_id ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
|
||||
$result = $this->delete_webp_versions( $media_id, $context );
|
||||
$result = $this->delete_nextgen_versions( $media_id, $context );
|
||||
|
||||
imagify_maybe_redirect( is_wp_error( $result ) ? $result : false );
|
||||
|
||||
|
||||
@@ -606,11 +606,11 @@ class Imagify_Files_List_Table extends WP_List_Table {
|
||||
</li>
|
||||
<?php
|
||||
if ( $item->process->get_media()->is_image() ) {
|
||||
$has_webp = $item->process->has_webp() ? __( 'Yes', 'imagify' ) : __( 'No', 'imagify' );
|
||||
$has_nextgen = $item->process->has_next_gen() ? __( 'Yes', 'imagify' ) : __( 'No', 'imagify' );
|
||||
?>
|
||||
<li class="imagify-data-item">
|
||||
<span class="data"><?php esc_html_e( 'WebP generated:', 'imagify' ); ?></span>
|
||||
<strong class="data-value"><?php echo esc_html( $has_webp ); ?></strong>
|
||||
<span class="data"><?php esc_html_e( 'Next-Gen generated:', 'imagify' ); ?></span>
|
||||
<strong class="data-value"><?php echo esc_html( $has_nextgen ); ?></strong>
|
||||
</li>
|
||||
<?php
|
||||
}
|
||||
@@ -700,8 +700,8 @@ class Imagify_Files_List_Table extends WP_List_Table {
|
||||
$this->optimize_button( $item );
|
||||
$this->retry_button( $item );
|
||||
$this->reoptimize_buttons( $item );
|
||||
$this->generate_webp_versions_button( $item );
|
||||
$this->delete_webp_versions_button( $item );
|
||||
$this->generate_nextgen_versions_button( $item );
|
||||
$this->delete_nextgen_versions_button( $item );
|
||||
$this->restore_button( $item );
|
||||
}
|
||||
|
||||
@@ -806,14 +806,14 @@ class Imagify_Files_List_Table extends WP_List_Table {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a button to generate WebP versions if they are missing.
|
||||
* Prints a button to generate Next gen versions if they are missing.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param object $item The current item. It must contain at least a $process property.
|
||||
*/
|
||||
protected function generate_webp_versions_button( $item ) {
|
||||
$button = get_imagify_attachment_generate_webp_versions_link( $item->process );
|
||||
protected function generate_nextgen_versions_button( $item ) {
|
||||
$button = get_imagify_attachment_generate_nextgen_versions_link( $item->process );
|
||||
|
||||
if ( $button ) {
|
||||
echo $button . '<br/>';
|
||||
@@ -821,14 +821,14 @@ class Imagify_Files_List_Table extends WP_List_Table {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a button to delete WebP versions when the status is "already_optimized".
|
||||
* Prints a button to delete next-gen versions when the status is "already_optimized".
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param object $item The current item. It must contain at least a $process property.
|
||||
*/
|
||||
protected function delete_webp_versions_button( $item ) {
|
||||
$button = get_imagify_attachment_delete_webp_versions_link( $item->process );
|
||||
protected function delete_nextgen_versions_button( $item ) {
|
||||
$button = get_imagify_attachment_delete_nextgen_versions_link( $item->process );
|
||||
|
||||
if ( $button ) {
|
||||
echo $button . '<br/>';
|
||||
|
||||
@@ -27,20 +27,23 @@ class Imagify_Options extends Imagify_Abstract_Options {
|
||||
* @since 1.7
|
||||
*/
|
||||
protected $default_values = [
|
||||
'api_key' => '',
|
||||
'optimization_level' => 2,
|
||||
'lossless' => 0,
|
||||
'auto_optimize' => 0,
|
||||
'backup' => 0,
|
||||
'resize_larger' => 0,
|
||||
'resize_larger_w' => 0,
|
||||
'convert_to_webp' => 0,
|
||||
'api_key' => '',
|
||||
'optimization_level' => 2,
|
||||
'lossless' => 0,
|
||||
'auto_optimize' => 0,
|
||||
'backup' => 0,
|
||||
'resize_larger' => 0,
|
||||
'resize_larger_w' => 0,
|
||||
'display_nextgen' => 0,
|
||||
'display_nextgen_method' => 'picture',
|
||||
'display_webp' => 0,
|
||||
'display_webp_method' => 'picture',
|
||||
'cdn_url' => '',
|
||||
'disallowed-sizes' => [],
|
||||
'admin_bar_menu' => 0,
|
||||
'partner_links' => 0,
|
||||
'cdn_url' => '',
|
||||
'disallowed-sizes' => [],
|
||||
'admin_bar_menu' => 0,
|
||||
'partner_links' => 0,
|
||||
'convert_to_avif' => 0,
|
||||
'convert_to_webp' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -54,7 +57,6 @@ class Imagify_Options extends Imagify_Abstract_Options {
|
||||
'optimization_level' => 2,
|
||||
'auto_optimize' => 1,
|
||||
'backup' => 1,
|
||||
'convert_to_webp' => 1,
|
||||
'admin_bar_menu' => 1,
|
||||
'partner_links' => 1,
|
||||
];
|
||||
@@ -131,10 +133,12 @@ class Imagify_Options extends Imagify_Abstract_Options {
|
||||
case 'lossless':
|
||||
case 'resize_larger':
|
||||
case 'convert_to_webp':
|
||||
case 'display_nextgen':
|
||||
case 'display_webp':
|
||||
case 'admin_bar_menu':
|
||||
case 'partner_links':
|
||||
return 1;
|
||||
case 'convert_to_avif':
|
||||
return empty( $value ) ? 0 : 1;
|
||||
|
||||
case 'resize_larger_w':
|
||||
if ( $value <= 0 ) {
|
||||
@@ -159,6 +163,7 @@ class Imagify_Options extends Imagify_Abstract_Options {
|
||||
$value = array_map( 'sanitize_text_field', $value );
|
||||
return array_fill_keys( $value, 1 );
|
||||
|
||||
case 'display_nextgen_method':
|
||||
case 'display_webp_method':
|
||||
$values = [
|
||||
'picture' => 1,
|
||||
@@ -172,7 +177,7 @@ class Imagify_Options extends Imagify_Abstract_Options {
|
||||
return $reset_values[ $key ];
|
||||
|
||||
case 'cdn_url':
|
||||
$cdn_source = \Imagify\Webp\Picture\Display::get_instance()->get_cdn_source( $value );
|
||||
$cdn_source = apply_filters( 'imagify_cdn_source_url', $value );
|
||||
|
||||
if ( 'option' !== $cdn_source['source'] ) {
|
||||
/**
|
||||
@@ -202,11 +207,6 @@ class Imagify_Options extends Imagify_Abstract_Options {
|
||||
unset( $values['resize_larger'], $values['resize_larger_w'] );
|
||||
}
|
||||
|
||||
// Don't display wepb if conversion is disabled.
|
||||
if ( empty( $values['convert_to_webp'] ) ) {
|
||||
unset( $values['convert_to_webp'], $values['display_webp'] );
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ function imagify_trigger_delete_attachment_hook( $post_id ) {
|
||||
|
||||
add_action( 'imagify_delete_media', 'imagify_cleanup_after_media_deletion' );
|
||||
/**
|
||||
* Delete the backup file and the WebP files when an attachement is deleted.
|
||||
* Delete the backup file and the next-gen files when an attachement is deleted.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
@@ -36,15 +36,16 @@ function imagify_cleanup_after_media_deletion( $process ) {
|
||||
|
||||
/**
|
||||
* The optimization data will be automatically deleted by WP (post metas).
|
||||
* Delete the WebP versions and the backup file.
|
||||
* Delete the Nextgen versions and the backup file.
|
||||
*/
|
||||
$process->delete_webp_files();
|
||||
$process->delete_nextgen_files( false, true );
|
||||
|
||||
$process->delete_backup();
|
||||
}
|
||||
|
||||
add_filter( 'ext2type', 'imagify_add_webp_type' );
|
||||
add_filter( 'ext2type', 'imagify_add_avif_type' );
|
||||
/**
|
||||
* Add the WebP extension to wp_get_ext_types().
|
||||
* Add the AVIF extension to wp_get_ext_types().
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
@@ -52,9 +53,9 @@ add_filter( 'ext2type', 'imagify_add_webp_type' );
|
||||
* @param array $ext2type Multi-dimensional array with extensions for a default set of file types.
|
||||
* @return array
|
||||
*/
|
||||
function imagify_add_webp_type( $ext2type ) {
|
||||
if ( ! in_array( 'webp', $ext2type['image'], true ) ) {
|
||||
$ext2type['image'][] = 'webp';
|
||||
function imagify_add_avif_type( $ext2type ) {
|
||||
if ( ! in_array( 'avif', $ext2type['image'], true ) ) {
|
||||
$ext2type['image'][] = 'avif';
|
||||
}
|
||||
return $ext2type;
|
||||
}
|
||||
@@ -67,3 +68,37 @@ function imagify_add_webp_type( $ext2type ) {
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
add_filter( 'big_image_size_threshold', [ imagify_get_context( 'wp' ), 'get_resizing_threshold' ], IMAGIFY_INT_MAX );
|
||||
|
||||
/**
|
||||
* Add filters to manage images formats that will be generated
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function imagify_nextgen_images_formats() {
|
||||
$formats = [
|
||||
'webp' => 'webp',
|
||||
];
|
||||
|
||||
if ( get_imagify_option( 'convert_to_avif' ) ) {
|
||||
$formats['avif'] = 'avif';
|
||||
|
||||
unset( $formats['webp'] );
|
||||
}
|
||||
|
||||
$default = $formats;
|
||||
|
||||
/**
|
||||
* Filters the array of next gen formats to generate with Imagify
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param array $formats Array of image formats
|
||||
*/
|
||||
$formats = apply_filters( 'imagify_nextgen_images_formats', $formats );
|
||||
|
||||
if ( ! is_array( $formats ) ) {
|
||||
$formats = $default;
|
||||
}
|
||||
|
||||
return $formats;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ function get_imagify_attachment_optimization_text( $process ) {
|
||||
$output_after = $is_media_page ? '<br/>' : '</li>';
|
||||
$reoptimize_link = get_imagify_attachment_reoptimize_link( $process );
|
||||
$reoptimize_link .= get_imagify_attachment_optimize_missing_thumbnails_link( $process );
|
||||
$reoptimize_link .= get_imagify_attachment_generate_webp_versions_link( $process );
|
||||
$reoptimize_link .= get_imagify_attachment_delete_webp_versions_link( $process );
|
||||
$reoptimize_link .= get_imagify_attachment_generate_nextgen_versions_link( $process );
|
||||
$reoptimize_link .= get_imagify_attachment_delete_nextgen_versions_link( $process );
|
||||
$reoptimize_output = $reoptimize_link ? $reoptimize_link : '';
|
||||
$reoptimize_output_before = '<div class="imagify-datas-actions-links">';
|
||||
$reoptimize_output_after = '</div><!-- .imagify-datas-actions-links -->';
|
||||
@@ -94,12 +94,12 @@ function get_imagify_attachment_optimization_text( $process ) {
|
||||
$output .= $output_before . '<span class="data">' . __( 'Level:', 'imagify' ) . '</span> <strong>' . $optimization_level . '</strong>' . $output_after;
|
||||
|
||||
if ( $media->is_image() ) {
|
||||
$has_webp = $process->has_webp() ? __( 'Yes', 'imagify' ) : __( 'No', 'imagify' );
|
||||
$has_nextgen = $process->has_next_gen() ? __( 'Yes', 'imagify' ) : __( 'No', 'imagify' );
|
||||
|
||||
if ( $process->has_webp() ) {
|
||||
$has_webp = $process->is_full_webp() ? __( 'Yes', 'imagify' ) : __( 'Partially', 'imagify' );
|
||||
if ( $process->has_next_gen() ) {
|
||||
$has_nextgen = $process->is_full_next_gen() ? __( 'Yes', 'imagify' ) : __( 'Partially', 'imagify' );
|
||||
}
|
||||
$output .= $output_before . '<span class="data">' . __( 'WebP generated:', 'imagify' ) . '</span> <strong class="big">' . esc_html( $has_webp ) . '</strong>' . $output_after;
|
||||
$output .= $output_before . '<span class="data">' . __( 'Next-Gen generated:', 'imagify' ) . '</span> <strong class="big">' . esc_html( $has_nextgen ) . '</strong>' . $output_after;
|
||||
|
||||
$total_optimized_thumbnails = $data->get_optimized_sizes_count();
|
||||
|
||||
@@ -317,20 +317,22 @@ function get_imagify_attachment_optimize_missing_thumbnails_link( $process ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the link to generate WebP versions if they are missing.
|
||||
* Get the link to generate next-gen versions if they are missing.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param ProcessInterface $process The optimization process object.
|
||||
* @return string The output to print.
|
||||
* @param ProcessInterface $process The optimization process object.
|
||||
*
|
||||
* @return string The output to print.
|
||||
*/
|
||||
function get_imagify_attachment_generate_webp_versions_link( $process ) {
|
||||
function get_imagify_attachment_generate_nextgen_versions_link( $process ) {
|
||||
if ( ! $process->is_valid() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( ! get_imagify_option( 'convert_to_webp' ) ) {
|
||||
$formats = imagify_nextgen_images_formats();
|
||||
|
||||
if ( empty( $formats ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -340,7 +342,13 @@ function get_imagify_attachment_generate_webp_versions_link( $process ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( 'image/webp' === $media->get_mime_type() ) {
|
||||
if (
|
||||
get_imagify_option( 'convert_to_avif' )
|
||||
&&
|
||||
'image/avif' === $media->get_mime_type()
|
||||
) {
|
||||
return '';
|
||||
} elseif ( 'image/webp' === $media->get_mime_type() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -350,14 +358,16 @@ function get_imagify_attachment_generate_webp_versions_link( $process ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( $process->has_webp() ) {
|
||||
if ( $process->has_next_gen() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$context = $media->get_context();
|
||||
|
||||
$display = apply_filters_deprecated( 'imagify_display_generate_webp_versions_link', array( true, $process, $context ), '2.2', 'imagify_display_generate_next_gen_versions_link' );
|
||||
|
||||
/**
|
||||
* Allow to not display the "Generate WebP versions" link.
|
||||
* Allow to not display the "Generate next-gen versions" link.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
@@ -366,14 +376,14 @@ function get_imagify_attachment_generate_webp_versions_link( $process ) {
|
||||
* @param ProcessInterface $process The optimization process object.
|
||||
* @param string $context The context.
|
||||
*/
|
||||
$display = apply_filters( 'imagify_display_generate_webp_versions_link', true, $process, $context );
|
||||
$display = apply_filters( 'imagify_display_generate_next_gen_versions_link', $display, $process, $context );
|
||||
|
||||
// Stop the process if the filter is false.
|
||||
if ( ! $display ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$url = get_imagify_admin_url( 'generate-webp-versions', [
|
||||
$url = get_imagify_admin_url( 'generate-nextgen-versions', [
|
||||
'attachment_id' => $media->get_id(),
|
||||
'context' => $context,
|
||||
] );
|
||||
@@ -386,7 +396,7 @@ function get_imagify_attachment_generate_webp_versions_link( $process ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the link to delete WebP versions when the status is "already_optimized".
|
||||
* Get the link to delete next-gen versions when the status is "already_optimized".
|
||||
*
|
||||
* @since 1.9.6
|
||||
* @author Grégory Viguier
|
||||
@@ -394,7 +404,7 @@ function get_imagify_attachment_generate_webp_versions_link( $process ) {
|
||||
* @param ProcessInterface $process The optimization process object.
|
||||
* @return string The output to print.
|
||||
*/
|
||||
function get_imagify_attachment_delete_webp_versions_link( $process ) {
|
||||
function get_imagify_attachment_delete_nextgen_versions_link( $process ) {
|
||||
if ( ! $process->is_valid() ) {
|
||||
return '';
|
||||
}
|
||||
@@ -409,12 +419,12 @@ function get_imagify_attachment_delete_webp_versions_link( $process ) {
|
||||
|
||||
$data = $process->get_data();
|
||||
|
||||
if ( ! $data->is_already_optimized() || ! $process->has_webp() ) {
|
||||
if ( ! $data->is_already_optimized() || ! $process->has_next_gen() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$class = '';
|
||||
$url = get_imagify_admin_url( 'delete-webp-versions', [
|
||||
$url = get_imagify_admin_url( 'delete-nextgen-versions', [
|
||||
'attachment_id' => $media_id,
|
||||
'context' => $context,
|
||||
] );
|
||||
|
||||
@@ -103,11 +103,11 @@ function get_imagify_admin_url( $action = 'settings', $arg = [] ) {
|
||||
case 'optimize-missing-sizes':
|
||||
return wp_nonce_url( admin_url( 'admin-post.php?action=imagify_optimize_missing_sizes&attachment_id=' . $id . '&context=' . $context ), 'imagify-optimize-missing-sizes-' . $id . '-' . $context );
|
||||
|
||||
case 'generate-webp-versions':
|
||||
return wp_nonce_url( admin_url( 'admin-post.php?action=imagify_generate_webp_versions&attachment_id=' . $id . '&context=' . $context ), 'imagify-generate-webp-versions-' . $id . '-' . $context );
|
||||
case 'generate-nextgen-versions':
|
||||
return wp_nonce_url( admin_url( 'admin-post.php?action=imagify_generate_nextgen_versions&attachment_id=' . $id . '&context=' . $context ), 'imagify-generate-nextgen-versions-' . $id . '-' . $context );
|
||||
|
||||
case 'delete-webp-versions':
|
||||
return wp_nonce_url( admin_url( 'admin-post.php?action=imagify_delete_webp_versions&attachment_id=' . $id . '&context=' . $context ), 'imagify-delete-webp-versions-' . $id . '-' . $context );
|
||||
case 'delete-nextgen-versions':
|
||||
return wp_nonce_url( admin_url( 'admin-post.php?action=imagify_delete_nextgen_versions&attachment_id=' . $id . '&context=' . $context ), 'imagify-delete-nextgen-versions-' . $id . '-' . $context );
|
||||
|
||||
case 'optimize':
|
||||
case 'manual-upload': // Deprecated.
|
||||
|
||||
@@ -277,14 +277,14 @@ function imagify_bulk_optimize( $contexts, $optimization_level ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the WebP generation
|
||||
* Runs the next-gen generation
|
||||
*
|
||||
* @param array $contexts An array of contexts (WP/Custom folders).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function imagify_generate_webp( $contexts ) {
|
||||
Imagify\Bulk\Bulk::get_instance()->run_generate_webp( $contexts );
|
||||
function imagify_generate_nextgen( $contexts ) {
|
||||
Imagify\Bulk\Bulk::get_instance()->run_generate_nextgen( $contexts );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -185,6 +185,32 @@ function imagify_path_to_webp( $path ) {
|
||||
return $path . '.webp';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a path (or URL) to its next-gen version.
|
||||
* To keep the function simple:
|
||||
* - Not tested if it's an image.
|
||||
* - File existance is not tested.
|
||||
* - If an URL is given, make sure it doesn't contain query args.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $path A file path or URL.
|
||||
* @param string $format format we are targeting.
|
||||
* @return string
|
||||
*/
|
||||
function imagify_path_to_nextgen( $path, string $format ) {
|
||||
switch ( $format ) {
|
||||
case 'webp':
|
||||
$path = $path . '.webp';
|
||||
break;
|
||||
case 'avif':
|
||||
$path = $path . '.avif';
|
||||
break;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current user can optimize custom folders.
|
||||
*
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use Imagify\Imagifybeat\Actions;
|
||||
use Imagify\Imagifybeat\Core;
|
||||
use Imagify\Stats\OptimizedMediaWithoutWebp;
|
||||
use Imagify\Stats\OptimizedMediaWithoutNextGen;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
@@ -79,27 +79,27 @@ function get_imagify_localize_script_translations( $context ) {
|
||||
],
|
||||
];
|
||||
|
||||
if ( OptimizedMediaWithoutWebp::get_instance()->get_cached_stat() ) {
|
||||
if ( OptimizedMediaWithoutNextGen::get_instance()->get_cached_stat() ) {
|
||||
$contexts = imagify_get_context_names();
|
||||
$translations['bulk'] = [
|
||||
'curlMissing' => ! Imagify_Requirements::supports_curl(),
|
||||
'editorMissing' => ! Imagify_Requirements::supports_image_editor(),
|
||||
'extHttpBlocked' => Imagify_Requirements::is_imagify_blocked(),
|
||||
'apiDown' => ! Imagify_Requirements::is_api_up(),
|
||||
'keyIsValid' => Imagify_Requirements::is_api_key_valid(),
|
||||
'isOverQuota' => Imagify_Requirements::is_over_quota(),
|
||||
'imagifybeatIDs' => [
|
||||
'curlMissing' => ! Imagify_Requirements::supports_curl(),
|
||||
'editorMissing' => ! Imagify_Requirements::supports_image_editor(),
|
||||
'extHttpBlocked' => Imagify_Requirements::is_imagify_blocked(),
|
||||
'apiDown' => ! Imagify_Requirements::is_api_up(),
|
||||
'keyIsValid' => Imagify_Requirements::is_api_key_valid(),
|
||||
'isOverQuota' => Imagify_Requirements::is_over_quota(),
|
||||
'imagifybeatIDs' => [
|
||||
'progress' => $imagifybeat_actions->get_imagifybeat_id( 'options_optimization_status' ),
|
||||
'requirements' => $imagifybeat_actions->get_imagifybeat_id( 'requirements' ),
|
||||
],
|
||||
'ajaxActions' => [
|
||||
'MissingWebp' => 'imagify_missing_webp_generation',
|
||||
'ajaxActions' => [
|
||||
'MissingNextGen' => 'imagify_missing_nextgen_generation',
|
||||
],
|
||||
'ajaxNonce' => wp_create_nonce( 'imagify-bulk-optimize' ),
|
||||
'contexts' => $contexts,
|
||||
'progress_webp' => [
|
||||
'remaining' => OptimizedMediaWithoutWebp::get_instance()->get_stat(),
|
||||
'total' => get_transient( 'imagify_missing_webp_total' ),
|
||||
'ajaxNonce' => wp_create_nonce( 'imagify-bulk-optimize' ),
|
||||
'contexts' => $contexts,
|
||||
'progress_next_gen' => [
|
||||
'remaining' => OptimizedMediaWithoutNextGen::get_instance()->get_stat(),
|
||||
'total' => get_transient( 'imagify_missing_next_gen_total' ),
|
||||
],
|
||||
'labels' => [
|
||||
'curlMissing' => __( 'cURL is not available on the server.', 'imagify' ),
|
||||
@@ -113,8 +113,8 @@ function get_imagify_localize_script_translations( $context ) {
|
||||
'invalidAPIKeyTitle' => __( 'Your API key is not valid!', 'imagify' ),
|
||||
'overQuotaTitle' => __( 'You have used all your credits!', 'imagify' ),
|
||||
'nothingToDoTitle' => __( 'Hold on!', 'imagify' ),
|
||||
'nothingToDoText' => __( 'All your optimized images already have a WebP version. Congratulations!', 'imagify' ),
|
||||
'nothingToDoNoBackupText' => __( 'Because the selected images did not have a backup copy, Imagify was unable to create WebP versions.', 'imagify' ),
|
||||
'nothingToDoText' => __( 'All your optimized images already have a next-gen version. Congratulations!', 'imagify' ),
|
||||
'nothingToDoNoBackupText' => __( 'Because the selected images did not have a backup copy, Imagify was unable to create next-gen versions.', 'imagify' ),
|
||||
'error' => __( 'Error', 'imagify' ),
|
||||
'ajaxErrorText' => __( 'The operation failed.', 'imagify' ),
|
||||
'getUnoptimizedImagesErrorTitle' => __( 'Oops, There is something wrong!', 'imagify' ),
|
||||
@@ -262,7 +262,7 @@ function get_imagify_localize_script_translations( $context ) {
|
||||
'nothingToDoTitle' => __( 'Hold on!', 'imagify' ),
|
||||
'nothingToDoText' => [
|
||||
'optimize' => __( 'All your media files have been optimized by Imagify. Congratulations!', 'imagify' ),
|
||||
'generate_webp' => __( 'All your optimized images already have a WebP version. Congratulations!', 'imagify' ),
|
||||
'generate_webp' => __( 'All your optimized images already have a next-gen version. Congratulations!', 'imagify' ),
|
||||
],
|
||||
'optimizing' => __( 'Optimizing', 'imagify' ),
|
||||
'error' => __( 'Error', 'imagify' ),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
use Imagify\Dependencies\League\Container\Container;
|
||||
use Imagify\Plugin;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
@@ -20,12 +21,15 @@ function imagify_init() {
|
||||
return;
|
||||
}
|
||||
|
||||
$providers = require_once IMAGIFY_PATH . 'config/providers.php';
|
||||
|
||||
$plugin = new Plugin(
|
||||
new Container(),
|
||||
array(
|
||||
'plugin_path' => IMAGIFY_PATH,
|
||||
)
|
||||
);
|
||||
|
||||
$plugin->init();
|
||||
$plugin->init( $providers );
|
||||
}
|
||||
add_action( 'plugins_loaded', 'imagify_init' );
|
||||
|
||||
Reference in New Issue
Block a user