MailChimp for WooCommerce - Version 2.7.6

Version Description

Download this release

Release Info

Developer ryanhungate
Plugin Icon wp plugin MailChimp for WooCommerce
Version 2.7.6
Comparing to
See all releases

Code changes from version 2.7.5 to 2.7.6

Files changed (33) hide show
  1. CHANGELOG.txt +4 -0
  2. README.txt +6 -5
  3. blocks/woocommerce-blocks-integration.php +4 -1
  4. bootstrap.php +1 -1
  5. includes/api/class-mailchimp-woocommerce-log-viewer.php +10 -1
  6. includes/api/class-mailchimp-woocommerce-logs.php +3 -0
  7. includes/api/class-mailchimp-woocommerce-tower.php +10 -1
  8. includes/api/class-mailchimp-woocommerce-transform-coupons.php +1 -1
  9. includes/api/class-mailchimp-woocommerce-transform-products.php +2 -2
  10. includes/vendor/action-scheduler/action-scheduler.php +7 -7
  11. includes/vendor/action-scheduler/classes/ActionScheduler_ActionFactory.php +132 -49
  12. includes/vendor/action-scheduler/classes/ActionScheduler_AdminView.php +91 -1
  13. includes/vendor/action-scheduler/classes/ActionScheduler_DateTime.php +3 -0
  14. includes/vendor/action-scheduler/classes/ActionScheduler_Exception.php +1 -1
  15. includes/vendor/action-scheduler/classes/ActionScheduler_ListTable.php +15 -1
  16. includes/vendor/action-scheduler/classes/ActionScheduler_QueueRunner.php +25 -6
  17. includes/vendor/action-scheduler/classes/WP_CLI/ActionScheduler_WPCLI_Scheduler_command.php +30 -0
  18. includes/vendor/action-scheduler/classes/WP_CLI/ProgressBar.php +1 -1
  19. includes/vendor/action-scheduler/classes/abstracts/ActionScheduler.php +2 -2
  20. includes/vendor/action-scheduler/classes/abstracts/ActionScheduler_Abstract_ListTable.php +1 -1
  21. includes/vendor/action-scheduler/classes/abstracts/ActionScheduler_Abstract_QueueRunner.php +58 -0
  22. includes/vendor/action-scheduler/classes/abstracts/ActionScheduler_Logger.php +1 -1
  23. includes/vendor/action-scheduler/classes/abstracts/ActionScheduler_Store.php +30 -2
  24. includes/vendor/action-scheduler/classes/actions/ActionScheduler_Action.php +22 -1
  25. includes/vendor/action-scheduler/classes/data-stores/ActionScheduler_DBStore.php +158 -20
  26. includes/vendor/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore.php +10 -1
  27. includes/vendor/action-scheduler/classes/migration/ActionMigrator.php +4 -4
  28. includes/vendor/action-scheduler/classes/migration/ActionScheduler_DBStoreMigrator.php +4 -4
  29. includes/vendor/action-scheduler/classes/schema/ActionScheduler_LoggerSchema.php +7 -7
  30. includes/vendor/action-scheduler/functions.php +76 -56
  31. includes/vendor/action-scheduler/lib/WP_Async_Request.php +24 -3
  32. mailchimp-woocommerce.php +2 -2
  33. public/class-mailchimp-woocommerce-public.php +1 -1
CHANGELOG.txt CHANGED
@@ -1,4 +1,8 @@
1
  == Changelog ==
 
 
 
 
2
  = 2.7.5 =
3
  * fix admin subscription status checkbox flow
4
  * remove support flag on plugin uninstall
1
  == Changelog ==
2
+ = 2.7.6 =
3
+ * deprecated ExtendRestAPI filling logs
4
+ * update action scheduler version
5
+ * fix for saving profile unsubscribes
6
  = 2.7.5 =
7
  * fix admin subscription status checkbox flow
8
  * remove support flag on plugin uninstall
README.txt CHANGED
@@ -4,10 +4,10 @@ Tags: ecommerce,email,workflows,mailchimp
4
  Donate link: https://mailchimp.com
5
  Requires at least: 4.9
6
  Tested up to: 6.0
7
- Stable tag: 2.7.5
8
  Requires PHP: 7.4
9
  WC requires at least: 3.5
10
- WC tested up to: 6.9
11
  License: GPLv2 or later
12
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
13
  Connect your store to your Mailchimp audience to track sales, create targeted emails, send abandoned cart emails, and more.
@@ -78,8 +78,9 @@ At this time, the synchronization of product categories from WooCommerce to Mail
78
  If you are unable to sync or connect with Mailchimp, you can open a ticket on our [Github plugin page](https://github.com/mailchimp/mc-woocommerce/issues). Please provide the version of the plugin and PHP you're using, any fatal errors in the WooCommerce logs (WooCommerce -> Status -> Logs) you're seeing, along with relevant information to the problem you're experiencing.
79
 
80
  == Changelog ==
81
- = 2.7.5 =
82
- * fix admin subscription status checkbox flow
83
- * remove support flag on plugin uninstall
 
84
 
85
  [Historical Changelog](https://raw.githubusercontent.com/mailchimp/mc-woocommerce/master/CHANGELOG.txt)
4
  Donate link: https://mailchimp.com
5
  Requires at least: 4.9
6
  Tested up to: 6.0
7
+ Stable tag: 2.7.6
8
  Requires PHP: 7.4
9
  WC requires at least: 3.5
10
+ WC tested up to: 7.1
11
  License: GPLv2 or later
12
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
13
  Connect your store to your Mailchimp audience to track sales, create targeted emails, send abandoned cart emails, and more.
78
  If you are unable to sync or connect with Mailchimp, you can open a ticket on our [Github plugin page](https://github.com/mailchimp/mc-woocommerce/issues). Please provide the version of the plugin and PHP you're using, any fatal errors in the WooCommerce logs (WooCommerce -> Status -> Logs) you're seeing, along with relevant information to the problem you're experiencing.
79
 
80
  == Changelog ==
81
+ = 2.7.6 =
82
+ * Deprecated ExtendRestAPI filling logs
83
+ * update action scheduler version
84
+ * fix for saving profile unsubscribes
85
 
86
  [Historical Changelog](https://raw.githubusercontent.com/mailchimp/mc-woocommerce/master/CHANGELOG.txt)
blocks/woocommerce-blocks-integration.php CHANGED
@@ -4,6 +4,8 @@ use Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface;
4
  use Automattic\WooCommerce\Blocks\Package;
5
  use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi;
6
  use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CheckoutSchema;
 
 
7
  defined( 'ABSPATH' ) || exit;
8
 
9
  /**
@@ -177,7 +179,8 @@ class Mailchimp_Woocommerce_Newsletter_Blocks_Integration implements Integration
177
  public function extend_store_api()
178
  {
179
  /** @var ExtendRestApi $extend */
180
- $extend = Package::container()->get(ExtendRestApi::class);
 
181
 
182
  $extend->register_endpoint_data(
183
  array(
4
  use Automattic\WooCommerce\Blocks\Package;
5
  use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi;
6
  use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CheckoutSchema;
7
+ use Automattic\WooCommerce\StoreApi\StoreApi;
8
+ use Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema;
9
  defined( 'ABSPATH' ) || exit;
10
 
11
  /**
179
  public function extend_store_api()
180
  {
181
  /** @var ExtendRestApi $extend */
182
+ /** @var ExtendSchema $extend */
183
+ $extend = class_exists('Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema') ? StoreApi::container()->get(ExtendSchema::class) : Package::container()->get(ExtendRestApi::class);
184
 
185
  $extend->register_endpoint_data(
186
  array(
bootstrap.php CHANGED
@@ -94,7 +94,7 @@ function mailchimp_environment_variables() {
94
  return (object) array(
95
  'repo' => 'master',
96
  'environment' => 'production', // staging or production
97
- 'version' => '2.7.5',
98
  'php_version' => phpversion(),
99
  'wp_version' => (empty($wp_version) ? 'Unknown' : $wp_version),
100
  'wc_version' => function_exists('WC') ? WC()->version : null,
94
  return (object) array(
95
  'repo' => 'master',
96
  'environment' => 'production', // staging or production
97
+ 'version' => '2.7.6',
98
  'php_version' => phpversion(),
99
  'wp_version' => (empty($wp_version) ? 'Unknown' : $wp_version),
100
  'wc_version' => function_exists('WC') ? WC()->version : null,
includes/api/class-mailchimp-woocommerce-log-viewer.php CHANGED
@@ -158,7 +158,7 @@ class MailChimp_WooCommerce_Log_Viewer {
158
  $files = array_filter(
159
  $files,
160
  function( $value ) {
161
- return static::stringEndsWith( $value, '.log' );
162
  }
163
  );
164
  if ( $basename && is_array( $files ) ) {
@@ -189,4 +189,13 @@ class MailChimp_WooCommerce_Log_Viewer {
189
  }
190
  return false;
191
  }
 
 
 
 
 
 
 
 
 
192
  }
158
  $files = array_filter(
159
  $files,
160
  function( $value ) {
161
+ return static::stringEndsWith( $value, '.log' ) && static::isValidLogFile( $value );
162
  }
163
  );
164
  if ( $basename && is_array( $files ) ) {
189
  }
190
  return false;
191
  }
192
+
193
+ /**
194
+ * @param $file
195
+ *
196
+ * @return bool
197
+ */
198
+ public static function isValidLogFile( $file ) {
199
+ return mailchimp_string_contains( $file, array('mailchimp_', 'fatal-') );
200
+ }
201
  }
includes/api/class-mailchimp-woocommerce-logs.php CHANGED
@@ -72,6 +72,9 @@ class MailChimp_WooCommerce_Logs {
72
  if ( ! isset( $matches[2] ) ) {
73
  continue;
74
  }
 
 
 
75
  $files[] = array(
76
  'value' => base64_encode( $file ),
77
  'filename' => $file,
72
  if ( ! isset( $matches[2] ) ) {
73
  continue;
74
  }
75
+ if ( !mailchimp_string_contains($file, array('mailchimp_', 'fatal-')) ) {
76
+ continue;
77
+ }
78
  $files[] = array(
79
  'value' => base64_encode( $file ),
80
  'filename' => $file,
includes/api/class-mailchimp-woocommerce-tower.php CHANGED
@@ -102,15 +102,24 @@ class MailChimp_WooCommerce_Tower extends Mailchimp_Woocommerce_Job {
102
 
103
  if ( is_array( $stores ) && ! empty( $stores ) ) {
104
  foreach ( $stores as $mc_store ) {
 
105
  $store_url = $this->baseDomain( $mc_store->getDomain() );
106
  $public_key_matched = $mc_store->getId() === $store_id;
107
  // make sure the current store in context is inside the Mailchimp array of stores.
108
  if ( $public_key_matched ) {
109
  $shop = $mc_store;
110
- $syncing_mc = $mc_store->isSyncing();
111
  $store_attached = true;
112
  $list_is_valid = $mc_store->getListId() === $list_id;
113
  $has_mailchimp_script = (bool) $mc_store->getConnectedSiteScriptFragment();
 
 
 
 
 
 
 
 
114
  }
115
  if ( $store_url === $compare_url ) {
116
  if ( ! $public_key_matched && $mc_store->getPlatform() === 'Woocommerce' ) {
102
 
103
  if ( is_array( $stores ) && ! empty( $stores ) ) {
104
  foreach ( $stores as $mc_store ) {
105
+ /** @var MailChimp_WooCommerce_Store $mc_store */
106
  $store_url = $this->baseDomain( $mc_store->getDomain() );
107
  $public_key_matched = $mc_store->getId() === $store_id;
108
  // make sure the current store in context is inside the Mailchimp array of stores.
109
  if ( $public_key_matched ) {
110
  $shop = $mc_store;
111
+ $syncing_mc = (bool) mailchimp_get_data('sync.syncing' );
112
  $store_attached = true;
113
  $list_is_valid = $mc_store->getListId() === $list_id;
114
  $has_mailchimp_script = (bool) $mc_store->getConnectedSiteScriptFragment();
115
+
116
+ // if the store is not syncing locally, but is still marked as syncing on Mailchimp,
117
+ // clean it up by toggling to false.
118
+ if ( !$syncing_mc && $mc_store->isSyncing() ) {
119
+ if ( $api->flagStoreSync($store_id, false) ) {
120
+ mailchimp_log('tower', 'Toggled Mailchimp store to not syncing historical data');
121
+ }
122
+ }
123
  }
124
  if ( $store_url === $compare_url ) {
125
  if ( ! $public_key_matched && $mc_store->getPlatform() === 'Woocommerce' ) {
includes/api/class-mailchimp-woocommerce-transform-coupons.php CHANGED
@@ -98,7 +98,7 @@ class MailChimp_WooCommerce_Transform_Coupons {
98
  // attach the rule for use.
99
  $code->attachPromoRule( $rule );
100
 
101
- return $code;
102
  }
103
 
104
  /**
98
  // attach the rule for use.
99
  $code->attachPromoRule( $rule );
100
 
101
+ return apply_filters('mailchimp_sync_promocode', $code, $resource);
102
  }
103
 
104
  /**
includes/api/class-mailchimp-woocommerce-transform-products.php CHANGED
@@ -62,7 +62,7 @@ class MailChimp_WooCommerce_Transform_Products {
62
 
63
  $product->addVariant( $variant );
64
 
65
- return $product;
66
  }
67
 
68
  /**
@@ -129,7 +129,7 @@ class MailChimp_WooCommerce_Transform_Products {
129
  $product->addVariant( $product_variant );
130
  }
131
 
132
- return $product;
133
  }
134
 
135
  /**
62
 
63
  $product->addVariant( $variant );
64
 
65
+ return apply_filters('mailchimp_sync_lineitem', $product);
66
  }
67
 
68
  /**
129
  $product->addVariant( $product_variant );
130
  }
131
 
132
+ return apply_filters('mailchimp_sync_product', $product, $woo);
133
  }
134
 
135
  /**
includes/vendor/action-scheduler/action-scheduler.php CHANGED
@@ -5,7 +5,7 @@
5
  * Description: A robust scheduling library for use in WordPress plugins.
6
  * Author: Automattic
7
  * Author URI: https://automattic.com/
8
- * Version: 3.4.0
9
  * License: GPLv3
10
  *
11
  * Copyright 2019 Automattic, Inc. (https://automattic.com/contact/)
@@ -26,27 +26,27 @@
26
  * @package ActionScheduler
27
  */
28
 
29
- if ( ! function_exists( 'action_scheduler_register_3_dot_4_dot_0' ) && function_exists( 'add_action' ) ) {
30
 
31
  if ( ! class_exists( 'ActionScheduler_Versions', false ) ) {
32
  require_once __DIR__ . '/classes/ActionScheduler_Versions.php';
33
  add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
34
  }
35
 
36
- add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_4_dot_0', 0, 0 );
37
 
38
  /**
39
  * Registers this version of Action Scheduler.
40
  */
41
- function action_scheduler_register_3_dot_4_dot_0() {
42
  $versions = ActionScheduler_Versions::instance();
43
- $versions->register( '3.4.0', 'action_scheduler_initialize_3_dot_4_dot_0' );
44
  }
45
 
46
  /**
47
  * Initializes this version of Action Scheduler.
48
  */
49
- function action_scheduler_initialize_3_dot_4_dot_0() {
50
  // A final safety check is required even here, because historic versions of Action Scheduler
51
  // followed a different pattern (in some unusual cases, we could reach this point and the
52
  // ActionScheduler class is already defined—so we need to guard against that).
@@ -58,7 +58,7 @@ if ( ! function_exists( 'action_scheduler_register_3_dot_4_dot_0' ) && function_
58
 
59
  // Support usage in themes - load this version if no plugin has loaded a version yet.
60
  if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) {
61
- action_scheduler_initialize_3_dot_4_dot_0();
62
  do_action( 'action_scheduler_pre_theme_init' );
63
  ActionScheduler_Versions::initialize_latest_version();
64
  }
5
  * Description: A robust scheduling library for use in WordPress plugins.
6
  * Author: Automattic
7
  * Author URI: https://automattic.com/
8
+ * Version: 3.5.2
9
  * License: GPLv3
10
  *
11
  * Copyright 2019 Automattic, Inc. (https://automattic.com/contact/)
26
  * @package ActionScheduler
27
  */
28
 
29
+ if ( ! function_exists( 'action_scheduler_register_3_dot_5_dot_2' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
30
 
31
  if ( ! class_exists( 'ActionScheduler_Versions', false ) ) {
32
  require_once __DIR__ . '/classes/ActionScheduler_Versions.php';
33
  add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
34
  }
35
 
36
+ add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_5_dot_2', 0, 0 ); // WRCS: DEFINED_VERSION.
37
 
38
  /**
39
  * Registers this version of Action Scheduler.
40
  */
41
+ function action_scheduler_register_3_dot_5_dot_2() { // WRCS: DEFINED_VERSION.
42
  $versions = ActionScheduler_Versions::instance();
43
+ $versions->register( '3.5.2', 'action_scheduler_initialize_3_dot_5_dot_2' ); // WRCS: DEFINED_VERSION.
44
  }
45
 
46
  /**
47
  * Initializes this version of Action Scheduler.
48
  */
49
+ function action_scheduler_initialize_3_dot_5_dot_2() { // WRCS: DEFINED_VERSION.
50
  // A final safety check is required even here, because historic versions of Action Scheduler
51
  // followed a different pattern (in some unusual cases, we could reach this point and the
52
  // ActionScheduler class is already defined—so we need to guard against that).
58
 
59
  // Support usage in themes - load this version if no plugin has loaded a version yet.
60
  if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) {
61
+ action_scheduler_initialize_3_dot_5_dot_2(); // WRCS: DEFINED_VERSION.
62
  do_action( 'action_scheduler_pre_theme_init' );
63
  ActionScheduler_Versions::initialize_latest_version();
64
  }
includes/vendor/action-scheduler/classes/ActionScheduler_ActionFactory.php CHANGED
@@ -6,27 +6,29 @@
6
  class ActionScheduler_ActionFactory {
7
 
8
  /**
9
- * @param string $status The action's status in the data store
10
- * @param string $hook The hook to trigger when this action runs
11
- * @param array $args Args to pass to callbacks when the hook is triggered
12
- * @param ActionScheduler_Schedule $schedule The action's schedule
13
- * @param string $group A group to put the action in
14
  *
15
- * @return ActionScheduler_Action An instance of the stored action
 
 
 
 
 
 
16
  */
17
  public function get_stored_action( $status, $hook, array $args = array(), ActionScheduler_Schedule $schedule = null, $group = '' ) {
18
 
19
  switch ( $status ) {
20
- case ActionScheduler_Store::STATUS_PENDING :
21
  $action_class = 'ActionScheduler_Action';
22
  break;
23
- case ActionScheduler_Store::STATUS_CANCELED :
24
  $action_class = 'ActionScheduler_CanceledAction';
25
  if ( ! is_null( $schedule ) && ! is_a( $schedule, 'ActionScheduler_CanceledSchedule' ) && ! is_a( $schedule, 'ActionScheduler_NullSchedule' ) ) {
26
  $schedule = new ActionScheduler_CanceledSchedule( $schedule->get_date() );
27
  }
28
  break;
29
- default :
30
  $action_class = 'ActionScheduler_FinishedAction';
31
  break;
32
  }
@@ -57,76 +59,142 @@ class ActionScheduler_ActionFactory {
57
  * given priority in queue processing. This has the added advantage of making sure async actions can be
58
  * claimed by both the existing WP Cron and WP CLI runners, as well as a new async request runner.
59
  *
60
- * @param string $hook The hook to trigger when this action runs
61
- * @param array $args Args to pass when the hook is triggered
62
- * @param string $group A group to put the action in
63
  *
64
- * @return int The ID of the stored action
65
  */
66
  public function async( $hook, $args = array(), $group = '' ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  $schedule = new ActionScheduler_NullSchedule();
68
- $action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
69
- return $this->store( $action );
70
  }
71
 
72
  /**
73
- * @param string $hook The hook to trigger when this action runs
74
- * @param array $args Args to pass when the hook is triggered
75
- * @param int $when Unix timestamp when the action will run
76
- * @param string $group A group to put the action in
77
  *
78
- * @return int The ID of the stored action
 
 
 
 
 
79
  */
80
  public function single( $hook, $args = array(), $when = null, $group = '' ) {
81
- $date = as_get_datetime_object( $when );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  $schedule = new ActionScheduler_SimpleSchedule( $date );
83
- $action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
84
- return $this->store( $action );
85
  }
86
 
87
  /**
88
  * Create the first instance of an action recurring on a given interval.
89
  *
90
- * @param string $hook The hook to trigger when this action runs
91
- * @param array $args Args to pass when the hook is triggered
92
- * @param int $first Unix timestamp for the first run
93
- * @param int $interval Seconds between runs
94
- * @param string $group A group to put the action in
95
  *
96
- * @return int The ID of the stored action
97
  */
98
  public function recurring( $hook, $args = array(), $first = null, $interval = null, $group = '' ) {
99
- if ( empty($interval) ) {
100
- return $this->single( $hook, $args, $first, $group );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  }
102
- $date = as_get_datetime_object( $first );
103
  $schedule = new ActionScheduler_IntervalSchedule( $date, $interval );
104
- $action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
105
- return $this->store( $action );
106
  }
107
 
108
  /**
109
  * Create the first instance of an action recurring on a Cron schedule.
110
  *
111
- * @param string $hook The hook to trigger when this action runs
112
- * @param array $args Args to pass when the hook is triggered
113
- * @param int $base_timestamp The first instance of the action will be scheduled
114
  * to run at a time calculated after this timestamp matching the cron
115
  * expression. This can be used to delay the first instance of the action.
116
- * @param int $schedule A cron definition string
117
- * @param string $group A group to put the action in
118
  *
119
- * @return int The ID of the stored action
120
  */
121
  public function cron( $hook, $args = array(), $base_timestamp = null, $schedule = null, $group = '' ) {
122
- if ( empty($schedule) ) {
123
- return $this->single( $hook, $args, $base_timestamp, $group );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  }
125
- $date = as_get_datetime_object( $base_timestamp );
126
- $cron = CronExpression::factory( $schedule );
127
  $schedule = new ActionScheduler_CronSchedule( $date, $cron );
128
- $action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
129
- return $this->store( $action );
130
  }
131
 
132
  /**
@@ -162,13 +230,15 @@ class ActionScheduler_ActionFactory {
162
  }
163
 
164
  $schedule_class = get_class( $schedule );
165
- $new_schedule = new $schedule( $next, $schedule->get_recurrence(), $schedule->get_first_date() );
166
- $new_action = new ActionScheduler_Action( $action->get_hook(), $action->get_args(), $new_schedule, $action->get_group() );
167
  return $this->store( $new_action );
168
  }
169
 
170
  /**
171
- * @param ActionScheduler_Action $action
 
 
172
  *
173
  * @return int The ID of the stored action
174
  */
@@ -176,4 +246,17 @@ class ActionScheduler_ActionFactory {
176
  $store = ActionScheduler_Store::instance();
177
  return $store->save_action( $action );
178
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  }
6
  class ActionScheduler_ActionFactory {
7
 
8
  /**
9
+ * Return stored actions for given params.
 
 
 
 
10
  *
11
+ * @param string $status The action's status in the data store.
12
+ * @param string $hook The hook to trigger when this action runs.
13
+ * @param array $args Args to pass to callbacks when the hook is triggered.
14
+ * @param ActionScheduler_Schedule $schedule The action's schedule.
15
+ * @param string $group A group to put the action in.
16
+ *
17
+ * @return ActionScheduler_Action An instance of the stored action.
18
  */
19
  public function get_stored_action( $status, $hook, array $args = array(), ActionScheduler_Schedule $schedule = null, $group = '' ) {
20
 
21
  switch ( $status ) {
22
+ case ActionScheduler_Store::STATUS_PENDING:
23
  $action_class = 'ActionScheduler_Action';
24
  break;
25
+ case ActionScheduler_Store::STATUS_CANCELED:
26
  $action_class = 'ActionScheduler_CanceledAction';
27
  if ( ! is_null( $schedule ) && ! is_a( $schedule, 'ActionScheduler_CanceledSchedule' ) && ! is_a( $schedule, 'ActionScheduler_NullSchedule' ) ) {
28
  $schedule = new ActionScheduler_CanceledSchedule( $schedule->get_date() );
29
  }
30
  break;
31
+ default:
32
  $action_class = 'ActionScheduler_FinishedAction';
33
  break;
34
  }
59
  * given priority in queue processing. This has the added advantage of making sure async actions can be
60
  * claimed by both the existing WP Cron and WP CLI runners, as well as a new async request runner.
61
  *
62
+ * @param string $hook The hook to trigger when this action runs.
63
+ * @param array $args Args to pass when the hook is triggered.
64
+ * @param string $group A group to put the action in.
65
  *
66
+ * @return int The ID of the stored action.
67
  */
68
  public function async( $hook, $args = array(), $group = '' ) {
69
+ return $this->async_unique( $hook, $args, $group, false );
70
+ }
71
+
72
+ /**
73
+ * Same as async, but also supports $unique param.
74
+ *
75
+ * @param string $hook The hook to trigger when this action runs.
76
+ * @param array $args Args to pass when the hook is triggered.
77
+ * @param string $group A group to put the action in.
78
+ * @param bool $unique Whether to ensure the action is unique.
79
+ *
80
+ * @return int The ID of the stored action.
81
+ */
82
+ public function async_unique( $hook, $args = array(), $group = '', $unique = true ) {
83
  $schedule = new ActionScheduler_NullSchedule();
84
+ $action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
85
+ return $unique ? $this->store_unique_action( $action, $unique ) : $this->store( $action );
86
  }
87
 
88
  /**
89
+ * Create single action.
 
 
 
90
  *
91
+ * @param string $hook The hook to trigger when this action runs.
92
+ * @param array $args Args to pass when the hook is triggered.
93
+ * @param int $when Unix timestamp when the action will run.
94
+ * @param string $group A group to put the action in.
95
+ *
96
+ * @return int The ID of the stored action.
97
  */
98
  public function single( $hook, $args = array(), $when = null, $group = '' ) {
99
+ return $this->single_unique( $hook, $args, $when, $group, false );
100
+ }
101
+
102
+ /**
103
+ * Create single action only if there is no pending or running action with same name and params.
104
+ *
105
+ * @param string $hook The hook to trigger when this action runs.
106
+ * @param array $args Args to pass when the hook is triggered.
107
+ * @param int $when Unix timestamp when the action will run.
108
+ * @param string $group A group to put the action in.
109
+ * @param bool $unique Whether action scheduled should be unique.
110
+ *
111
+ * @return int The ID of the stored action.
112
+ */
113
+ public function single_unique( $hook, $args = array(), $when = null, $group = '', $unique = true ) {
114
+ $date = as_get_datetime_object( $when );
115
  $schedule = new ActionScheduler_SimpleSchedule( $date );
116
+ $action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
117
+ return $unique ? $this->store_unique_action( $action ) : $this->store( $action );
118
  }
119
 
120
  /**
121
  * Create the first instance of an action recurring on a given interval.
122
  *
123
+ * @param string $hook The hook to trigger when this action runs.
124
+ * @param array $args Args to pass when the hook is triggered.
125
+ * @param int $first Unix timestamp for the first run.
126
+ * @param int $interval Seconds between runs.
127
+ * @param string $group A group to put the action in.
128
  *
129
+ * @return int The ID of the stored action.
130
  */
131
  public function recurring( $hook, $args = array(), $first = null, $interval = null, $group = '' ) {
132
+ return $this->recurring_unique( $hook, $args, $first, $interval, $group, false );
133
+ }
134
+
135
+ /**
136
+ * Create the first instance of an action recurring on a given interval only if there is no pending or running action with same name and params.
137
+ *
138
+ * @param string $hook The hook to trigger when this action runs.
139
+ * @param array $args Args to pass when the hook is triggered.
140
+ * @param int $first Unix timestamp for the first run.
141
+ * @param int $interval Seconds between runs.
142
+ * @param string $group A group to put the action in.
143
+ * @param bool $unique Whether action scheduled should be unique.
144
+ *
145
+ * @return int The ID of the stored action.
146
+ */
147
+ public function recurring_unique( $hook, $args = array(), $first = null, $interval = null, $group = '', $unique = true ) {
148
+ if ( empty( $interval ) ) {
149
+ return $this->single_unique( $hook, $unique, $args, $first, $group );
150
  }
151
+ $date = as_get_datetime_object( $first );
152
  $schedule = new ActionScheduler_IntervalSchedule( $date, $interval );
153
+ $action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
154
+ return $unique ? $this->store_unique_action( $action ) : $this->store( $action );
155
  }
156
 
157
  /**
158
  * Create the first instance of an action recurring on a Cron schedule.
159
  *
160
+ * @param string $hook The hook to trigger when this action runs.
161
+ * @param array $args Args to pass when the hook is triggered.
162
+ * @param int $base_timestamp The first instance of the action will be scheduled
163
  * to run at a time calculated after this timestamp matching the cron
164
  * expression. This can be used to delay the first instance of the action.
165
+ * @param int $schedule A cron definition string.
166
+ * @param string $group A group to put the action in.
167
  *
168
+ * @return int The ID of the stored action.
169
  */
170
  public function cron( $hook, $args = array(), $base_timestamp = null, $schedule = null, $group = '' ) {
171
+ return $this->cron_unique( $hook, $args, $base_timestamp, $schedule, $group, false );
172
+ }
173
+
174
+
175
+ /**
176
+ * Create the first instance of an action recurring on a Cron schedule only if there is no pending or running action with same name and params.
177
+ *
178
+ * @param string $hook The hook to trigger when this action runs.
179
+ * @param array $args Args to pass when the hook is triggered.
180
+ * @param int $base_timestamp The first instance of the action will be scheduled
181
+ * to run at a time calculated after this timestamp matching the cron
182
+ * expression. This can be used to delay the first instance of the action.
183
+ * @param int $schedule A cron definition string.
184
+ * @param string $group A group to put the action in.
185
+ * @param bool $unique Whether action scheduled should be unique.
186
+ *
187
+ * @return int The ID of the stored action.
188
+ **/
189
+ public function cron_unique( $hook, $args = array(), $base_timestamp = null, $schedule = null, $group = '', $unique = true ) {
190
+ if ( empty( $schedule ) ) {
191
+ return $this->single_unique( $hook, $unique, $args, $base_timestamp, $group );
192
  }
193
+ $date = as_get_datetime_object( $base_timestamp );
194
+ $cron = CronExpression::factory( $schedule );
195
  $schedule = new ActionScheduler_CronSchedule( $date, $cron );
196
+ $action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
197
+ return $unique ? $this->store_unique_action( $action ) : $this->store( $action );
198
  }
199
 
200
  /**
230
  }
231
 
232
  $schedule_class = get_class( $schedule );
233
+ $new_schedule = new $schedule( $next, $schedule->get_recurrence(), $schedule->get_first_date() );
234
+ $new_action = new ActionScheduler_Action( $action->get_hook(), $action->get_args(), $new_schedule, $action->get_group() );
235
  return $this->store( $new_action );
236
  }
237
 
238
  /**
239
+ * Save action to database.
240
+ *
241
+ * @param ActionScheduler_Action $action Action object to save.
242
  *
243
  * @return int The ID of the stored action
244
  */
246
  $store = ActionScheduler_Store::instance();
247
  return $store->save_action( $action );
248
  }
249
+
250
+ /**
251
+ * Store action if it's unique.
252
+ *
253
+ * @param ActionScheduler_Action $action Action object to store.
254
+ *
255
+ * @return int ID of the created action. Will be 0 if action was not created.
256
+ */
257
+ protected function store_unique_action( ActionScheduler_Action $action ) {
258
+ $store = ActionScheduler_Store::instance();
259
+ return method_exists( $store, 'save_unique_action' ) ?
260
+ $store->save_unique_action( $action ) : $store->save_action( $action );
261
+ }
262
  }
includes/vendor/action-scheduler/classes/ActionScheduler_AdminView.php CHANGED
@@ -40,7 +40,7 @@ class ActionScheduler_AdminView extends ActionScheduler_AdminView_Deprecated {
40
  }
41
 
42
  add_action( 'admin_menu', array( $this, 'register_menu' ) );
43
-
44
  add_action( 'current_screen', array( $this, 'add_help_tabs' ) );
45
  }
46
  }
@@ -110,6 +110,96 @@ class ActionScheduler_AdminView extends ActionScheduler_AdminView_Deprecated {
110
  return $this->list_table;
111
  }
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  /**
114
  * Provide more information about the screen and its data in the help tab.
115
  */
40
  }
41
 
42
  add_action( 'admin_menu', array( $this, 'register_menu' ) );
43
+ add_action( 'admin_notices', array( $this, 'maybe_check_pastdue_actions' ) );
44
  add_action( 'current_screen', array( $this, 'add_help_tabs' ) );
45
  }
46
  }
110
  return $this->list_table;
111
  }
112
 
113
+ /**
114
+ * Action: admin_notices
115
+ *
116
+ * Maybe check past-due actions, and print notice.
117
+ *
118
+ * @uses $this->check_pastdue_actions()
119
+ */
120
+ public function maybe_check_pastdue_actions() {
121
+
122
+ # Filter to prevent checking actions (ex: inappropriate user).
123
+ if ( ! apply_filters( 'action_scheduler_check_pastdue_actions', current_user_can( 'manage_options' ) ) ) {
124
+ return;
125
+ }
126
+
127
+ # Get last check transient.
128
+ $last_check = get_transient( 'action_scheduler_last_pastdue_actions_check' );
129
+
130
+ # If transient exists, we're within interval, so bail.
131
+ if ( ! empty( $last_check ) ) {
132
+ return;
133
+ }
134
+
135
+ # Perform the check.
136
+ $this->check_pastdue_actions();
137
+ }
138
+
139
+ /**
140
+ * Check past-due actions, and print notice.
141
+ *
142
+ * @todo update $link_url to "Past-due" filter when released (see issue #510, PR #511)
143
+ */
144
+ protected function check_pastdue_actions() {
145
+
146
+ # Set thresholds.
147
+ $threshold_seconds = ( int ) apply_filters( 'action_scheduler_pastdue_actions_seconds', DAY_IN_SECONDS );
148
+ $threshhold_min = ( int ) apply_filters( 'action_scheduler_pastdue_actions_min', 1 );
149
+
150
+ # Allow third-parties to preempt the default check logic.
151
+ $check = apply_filters( 'action_scheduler_pastdue_actions_check_pre', null );
152
+
153
+ # Scheduled actions query arguments.
154
+ $query_args = array(
155
+ 'date' => as_get_datetime_object( time() - $threshold_seconds ),
156
+ 'status' => ActionScheduler_Store::STATUS_PENDING,
157
+ 'per_page' => $threshhold_min,
158
+ );
159
+
160
+ # If no third-party preempted, run default check.
161
+ if ( is_null( $check ) ) {
162
+ $store = ActionScheduler_Store::instance();
163
+ $num_pastdue_actions = ( int ) $store->query_actions( $query_args, 'count' );
164
+
165
+ # Check if past-due actions count is greater than or equal to threshold.
166
+ $check = ( $num_pastdue_actions >= $threshhold_min );
167
+ $check = ( bool ) apply_filters( 'action_scheduler_pastdue_actions_check', $check, $num_pastdue_actions, $threshold_seconds, $threshhold_min );
168
+ }
169
+
170
+ # If check failed, set transient and abort.
171
+ if ( ! boolval( $check ) ) {
172
+ $interval = apply_filters( 'action_scheduler_pastdue_actions_check_interval', round( $threshold_seconds / 4 ), $threshold_seconds );
173
+ set_transient( 'action_scheduler_last_pastdue_actions_check', time(), $interval );
174
+
175
+ return;
176
+ }
177
+
178
+ $actions_url = add_query_arg( array(
179
+ 'page' => 'action-scheduler',
180
+ 'status' => 'past-due',
181
+ 'order' => 'asc',
182
+ ), admin_url( 'tools.php' ) );
183
+
184
+ # Print notice.
185
+ echo '<div class="notice notice-warning"><p>';
186
+ printf(
187
+ _n(
188
+ // translators: 1) is the number of affected actions, 2) is a link to an admin screen.
189
+ '<strong>Action Scheduler:</strong> %1$d <a href="%2$s">past-due action</a> found; something may be wrong. <a href="https://actionscheduler.org/faq/#my-site-has-past-due-actions-what-can-i-do" target="_blank">Read documentation &raquo;</a>',
190
+ '<strong>Action Scheduler:</strong> %1$d <a href="%2$s">past-due actions</a> found; something may be wrong. <a href="https://actionscheduler.org/faq/#my-site-has-past-due-actions-what-can-i-do" target="_blank">Read documentation &raquo;</a>',
191
+ $num_pastdue_actions,
192
+ 'action-scheduler'
193
+ ),
194
+ $num_pastdue_actions,
195
+ esc_attr( esc_url( $actions_url ) )
196
+ );
197
+ echo '</p></div>';
198
+
199
+ # Facilitate third-parties to evaluate and print notices.
200
+ do_action( 'action_scheduler_pastdue_actions_extra_notices', $query_args );
201
+ }
202
+
203
  /**
204
  * Provide more information about the screen and its data in the help tab.
205
  */
includes/vendor/action-scheduler/classes/ActionScheduler_DateTime.php CHANGED
@@ -24,6 +24,7 @@ class ActionScheduler_DateTime extends DateTime {
24
  *
25
  * @return int
26
  */
 
27
  public function getTimestamp() {
28
  return method_exists( 'DateTime', 'getTimestamp' ) ? parent::getTimestamp() : $this->format( 'U' );
29
  }
@@ -45,6 +46,7 @@ class ActionScheduler_DateTime extends DateTime {
45
  * @return int
46
  * @link http://php.net/manual/en/datetime.getoffset.php
47
  */
 
48
  public function getOffset() {
49
  return $this->utcOffset ? $this->utcOffset : parent::getOffset();
50
  }
@@ -57,6 +59,7 @@ class ActionScheduler_DateTime extends DateTime {
57
  * @return static
58
  * @link http://php.net/manual/en/datetime.settimezone.php
59
  */
 
60
  public function setTimezone( $timezone ) {
61
  $this->utcOffset = 0;
62
  parent::setTimezone( $timezone );
24
  *
25
  * @return int
26
  */
27
+ #[\ReturnTypeWillChange]
28
  public function getTimestamp() {
29
  return method_exists( 'DateTime', 'getTimestamp' ) ? parent::getTimestamp() : $this->format( 'U' );
30
  }
46
  * @return int
47
  * @link http://php.net/manual/en/datetime.getoffset.php
48
  */
49
+ #[\ReturnTypeWillChange]
50
  public function getOffset() {
51
  return $this->utcOffset ? $this->utcOffset : parent::getOffset();
52
  }
59
  * @return static
60
  * @link http://php.net/manual/en/datetime.settimezone.php
61
  */
62
+ #[\ReturnTypeWillChange]
63
  public function setTimezone( $timezone ) {
64
  $this->utcOffset = 0;
65
  parent::setTimezone( $timezone );
includes/vendor/action-scheduler/classes/ActionScheduler_Exception.php CHANGED
@@ -6,6 +6,6 @@
6
  * Facilitates catching Exceptions unique to Action Scheduler.
7
  *
8
  * @package ActionScheduler
9
- * @since %VERSION%
10
  */
11
  interface ActionScheduler_Exception {}
6
  * Facilitates catching Exceptions unique to Action Scheduler.
7
  *
8
  * @package ActionScheduler
9
+ * @since 2.1.0
10
  */
11
  interface ActionScheduler_Exception {}
includes/vendor/action-scheduler/classes/ActionScheduler_ListTable.php CHANGED
@@ -467,6 +467,10 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
467
 
468
  $schedule_display_string = '';
469
 
 
 
 
 
470
  if ( ! $schedule->get_date() ) {
471
  return '0000-00-00 00:00:00';
472
  }
@@ -583,6 +587,16 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
583
  'search' => $this->get_request_search_query(),
584
  );
585
 
 
 
 
 
 
 
 
 
 
 
586
  $this->items = array();
587
 
588
  $total_items = $this->store->query_actions( $query, 'count' );
@@ -623,7 +637,7 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
623
  * Prints the available statuses so the user can click to filter.
624
  */
625
  protected function display_filter_by_status() {
626
- $this->status_counts = $this->store->action_counts();
627
  parent::display_filter_by_status();
628
  }
629
 
467
 
468
  $schedule_display_string = '';
469
 
470
+ if ( is_a( $schedule, 'ActionScheduler_NullSchedule' ) ) {
471
+ return __( 'async', 'action-scheduler' );
472
+ }
473
+
474
  if ( ! $schedule->get_date() ) {
475
  return '0000-00-00 00:00:00';
476
  }
587
  'search' => $this->get_request_search_query(),
588
  );
589
 
590
+ /**
591
+ * Change query arguments to query for past-due actions.
592
+ * Past-due actions have the 'pending' status and are in the past.
593
+ * This is needed because registering 'past-due' as a status is overkill.
594
+ */
595
+ if ( 'past-due' === $this->get_request_status() ) {
596
+ $query['status'] = ActionScheduler_Store::STATUS_PENDING;
597
+ $query['date'] = as_get_datetime_object();
598
+ }
599
+
600
  $this->items = array();
601
 
602
  $total_items = $this->store->query_actions( $query, 'count' );
637
  * Prints the available statuses so the user can click to filter.
638
  */
639
  protected function display_filter_by_status() {
640
+ $this->status_counts = $this->store->action_counts() + $this->store->extra_action_counts();
641
  parent::display_filter_by_status();
642
  }
643
 
includes/vendor/action-scheduler/classes/ActionScheduler_QueueRunner.php CHANGED
@@ -173,15 +173,34 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {
173
  }
174
 
175
  /**
176
- * Running large batches can eat up memory, as WP adds data to its object cache.
177
  *
178
- * If using a persistent object store, this has the side effect of flushing that
179
- * as well, so this is disabled by default. To enable:
180
- *
181
- * add_filter( 'action_scheduler_queue_runner_flush_cache', '__return_true' );
182
  */
183
  protected function clear_caches() {
184
- if ( ! wp_using_ext_object_cache() || apply_filters( 'action_scheduler_queue_runner_flush_cache', false ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  wp_cache_flush();
186
  }
187
  }
173
  }
174
 
175
  /**
176
+ * Flush the cache if possible (intended for use after a batch of actions has been processed).
177
  *
178
+ * This is useful because running large batches can eat up memory and because invalid data can accrue in the
179
+ * runtime cache, which may lead to unexpected results.
 
 
180
  */
181
  protected function clear_caches() {
182
+ /*
183
+ * Calling wp_cache_flush_runtime() lets us clear the runtime cache without invalidating the external object
184
+ * cache, so we will always prefer this when it is available (but it was only introduced in WordPress 6.0).
185
+ */
186
+ if ( function_exists( 'wp_cache_flush_runtime' ) ) {
187
+ wp_cache_flush_runtime();
188
+ } elseif (
189
+ ! wp_using_ext_object_cache()
190
+ /**
191
+ * When an external object cache is in use, and when wp_cache_flush_runtime() is not available, then
192
+ * normally the cache will not be flushed after processing a batch of actions (to avoid a performance
193
+ * penalty for other processes).
194
+ *
195
+ * This filter makes it possible to override this behavior and always flush the cache, even if an external
196
+ * object cache is in use.
197
+ *
198
+ * @since 1.0
199
+ *
200
+ * @param bool $flush_cache If the cache should be flushed.
201
+ */
202
+ || apply_filters( 'action_scheduler_queue_runner_flush_cache', false )
203
+ ) {
204
  wp_cache_flush();
205
  }
206
  }
includes/vendor/action-scheduler/classes/WP_CLI/ActionScheduler_WPCLI_Scheduler_command.php CHANGED
@@ -5,6 +5,36 @@
5
  */
6
  class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  /**
9
  * Run the Action Scheduler
10
  *
5
  */
6
  class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
7
 
8
+ /**
9
+ * Force tables schema creation for Action Scheduler
10
+ *
11
+ * ## OPTIONS
12
+ *
13
+ * @param array $args Positional arguments.
14
+ * @param array $assoc_args Keyed arguments.
15
+ *
16
+ * @subcommand fix-schema
17
+ */
18
+ public function fix_schema( $args, $assoc_args ) {
19
+ $schema_classes = array( ActionScheduler_LoggerSchema::class, ActionScheduler_StoreSchema::class );
20
+
21
+ foreach ( $schema_classes as $classname ) {
22
+ if ( is_subclass_of( $classname, ActionScheduler_Abstract_Schema::class ) ) {
23
+ $obj = new $classname();
24
+ $obj->init();
25
+ $obj->register_tables( true );
26
+
27
+ WP_CLI::success(
28
+ sprintf(
29
+ /* translators: %s refers to the schema name*/
30
+ __( 'Registered schema for %s', 'action-scheduler' ),
31
+ $classname
32
+ )
33
+ );
34
+ }
35
+ }
36
+ }
37
+
38
  /**
39
  * Run the Action Scheduler
40
  *
includes/vendor/action-scheduler/classes/WP_CLI/ProgressBar.php CHANGED
@@ -44,7 +44,7 @@ class ProgressBar {
44
  public function __construct( $message, $count, $interval = 100 ) {
45
  if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) ) {
46
  /* translators: %s php class name */
47
- throw new Exception( sprintf( __( 'The %s class can only be run within WP CLI.', 'action-scheduler' ), __CLASS__ ) );
48
  }
49
 
50
  $this->total_ticks = 0;
44
  public function __construct( $message, $count, $interval = 100 ) {
45
  if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) ) {
46
  /* translators: %s php class name */
47
+ throw new \Exception( sprintf( __( 'The %s class can only be run within WP CLI.', 'action-scheduler' ), __CLASS__ ) );
48
  }
49
 
50
  $this->total_ticks = 0;
includes/vendor/action-scheduler/classes/abstracts/ActionScheduler.php CHANGED
@@ -118,8 +118,8 @@ abstract class ActionScheduler {
118
  return;
119
  }
120
 
121
- if ( file_exists( "{$dir}{$class}.php" ) ) {
122
- include( "{$dir}{$class}.php" );
123
  return;
124
  }
125
  }
118
  return;
119
  }
120
 
121
+ if ( file_exists( $dir . "{$class}.php" ) ) {
122
+ include( $dir . "{$class}.php" );
123
  return;
124
  }
125
  }
includes/vendor/action-scheduler/classes/abstracts/ActionScheduler_Abstract_ListTable.php CHANGED
@@ -683,7 +683,7 @@ abstract class ActionScheduler_Abstract_ListTable extends WP_List_Table {
683
  }
684
 
685
  if ( $status_name === $request_status || ( empty( $request_status ) && 'all' === $status_name ) ) {
686
- $status_list_item = '<li class="%1$s"><strong>%3$s</strong> (%4$d)</li>';
687
  } else {
688
  $status_list_item = '<li class="%1$s"><a href="%2$s">%3$s</a> (%4$d)</li>';
689
  }
683
  }
684
 
685
  if ( $status_name === $request_status || ( empty( $request_status ) && 'all' === $status_name ) ) {
686
+ $status_list_item = '<li class="%1$s"><a href="%2$s" class="current">%3$s</a> (%4$d)</li>';
687
  } else {
688
  $status_list_item = '<li class="%1$s"><a href="%2$s">%3$s</a> (%4$d)</li>';
689
  }
includes/vendor/action-scheduler/classes/abstracts/ActionScheduler_Abstract_QueueRunner.php CHANGED
@@ -86,6 +86,19 @@ abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abst
86
  * @param int $action_id
87
  */
88
  protected function schedule_next_instance( ActionScheduler_Action $action, $action_id ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  try {
90
  ActionScheduler::factory()->repeat( $action );
91
  } catch ( Exception $e ) {
@@ -93,6 +106,51 @@ abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abst
93
  }
94
  }
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  /**
97
  * Run the queue cleaner.
98
  *
86
  * @param int $action_id
87
  */
88
  protected function schedule_next_instance( ActionScheduler_Action $action, $action_id ) {
89
+ // If a recurring action has been consistently failing, we may wish to stop rescheduling it.
90
+ if (
91
+ ActionScheduler_Store::STATUS_FAILED === $this->store->get_status( $action_id )
92
+ && $this->recurring_action_is_consistently_failing( $action, $action_id )
93
+ ) {
94
+ ActionScheduler_Logger::instance()->log(
95
+ $action_id,
96
+ __( 'This action appears to be consistently failing. A new instance will not be scheduled.', 'action-scheduler' )
97
+ );
98
+
99
+ return;
100
+ }
101
+
102
  try {
103
  ActionScheduler::factory()->repeat( $action );
104
  } catch ( Exception $e ) {
106
  }
107
  }
108
 
109
+ /**
110
+ * Determine if the specified recurring action has been consistently failing.
111
+ *
112
+ * @param ActionScheduler_Action $action The recurring action to be rescheduled.
113
+ * @param int $action_id The ID of the recurring action.
114
+ *
115
+ * @return bool
116
+ */
117
+ private function recurring_action_is_consistently_failing( ActionScheduler_Action $action, $action_id ) {
118
+ /**
119
+ * Controls the failure threshold for recurring actions.
120
+ *
121
+ * Before rescheduling a recurring action, we look at its status. If it failed, we then check if all of the most
122
+ * recent actions (upto the threshold set by this filter) sharing the same hook have also failed: if they have,
123
+ * that is considered consistent failure and a new instance of the action will not be scheduled.
124
+ *
125
+ * @param int $failure_threshold Number of actions of the same hook to examine for failure. Defaults to 5.
126
+ */
127
+ $consistent_failure_threshold = (int) apply_filters( 'action_scheduler_recurring_action_failure_threshold', 5 );
128
+
129
+ // This query should find the earliest *failing* action (for the hook we are interested in) within our threshold.
130
+ $query_args = array(
131
+ 'hook' => $action->get_hook(),
132
+ 'status' => ActionScheduler_Store::STATUS_FAILED,
133
+ 'date' => date_create( 'now', timezone_open( 'UTC' ) )->format( 'Y-m-d H:i:s' ),
134
+ 'date_compare' => '<',
135
+ 'per_page' => 1,
136
+ 'offset' => $consistent_failure_threshold - 1
137
+ );
138
+
139
+ $first_failing_action_id = $this->store->query_actions( $query_args );
140
+
141
+ // If we didn't retrieve an action ID, then there haven't been enough failures for us to worry about.
142
+ if ( empty( $first_failing_action_id ) ) {
143
+ return false;
144
+ }
145
+
146
+ // Now let's fetch the first action (having the same hook) of *any status*ithin the same window.
147
+ unset( $query_args['status'] );
148
+ $first_action_id_with_the_same_hook = $this->store->query_actions( $query_args );
149
+
150
+ // If the IDs match, then actions for this hook must be consistently failing.
151
+ return $first_action_id_with_the_same_hook === $first_failing_action_id;
152
+ }
153
+
154
  /**
155
  * Run the queue cleaner.
156
  *
includes/vendor/action-scheduler/classes/abstracts/ActionScheduler_Logger.php CHANGED
@@ -109,7 +109,7 @@ abstract class ActionScheduler_Logger {
109
 
110
  public function log_timed_out_action( $action_id, $timeout ) {
111
  /* translators: %s: amount of time */
112
- $this->log( $action_id, sprintf( __( 'action timed out after %s seconds', 'action-scheduler' ), $timeout ) );
113
  }
114
 
115
  public function log_unexpected_shutdown( $action_id, $error ) {
109
 
110
  public function log_timed_out_action( $action_id, $timeout ) {
111
  /* translators: %s: amount of time */
112
+ $this->log( $action_id, sprintf( __( 'action marked as failed after %s seconds. Unknown error occurred. Check server, PHP and database error logs to diagnose cause.', 'action-scheduler' ), $timeout ) );
113
  }
114
 
115
  public function log_unexpected_shutdown( $action_id, $error ) {
includes/vendor/action-scheduler/classes/abstracts/ActionScheduler_Store.php CHANGED
@@ -76,7 +76,7 @@ abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated {
76
  /**
77
  * Query for action count or list of action IDs.
78
  *
79
- * @since x.x.x $query['status'] accepts array of statuses instead of a single status.
80
  *
81
  * @param array $query {
82
  * Query filtering options.
@@ -104,7 +104,7 @@ abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated {
104
  /**
105
  * Run query to get a single action ID.
106
  *
107
- * @since x.x.x
108
  *
109
  * @see ActionScheduler_Store::query_actions for $query arg usage but 'per_page' and 'offset' can't be used.
110
  *
@@ -131,6 +131,34 @@ abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated {
131
  */
132
  abstract public function action_counts();
133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  /**
135
  * @param string $action_id
136
  */
76
  /**
77
  * Query for action count or list of action IDs.
78
  *
79
+ * @since 3.3.0 $query['status'] accepts array of statuses instead of a single status.
80
  *
81
  * @param array $query {
82
  * Query filtering options.
104
  /**
105
  * Run query to get a single action ID.
106
  *
107
+ * @since 3.3.0
108
  *
109
  * @see ActionScheduler_Store::query_actions for $query arg usage but 'per_page' and 'offset' can't be used.
110
  *
131
  */
132
  abstract public function action_counts();
133
 
134
+ /**
135
+ * Get additional action counts.
136
+ *
137
+ * - add past-due actions
138
+ *
139
+ * @return array
140
+ */
141
+ public function extra_action_counts() {
142
+ $extra_actions = array();
143
+
144
+ $pastdue_action_counts = ( int ) $this->query_actions( array(
145
+ 'status' => self::STATUS_PENDING,
146
+ 'date' => as_get_datetime_object(),
147
+ ), 'count' );
148
+
149
+ if ( $pastdue_action_counts ) {
150
+ $extra_actions['past-due'] = $pastdue_action_counts;
151
+ }
152
+
153
+ /**
154
+ * Allows 3rd party code to add extra action counts (used in filters in the list table).
155
+ *
156
+ * @since 3.5.0
157
+ * @param $extra_actions array Array with format action_count_identifier => action count.
158
+ */
159
+ return apply_filters( 'action_scheduler_extra_action_counts', $extra_actions );
160
+ }
161
+
162
  /**
163
  * @param string $action_id
164
  */
includes/vendor/action-scheduler/classes/actions/ActionScheduler_Action.php CHANGED
@@ -18,8 +18,29 @@ class ActionScheduler_Action {
18
  $this->set_group($group);
19
  }
20
 
 
 
 
 
 
 
 
 
 
21
  public function execute() {
22
- return do_action_ref_array( $this->get_hook(), array_values( $this->get_args() ) );
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
 
25
  /**
18
  $this->set_group($group);
19
  }
20
 
21
+ /**
22
+ * Executes the action.
23
+ *
24
+ * If no callbacks are registered, an exception will be thrown and the action will not be
25
+ * fired. This is useful to help detect cases where the code responsible for setting up
26
+ * a scheduled action no longer exists.
27
+ *
28
+ * @throws Exception If no callbacks are registered for this action.
29
+ */
30
  public function execute() {
31
+ $hook = $this->get_hook();
32
+
33
+ if ( ! has_action( $hook ) ) {
34
+ throw new Exception(
35
+ sprintf(
36
+ /* translators: 1: action hook. */
37
+ __( 'Scheduled action for %1$s will not be executed as no callbacks are registered.', 'action-scheduler' ),
38
+ $hook
39
+ )
40
+ );
41
+ }
42
+
43
+ do_action_ref_array( $hook, array_values( $this->get_args() ) );
44
  }
45
 
46
  /**
includes/vendor/action-scheduler/classes/data-stores/ActionScheduler_DBStore.php CHANGED
@@ -36,22 +36,48 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
36
  $table_maker->register_tables();
37
  }
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  /**
40
  * Save an action.
41
  *
42
  * @param ActionScheduler_Action $action Action object.
43
- * @param DateTime $date Optional schedule date. Default null.
 
44
  *
45
  * @return int Action ID.
46
  * @throws RuntimeException Throws exception when saving the action fails.
47
  */
48
- public function save_action( ActionScheduler_Action $action, DateTime $date = null ) {
49
- try {
50
 
 
51
  $this->validate_action( $action );
52
 
53
- /** @var \wpdb $wpdb */
54
- global $wpdb;
55
  $data = array(
56
  'hook' => $action->get_hook(),
57
  'status' => ( $action->is_finished() ? self::STATUS_COMPLETE : self::STATUS_PENDING ),
@@ -60,6 +86,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
60
  'schedule' => serialize( $action->get_schedule() ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
61
  'group_id' => $this->get_group_id( $action->get_group() ),
62
  );
 
63
  $args = wp_json_encode( $action->get_args() );
64
  if ( strlen( $args ) <= static::$max_index_length ) {
65
  $data['args'] = $args;
@@ -68,25 +95,127 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
68
  $data['extended_args'] = $args;
69
  }
70
 
71
- $table_name = ! empty( $wpdb->actionscheduler_actions ) ? $wpdb->actionscheduler_actions : $wpdb->prefix . 'actionscheduler_actions';
72
- $wpdb->insert( $table_name, $data );
 
 
73
  $action_id = $wpdb->insert_id;
74
 
75
  if ( is_wp_error( $action_id ) ) {
76
  throw new \RuntimeException( $action_id->get_error_message() );
77
  } elseif ( empty( $action_id ) ) {
 
 
 
78
  throw new \RuntimeException( $wpdb->last_error ? $wpdb->last_error : __( 'Database error.', 'action-scheduler' ) );
79
  }
80
 
81
  do_action( 'action_scheduler_stored_action', $action_id );
82
 
83
  return $action_id;
84
- } catch ( Exception $e ) {
85
  /* translators: %s: error message */
86
  throw new \RuntimeException( sprintf( __( 'Error saving action: %s', 'action-scheduler' ), $e->getMessage() ), 0 );
87
  }
88
  }
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  /**
91
  * Generate a hash from json_encoded $args using MD5 as this isn't for security.
92
  *
@@ -232,7 +361,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
232
  /**
233
  * Returns the SQL statement to query (or count) actions.
234
  *
235
- * @since x.x.x $query['status'] accepts array of statuses instead of a single status.
236
  *
237
  * @param array $query Filtering options.
238
  * @param string $select_or_count Whether the SQL should select and return the IDs or just the row count.
@@ -298,7 +427,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
298
  $sql_params = array_merge( $sql_params, array_values( $statuses ) );
299
  }
300
 
301
- if ( $query['date'] instanceof DateTime ) {
302
  $date = clone $query['date'];
303
  $date->setTimezone( new \DateTimeZone( 'UTC' ) );
304
  $date_string = $date->format( 'Y-m-d H:i:s' );
@@ -307,7 +436,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
307
  $sql_params[] = $date_string;
308
  }
309
 
310
- if ( $query['modified'] instanceof DateTime ) {
311
  $modified = clone $query['modified'];
312
  $modified->setTimezone( new \DateTimeZone( 'UTC' ) );
313
  $date_string = $modified->format( 'Y-m-d H:i:s' );
@@ -384,7 +513,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
384
  /**
385
  * Query for action count or list of action IDs.
386
  *
387
- * @since x.x.x $query['status'] accepts array of statuses instead of a single status.
388
  *
389
  * @see ActionScheduler_Store::query_actions for $query arg usage.
390
  *
@@ -446,7 +575,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
446
  array( '%s' ),
447
  array( '%d' )
448
  );
449
- if ( empty( $updated ) ) {
450
  /* translators: %s: action ID */
451
  throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) );
452
  }
@@ -550,7 +679,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
550
  *
551
  * @param string $action_id Action ID.
552
  *
553
- * @return DateTime The local date the action is scheduled to run, or the date that it ran.
554
  */
555
  public function get_date( $action_id ) {
556
  $date = $this->get_date_gmt( $action_id );
@@ -564,7 +693,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
564
  * @param int $action_id Action ID.
565
  *
566
  * @throws \InvalidArgumentException If action cannot be identified.
567
- * @return DateTime The GMT date the action is scheduled to run, or the date that it ran.
568
  */
569
  protected function get_date_gmt( $action_id ) {
570
  /** @var \wpdb $wpdb */
@@ -584,13 +713,13 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
584
  * Stake a claim on actions.
585
  *
586
  * @param int $max_actions Maximum number of action to include in claim.
587
- * @param DateTime $before_date Jobs must be schedule before this date. Defaults to now.
588
  * @param array $hooks Hooks to filter for.
589
  * @param string $group Group to filter for.
590
  *
591
  * @return ActionScheduler_ActionClaim
592
  */
593
- public function stake_claim( $max_actions = 10, DateTime $before_date = null, $hooks = array(), $group = '' ) {
594
  $claim_id = $this->generate_claim_id();
595
 
596
  $this->claim_before_date = $before_date;
@@ -620,7 +749,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
620
  *
621
  * @param string $claim_id Claim Id.
622
  * @param int $limit Number of action to include in claim.
623
- * @param DateTime $before_date Should use UTC timezone.
624
  * @param array $hooks Hooks to filter for.
625
  * @param string $group Group to filter for.
626
  *
@@ -628,7 +757,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
628
  * @throws \InvalidArgumentException Throws InvalidArgumentException if group doesn't exist.
629
  * @throws \RuntimeException Throws RuntimeException if unable to claim action.
630
  */
631
- protected function claim_actions( $claim_id, $limit, DateTime $before_date = null, $hooks = array(), $group = '' ) {
632
  /** @var \wpdb $wpdb */
633
  global $wpdb;
634
 
@@ -670,7 +799,7 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
670
  /**
671
  * Sets the order-by clause used in the action claim query.
672
  *
673
- * @since x.x.x
674
  *
675
  * @param string $order_by_sql
676
  */
@@ -839,6 +968,15 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
839
  if ( empty( $updated ) ) {
840
  throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment
841
  }
 
 
 
 
 
 
 
 
 
842
  }
843
 
844
  /**
36
  $table_maker->register_tables();
37
  }
38
 
39
+ /**
40
+ * Save an action, checks if this is a unique action before actually saving.
41
+ *
42
+ * @param ActionScheduler_Action $action Action object.
43
+ * @param \DateTime $scheduled_date Optional schedule date. Default null.
44
+ *
45
+ * @return int Action ID.
46
+ * @throws RuntimeException Throws exception when saving the action fails.
47
+ */
48
+ public function save_unique_action( ActionScheduler_Action $action, \DateTime $scheduled_date = null ) {
49
+ return $this->save_action_to_db( $action, $scheduled_date, true );
50
+ }
51
+
52
+ /**
53
+ * Save an action. Can save duplicate action as well, prefer using `save_unique_action` instead.
54
+ *
55
+ * @param ActionScheduler_Action $action Action object.
56
+ * @param \DateTime $scheduled_date Optional schedule date. Default null.
57
+ *
58
+ * @return int Action ID.
59
+ * @throws RuntimeException Throws exception when saving the action fails.
60
+ */
61
+ public function save_action( ActionScheduler_Action $action, \DateTime $scheduled_date = null ) {
62
+ return $this->save_action_to_db( $action, $scheduled_date, false );
63
+ }
64
+
65
  /**
66
  * Save an action.
67
  *
68
  * @param ActionScheduler_Action $action Action object.
69
+ * @param ?DateTime $date Optional schedule date. Default null.
70
+ * @param bool $unique Whether the action should be unique.
71
  *
72
  * @return int Action ID.
73
  * @throws RuntimeException Throws exception when saving the action fails.
74
  */
75
+ private function save_action_to_db( ActionScheduler_Action $action, DateTime $date = null, $unique = false ) {
76
+ global $wpdb;
77
 
78
+ try {
79
  $this->validate_action( $action );
80
 
 
 
81
  $data = array(
82
  'hook' => $action->get_hook(),
83
  'status' => ( $action->is_finished() ? self::STATUS_COMPLETE : self::STATUS_PENDING ),
86
  'schedule' => serialize( $action->get_schedule() ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
87
  'group_id' => $this->get_group_id( $action->get_group() ),
88
  );
89
+
90
  $args = wp_json_encode( $action->get_args() );
91
  if ( strlen( $args ) <= static::$max_index_length ) {
92
  $data['args'] = $args;
95
  $data['extended_args'] = $args;
96
  }
97
 
98
+ $insert_sql = $this->build_insert_sql( $data, $unique );
99
+
100
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $insert_sql should be already prepared.
101
+ $wpdb->query( $insert_sql );
102
  $action_id = $wpdb->insert_id;
103
 
104
  if ( is_wp_error( $action_id ) ) {
105
  throw new \RuntimeException( $action_id->get_error_message() );
106
  } elseif ( empty( $action_id ) ) {
107
+ if ( $unique ) {
108
+ return 0;
109
+ }
110
  throw new \RuntimeException( $wpdb->last_error ? $wpdb->last_error : __( 'Database error.', 'action-scheduler' ) );
111
  }
112
 
113
  do_action( 'action_scheduler_stored_action', $action_id );
114
 
115
  return $action_id;
116
+ } catch ( \Exception $e ) {
117
  /* translators: %s: error message */
118
  throw new \RuntimeException( sprintf( __( 'Error saving action: %s', 'action-scheduler' ), $e->getMessage() ), 0 );
119
  }
120
  }
121
 
122
+ /**
123
+ * Helper function to build insert query.
124
+ *
125
+ * @param array $data Row data for action.
126
+ * @param bool $unique Whether the action should be unique.
127
+ *
128
+ * @return string Insert query.
129
+ */
130
+ private function build_insert_sql( array $data, $unique ) {
131
+ global $wpdb;
132
+ $columns = array_keys( $data );
133
+ $values = array_values( $data );
134
+ $placeholders = array_map( array( $this, 'get_placeholder_for_column' ), $columns );
135
+
136
+ $table_name = ! empty( $wpdb->actionscheduler_actions ) ? $wpdb->actionscheduler_actions : $wpdb->prefix . 'actionscheduler_actions';
137
+
138
+ $column_sql = '`' . implode( '`, `', $columns ) . '`';
139
+ $placeholder_sql = implode( ', ', $placeholders );
140
+ $where_clause = $this->build_where_clause_for_insert( $data, $table_name, $unique );
141
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $column_sql and $where_clause are already prepared. $placeholder_sql is hardcoded.
142
+ $insert_query = $wpdb->prepare(
143
+ "
144
+ INSERT INTO $table_name ( $column_sql )
145
+ SELECT $placeholder_sql FROM DUAL
146
+ WHERE ( $where_clause ) IS NULL",
147
+ $values
148
+ );
149
+ // phpcs:enable
150
+
151
+ return $insert_query;
152
+ }
153
+
154
+ /**
155
+ * Helper method to build where clause for action insert statement.
156
+ *
157
+ * @param array $data Row data for action.
158
+ * @param string $table_name Action table name.
159
+ * @param bool $unique Where action should be unique.
160
+ *
161
+ * @return string Where clause to be used with insert.
162
+ */
163
+ private function build_where_clause_for_insert( $data, $table_name, $unique ) {
164
+ global $wpdb;
165
+
166
+ if ( ! $unique ) {
167
+ return 'SELECT NULL FROM DUAL';
168
+ }
169
+
170
+ $pending_statuses = array(
171
+ ActionScheduler_Store::STATUS_PENDING,
172
+ ActionScheduler_Store::STATUS_RUNNING,
173
+ );
174
+ $pending_status_placeholders = implode( ', ', array_fill( 0, count( $pending_statuses ), '%s' ) );
175
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $pending_status_placeholders is hardcoded.
176
+ $where_clause = $wpdb->prepare(
177
+ "
178
+ SELECT action_id FROM $table_name
179
+ WHERE status IN ( $pending_status_placeholders )
180
+ AND hook = %s
181
+ AND `group_id` = %d
182
+ ",
183
+ array_merge(
184
+ $pending_statuses,
185
+ array(
186
+ $data['hook'],
187
+ $data['group_id'],
188
+ )
189
+ )
190
+ );
191
+ // phpcs:enable
192
+
193
+ return "$where_clause" . ' LIMIT 1';
194
+ }
195
+
196
+ /**
197
+ * Helper method to get $wpdb->prepare placeholder for a given column name.
198
+ *
199
+ * @param string $column_name Name of column in actions table.
200
+ *
201
+ * @return string Placeholder to use for given column.
202
+ */
203
+ private function get_placeholder_for_column( $column_name ) {
204
+ $string_columns = array(
205
+ 'hook',
206
+ 'status',
207
+ 'scheduled_date_gmt',
208
+ 'scheduled_date_local',
209
+ 'args',
210
+ 'schedule',
211
+ 'last_attempt_gmt',
212
+ 'last_attempt_local',
213
+ 'extended_args',
214
+ );
215
+
216
+ return in_array( $column_name, $string_columns ) ? '%s' : '%d';
217
+ }
218
+
219
  /**
220
  * Generate a hash from json_encoded $args using MD5 as this isn't for security.
221
  *
361
  /**
362
  * Returns the SQL statement to query (or count) actions.
363
  *
364
+ * @since 3.3.0 $query['status'] accepts array of statuses instead of a single status.
365
  *
366
  * @param array $query Filtering options.
367
  * @param string $select_or_count Whether the SQL should select and return the IDs or just the row count.
427
  $sql_params = array_merge( $sql_params, array_values( $statuses ) );
428
  }
429
 
430
+ if ( $query['date'] instanceof \DateTime ) {
431
  $date = clone $query['date'];
432
  $date->setTimezone( new \DateTimeZone( 'UTC' ) );
433
  $date_string = $date->format( 'Y-m-d H:i:s' );
436
  $sql_params[] = $date_string;
437
  }
438
 
439
+ if ( $query['modified'] instanceof \DateTime ) {
440
  $modified = clone $query['modified'];
441
  $modified->setTimezone( new \DateTimeZone( 'UTC' ) );
442
  $date_string = $modified->format( 'Y-m-d H:i:s' );
513
  /**
514
  * Query for action count or list of action IDs.
515
  *
516
+ * @since 3.3.0 $query['status'] accepts array of statuses instead of a single status.
517
  *
518
  * @see ActionScheduler_Store::query_actions for $query arg usage.
519
  *
575
  array( '%s' ),
576
  array( '%d' )
577
  );
578
+ if ( false === $updated ) {
579
  /* translators: %s: action ID */
580
  throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) );
581
  }
679
  *
680
  * @param string $action_id Action ID.
681
  *
682
+ * @return \DateTime The local date the action is scheduled to run, or the date that it ran.
683
  */
684
  public function get_date( $action_id ) {
685
  $date = $this->get_date_gmt( $action_id );
693
  * @param int $action_id Action ID.
694
  *
695
  * @throws \InvalidArgumentException If action cannot be identified.
696
+ * @return \DateTime The GMT date the action is scheduled to run, or the date that it ran.
697
  */
698
  protected function get_date_gmt( $action_id ) {
699
  /** @var \wpdb $wpdb */
713
  * Stake a claim on actions.
714
  *
715
  * @param int $max_actions Maximum number of action to include in claim.
716
+ * @param \DateTime $before_date Jobs must be schedule before this date. Defaults to now.
717
  * @param array $hooks Hooks to filter for.
718
  * @param string $group Group to filter for.
719
  *
720
  * @return ActionScheduler_ActionClaim
721
  */
722
+ public function stake_claim( $max_actions = 10, \DateTime $before_date = null, $hooks = array(), $group = '' ) {
723
  $claim_id = $this->generate_claim_id();
724
 
725
  $this->claim_before_date = $before_date;
749
  *
750
  * @param string $claim_id Claim Id.
751
  * @param int $limit Number of action to include in claim.
752
+ * @param \DateTime $before_date Should use UTC timezone.
753
  * @param array $hooks Hooks to filter for.
754
  * @param string $group Group to filter for.
755
  *
757
  * @throws \InvalidArgumentException Throws InvalidArgumentException if group doesn't exist.
758
  * @throws \RuntimeException Throws RuntimeException if unable to claim action.
759
  */
760
+ protected function claim_actions( $claim_id, $limit, \DateTime $before_date = null, $hooks = array(), $group = '' ) {
761
  /** @var \wpdb $wpdb */
762
  global $wpdb;
763
 
799
  /**
800
  * Sets the order-by clause used in the action claim query.
801
  *
802
+ * @since 3.4.0
803
  *
804
  * @param string $order_by_sql
805
  */
968
  if ( empty( $updated ) ) {
969
  throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment
970
  }
971
+
972
+ /**
973
+ * Fires after a scheduled action has been completed.
974
+ *
975
+ * @since 3.4.2
976
+ *
977
+ * @param int $action_id Action ID.
978
+ */
979
+ do_action( 'action_scheduler_completed_action', $action_id );
980
  }
981
 
982
  /**
includes/vendor/action-scheduler/classes/data-stores/ActionScheduler_wpPostStore.php CHANGED
@@ -452,7 +452,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
452
  /**
453
  * Query for action count or list of action IDs.
454
  *
455
- * @since x.x.x $query['status'] accepts array of statuses instead of a single status.
456
  *
457
  * @see ActionScheduler_Store::query_actions for $query arg usage.
458
  *
@@ -987,6 +987,15 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
987
  if ( is_wp_error( $result ) ) {
988
  throw new RuntimeException( $result->get_error_message() );
989
  }
 
 
 
 
 
 
 
 
 
990
  }
991
 
992
  /**
452
  /**
453
  * Query for action count or list of action IDs.
454
  *
455
+ * @since 3.3.0 $query['status'] accepts array of statuses instead of a single status.
456
  *
457
  * @see ActionScheduler_Store::query_actions for $query arg usage.
458
  *
987
  if ( is_wp_error( $result ) ) {
988
  throw new RuntimeException( $result->get_error_message() );
989
  }
990
+
991
+ /**
992
+ * Fires after a scheduled action has been completed.
993
+ *
994
+ * @since 3.4.2
995
+ *
996
+ * @param int $action_id Action ID.
997
+ */
998
+ do_action( 'action_scheduler_completed_action', $action_id );
999
  }
1000
 
1001
  /**
includes/vendor/action-scheduler/classes/migration/ActionMigrator.php CHANGED
@@ -46,7 +46,7 @@ class ActionMigrator {
46
  try {
47
  $action = $this->source->fetch_action( $source_action_id );
48
  $status = $this->source->get_status( $source_action_id );
49
- } catch ( Exception $e ) {
50
  $action = null;
51
  $status = '';
52
  }
@@ -57,7 +57,7 @@ class ActionMigrator {
57
  // delete it and move on
58
  try {
59
  $this->source->delete_action( $source_action_id );
60
- } catch ( Exception $e ) {
61
  // nothing to do, it didn't exist in the first place
62
  }
63
  do_action( 'action_scheduler/no_action_to_migrate', $source_action_id, $this->source, $this->destination );
@@ -71,7 +71,7 @@ class ActionMigrator {
71
  $last_attempt_date = ( $status !== \ActionScheduler_Store::STATUS_PENDING ) ? $this->source->get_date( $source_action_id ) : null;
72
 
73
  $destination_action_id = $this->destination->save_action( $action, null, $last_attempt_date );
74
- } catch ( Exception $e ) {
75
  do_action( 'action_scheduler/migrate_action_failed', $source_action_id, $this->source, $this->destination );
76
 
77
  return 0; // could not save the action in the new store
@@ -97,7 +97,7 @@ class ActionMigrator {
97
  do_action( 'action_scheduler/migrated_action', $source_action_id, $destination_action_id, $this->source, $this->destination );
98
 
99
  return $destination_action_id;
100
- } catch ( Exception $e ) {
101
  // could not delete from the old store
102
  $this->source->mark_migrated( $source_action_id );
103
  do_action( 'action_scheduler/migrate_action_incomplete', $source_action_id, $destination_action_id, $this->source, $this->destination );
46
  try {
47
  $action = $this->source->fetch_action( $source_action_id );
48
  $status = $this->source->get_status( $source_action_id );
49
+ } catch ( \Exception $e ) {
50
  $action = null;
51
  $status = '';
52
  }
57
  // delete it and move on
58
  try {
59
  $this->source->delete_action( $source_action_id );
60
+ } catch ( \Exception $e ) {
61
  // nothing to do, it didn't exist in the first place
62
  }
63
  do_action( 'action_scheduler/no_action_to_migrate', $source_action_id, $this->source, $this->destination );
71
  $last_attempt_date = ( $status !== \ActionScheduler_Store::STATUS_PENDING ) ? $this->source->get_date( $source_action_id ) : null;
72
 
73
  $destination_action_id = $this->destination->save_action( $action, null, $last_attempt_date );
74
+ } catch ( \Exception $e ) {
75
  do_action( 'action_scheduler/migrate_action_failed', $source_action_id, $this->source, $this->destination );
76
 
77
  return 0; // could not save the action in the new store
97
  do_action( 'action_scheduler/migrated_action', $source_action_id, $destination_action_id, $this->source, $this->destination );
98
 
99
  return $destination_action_id;
100
+ } catch ( \Exception $e ) {
101
  // could not delete from the old store
102
  $this->source->mark_migrated( $source_action_id );
103
  do_action( 'action_scheduler/migrate_action_incomplete', $source_action_id, $destination_action_id, $this->source, $this->destination );
includes/vendor/action-scheduler/classes/migration/ActionScheduler_DBStoreMigrator.php CHANGED
@@ -17,13 +17,13 @@ class ActionScheduler_DBStoreMigrator extends ActionScheduler_DBStore {
17
  * that when first saving the action.
18
  *
19
  * @param ActionScheduler_Action $action
20
- * @param DateTime $scheduled_date Optional date of the first instance to store.
21
- * @param DateTime $last_attempt_date Optional date the action was last attempted.
22
  *
23
  * @return string The action ID
24
  * @throws \RuntimeException When the action is not saved.
25
  */
26
- public function save_action( ActionScheduler_Action $action, DateTime $scheduled_date = null, DateTime $last_attempt_date = null ){
27
  try {
28
  /** @var \wpdb $wpdb */
29
  global $wpdb;
@@ -40,7 +40,7 @@ class ActionScheduler_DBStoreMigrator extends ActionScheduler_DBStore {
40
  }
41
 
42
  return $action_id;
43
- } catch ( Exception $e ) {
44
  throw new \RuntimeException( sprintf( __( 'Error saving action: %s', 'action-scheduler' ), $e->getMessage() ), 0 );
45
  }
46
  }
17
  * that when first saving the action.
18
  *
19
  * @param ActionScheduler_Action $action
20
+ * @param \DateTime $scheduled_date Optional date of the first instance to store.
21
+ * @param \DateTime $last_attempt_date Optional date the action was last attempted.
22
  *
23
  * @return string The action ID
24
  * @throws \RuntimeException When the action is not saved.
25
  */
26
+ public function save_action( ActionScheduler_Action $action, \DateTime $scheduled_date = null, \DateTime $last_attempt_date = null ){
27
  try {
28
  /** @var \wpdb $wpdb */
29
  global $wpdb;
40
  }
41
 
42
  return $action_id;
43
+ } catch ( \Exception $e ) {
44
  throw new \RuntimeException( sprintf( __( 'Error saving action: %s', 'action-scheduler' ), $e->getMessage() ), 0 );
45
  }
46
  }
includes/vendor/action-scheduler/classes/schema/ActionScheduler_LoggerSchema.php CHANGED
@@ -37,12 +37,12 @@ class ActionScheduler_LoggerSchema extends ActionScheduler_Abstract_Schema {
37
  case self::LOG_TABLE:
38
 
39
  $default_date = ActionScheduler_StoreSchema::DEFAULT_DATE;
40
- return "CREATE TABLE {$table_name} (
41
  log_id bigint(20) unsigned NOT NULL auto_increment,
42
  action_id bigint(20) unsigned NOT NULL,
43
  message text NOT NULL,
44
- log_date_gmt datetime NULL default '${default_date}',
45
- log_date_local datetime NULL default '${default_date}',
46
  PRIMARY KEY (log_id),
47
  KEY action_id (action_id),
48
  KEY log_date_gmt (log_date_gmt)
@@ -74,14 +74,14 @@ class ActionScheduler_LoggerSchema extends ActionScheduler_Abstract_Schema {
74
 
75
  // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
76
  $table_name = $wpdb->prefix . 'actionscheduler_logs';
77
- $table_list = $wpdb->get_col( "SHOW TABLES LIKE '${table_name}'" );
78
  $default_date = ActionScheduler_StoreSchema::DEFAULT_DATE;
79
 
80
  if ( ! empty( $table_list ) ) {
81
  $query = "
82
- ALTER TABLE ${table_name}
83
- MODIFY COLUMN log_date_gmt datetime NULL default '${default_date}',
84
- MODIFY COLUMN log_date_local datetime NULL default '${default_date}'
85
  ";
86
  $wpdb->query( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
87
  }
37
  case self::LOG_TABLE:
38
 
39
  $default_date = ActionScheduler_StoreSchema::DEFAULT_DATE;
40
+ return "CREATE TABLE $table_name (
41
  log_id bigint(20) unsigned NOT NULL auto_increment,
42
  action_id bigint(20) unsigned NOT NULL,
43
  message text NOT NULL,
44
+ log_date_gmt datetime NULL default '{$default_date}',
45
+ log_date_local datetime NULL default '{$default_date}',
46
  PRIMARY KEY (log_id),
47
  KEY action_id (action_id),
48
  KEY log_date_gmt (log_date_gmt)
74
 
75
  // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
76
  $table_name = $wpdb->prefix . 'actionscheduler_logs';
77
+ $table_list = $wpdb->get_col( "SHOW TABLES LIKE '{$table_name}'" );
78
  $default_date = ActionScheduler_StoreSchema::DEFAULT_DATE;
79
 
80
  if ( ! empty( $table_list ) ) {
81
  $query = "
82
+ ALTER TABLE {$table_name}
83
+ MODIFY COLUMN log_date_gmt datetime NULL default '{$default_date}',
84
+ MODIFY COLUMN log_date_local datetime NULL default '{$default_date}'
85
  ";
86
  $wpdb->query( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
87
  }
includes/vendor/action-scheduler/functions.php CHANGED
@@ -1,7 +1,8 @@
1
  <?php
2
-
3
  /**
4
  * General API functions for scheduling actions
 
 
5
  */
6
 
7
  /**
@@ -10,57 +11,61 @@
10
  * @param string $hook The hook to trigger.
11
  * @param array $args Arguments to pass when the hook triggers.
12
  * @param string $group The group to assign this job to.
 
 
13
  * @return int The action ID.
14
  */
15
- function as_enqueue_async_action( $hook, $args = array(), $group = '' ) {
16
  if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
17
  return 0;
18
  }
19
- return ActionScheduler::factory()->async( $hook, $args, $group );
20
  }
21
 
22
  /**
23
  * Schedule an action to run one time
24
  *
25
- * @param int $timestamp When the job will run.
26
  * @param string $hook The hook to trigger.
27
- * @param array $args Arguments to pass when the hook triggers.
28
  * @param string $group The group to assign this job to.
 
29
  *
30
  * @return int The action ID.
31
  */
32
- function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '' ) {
33
  if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
34
  return 0;
35
  }
36
- return ActionScheduler::factory()->single( $hook, $args, $timestamp, $group );
37
  }
38
 
39
  /**
40
  * Schedule a recurring action
41
  *
42
- * @param int $timestamp When the first instance of the job will run.
43
- * @param int $interval_in_seconds How long to wait between runs.
44
  * @param string $hook The hook to trigger.
45
- * @param array $args Arguments to pass when the hook triggers.
46
  * @param string $group The group to assign this job to.
 
47
  *
48
  * @return int The action ID.
49
  */
50
- function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '' ) {
51
  if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
52
  return 0;
53
  }
54
- return ActionScheduler::factory()->recurring( $hook, $args, $timestamp, $interval_in_seconds, $group );
55
  }
56
 
57
  /**
58
  * Schedule an action that recurs on a cron-like schedule.
59
  *
60
- * @param int $base_timestamp The first instance of the action will be scheduled
61
- * to run at a time calculated after this timestamp matching the cron
62
- * expression. This can be used to delay the first instance of the action.
63
- * @param string $schedule A cron-link schedule string
64
  * @see http://en.wikipedia.org/wiki/Cron
65
  * * * * * * *
66
  * ┬ ┬ ┬ ┬ ┬ ┬
@@ -72,16 +77,17 @@ function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook,
72
  * | +-------------------- hour (0 - 23)
73
  * +------------------------- min (0 - 59)
74
  * @param string $hook The hook to trigger.
75
- * @param array $args Arguments to pass when the hook triggers.
76
  * @param string $group The group to assign this job to.
 
77
  *
78
  * @return int The action ID.
79
  */
80
- function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '' ) {
81
  if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
82
  return 0;
83
  }
84
- return ActionScheduler::factory()->cron( $hook, $args, $timestamp, $schedule, $group );
85
  }
86
 
87
  /**
@@ -95,10 +101,10 @@ function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(),
95
  * by this method also.
96
  *
97
  * @param string $hook The hook that the job will trigger.
98
- * @param array $args Args that would have been passed to the job.
99
  * @param string $group The group the job is assigned to.
100
  *
101
- * @return string|null The scheduled action ID if a scheduled action was found, or null if no matching action found.
102
  */
103
  function as_unschedule_action( $hook, $args = array(), $group = '' ) {
104
  if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
@@ -116,8 +122,22 @@ function as_unschedule_action( $hook, $args = array(), $group = '' ) {
116
  }
117
 
118
  $action_id = ActionScheduler::store()->query_action( $params );
 
119
  if ( $action_id ) {
120
- ActionScheduler::store()->cancel_action( $action_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  }
122
 
123
  return $action_id;
@@ -127,7 +147,7 @@ function as_unschedule_action( $hook, $args = array(), $group = '' ) {
127
  * Cancel all occurrences of a scheduled action.
128
  *
129
  * @param string $hook The hook that the job will trigger.
130
- * @param array $args Args that would have been passed to the job.
131
  * @param string $group The group the job is assigned to.
132
  */
133
  function as_unschedule_all_actions( $hook, $args = array(), $group = '' ) {
@@ -158,9 +178,9 @@ function as_unschedule_all_actions( $hook, $args = array(), $group = '' ) {
158
  * returned. Or there may be no async, in-progress or pending action for this hook, in which case,
159
  * boolean false will be the return value.
160
  *
161
- * @param string $hook
162
- * @param array $args
163
- * @param string $group
164
  *
165
  * @return int|bool The timestamp for the next occurrence of a pending scheduled action, true for an async or in-progress action or false if there is no matching action.
166
  */
@@ -196,7 +216,7 @@ function as_next_scheduled_action( $hook, $args = null, $group = '' ) {
196
  $scheduled_date = $action->get_schedule()->get_date();
197
  if ( $scheduled_date ) {
198
  return (int) $scheduled_date->format( 'U' );
199
- } elseif ( null === $scheduled_date ) { // pending async action with NullSchedule
200
  return true;
201
  }
202
 
@@ -209,7 +229,7 @@ function as_next_scheduled_action( $hook, $args = null, $group = '' ) {
209
  * It's recommended to use this function when you need to know whether a specific action is currently scheduled
210
  * (pending or in-progress).
211
  *
212
- * @since x.x.x
213
  *
214
  * @param string $hook The hook of the action.
215
  * @param array $args Args that have been passed to the action. Null will matches any args.
@@ -223,10 +243,10 @@ function as_has_scheduled_action( $hook, $args = null, $group = '' ) {
223
  }
224
 
225
  $query_args = array(
226
- 'hook' => $hook,
227
- 'status' => array( ActionScheduler_Store::STATUS_RUNNING, ActionScheduler_Store::STATUS_PENDING ),
228
- 'group' => $group,
229
- 'orderby' => 'none',
230
  );
231
 
232
  if ( null !== $args ) {
@@ -235,26 +255,26 @@ function as_has_scheduled_action( $hook, $args = null, $group = '' ) {
235
 
236
  $action_id = ActionScheduler::store()->query_action( $query_args );
237
 
238
- return $action_id !== null;
239
  }
240
 
241
  /**
242
  * Find scheduled actions
243
  *
244
- * @param array $args Possible arguments, with their default values:
245
- * 'hook' => '' - the name of the action that will be triggered
246
- * 'args' => NULL - the args array that will be passed with the action
247
- * 'date' => NULL - the scheduled date of the action. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone.
248
- * 'date_compare' => '<=' - operator for testing "date". accepted values are '!=', '>', '>=', '<', '<=', '='
249
- * 'modified' => NULL - the date the action was last updated. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone.
250
- * 'modified_compare' => '<=' - operator for testing "modified". accepted values are '!=', '>', '>=', '<', '<=', '='
251
- * 'group' => '' - the group the action belongs to
252
- * 'status' => '' - ActionScheduler_Store::STATUS_COMPLETE or ActionScheduler_Store::STATUS_PENDING
253
- * 'claimed' => NULL - TRUE to find claimed actions, FALSE to find unclaimed actions, a string to find a specific claim ID
254
- * 'per_page' => 5 - Number of results to return
255
- * 'offset' => 0
256
- * 'orderby' => 'date' - accepted values are 'hook', 'group', 'modified', 'date' or 'none'
257
- * 'order' => 'ASC'
258
  *
259
  * @param string $return_format OBJECT, ARRAY_A, or ids.
260
  *
@@ -265,25 +285,25 @@ function as_get_scheduled_actions( $args = array(), $return_format = OBJECT ) {
265
  return array();
266
  }
267
  $store = ActionScheduler::store();
268
- foreach ( array('date', 'modified') as $key ) {
269
- if ( isset($args[$key]) ) {
270
- $args[$key] = as_get_datetime_object($args[$key]);
271
  }
272
  }
273
  $ids = $store->query_actions( $args );
274
 
275
- if ( $return_format == 'ids' || $return_format == 'int' ) {
276
  return $ids;
277
  }
278
 
279
  $actions = array();
280
  foreach ( $ids as $action_id ) {
281
- $actions[$action_id] = $store->fetch_action( $action_id );
282
  }
283
 
284
- if ( $return_format == ARRAY_A ) {
285
  foreach ( $actions as $action_id => $action_object ) {
286
- $actions[$action_id] = get_object_vars($action_object);
287
  }
288
  }
289
 
@@ -302,7 +322,7 @@ function as_get_scheduled_actions( $args = array(), $return_format = OBJECT ) {
302
  * timezone when instantiating datetimes rather than leaving it up to
303
  * the PHP default.
304
  *
305
- * @param mixed $date_string A date/time string. Valid formats are explained in http://php.net/manual/en/datetime.formats.php.
306
  * @param string $timezone A timezone identifier, like UTC or Europe/Lisbon. The list of valid identifiers is available http://php.net/manual/en/timezones.php.
307
  *
308
  * @return ActionScheduler_DateTime
@@ -313,7 +333,7 @@ function as_get_datetime_object( $date_string = null, $timezone = 'UTC' ) {
313
  } elseif ( is_numeric( $date_string ) ) {
314
  $date = new ActionScheduler_DateTime( '@' . $date_string, new DateTimeZone( $timezone ) );
315
  } else {
316
- $date = new ActionScheduler_DateTime( $date_string, new DateTimeZone( $timezone ) );
317
  }
318
  return $date;
319
  }
1
  <?php
 
2
  /**
3
  * General API functions for scheduling actions
4
+ *
5
+ * @package ActionScheduler.
6
  */
7
 
8
  /**
11
  * @param string $hook The hook to trigger.
12
  * @param array $args Arguments to pass when the hook triggers.
13
  * @param string $group The group to assign this job to.
14
+ * @param bool $unique Whether the action should be unique.
15
+ *
16
  * @return int The action ID.
17
  */
18
+ function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = false ) {
19
  if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
20
  return 0;
21
  }
22
+ return ActionScheduler::factory()->async_unique( $hook, $args, $group, $unique );
23
  }
24
 
25
  /**
26
  * Schedule an action to run one time
27
  *
28
+ * @param int $timestamp When the job will run.
29
  * @param string $hook The hook to trigger.
30
+ * @param array $args Arguments to pass when the hook triggers.
31
  * @param string $group The group to assign this job to.
32
+ * @param bool $unique Whether the action should be unique.
33
  *
34
  * @return int The action ID.
35
  */
36
+ function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '', $unique = false ) {
37
  if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
38
  return 0;
39
  }
40
+ return ActionScheduler::factory()->single_unique( $hook, $args, $timestamp, $group, $unique );
41
  }
42
 
43
  /**
44
  * Schedule a recurring action
45
  *
46
+ * @param int $timestamp When the first instance of the job will run.
47
+ * @param int $interval_in_seconds How long to wait between runs.
48
  * @param string $hook The hook to trigger.
49
+ * @param array $args Arguments to pass when the hook triggers.
50
  * @param string $group The group to assign this job to.
51
+ * @param bool $unique Whether the action should be unique.
52
  *
53
  * @return int The action ID.
54
  */
55
+ function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '', $unique = false ) {
56
  if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
57
  return 0;
58
  }
59
+ return ActionScheduler::factory()->recurring_unique( $hook, $args, $timestamp, $interval_in_seconds, $group, $unique );
60
  }
61
 
62
  /**
63
  * Schedule an action that recurs on a cron-like schedule.
64
  *
65
+ * @param int $timestamp The first instance of the action will be scheduled
66
+ * to run at a time calculated after this timestamp matching the cron
67
+ * expression. This can be used to delay the first instance of the action.
68
+ * @param string $schedule A cron-link schedule string.
69
  * @see http://en.wikipedia.org/wiki/Cron
70
  * * * * * * *
71
  * ┬ ┬ ┬ ┬ ┬ ┬
77
  * | +-------------------- hour (0 - 23)
78
  * +------------------------- min (0 - 59)
79
  * @param string $hook The hook to trigger.
80
+ * @param array $args Arguments to pass when the hook triggers.
81
  * @param string $group The group to assign this job to.
82
+ * @param bool $unique Whether the action should be unique.
83
  *
84
  * @return int The action ID.
85
  */
86
+ function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '', $unique = false ) {
87
  if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
88
  return 0;
89
  }
90
+ return ActionScheduler::factory()->cron_unique( $hook, $args, $timestamp, $schedule, $group, $unique );
91
  }
92
 
93
  /**
101
  * by this method also.
102
  *
103
  * @param string $hook The hook that the job will trigger.
104
+ * @param array $args Args that would have been passed to the job.
105
  * @param string $group The group the job is assigned to.
106
  *
107
+ * @return int|null The scheduled action ID if a scheduled action was found, or null if no matching action found.
108
  */
109
  function as_unschedule_action( $hook, $args = array(), $group = '' ) {
110
  if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
122
  }
123
 
124
  $action_id = ActionScheduler::store()->query_action( $params );
125
+
126
  if ( $action_id ) {
127
+ try {
128
+ ActionScheduler::store()->cancel_action( $action_id );
129
+ } catch ( Exception $exception ) {
130
+ ActionScheduler::logger()->log(
131
+ $action_id,
132
+ sprintf(
133
+ /* translators: %s is the name of the hook to be cancelled. */
134
+ __( 'Caught exception while cancelling action: %s', 'action-scheduler' ),
135
+ esc_attr( $hook )
136
+ )
137
+ );
138
+
139
+ $action_id = null;
140
+ }
141
  }
142
 
143
  return $action_id;
147
  * Cancel all occurrences of a scheduled action.
148
  *
149
  * @param string $hook The hook that the job will trigger.
150
+ * @param array $args Args that would have been passed to the job.
151
  * @param string $group The group the job is assigned to.
152
  */
153
  function as_unschedule_all_actions( $hook, $args = array(), $group = '' ) {
178
  * returned. Or there may be no async, in-progress or pending action for this hook, in which case,
179
  * boolean false will be the return value.
180
  *
181
+ * @param string $hook Name of the hook to search for.
182
+ * @param array $args Arguments of the action to be searched.
183
+ * @param string $group Group of the action to be searched.
184
  *
185
  * @return int|bool The timestamp for the next occurrence of a pending scheduled action, true for an async or in-progress action or false if there is no matching action.
186
  */
216
  $scheduled_date = $action->get_schedule()->get_date();
217
  if ( $scheduled_date ) {
218
  return (int) $scheduled_date->format( 'U' );
219
+ } elseif ( null === $scheduled_date ) { // pending async action with NullSchedule.
220
  return true;
221
  }
222
 
229
  * It's recommended to use this function when you need to know whether a specific action is currently scheduled
230
  * (pending or in-progress).
231
  *
232
+ * @since 3.3.0
233
  *
234
  * @param string $hook The hook of the action.
235
  * @param array $args Args that have been passed to the action. Null will matches any args.
243
  }
244
 
245
  $query_args = array(
246
+ 'hook' => $hook,
247
+ 'status' => array( ActionScheduler_Store::STATUS_RUNNING, ActionScheduler_Store::STATUS_PENDING ),
248
+ 'group' => $group,
249
+ 'orderby' => 'none',
250
  );
251
 
252
  if ( null !== $args ) {
255
 
256
  $action_id = ActionScheduler::store()->query_action( $query_args );
257
 
258
+ return null !== $action_id;
259
  }
260
 
261
  /**
262
  * Find scheduled actions
263
  *
264
+ * @param array $args Possible arguments, with their default values.
265
+ * 'hook' => '' - the name of the action that will be triggered.
266
+ * 'args' => NULL - the args array that will be passed with the action.
267
+ * 'date' => NULL - the scheduled date of the action. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone.
268
+ * 'date_compare' => '<=' - operator for testing "date". accepted values are '!=', '>', '>=', '<', '<=', '='.
269
+ * 'modified' => NULL - the date the action was last updated. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone.
270
+ * 'modified_compare' => '<=' - operator for testing "modified". accepted values are '!=', '>', '>=', '<', '<=', '='.
271
+ * 'group' => '' - the group the action belongs to.
272
+ * 'status' => '' - ActionScheduler_Store::STATUS_COMPLETE or ActionScheduler_Store::STATUS_PENDING.
273
+ * 'claimed' => NULL - TRUE to find claimed actions, FALSE to find unclaimed actions, a string to find a specific claim ID.
274
+ * 'per_page' => 5 - Number of results to return.
275
+ * 'offset' => 0.
276
+ * 'orderby' => 'date' - accepted values are 'hook', 'group', 'modified', 'date' or 'none'.
277
+ * 'order' => 'ASC'.
278
  *
279
  * @param string $return_format OBJECT, ARRAY_A, or ids.
280
  *
285
  return array();
286
  }
287
  $store = ActionScheduler::store();
288
+ foreach ( array( 'date', 'modified' ) as $key ) {
289
+ if ( isset( $args[ $key ] ) ) {
290
+ $args[ $key ] = as_get_datetime_object( $args[ $key ] );
291
  }
292
  }
293
  $ids = $store->query_actions( $args );
294
 
295
+ if ( 'ids' === $return_format || 'int' === $return_format ) {
296
  return $ids;
297
  }
298
 
299
  $actions = array();
300
  foreach ( $ids as $action_id ) {
301
+ $actions[ $action_id ] = $store->fetch_action( $action_id );
302
  }
303
 
304
+ if ( ARRAY_A == $return_format ) {
305
  foreach ( $actions as $action_id => $action_object ) {
306
+ $actions[ $action_id ] = get_object_vars( $action_object );
307
  }
308
  }
309
 
322
  * timezone when instantiating datetimes rather than leaving it up to
323
  * the PHP default.
324
  *
325
+ * @param mixed $date_string A date/time string. Valid formats are explained in http://php.net/manual/en/datetime.formats.php.
326
  * @param string $timezone A timezone identifier, like UTC or Europe/Lisbon. The list of valid identifiers is available http://php.net/manual/en/timezones.php.
327
  *
328
  * @return ActionScheduler_DateTime
333
  } elseif ( is_numeric( $date_string ) ) {
334
  $date = new ActionScheduler_DateTime( '@' . $date_string, new DateTimeZone( $timezone ) );
335
  } else {
336
+ $date = new ActionScheduler_DateTime( null === $date_string ? 'now' : $date_string, new DateTimeZone( $timezone ) );
337
  }
338
  return $date;
339
  }
includes/vendor/action-scheduler/lib/WP_Async_Request.php CHANGED
@@ -104,10 +104,17 @@ if ( ! class_exists( 'WP_Async_Request' ) ) {
104
  return $this->query_args;
105
  }
106
 
107
- return array(
108
  'action' => $this->identifier,
109
  'nonce' => wp_create_nonce( $this->identifier ),
110
  );
 
 
 
 
 
 
 
111
  }
112
 
113
  /**
@@ -120,7 +127,14 @@ if ( ! class_exists( 'WP_Async_Request' ) ) {
120
  return $this->query_url;
121
  }
122
 
123
- return admin_url( 'admin-ajax.php' );
 
 
 
 
 
 
 
124
  }
125
 
126
  /**
@@ -133,13 +147,20 @@ if ( ! class_exists( 'WP_Async_Request' ) ) {
133
  return $this->post_args;
134
  }
135
 
136
- return array(
137
  'timeout' => 0.01,
138
  'blocking' => false,
139
  'body' => $this->data,
140
  'cookies' => $_COOKIE,
141
  'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
142
  );
 
 
 
 
 
 
 
143
  }
144
 
145
  /**
104
  return $this->query_args;
105
  }
106
 
107
+ $args = array(
108
  'action' => $this->identifier,
109
  'nonce' => wp_create_nonce( $this->identifier ),
110
  );
111
+
112
+ /**
113
+ * Filters the post arguments used during an async request.
114
+ *
115
+ * @param array $url
116
+ */
117
+ return apply_filters( $this->identifier . '_query_args', $args );
118
  }
119
 
120
  /**
127
  return $this->query_url;
128
  }
129
 
130
+ $url = admin_url( 'admin-ajax.php' );
131
+
132
+ /**
133
+ * Filters the post arguments used during an async request.
134
+ *
135
+ * @param string $url
136
+ */
137
+ return apply_filters( $this->identifier . '_query_url', $url );
138
  }
139
 
140
  /**
147
  return $this->post_args;
148
  }
149
 
150
+ $args = array(
151
  'timeout' => 0.01,
152
  'blocking' => false,
153
  'body' => $this->data,
154
  'cookies' => $_COOKIE,
155
  'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
156
  );
157
+
158
+ /**
159
+ * Filters the post arguments used during an async request.
160
+ *
161
+ * @param array $args
162
+ */
163
+ return apply_filters( $this->identifier . '_post_args', $args );
164
  }
165
 
166
  /**
mailchimp-woocommerce.php CHANGED
@@ -16,7 +16,7 @@
16
  * Plugin Name: Mailchimp for WooCommerce
17
  * Plugin URI: https://mailchimp.com/connect-your-store/
18
  * Description: Connects WooCommerce to Mailchimp to sync your store data, send targeted campaigns to your customers, and sell more stuff.
19
- * Version: 2.7.5
20
  * Author: Mailchimp
21
  * Author URI: https://mailchimp.com
22
  * License: GPL-2.0+
@@ -26,7 +26,7 @@
26
  * Requires at least: 4.9
27
  * Tested up to: 6.0
28
  * WC requires at least: 3.5
29
- * WC tested up to: 6.9
30
  */
31
 
32
  // If this file is called directly, abort.
16
  * Plugin Name: Mailchimp for WooCommerce
17
  * Plugin URI: https://mailchimp.com/connect-your-store/
18
  * Description: Connects WooCommerce to Mailchimp to sync your store data, send targeted campaigns to your customers, and sell more stuff.
19
+ * Version: 2.7.6
20
  * Author: Mailchimp
21
  * Author URI: https://mailchimp.com
22
  * License: GPL-2.0+
26
  * Requires at least: 4.9
27
  * Tested up to: 6.0
28
  * WC requires at least: 3.5
29
+ * WC tested up to: 7.1
30
  */
31
 
32
  // If this file is called directly, abort.
public/class-mailchimp-woocommerce-public.php CHANGED
@@ -108,7 +108,7 @@ class MailChimp_WooCommerce_Public {
108
  public function user_my_account_opt_in_save($user_id)
109
  {
110
  $subscribed = isset($_POST['mailchimp_woocommerce_is_subscribed_checkbox']) &&
111
- $_POST['mailchimp_woocommerce_is_subscribed_checkbox'] == 'on';
112
  update_user_meta( $user_id, 'mailchimp_woocommerce_is_subscribed', $subscribed);
113
  }
114
 
108
  public function user_my_account_opt_in_save($user_id)
109
  {
110
  $subscribed = isset($_POST['mailchimp_woocommerce_is_subscribed_checkbox']) &&
111
+ ( $_POST['mailchimp_woocommerce_is_subscribed_checkbox'] == 'on' || $_POST['mailchimp_woocommerce_is_subscribed_checkbox'] == '1');
112
  update_user_meta( $user_id, 'mailchimp_woocommerce_is_subscribed', $subscribed);
113
  }
114