Version Description
Download this release
Release Info
Developer | anderly |
Plugin | WooCommerce MailChimp |
Version | 2.3.2 |
Comparing to | |
See all releases |
Code changes from version 2.3.1 to 2.3.2
- includes/class-ss-wc-mailchimp-handler.php +14 -42
- includes/class-ss-wc-mailchimp-plugin.php +61 -1
- includes/lib/action-scheduler/.gitattributes +9 -0
- includes/lib/action-scheduler/action-scheduler.php +9 -9
- includes/lib/action-scheduler/classes/ActionScheduler.php +9 -0
- includes/lib/action-scheduler/classes/ActionScheduler_Abstract_QueueRunner.php +6 -3
- includes/lib/action-scheduler/classes/ActionScheduler_AdminHelp.php +47 -0
- includes/lib/action-scheduler/classes/ActionScheduler_AdminView.php +6 -1
- includes/lib/action-scheduler/classes/ActionScheduler_CronSchedule.php +7 -0
- includes/lib/action-scheduler/classes/ActionScheduler_IntervalSchedule.php +1 -3
- includes/lib/action-scheduler/classes/ActionScheduler_ListTable.php +11 -4
- includes/lib/action-scheduler/classes/ActionScheduler_Logger.php +6 -2
- includes/lib/action-scheduler/classes/ActionScheduler_QueueCleaner.php +25 -14
- includes/lib/action-scheduler/classes/ActionScheduler_Store.php +31 -1
- includes/lib/action-scheduler/classes/ActionScheduler_WPCLI_QueueRunner.php +21 -9
- includes/lib/action-scheduler/classes/ActionScheduler_wcSystemStatus.php +147 -0
- includes/lib/action-scheduler/classes/ActionScheduler_wpPostStore.php +97 -25
- includes/lib/action-scheduler/composer.json +1 -1
- includes/lib/action-scheduler/docs/_layouts/default.html +5 -0
- includes/lib/action-scheduler/docs/assets/css/style.scss +25 -0
- includes/lib/action-scheduler/docs/faq.md +2 -2
- includes/lib/action-scheduler/docs/usage.md +2 -2
- includes/lib/action-scheduler/license.txt +4 -4
- includes/lib/action-scheduler/tests/phpunit/jobstore/ActionScheduler_wpPostStore_Test.php +3 -3
- includes/lib/action-scheduler/tests/phpunit/runner/ActionScheduler_QueueRunner_Test.php +54 -1
- readme.txt +5 -1
- woocommerce-mailchimp.php +1 -1
includes/class-ss-wc-mailchimp-handler.php
CHANGED
@@ -125,7 +125,7 @@ if ( ! class_exists( 'SS_WC_MailChimp_Handler' ) ) {
|
|
125 |
$this->log( sprintf( __( __METHOD__ . '(): Queueing maybe subscribe ($subscribe_customer: %s) for customer (%s) to list %s for order (%s)', 'woocommerce-mailchimp'), $subscribe_customer, $order_billing_email, $list_id, $order_id ) );
|
126 |
|
127 |
// Queue the subscription.
|
128 |
-
as_schedule_single_action( time(), 'queue_ss_wc_mailchimp_maybe_subscribe', array( $
|
129 |
|
130 |
}
|
131 |
}
|
@@ -369,59 +369,31 @@ if ( ! class_exists( 'SS_WC_MailChimp_Handler' ) ) {
|
|
369 |
* @param string $listid (default: 'false')
|
370 |
* @return void
|
371 |
*/
|
372 |
-
public function maybe_subscribe( $
|
373 |
|
374 |
-
|
375 |
-
|
376 |
-
if ( ! $email ) {
|
377 |
-
return; // Email is required.
|
378 |
-
}
|
379 |
|
380 |
-
|
381 |
-
|
382 |
-
}
|
383 |
|
384 |
-
$
|
385 |
-
|
386 |
-
'LNAME' => $last_name,
|
387 |
-
);
|
388 |
|
389 |
-
$
|
390 |
|
391 |
-
if ( !
|
392 |
-
|
393 |
}
|
394 |
|
395 |
// Allow hooking into interest groups.
|
396 |
-
$interest_groups = apply_filters( 'ss_wc_mailchimp_subscribe_interest_groups', $interest_groups, $order_id, $email );
|
397 |
-
|
398 |
-
$tags = $this->sswcmc->tags();
|
399 |
-
|
400 |
-
$mc_tags = $this->sswcmc->mailchimp()->get_tags( $list_id );
|
401 |
-
|
402 |
-
$tags = array_map( function( $tag ) use ( $mc_tags ) {
|
403 |
-
return array(
|
404 |
-
'name' => $mc_tags[$tag],
|
405 |
-
'status' => 'active',
|
406 |
-
);
|
407 |
-
}, $tags );
|
408 |
|
409 |
// Allow hooking into tags.
|
410 |
-
$tags = apply_filters( 'ss_wc_mailchimp_subscribe_tags', $tags, $order_id, $email );
|
411 |
|
412 |
// Allow hooking into variables.
|
413 |
-
$merge_tags = apply_filters( 'ss_wc_mailchimp_subscribe_merge_tags', $merge_tags, $order_id, $email );
|
414 |
-
|
415 |
-
// Set subscription options.
|
416 |
-
$subscribe_options = array(
|
417 |
-
'list_id' => $list_id,
|
418 |
-
'email' => $email,
|
419 |
-
'merge_tags' => $merge_tags,
|
420 |
-
'interest_groups' => $interest_groups,
|
421 |
-
'tags' => $tags,
|
422 |
-
'email_type' => 'html',
|
423 |
-
'double_opt_in' => $this->sswcmc->double_opt_in(),
|
424 |
-
);
|
425 |
|
426 |
// Allow hooking into subscription options.
|
427 |
$options = apply_filters( 'ss_wc_mailchimp_subscribe_options', $subscribe_options, $order_id );
|
125 |
$this->log( sprintf( __( __METHOD__ . '(): Queueing maybe subscribe ($subscribe_customer: %s) for customer (%s) to list %s for order (%s)', 'woocommerce-mailchimp'), $subscribe_customer, $order_billing_email, $list_id, $order_id ) );
|
126 |
|
127 |
// Queue the subscription.
|
128 |
+
as_schedule_single_action( time(), 'queue_ss_wc_mailchimp_maybe_subscribe', array( $order_id ), 'sswcmc' );
|
129 |
|
130 |
}
|
131 |
}
|
369 |
* @param string $listid (default: 'false')
|
370 |
* @return void
|
371 |
*/
|
372 |
+
public function maybe_subscribe( $order_id ) {
|
373 |
|
374 |
+
// get the ss_wc_mailchimp_opt_in value from the post meta. "order_custom_fields" was removed with WooCommerce 2.1
|
375 |
+
$subscribe_customer = get_post_meta( $order_id, 'ss_wc_mailchimp_opt_in', true );
|
|
|
|
|
|
|
376 |
|
377 |
+
// Get the subscribe options
|
378 |
+
$subscribe_options = $this->sswcmc->get_subscribe_options_for_order( $order_id );
|
|
|
379 |
|
380 |
+
$email = $subscribe_options['email'];
|
381 |
+
$list_id = $subscribe_options['list_id'];
|
|
|
|
|
382 |
|
383 |
+
$this->log( sprintf( __( __METHOD__ . '(): Processing queued maybe_subscribe ($subscribe_customer: %s) for customer (%s) to list %s for order (%s)', 'woocommerce-mailchimp' ), $subscribe_customer, $email, $list_id, $order_id ) );
|
384 |
|
385 |
+
if ( ! $email ) {
|
386 |
+
return; // Email is required.
|
387 |
}
|
388 |
|
389 |
// Allow hooking into interest groups.
|
390 |
+
$subscribe_options['interest_groups'] = apply_filters( 'ss_wc_mailchimp_subscribe_interest_groups', $subscribe_options['interest_groups'], $order_id, $email );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
391 |
|
392 |
// Allow hooking into tags.
|
393 |
+
$subscribe_options['tags'] = apply_filters( 'ss_wc_mailchimp_subscribe_tags', $subscribe_options['tags'], $order_id, $email );
|
394 |
|
395 |
// Allow hooking into variables.
|
396 |
+
$subscribe_options['merge_tags'] = apply_filters( 'ss_wc_mailchimp_subscribe_merge_tags', $subscribe_options['merge_tags'], $order_id, $email );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
397 |
|
398 |
// Allow hooking into subscription options.
|
399 |
$options = apply_filters( 'ss_wc_mailchimp_subscribe_options', $subscribe_options, $order_id );
|
includes/class-ss-wc-mailchimp-plugin.php
CHANGED
@@ -15,7 +15,7 @@ final class SS_WC_MailChimp_Plugin {
|
|
15 |
*
|
16 |
* @var string
|
17 |
*/
|
18 |
-
private static $version = '2.3.
|
19 |
|
20 |
/**
|
21 |
* Plugin singleton instance
|
@@ -248,6 +248,66 @@ final class SS_WC_MailChimp_Plugin {
|
|
248 |
return $this->settings['tags'];
|
249 |
}
|
250 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
251 |
/**
|
252 |
* Whether or not an api key has been set.
|
253 |
*
|
15 |
*
|
16 |
* @var string
|
17 |
*/
|
18 |
+
private static $version = '2.3.2';
|
19 |
|
20 |
/**
|
21 |
* Plugin singleton instance
|
248 |
return $this->settings['tags'];
|
249 |
}
|
250 |
|
251 |
+
/**
|
252 |
+
* Get the global subscribe options for the passed $order_id
|
253 |
+
*
|
254 |
+
* @since 2.3.2
|
255 |
+
* @access public
|
256 |
+
* @param $order_id int The order id.
|
257 |
+
*/
|
258 |
+
public function get_subscribe_options_for_order( $order_id ) {
|
259 |
+
|
260 |
+
// Get WC order
|
261 |
+
$order = wc_get_order( $order_id );
|
262 |
+
|
263 |
+
$order_id = method_exists( $order, 'get_id' ) ? $order->get_id() : $order->id;
|
264 |
+
$email = method_exists( $order, 'get_billing_email' ) ? $order->get_billing_email() : $order->billing_email;
|
265 |
+
$first_name = method_exists( $order, 'get_billing_first_name' ) ? $order->get_billing_first_name() : $order->billing_first_name;
|
266 |
+
$last_name = method_exists( $order, 'get_billing_last_name' ) ? $order->get_billing_last_name() : $order->billing_last_name;
|
267 |
+
|
268 |
+
$list_id = $this->get_list();
|
269 |
+
|
270 |
+
if ( ! $email ) {
|
271 |
+
return; // Email is required.
|
272 |
+
}
|
273 |
+
|
274 |
+
$merge_tags = array(
|
275 |
+
'FNAME' => $first_name,
|
276 |
+
'LNAME' => $last_name,
|
277 |
+
);
|
278 |
+
|
279 |
+
$interest_groups = $this->interest_groups();
|
280 |
+
|
281 |
+
if ( ! empty( $interest_groups ) ) {
|
282 |
+
$interest_groups = array_fill_keys( $interest_groups, true );
|
283 |
+
}
|
284 |
+
|
285 |
+
$tags = $this->tags();
|
286 |
+
|
287 |
+
$mc_tags = $this->mailchimp()->get_tags( $list_id );
|
288 |
+
|
289 |
+
$tags = array_map( function( $tag ) use ( $mc_tags ) {
|
290 |
+
return array(
|
291 |
+
'name' => $mc_tags[$tag],
|
292 |
+
'status' => 'active',
|
293 |
+
);
|
294 |
+
}, $tags );
|
295 |
+
|
296 |
+
// Set subscription options.
|
297 |
+
$subscribe_options = array(
|
298 |
+
'list_id' => $list_id,
|
299 |
+
'email' => $email,
|
300 |
+
'merge_tags' => $merge_tags,
|
301 |
+
'interest_groups' => $interest_groups,
|
302 |
+
'tags' => $tags,
|
303 |
+
'email_type' => 'html',
|
304 |
+
'double_opt_in' => $this->double_opt_in(),
|
305 |
+
);
|
306 |
+
|
307 |
+
return $subscribe_options;
|
308 |
+
|
309 |
+
} //end function get_subscribe_options_for_order
|
310 |
+
|
311 |
/**
|
312 |
* Whether or not an api key has been set.
|
313 |
*
|
includes/lib/action-scheduler/.gitattributes
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
docs export-ignore
|
2 |
+
tests export-ignore
|
3 |
+
codecov.yml export-ignore
|
4 |
+
.github export-ignore
|
5 |
+
.travis.yml export-ignore
|
6 |
+
.gitattributes export-ignore
|
7 |
+
.gitignore export-ignore
|
8 |
+
phpunit.xml.dist export-ignore
|
9 |
+
|
includes/lib/action-scheduler/action-scheduler.php
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
<?php
|
2 |
/*
|
3 |
* Plugin Name: Action Scheduler
|
4 |
-
* Plugin URI: https://
|
5 |
* Description: A robust scheduling library for use in WordPress plugins.
|
6 |
* Author: Prospress
|
7 |
-
* Author URI:
|
8 |
-
* Version: 2.
|
9 |
* License: GPLv3
|
10 |
*
|
11 |
-
* Copyright
|
12 |
*
|
13 |
* This program is free software: you can redistribute it and/or modify
|
14 |
* it under the terms of the GNU General Public License as published by
|
@@ -25,21 +25,21 @@
|
|
25 |
*
|
26 |
*/
|
27 |
|
28 |
-
if ( ! function_exists( '
|
29 |
|
30 |
if ( ! class_exists( 'ActionScheduler_Versions' ) ) {
|
31 |
require_once( 'classes/ActionScheduler_Versions.php' );
|
32 |
add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
|
33 |
}
|
34 |
|
35 |
-
add_action( 'plugins_loaded', '
|
36 |
|
37 |
-
function
|
38 |
$versions = ActionScheduler_Versions::instance();
|
39 |
-
$versions->register( '2.
|
40 |
}
|
41 |
|
42 |
-
function
|
43 |
require_once( 'classes/ActionScheduler.php' );
|
44 |
ActionScheduler::init( __FILE__ );
|
45 |
}
|
1 |
<?php
|
2 |
/*
|
3 |
* Plugin Name: Action Scheduler
|
4 |
+
* Plugin URI: https://actionscheduler.org
|
5 |
* Description: A robust scheduling library for use in WordPress plugins.
|
6 |
* Author: Prospress
|
7 |
+
* Author URI: https://prospress.com/
|
8 |
+
* Version: 2.2.5
|
9 |
* License: GPLv3
|
10 |
*
|
11 |
+
* Copyright 2019 Prospress, Inc. (email : freedoms@prospress.com)
|
12 |
*
|
13 |
* This program is free software: you can redistribute it and/or modify
|
14 |
* it under the terms of the GNU General Public License as published by
|
25 |
*
|
26 |
*/
|
27 |
|
28 |
+
if ( ! function_exists( 'action_scheduler_register_2_dot_2_dot_5' ) && function_exists( 'add_action' ) ) {
|
29 |
|
30 |
if ( ! class_exists( 'ActionScheduler_Versions' ) ) {
|
31 |
require_once( 'classes/ActionScheduler_Versions.php' );
|
32 |
add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
|
33 |
}
|
34 |
|
35 |
+
add_action( 'plugins_loaded', 'action_scheduler_register_2_dot_2_dot_5', 0, 0 );
|
36 |
|
37 |
+
function action_scheduler_register_2_dot_2_dot_5() {
|
38 |
$versions = ActionScheduler_Versions::instance();
|
39 |
+
$versions->register( '2.2.5', 'action_scheduler_initialize_2_dot_2_dot_5' );
|
40 |
}
|
41 |
|
42 |
+
function action_scheduler_initialize_2_dot_2_dot_5() {
|
43 |
require_once( 'classes/ActionScheduler.php' );
|
44 |
ActionScheduler::init( __FILE__ );
|
45 |
}
|
includes/lib/action-scheduler/classes/ActionScheduler.php
CHANGED
@@ -85,6 +85,11 @@ abstract class ActionScheduler {
|
|
85 |
self::$plugin_file = $plugin_file;
|
86 |
spl_autoload_register( array( __CLASS__, 'autoload' ) );
|
87 |
|
|
|
|
|
|
|
|
|
|
|
88 |
$store = self::store();
|
89 |
add_action( 'init', array( $store, 'init' ), 1, 0 );
|
90 |
|
@@ -97,6 +102,10 @@ abstract class ActionScheduler {
|
|
97 |
$admin_view = self::admin_view();
|
98 |
add_action( 'init', array( $admin_view, 'init' ), 0, 0 ); // run before $store::init()
|
99 |
|
|
|
|
|
|
|
|
|
100 |
require_once( self::plugin_path('functions.php') );
|
101 |
|
102 |
if ( apply_filters( 'action_scheduler_load_deprecated_functions', true ) ) {
|
85 |
self::$plugin_file = $plugin_file;
|
86 |
spl_autoload_register( array( __CLASS__, 'autoload' ) );
|
87 |
|
88 |
+
/**
|
89 |
+
* Fires in the early stages of Action Scheduler init hook.
|
90 |
+
*/
|
91 |
+
do_action( 'action_scheduler_pre_init' );
|
92 |
+
|
93 |
$store = self::store();
|
94 |
add_action( 'init', array( $store, 'init' ), 1, 0 );
|
95 |
|
102 |
$admin_view = self::admin_view();
|
103 |
add_action( 'init', array( $admin_view, 'init' ), 0, 0 ); // run before $store::init()
|
104 |
|
105 |
+
if ( is_admin() ) {
|
106 |
+
add_action( 'current_screen', array( 'ActionScheduler_AdminHelp', 'add_help_tabs' ) );
|
107 |
+
}
|
108 |
+
|
109 |
require_once( self::plugin_path('functions.php') );
|
110 |
|
111 |
if ( apply_filters( 'action_scheduler_load_deprecated_functions', true ) ) {
|
includes/lib/action-scheduler/classes/ActionScheduler_Abstract_QueueRunner.php
CHANGED
@@ -57,13 +57,16 @@ abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abst
|
|
57 |
$action = $this->store->fetch_action( $action_id );
|
58 |
$this->store->log_execution( $action_id );
|
59 |
$action->execute();
|
60 |
-
do_action( 'action_scheduler_after_execute', $action_id );
|
61 |
$this->store->mark_complete( $action_id );
|
62 |
} catch ( Exception $e ) {
|
63 |
$this->store->mark_failure( $action_id );
|
64 |
do_action( 'action_scheduler_failed_execution', $action_id, $e );
|
65 |
}
|
66 |
-
|
|
|
|
|
|
|
67 |
}
|
68 |
|
69 |
/**
|
@@ -86,7 +89,7 @@ abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abst
|
|
86 |
* @author Jeremy Pry
|
87 |
*/
|
88 |
protected function run_cleanup() {
|
89 |
-
$this->cleaner->clean();
|
90 |
}
|
91 |
|
92 |
/**
|
57 |
$action = $this->store->fetch_action( $action_id );
|
58 |
$this->store->log_execution( $action_id );
|
59 |
$action->execute();
|
60 |
+
do_action( 'action_scheduler_after_execute', $action_id, $action );
|
61 |
$this->store->mark_complete( $action_id );
|
62 |
} catch ( Exception $e ) {
|
63 |
$this->store->mark_failure( $action_id );
|
64 |
do_action( 'action_scheduler_failed_execution', $action_id, $e );
|
65 |
}
|
66 |
+
|
67 |
+
if ( isset( $action ) && is_a( $action, 'ActionScheduler_Action' ) ) {
|
68 |
+
$this->schedule_next_instance( $action );
|
69 |
+
}
|
70 |
}
|
71 |
|
72 |
/**
|
89 |
* @author Jeremy Pry
|
90 |
*/
|
91 |
protected function run_cleanup() {
|
92 |
+
$this->cleaner->clean( 10 * $this->get_time_limit() );
|
93 |
}
|
94 |
|
95 |
/**
|
includes/lib/action-scheduler/classes/ActionScheduler_AdminHelp.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class ActionScheduler_AdminHelp
|
5 |
+
* @codeCoverageIgnore
|
6 |
+
*/
|
7 |
+
class ActionScheduler_AdminHelp {
|
8 |
+
const SCREEN_ID = 'tools_page_action-scheduler';
|
9 |
+
|
10 |
+
public function add_help_tabs() {
|
11 |
+
$screen = get_current_screen();
|
12 |
+
|
13 |
+
if ( ! $screen || self::SCREEN_ID != $screen->id ) {
|
14 |
+
return;
|
15 |
+
}
|
16 |
+
|
17 |
+
$screen->add_help_tab(
|
18 |
+
array(
|
19 |
+
'id' => 'action_scheduler_about',
|
20 |
+
'title' => __( 'About', 'action-scheduler' ),
|
21 |
+
'content' =>
|
22 |
+
'<h2>' . __( 'About Action Scheduler', 'action-scheduler' ) . '</h2>' .
|
23 |
+
'<p>' .
|
24 |
+
__( 'Action Scheduler is a scalable, traceable job queue for background processing large sets of actions. Action Scheduler works by triggering an action hook to run at some time in the future. Scheduled actions can also be scheduled to run on a recurring schedule.', 'action-scheduler' ) .
|
25 |
+
'</p>',
|
26 |
+
)
|
27 |
+
);
|
28 |
+
|
29 |
+
$screen->add_help_tab(
|
30 |
+
array(
|
31 |
+
'id' => 'action_scheduler_columns',
|
32 |
+
'title' => __( 'Columns', 'action-scheduler' ),
|
33 |
+
'content' =>
|
34 |
+
'<h2>' . __( 'Scheduled Action Columns', 'action-scheduler' ) . '</h2>' .
|
35 |
+
'<ul>' .
|
36 |
+
sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Hook', 'action-scheduler' ), __( 'Name of the action hook that will be triggered.', 'action-scheduler' ) ) .
|
37 |
+
sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Status', 'action-scheduler' ), __( 'Action statuses are Pending, Complete, Canceled, Failed', 'action-scheduler' ) ) .
|
38 |
+
sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Arguments', 'action-scheduler' ), __( 'Optional data array passed to the action hook.', 'action-scheduler' ) ) .
|
39 |
+
sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Group', 'action-scheduler' ), __( 'Optional action group.', 'action-scheduler' ) ) .
|
40 |
+
sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Recurrence', 'action-scheduler' ), __( 'The action\'s schedule frequency.', 'action-scheduler' ) ) .
|
41 |
+
sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Scheduled', 'action-scheduler' ), __( 'The date/time the action is/was scheduled to run.', 'action-scheduler' ) ) .
|
42 |
+
sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Log', 'action-scheduler' ), __( 'Activity log for the action.', 'action-scheduler' ) ) .
|
43 |
+
'</ul>',
|
44 |
+
)
|
45 |
+
);
|
46 |
+
}
|
47 |
+
}
|
includes/lib/action-scheduler/classes/ActionScheduler_AdminView.php
CHANGED
@@ -9,7 +9,7 @@ class ActionScheduler_AdminView extends ActionScheduler_AdminView_Deprecated {
|
|
9 |
private static $admin_view = NULL;
|
10 |
|
11 |
/**
|
12 |
-
* @return
|
13 |
* @codeCoverageIgnore
|
14 |
*/
|
15 |
public static function instance() {
|
@@ -30,6 +30,7 @@ class ActionScheduler_AdminView extends ActionScheduler_AdminView_Deprecated {
|
|
30 |
|
31 |
if ( class_exists( 'WooCommerce' ) ) {
|
32 |
add_action( 'woocommerce_admin_status_content_action-scheduler', array( $this, 'render_admin_ui' ) );
|
|
|
33 |
add_filter( 'woocommerce_admin_status_tabs', array( $this, 'register_system_status_tab' ) );
|
34 |
}
|
35 |
|
@@ -37,6 +38,10 @@ class ActionScheduler_AdminView extends ActionScheduler_AdminView_Deprecated {
|
|
37 |
}
|
38 |
}
|
39 |
|
|
|
|
|
|
|
|
|
40 |
|
41 |
/**
|
42 |
* Registers action-scheduler into WooCommerce > System status.
|
9 |
private static $admin_view = NULL;
|
10 |
|
11 |
/**
|
12 |
+
* @return ActionScheduler_AdminView
|
13 |
* @codeCoverageIgnore
|
14 |
*/
|
15 |
public static function instance() {
|
30 |
|
31 |
if ( class_exists( 'WooCommerce' ) ) {
|
32 |
add_action( 'woocommerce_admin_status_content_action-scheduler', array( $this, 'render_admin_ui' ) );
|
33 |
+
add_action( 'woocommerce_system_status_report', array( $this, 'system_status_report' ) );
|
34 |
add_filter( 'woocommerce_admin_status_tabs', array( $this, 'register_system_status_tab' ) );
|
35 |
}
|
36 |
|
38 |
}
|
39 |
}
|
40 |
|
41 |
+
public function system_status_report() {
|
42 |
+
$table = new ActionScheduler_wcSystemStatus( ActionScheduler::store() );
|
43 |
+
$table->render();
|
44 |
+
}
|
45 |
|
46 |
/**
|
47 |
* Registers action-scheduler into WooCommerce > System status.
|
includes/lib/action-scheduler/classes/ActionScheduler_CronSchedule.php
CHANGED
@@ -31,6 +31,13 @@ class ActionScheduler_CronSchedule implements ActionScheduler_Schedule {
|
|
31 |
return true;
|
32 |
}
|
33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
/**
|
35 |
* For PHP 5.2 compat, since DateTime objects can't be serialized
|
36 |
* @return array
|
31 |
return true;
|
32 |
}
|
33 |
|
34 |
+
/**
|
35 |
+
* @return string
|
36 |
+
*/
|
37 |
+
public function get_recurrence() {
|
38 |
+
return strval($this->cron);
|
39 |
+
}
|
40 |
+
|
41 |
/**
|
42 |
* For PHP 5.2 compat, since DateTime objects can't be serialized
|
43 |
* @return array
|
includes/lib/action-scheduler/classes/ActionScheduler_IntervalSchedule.php
CHANGED
@@ -36,9 +36,7 @@ class ActionScheduler_IntervalSchedule implements ActionScheduler_Schedule {
|
|
36 |
}
|
37 |
|
38 |
/**
|
39 |
-
* @
|
40 |
-
*
|
41 |
-
* @return DateTime|null
|
42 |
*/
|
43 |
public function interval_in_seconds() {
|
44 |
return $this->interval_in_seconds;
|
36 |
}
|
37 |
|
38 |
/**
|
39 |
+
* @return int
|
|
|
|
|
40 |
*/
|
41 |
public function interval_in_seconds() {
|
42 |
return $this->interval_in_seconds;
|
includes/lib/action-scheduler/classes/ActionScheduler_ListTable.php
CHANGED
@@ -222,9 +222,16 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
|
|
222 |
*/
|
223 |
protected function get_recurrence( $action ) {
|
224 |
$recurrence = $action->get_schedule();
|
225 |
-
if (
|
226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
227 |
}
|
|
|
228 |
return __( 'Non-repeating', 'action-scheduler' );
|
229 |
}
|
230 |
|
@@ -280,7 +287,7 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
|
|
280 |
protected function get_log_entry_html( ActionScheduler_LogEntry $log_entry, DateTimezone $timezone ) {
|
281 |
$date = $log_entry->get_date();
|
282 |
$date->setTimezone( $timezone );
|
283 |
-
return sprintf( '<li><strong>%s</strong><br/>%s</li>', esc_html( $date->format( 'Y-m-d H:i:s
|
284 |
}
|
285 |
|
286 |
/**
|
@@ -378,7 +385,7 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
|
|
378 |
|
379 |
$next_timestamp = $schedule->next()->getTimestamp();
|
380 |
|
381 |
-
$schedule_display_string .= $schedule->next()->format( 'Y-m-d H:i:s
|
382 |
$schedule_display_string .= '<br/>';
|
383 |
|
384 |
if ( gmdate( 'U' ) > $next_timestamp ) {
|
222 |
*/
|
223 |
protected function get_recurrence( $action ) {
|
224 |
$recurrence = $action->get_schedule();
|
225 |
+
if ( $recurrence->is_recurring() ) {
|
226 |
+
if ( method_exists( $recurrence, 'interval_in_seconds' ) ) {
|
227 |
+
return sprintf( __( 'Every %s', 'action-scheduler' ), self::human_interval( $recurrence->interval_in_seconds() ) );
|
228 |
+
}
|
229 |
+
|
230 |
+
if ( method_exists( $recurrence, 'get_recurrence' ) ) {
|
231 |
+
return sprintf( __( 'Cron %s', 'action-scheduler' ), $recurrence->get_recurrence() );
|
232 |
+
}
|
233 |
}
|
234 |
+
|
235 |
return __( 'Non-repeating', 'action-scheduler' );
|
236 |
}
|
237 |
|
287 |
protected function get_log_entry_html( ActionScheduler_LogEntry $log_entry, DateTimezone $timezone ) {
|
288 |
$date = $log_entry->get_date();
|
289 |
$date->setTimezone( $timezone );
|
290 |
+
return sprintf( '<li><strong>%s</strong><br/>%s</li>', esc_html( $date->format( 'Y-m-d H:i:s O' ) ), esc_html( $log_entry->get_message() ) );
|
291 |
}
|
292 |
|
293 |
/**
|
385 |
|
386 |
$next_timestamp = $schedule->next()->getTimestamp();
|
387 |
|
388 |
+
$schedule_display_string .= $schedule->next()->format( 'Y-m-d H:i:s O' );
|
389 |
$schedule_display_string .= '<br/>';
|
390 |
|
391 |
if ( gmdate( 'U' ) > $next_timestamp ) {
|
includes/lib/action-scheduler/classes/ActionScheduler_Logger.php
CHANGED
@@ -55,6 +55,7 @@ abstract class ActionScheduler_Logger {
|
|
55 |
add_action( 'action_scheduler_unexpected_shutdown', array( $this, 'log_unexpected_shutdown' ), 10, 2 );
|
56 |
add_action( 'action_scheduler_reset_action', array( $this, 'log_reset_action' ), 10, 1 );
|
57 |
add_action( 'action_scheduler_execution_ignored', array( $this, 'log_ignored_action' ), 10, 1 );
|
|
|
58 |
}
|
59 |
|
60 |
public function log_stored_action( $action_id ) {
|
@@ -73,7 +74,7 @@ abstract class ActionScheduler_Logger {
|
|
73 |
$this->log( $action_id, __( 'action complete', 'action-scheduler' ) );
|
74 |
}
|
75 |
|
76 |
-
public function log_failed_action( $action_id,
|
77 |
$this->log( $action_id, sprintf( __( 'action failed: %s', 'action-scheduler' ), $exception->getMessage() ) );
|
78 |
}
|
79 |
|
@@ -94,5 +95,8 @@ abstract class ActionScheduler_Logger {
|
|
94 |
public function log_ignored_action( $action_id ) {
|
95 |
$this->log( $action_id, __( 'action ignored', 'action-scheduler' ) );
|
96 |
}
|
|
|
|
|
|
|
|
|
97 |
}
|
98 |
-
|
55 |
add_action( 'action_scheduler_unexpected_shutdown', array( $this, 'log_unexpected_shutdown' ), 10, 2 );
|
56 |
add_action( 'action_scheduler_reset_action', array( $this, 'log_reset_action' ), 10, 1 );
|
57 |
add_action( 'action_scheduler_execution_ignored', array( $this, 'log_ignored_action' ), 10, 1 );
|
58 |
+
add_action( 'action_scheduler_failed_fetch_action', array( $this, 'log_failed_fetch_action' ), 10, 1 );
|
59 |
}
|
60 |
|
61 |
public function log_stored_action( $action_id ) {
|
74 |
$this->log( $action_id, __( 'action complete', 'action-scheduler' ) );
|
75 |
}
|
76 |
|
77 |
+
public function log_failed_action( $action_id, Exception $exception ) {
|
78 |
$this->log( $action_id, sprintf( __( 'action failed: %s', 'action-scheduler' ), $exception->getMessage() ) );
|
79 |
}
|
80 |
|
95 |
public function log_ignored_action( $action_id ) {
|
96 |
$this->log( $action_id, __( 'action ignored', 'action-scheduler' ) );
|
97 |
}
|
98 |
+
|
99 |
+
public function log_failed_fetch_action( $action_id ) {
|
100 |
+
$this->log( $action_id, __( 'There was a failure fetching this action', 'action-scheduler' ) );
|
101 |
+
}
|
102 |
}
|
|
includes/lib/action-scheduler/classes/ActionScheduler_QueueCleaner.php
CHANGED
@@ -18,13 +18,6 @@ class ActionScheduler_QueueCleaner {
|
|
18 |
*/
|
19 |
private $month_in_seconds = 2678400;
|
20 |
|
21 |
-
/**
|
22 |
-
* Five minutes in seconds
|
23 |
-
*
|
24 |
-
* @var int
|
25 |
-
*/
|
26 |
-
private $five_minutes = 300;
|
27 |
-
|
28 |
/**
|
29 |
* ActionScheduler_QueueCleaner constructor.
|
30 |
*
|
@@ -77,8 +70,16 @@ class ActionScheduler_QueueCleaner {
|
|
77 |
}
|
78 |
}
|
79 |
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
if ( $timeout < 0 ) {
|
83 |
return;
|
84 |
}
|
@@ -97,8 +98,17 @@ class ActionScheduler_QueueCleaner {
|
|
97 |
}
|
98 |
}
|
99 |
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
if ( $timeout < 0 ) {
|
103 |
return;
|
104 |
}
|
@@ -119,12 +129,13 @@ class ActionScheduler_QueueCleaner {
|
|
119 |
/**
|
120 |
* Do all of the cleaning actions.
|
121 |
*
|
|
|
122 |
* @author Jeremy Pry
|
123 |
*/
|
124 |
-
public function clean() {
|
125 |
$this->delete_old_actions();
|
126 |
-
$this->reset_timeouts();
|
127 |
-
$this->mark_failures();
|
128 |
}
|
129 |
|
130 |
/**
|
18 |
*/
|
19 |
private $month_in_seconds = 2678400;
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
/**
|
22 |
* ActionScheduler_QueueCleaner constructor.
|
23 |
*
|
70 |
}
|
71 |
}
|
72 |
|
73 |
+
/**
|
74 |
+
* Unclaim pending actions that have not been run within a given time limit.
|
75 |
+
*
|
76 |
+
* When called by ActionScheduler_Abstract_QueueRunner::run_cleanup(), the time limit passed
|
77 |
+
* as a parameter is 10x the time limit used for queue processing.
|
78 |
+
*
|
79 |
+
* @param int $time_limit The number of seconds to allow a queue to run before unclaiming its pending actions. Default 300 (5 minutes).
|
80 |
+
*/
|
81 |
+
public function reset_timeouts( $time_limit = 300 ) {
|
82 |
+
$timeout = apply_filters( 'action_scheduler_timeout_period', $time_limit );
|
83 |
if ( $timeout < 0 ) {
|
84 |
return;
|
85 |
}
|
98 |
}
|
99 |
}
|
100 |
|
101 |
+
/**
|
102 |
+
* Mark actions that have been running for more than a given time limit as failed, based on
|
103 |
+
* the assumption some uncatachable and unloggable fatal error occurred during processing.
|
104 |
+
*
|
105 |
+
* When called by ActionScheduler_Abstract_QueueRunner::run_cleanup(), the time limit passed
|
106 |
+
* as a parameter is 10x the time limit used for queue processing.
|
107 |
+
*
|
108 |
+
* @param int $time_limit The number of seconds to allow an action to run before it is considered to have failed. Default 300 (5 minutes).
|
109 |
+
*/
|
110 |
+
public function mark_failures( $time_limit = 300 ) {
|
111 |
+
$timeout = apply_filters( 'action_scheduler_failure_period', $time_limit );
|
112 |
if ( $timeout < 0 ) {
|
113 |
return;
|
114 |
}
|
129 |
/**
|
130 |
* Do all of the cleaning actions.
|
131 |
*
|
132 |
+
* @param int $time_limit The number of seconds to use as the timeout and failure period. Default 300 (5 minutes).
|
133 |
* @author Jeremy Pry
|
134 |
*/
|
135 |
+
public function clean( $time_limit = 300 ) {
|
136 |
$this->delete_old_actions();
|
137 |
+
$this->reset_timeouts( $time_limit );
|
138 |
+
$this->mark_failures( $time_limit );
|
139 |
}
|
140 |
|
141 |
/**
|
includes/lib/action-scheduler/classes/ActionScheduler_Store.php
CHANGED
@@ -188,13 +188,43 @@ abstract class ActionScheduler_Store {
|
|
188 |
|
189 |
public function init() {}
|
190 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
/**
|
192 |
* @return ActionScheduler_Store
|
193 |
*/
|
194 |
public static function instance() {
|
195 |
if ( empty(self::$store) ) {
|
196 |
-
$class = apply_filters('action_scheduler_store_class', 'ActionScheduler_wpPostStore');
|
197 |
self::$store = new $class();
|
|
|
198 |
}
|
199 |
return self::$store;
|
200 |
}
|
188 |
|
189 |
public function init() {}
|
190 |
|
191 |
+
/**
|
192 |
+
* Mark an action that failed to fetch correctly as failed.
|
193 |
+
*
|
194 |
+
* @since 2.2.6
|
195 |
+
*
|
196 |
+
* @param int $action_id The ID of the action.
|
197 |
+
*/
|
198 |
+
public function mark_failed_fetch_action( $action_id ) {
|
199 |
+
self::$store->mark_failure( $action_id );
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Add base hooks
|
204 |
+
*
|
205 |
+
* @since 2.2.6
|
206 |
+
*/
|
207 |
+
protected static function hook() {
|
208 |
+
add_action( 'action_scheduler_failed_fetch_action', array( self::$store, 'mark_failed_fetch_action' ), 20 );
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Remove base hooks
|
213 |
+
*
|
214 |
+
* @since 2.2.6
|
215 |
+
*/
|
216 |
+
protected static function unhook() {
|
217 |
+
remove_action( 'action_scheduler_failed_fetch_action', array( self::$store, 'mark_failed_fetch_action' ), 20 );
|
218 |
+
}
|
219 |
+
|
220 |
/**
|
221 |
* @return ActionScheduler_Store
|
222 |
*/
|
223 |
public static function instance() {
|
224 |
if ( empty(self::$store) ) {
|
225 |
+
$class = apply_filters( 'action_scheduler_store_class', 'ActionScheduler_wpPostStore' );
|
226 |
self::$store = new $class();
|
227 |
+
self::hook();
|
228 |
}
|
229 |
return self::$store;
|
230 |
}
|
includes/lib/action-scheduler/classes/ActionScheduler_WPCLI_QueueRunner.php
CHANGED
@@ -77,7 +77,7 @@ class ActionScheduler_WPCLI_QueueRunner extends ActionScheduler_Abstract_QueueRu
|
|
77 |
*/
|
78 |
protected function add_hooks() {
|
79 |
add_action( 'action_scheduler_before_execute', array( $this, 'before_execute' ) );
|
80 |
-
add_action( 'action_scheduler_after_execute', array( $this, 'after_execute' ) );
|
81 |
add_action( 'action_scheduler_failed_execution', array( $this, 'action_failed' ), 10, 2 );
|
82 |
}
|
83 |
|
@@ -112,11 +112,7 @@ class ActionScheduler_WPCLI_QueueRunner extends ActionScheduler_Abstract_QueueRu
|
|
112 |
|
113 |
$this->process_action( $action_id );
|
114 |
$this->progress_bar->tick();
|
115 |
-
|
116 |
-
// Free up memory after every 50 items
|
117 |
-
if ( 0 === $this->progress_bar->current() % 50 ) {
|
118 |
-
$this->stop_the_insanity();
|
119 |
-
}
|
120 |
}
|
121 |
|
122 |
$completed = $this->progress_bar->current();
|
@@ -144,11 +140,16 @@ class ActionScheduler_WPCLI_QueueRunner extends ActionScheduler_Abstract_QueueRu
|
|
144 |
*
|
145 |
* @author Jeremy Pry
|
146 |
*
|
147 |
-
* @param $action_id
|
|
|
148 |
*/
|
149 |
-
public function after_execute( $action_id ) {
|
|
|
|
|
|
|
|
|
150 |
/* translators: %s refers to the action ID */
|
151 |
-
WP_CLI::log( sprintf( __( 'Completed processing action %s', 'action-scheduler' ), $action_id ) );
|
152 |
}
|
153 |
|
154 |
/**
|
@@ -202,4 +203,15 @@ class ActionScheduler_WPCLI_QueueRunner extends ActionScheduler_Abstract_QueueRu
|
|
202 |
call_user_func( array( $wp_object_cache, '__remoteset' ) ); // important
|
203 |
}
|
204 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
}
|
77 |
*/
|
78 |
protected function add_hooks() {
|
79 |
add_action( 'action_scheduler_before_execute', array( $this, 'before_execute' ) );
|
80 |
+
add_action( 'action_scheduler_after_execute', array( $this, 'after_execute' ), 10, 2 );
|
81 |
add_action( 'action_scheduler_failed_execution', array( $this, 'action_failed' ), 10, 2 );
|
82 |
}
|
83 |
|
112 |
|
113 |
$this->process_action( $action_id );
|
114 |
$this->progress_bar->tick();
|
115 |
+
$this->maybe_stop_the_insanity();
|
|
|
|
|
|
|
|
|
116 |
}
|
117 |
|
118 |
$completed = $this->progress_bar->current();
|
140 |
*
|
141 |
* @author Jeremy Pry
|
142 |
*
|
143 |
+
* @param int $action_id
|
144 |
+
* @param null|ActionScheduler_Action $action The instance of the action. Default to null for backward compatibility.
|
145 |
*/
|
146 |
+
public function after_execute( $action_id, $action = null ) {
|
147 |
+
// backward compatibility
|
148 |
+
if ( null === $action ) {
|
149 |
+
$action = $this->store->fetch_action( $action_id );
|
150 |
+
}
|
151 |
/* translators: %s refers to the action ID */
|
152 |
+
WP_CLI::log( sprintf( __( 'Completed processing action %s with hook: %s', 'action-scheduler' ), $action_id, $action->get_hook() ) );
|
153 |
}
|
154 |
|
155 |
/**
|
203 |
call_user_func( array( $wp_object_cache, '__remoteset' ) ); // important
|
204 |
}
|
205 |
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Maybe trigger the stop_the_insanity() method to free up memory.
|
209 |
+
*/
|
210 |
+
protected function maybe_stop_the_insanity() {
|
211 |
+
// The value returned by progress_bar->current() might be padded. Remove padding, and convert to int.
|
212 |
+
$current_iteration = intval( trim( $this->progress_bar->current() ) );
|
213 |
+
if ( 0 === $current_iteration % 50 ) {
|
214 |
+
$this->stop_the_insanity();
|
215 |
+
}
|
216 |
+
}
|
217 |
}
|
includes/lib/action-scheduler/classes/ActionScheduler_wcSystemStatus.php
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class ActionScheduler_wcSystemStatus
|
5 |
+
*/
|
6 |
+
class ActionScheduler_wcSystemStatus {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* The active data stores
|
10 |
+
*
|
11 |
+
* @var ActionScheduler_Store
|
12 |
+
*/
|
13 |
+
protected $store;
|
14 |
+
|
15 |
+
function __construct( $store ) {
|
16 |
+
$this->store = $store;
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Display action data, including number of actions grouped by status and the oldest & newest action in each status.
|
21 |
+
*
|
22 |
+
* Helpful to identify issues, like a clogged queue.
|
23 |
+
*/
|
24 |
+
public function render() {
|
25 |
+
$action_counts = $this->store->action_counts();
|
26 |
+
$status_labels = $this->store->get_status_labels();
|
27 |
+
$oldest_and_newest = $this->get_oldest_and_newest( array_keys( $status_labels ) );
|
28 |
+
|
29 |
+
$this->get_template( $status_labels, $action_counts, $oldest_and_newest );
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Get oldest and newest scheduled dates for a given set of statuses.
|
34 |
+
*
|
35 |
+
* @param array $status_keys Set of statuses to find oldest & newest action for.
|
36 |
+
* @return array
|
37 |
+
*/
|
38 |
+
protected function get_oldest_and_newest( $status_keys ) {
|
39 |
+
|
40 |
+
$oldest_and_newest = array();
|
41 |
+
|
42 |
+
foreach ( $status_keys as $status ) {
|
43 |
+
$oldest_and_newest[ $status ] = array(
|
44 |
+
'oldest' => '–',
|
45 |
+
'newest' => '–',
|
46 |
+
);
|
47 |
+
|
48 |
+
if ( 'in-progress' === $status ) {
|
49 |
+
continue;
|
50 |
+
}
|
51 |
+
|
52 |
+
$oldest_and_newest[ $status ]['oldest'] = $this->get_action_status_date( $status, 'oldest' );
|
53 |
+
$oldest_and_newest[ $status ]['newest'] = $this->get_action_status_date( $status, 'newest' );
|
54 |
+
}
|
55 |
+
|
56 |
+
return $oldest_and_newest;
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Get oldest or newest scheduled date for a given status.
|
61 |
+
*
|
62 |
+
* @param string $status Action status label/name string.
|
63 |
+
* @param string $date_type Oldest or Newest.
|
64 |
+
* @return DateTime
|
65 |
+
*/
|
66 |
+
protected function get_action_status_date( $status, $date_type = 'oldest' ) {
|
67 |
+
|
68 |
+
$order = 'oldest' === $date_type ? 'ASC' : 'DESC';
|
69 |
+
|
70 |
+
$action = $this->store->query_actions( array(
|
71 |
+
'claimed' => false,
|
72 |
+
'status' => $status,
|
73 |
+
'per_page' => 1,
|
74 |
+
'order' => $order,
|
75 |
+
) );
|
76 |
+
|
77 |
+
if ( ! empty( $action ) ) {
|
78 |
+
$date_object = $this->store->get_date( $action[0] );
|
79 |
+
$action_date = $date_object->format( 'Y-m-d H:i:s O' );
|
80 |
+
} else {
|
81 |
+
$action_date = '–';
|
82 |
+
}
|
83 |
+
|
84 |
+
return $action_date;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Get oldest or newest scheduled date for a given status.
|
89 |
+
*
|
90 |
+
* @param array $status_labels Set of statuses to find oldest & newest action for.
|
91 |
+
* @param array $action_counts Number of actions grouped by status.
|
92 |
+
* @param array $oldest_and_newest Date of the oldest and newest action with each status.
|
93 |
+
*/
|
94 |
+
protected function get_template( $status_labels, $action_counts, $oldest_and_newest ) {
|
95 |
+
?>
|
96 |
+
|
97 |
+
<table class="wc_status_table widefat" cellspacing="0">
|
98 |
+
<thead>
|
99 |
+
<tr>
|
100 |
+
<th colspan="5" data-export-label="Action Scheduler"><h2><?php esc_html_e( 'Action Scheduler', 'action-scheduler' ); ?><?php echo wc_help_tip( esc_html__( 'This section shows scheduled action counts.', 'action-scheduler' ) ); ?></h2></th>
|
101 |
+
</tr>
|
102 |
+
<tr>
|
103 |
+
<td><strong><?php esc_html_e( 'Action Status', 'action-scheduler' ); ?></strong></td>
|
104 |
+
<td class="help"> </td>
|
105 |
+
<td><strong><?php esc_html_e( 'Count', 'action-scheduler' ); ?></strong></td>
|
106 |
+
<td><strong><?php esc_html_e( 'Oldest Scheduled Date', 'action-scheduler' ); ?></strong></td>
|
107 |
+
<td><strong><?php esc_html_e( 'Newest Scheduled Date', 'action-scheduler' ); ?></strong></td>
|
108 |
+
</tr>
|
109 |
+
</thead>
|
110 |
+
<tbody>
|
111 |
+
<?php
|
112 |
+
foreach ( $action_counts as $status => $count ) {
|
113 |
+
// WC uses the 3rd column for export, so we need to display more data in that (hidden when viewed as part of the table) and add an empty 2nd column.
|
114 |
+
printf(
|
115 |
+
'<tr><td>%1$s</td><td> </td><td>%2$s<span style="display: none;">, Oldest: %3$s, Newest: %4$s</span></td><td>%3$s</td><td>%4$s</td></tr>',
|
116 |
+
esc_html( $status_labels[ $status ] ),
|
117 |
+
number_format_i18n( $count ),
|
118 |
+
$oldest_and_newest[ $status ]['oldest'],
|
119 |
+
$oldest_and_newest[ $status ]['newest']
|
120 |
+
);
|
121 |
+
}
|
122 |
+
?>
|
123 |
+
</tbody>
|
124 |
+
</table>
|
125 |
+
|
126 |
+
<?php
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* is triggered when invoking inaccessible methods in an object context.
|
131 |
+
*
|
132 |
+
* @param string $name
|
133 |
+
* @param array $arguments
|
134 |
+
*
|
135 |
+
* @return mixed
|
136 |
+
* @link https://php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.methods
|
137 |
+
*/
|
138 |
+
public function __call( $name, $arguments ) {
|
139 |
+
switch ( $name ) {
|
140 |
+
case 'print':
|
141 |
+
_deprecated_function( __CLASS__ . '::print()', '2.2.4', __CLASS__ . '::render()' );
|
142 |
+
return call_user_func_array( array( $this, 'render' ), $arguments );
|
143 |
+
}
|
144 |
+
|
145 |
+
return null;
|
146 |
+
}
|
147 |
+
}
|
includes/lib/action-scheduler/classes/ActionScheduler_wpPostStore.php
CHANGED
@@ -32,7 +32,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|
32 |
$post = array(
|
33 |
'post_type' => self::POST_TYPE,
|
34 |
'post_title' => $action->get_hook(),
|
35 |
-
'post_content' => json_encode($action->get_args()),
|
36 |
'post_status' => ( $action->is_finished() ? 'publish' : 'pending' ),
|
37 |
'post_date_gmt' => $this->get_scheduled_date_string( $action, $scheduled_date ),
|
38 |
'post_date' => $this->get_scheduled_date_string_local( $action, $scheduled_date ),
|
@@ -42,8 +42,23 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|
42 |
|
43 |
protected function save_post_array( $post_array ) {
|
44 |
add_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10, 1 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
$post_id = wp_insert_post($post_array);
|
|
|
|
|
|
|
|
|
|
|
46 |
remove_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10 );
|
|
|
47 |
|
48 |
if ( is_wp_error($post_id) || empty($post_id) ) {
|
49 |
throw new RuntimeException(__('Unable to save action.', 'action-scheduler'));
|
@@ -61,6 +76,41 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|
61 |
return $postdata;
|
62 |
}
|
63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
protected function save_post_schedule( $post_id, $schedule ) {
|
65 |
update_post_meta( $post_id, self::SCHEDULE_META_KEY, $schedule );
|
66 |
}
|
@@ -94,17 +144,21 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|
94 |
|
95 |
protected function make_action_from_post( $post ) {
|
96 |
$hook = $post->post_title;
|
97 |
-
$args = json_decode( $post->post_content, true );
|
98 |
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
}
|
103 |
|
104 |
-
|
105 |
-
|
|
|
|
|
|
|
106 |
$schedule = new ActionScheduler_NullSchedule();
|
|
|
|
|
107 |
}
|
|
|
108 |
$group = wp_get_object_terms( $post->ID, self::GROUP_TAXONOMY, array('fields' => 'names') );
|
109 |
$group = empty( $group ) ? '' : reset($group);
|
110 |
|
@@ -253,15 +307,16 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|
253 |
$sql = ( 'count' === $select_or_count ) ? 'SELECT count(p.ID)' : 'SELECT p.ID ';
|
254 |
$sql .= "FROM {$wpdb->posts} p";
|
255 |
$sql_params = array();
|
256 |
-
if (
|
|
|
|
|
|
|
|
|
257 |
$sql .= " INNER JOIN {$wpdb->term_relationships} tr ON tr.object_id=p.ID";
|
258 |
$sql .= " INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id=tt.term_taxonomy_id";
|
259 |
$sql .= " INNER JOIN {$wpdb->terms} t ON tt.term_id=t.term_id";
|
260 |
-
|
261 |
-
|
262 |
-
$sql .= " AND t.slug=%s";
|
263 |
-
$sql_params[] = $query['group'];
|
264 |
-
}
|
265 |
}
|
266 |
$sql .= " WHERE post_type=%s";
|
267 |
$sql_params[] = self::POST_TYPE;
|
@@ -404,7 +459,9 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|
404 |
throw new InvalidArgumentException(sprintf(__('Unidentified action %s', 'action-scheduler'), $action_id));
|
405 |
}
|
406 |
do_action( 'action_scheduler_canceled_action', $action_id );
|
|
|
407 |
wp_trash_post($action_id);
|
|
|
408 |
}
|
409 |
|
410 |
public function delete_action( $action_id ) {
|
@@ -587,16 +644,9 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|
587 |
'ID' => 'ASC',
|
588 |
),
|
589 |
'date_query' => array(
|
590 |
-
'column' => '
|
591 |
-
|
592 |
-
|
593 |
-
'year' => $date->format( 'Y' ),
|
594 |
-
'month' => $date->format( 'n' ),
|
595 |
-
'day' => $date->format( 'j' ),
|
596 |
-
'hour' => $date->format( 'G' ),
|
597 |
-
'minute' => $date->format( 'i' ),
|
598 |
-
'second' => $date->format( 's' ),
|
599 |
-
),
|
600 |
),
|
601 |
'tax_query' => array(
|
602 |
array(
|
@@ -685,7 +735,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|
685 |
$status = $this->get_post_column( $action_id, 'post_status' );
|
686 |
|
687 |
if ( $status === null ) {
|
688 |
-
throw new
|
689 |
}
|
690 |
|
691 |
return $this->get_action_status_by_post_status( $status );
|
@@ -716,11 +766,13 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|
716 |
throw new InvalidArgumentException(sprintf(__('Unidentified action %s', 'action-scheduler'), $action_id));
|
717 |
}
|
718 |
add_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10, 1 );
|
|
|
719 |
$result = wp_update_post(array(
|
720 |
'ID' => $action_id,
|
721 |
'post_status' => 'publish',
|
722 |
), TRUE);
|
723 |
remove_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10 );
|
|
|
724 |
if ( is_wp_error($result) ) {
|
725 |
throw new RuntimeException($result->get_error_message());
|
726 |
}
|
@@ -754,4 +806,24 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|
754 |
$taxonomy_registrar = new ActionScheduler_wpPostStore_TaxonomyRegistrar();
|
755 |
$taxonomy_registrar->register();
|
756 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
757 |
}
|
32 |
$post = array(
|
33 |
'post_type' => self::POST_TYPE,
|
34 |
'post_title' => $action->get_hook(),
|
35 |
+
'post_content' => json_encode($action->get_args(), JSON_UNESCAPED_UNICODE),
|
36 |
'post_status' => ( $action->is_finished() ? 'publish' : 'pending' ),
|
37 |
'post_date_gmt' => $this->get_scheduled_date_string( $action, $scheduled_date ),
|
38 |
'post_date' => $this->get_scheduled_date_string_local( $action, $scheduled_date ),
|
42 |
|
43 |
protected function save_post_array( $post_array ) {
|
44 |
add_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10, 1 );
|
45 |
+
add_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10, 5 );
|
46 |
+
|
47 |
+
$has_kses = false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' );
|
48 |
+
|
49 |
+
if ( $has_kses ) {
|
50 |
+
// Prevent KSES from corrupting JSON in post_content.
|
51 |
+
kses_remove_filters();
|
52 |
+
}
|
53 |
+
|
54 |
$post_id = wp_insert_post($post_array);
|
55 |
+
|
56 |
+
if ( $has_kses ) {
|
57 |
+
kses_init_filters();
|
58 |
+
}
|
59 |
+
|
60 |
remove_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10 );
|
61 |
+
remove_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10 );
|
62 |
|
63 |
if ( is_wp_error($post_id) || empty($post_id) ) {
|
64 |
throw new RuntimeException(__('Unable to save action.', 'action-scheduler'));
|
76 |
return $postdata;
|
77 |
}
|
78 |
|
79 |
+
/**
|
80 |
+
* Create a (probably unique) post name for scheduled actions in a more performant manner than wp_unique_post_slug().
|
81 |
+
*
|
82 |
+
* When an action's post status is transitioned to something other than 'draft', 'pending' or 'auto-draft, like 'publish'
|
83 |
+
* or 'failed' or 'trash', WordPress will find a unique slug (stored in post_name column) using the wp_unique_post_slug()
|
84 |
+
* function. This is done to ensure URL uniqueness. The approach taken by wp_unique_post_slug() is to iterate over existing
|
85 |
+
* post_name values that match, and append a number 1 greater than the largest. This makes sense when manually creating a
|
86 |
+
* post from the Edit Post screen. It becomes a bottleneck when automatically processing thousands of actions, with a
|
87 |
+
* database containing thousands of related post_name values.
|
88 |
+
*
|
89 |
+
* WordPress 5.1 introduces the 'pre_wp_unique_post_slug' filter for plugins to address this issue.
|
90 |
+
*
|
91 |
+
* We can short-circuit WordPress's wp_unique_post_slug() approach using the 'pre_wp_unique_post_slug' filter. This
|
92 |
+
* method is available to be used as a callback on that filter. It provides a more scalable approach to generating a
|
93 |
+
* post_name/slug that is probably unique. Because Action Scheduler never actually uses the post_name field, or an
|
94 |
+
* action's slug, being probably unique is good enough.
|
95 |
+
*
|
96 |
+
* For more backstory on this issue, see:
|
97 |
+
* - https://github.com/Prospress/action-scheduler/issues/44 and
|
98 |
+
* - https://core.trac.wordpress.org/ticket/21112
|
99 |
+
*
|
100 |
+
* @param string $override_slug Short-circuit return value.
|
101 |
+
* @param string $slug The desired slug (post_name).
|
102 |
+
* @param int $post_ID Post ID.
|
103 |
+
* @param string $post_status The post status.
|
104 |
+
* @param string $post_type Post type.
|
105 |
+
* @return string
|
106 |
+
*/
|
107 |
+
public function set_unique_post_slug( $override_slug, $slug, $post_ID, $post_status, $post_type ) {
|
108 |
+
if ( self::POST_TYPE == $post_type ) {
|
109 |
+
$override_slug = uniqid( self::POST_TYPE . '-', true ) . '-' . wp_generate_password( 32, false );
|
110 |
+
}
|
111 |
+
return $override_slug;
|
112 |
+
}
|
113 |
+
|
114 |
protected function save_post_schedule( $post_id, $schedule ) {
|
115 |
update_post_meta( $post_id, self::SCHEDULE_META_KEY, $schedule );
|
116 |
}
|
144 |
|
145 |
protected function make_action_from_post( $post ) {
|
146 |
$hook = $post->post_title;
|
|
|
147 |
|
148 |
+
try {
|
149 |
+
$args = json_decode( $post->post_content, true );
|
150 |
+
$this->validate_args( $args, $post->ID );
|
|
|
151 |
|
152 |
+
$schedule = get_post_meta( $post->ID, self::SCHEDULE_META_KEY, true );
|
153 |
+
if ( empty( $schedule ) || ! is_a( $schedule, 'ActionScheduler_Schedule' ) ) {
|
154 |
+
throw ActionScheduler_InvalidActionException::from_decoding_args( $post->ID );
|
155 |
+
}
|
156 |
+
} catch ( ActionScheduler_InvalidActionException $exception ) {
|
157 |
$schedule = new ActionScheduler_NullSchedule();
|
158 |
+
$args = array();
|
159 |
+
do_action( 'action_scheduler_failed_fetch_action', $post->ID );
|
160 |
}
|
161 |
+
|
162 |
$group = wp_get_object_terms( $post->ID, self::GROUP_TAXONOMY, array('fields' => 'names') );
|
163 |
$group = empty( $group ) ? '' : reset($group);
|
164 |
|
307 |
$sql = ( 'count' === $select_or_count ) ? 'SELECT count(p.ID)' : 'SELECT p.ID ';
|
308 |
$sql .= "FROM {$wpdb->posts} p";
|
309 |
$sql_params = array();
|
310 |
+
if ( empty( $query['group'] ) && 'group' === $query['orderby'] ) {
|
311 |
+
$sql .= " LEFT JOIN {$wpdb->term_relationships} tr ON tr.object_id=p.ID";
|
312 |
+
$sql .= " LEFT JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id=tt.term_taxonomy_id";
|
313 |
+
$sql .= " LEFT JOIN {$wpdb->terms} t ON tt.term_id=t.term_id";
|
314 |
+
} elseif ( ! empty( $query['group'] ) ) {
|
315 |
$sql .= " INNER JOIN {$wpdb->term_relationships} tr ON tr.object_id=p.ID";
|
316 |
$sql .= " INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id=tt.term_taxonomy_id";
|
317 |
$sql .= " INNER JOIN {$wpdb->terms} t ON tt.term_id=t.term_id";
|
318 |
+
$sql .= " AND t.slug=%s";
|
319 |
+
$sql_params[] = $query['group'];
|
|
|
|
|
|
|
320 |
}
|
321 |
$sql .= " WHERE post_type=%s";
|
322 |
$sql_params[] = self::POST_TYPE;
|
459 |
throw new InvalidArgumentException(sprintf(__('Unidentified action %s', 'action-scheduler'), $action_id));
|
460 |
}
|
461 |
do_action( 'action_scheduler_canceled_action', $action_id );
|
462 |
+
add_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10, 5 );
|
463 |
wp_trash_post($action_id);
|
464 |
+
remove_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10 );
|
465 |
}
|
466 |
|
467 |
public function delete_action( $action_id ) {
|
644 |
'ID' => 'ASC',
|
645 |
),
|
646 |
'date_query' => array(
|
647 |
+
'column' => 'post_date_gmt',
|
648 |
+
'before' => $date->format( 'Y-m-d H:i' ),
|
649 |
+
'inclusive' => true,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
650 |
),
|
651 |
'tax_query' => array(
|
652 |
array(
|
735 |
$status = $this->get_post_column( $action_id, 'post_status' );
|
736 |
|
737 |
if ( $status === null ) {
|
738 |
+
throw new InvalidArgumentException( __( 'Invalid action ID. No status found.', 'action-scheduler' ) );
|
739 |
}
|
740 |
|
741 |
return $this->get_action_status_by_post_status( $status );
|
766 |
throw new InvalidArgumentException(sprintf(__('Unidentified action %s', 'action-scheduler'), $action_id));
|
767 |
}
|
768 |
add_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10, 1 );
|
769 |
+
add_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10, 5 );
|
770 |
$result = wp_update_post(array(
|
771 |
'ID' => $action_id,
|
772 |
'post_status' => 'publish',
|
773 |
), TRUE);
|
774 |
remove_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10 );
|
775 |
+
remove_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10 );
|
776 |
if ( is_wp_error($result) ) {
|
777 |
throw new RuntimeException($result->get_error_message());
|
778 |
}
|
806 |
$taxonomy_registrar = new ActionScheduler_wpPostStore_TaxonomyRegistrar();
|
807 |
$taxonomy_registrar->register();
|
808 |
}
|
809 |
+
|
810 |
+
/**
|
811 |
+
* Validate that we could decode action arguments.
|
812 |
+
*
|
813 |
+
* @param mixed $args The decoded arguments.
|
814 |
+
* @param int $action_id The action ID.
|
815 |
+
*
|
816 |
+
* @throws ActionScheduler_InvalidActionException When the decoded arguments are invalid.
|
817 |
+
*/
|
818 |
+
private function validate_args( $args, $action_id ) {
|
819 |
+
// Ensure we have an array of args.
|
820 |
+
if ( ! is_array( $args ) ) {
|
821 |
+
throw ActionScheduler_InvalidActionException::from_decoding_args( $action_id );
|
822 |
+
}
|
823 |
+
|
824 |
+
// Validate JSON decoding if possible.
|
825 |
+
if ( function_exists( 'json_last_error' ) && JSON_ERROR_NONE !== json_last_error() ) {
|
826 |
+
throw ActionScheduler_InvalidActionException::from_decoding_args( $action_id );
|
827 |
+
}
|
828 |
+
}
|
829 |
}
|
includes/lib/action-scheduler/composer.json
CHANGED
@@ -6,6 +6,6 @@
|
|
6 |
"minimum-stability": "dev",
|
7 |
"require": {},
|
8 |
"require-dev": {
|
9 |
-
"wp-cli/wp-cli": "
|
10 |
}
|
11 |
}
|
6 |
"minimum-stability": "dev",
|
7 |
"require": {},
|
8 |
"require-dev": {
|
9 |
+
"wp-cli/wp-cli": "1.5.1"
|
10 |
}
|
11 |
}
|
includes/lib/action-scheduler/docs/_layouts/default.html
CHANGED
@@ -21,6 +21,11 @@
|
|
21 |
<body>
|
22 |
|
23 |
<header>
|
|
|
|
|
|
|
|
|
|
|
24 |
<div class="container">
|
25 |
<p><a href="/usage/">Usage</a> | <a href="/admin/">Admin</a> | <a href="/wp-cli/">WP-CLI</a> | <a href="/perf/">Background Processing at Scale</a> | <a href="/api/">API</a> | <a href="/faq/">FAQ</a>
|
26 |
<h1><a href="/">action-scheduler</a></h1>
|
21 |
<body>
|
22 |
|
23 |
<header>
|
24 |
+
<a class="github-corner" href="https://github.com/Prospress/action-scheduler/" aria-label="View on GitHub">
|
25 |
+
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#b5e853; color:#151513; position: fixed; top: 0; border: 0; right: 0;" aria-hidden="true">
|
26 |
+
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>
|
27 |
+
</svg>
|
28 |
+
</a>
|
29 |
<div class="container">
|
30 |
<p><a href="/usage/">Usage</a> | <a href="/admin/">Admin</a> | <a href="/wp-cli/">WP-CLI</a> | <a href="/perf/">Background Processing at Scale</a> | <a href="/api/">API</a> | <a href="/faq/">FAQ</a>
|
31 |
<h1><a href="/">action-scheduler</a></h1>
|
includes/lib/action-scheduler/docs/assets/css/style.scss
CHANGED
@@ -29,4 +29,29 @@ footer {
|
|
29 |
.footer-image {
|
30 |
text-align: center;
|
31 |
padding-top: 1em;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
}
|
29 |
.footer-image {
|
30 |
text-align: center;
|
31 |
padding-top: 1em;
|
32 |
+
}
|
33 |
+
|
34 |
+
.github-corner:hover .octo-arm {
|
35 |
+
animation:octocat-wave 560ms ease-in-out
|
36 |
+
}
|
37 |
+
|
38 |
+
@keyframes octocat-wave {
|
39 |
+
0%,100%{
|
40 |
+
transform:rotate(0)
|
41 |
+
}
|
42 |
+
20%,60%{
|
43 |
+
transform:rotate(-25deg)
|
44 |
+
}
|
45 |
+
40%,80%{
|
46 |
+
transform:rotate(10deg)
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
@media (max-width:500px){
|
51 |
+
.github-corner:hover .octo-arm {
|
52 |
+
animation:none
|
53 |
+
}
|
54 |
+
.github-corner .octo-arm {
|
55 |
+
animation:octocat-wave 560ms ease-in-out
|
56 |
+
}
|
57 |
}
|
includes/lib/action-scheduler/docs/faq.md
CHANGED
@@ -30,7 +30,7 @@ Want to create some other method for initiating Action Scheduler? [Open a new is
|
|
30 |
|
31 |
By default, Action Scheduler is designed to work alongside WP-Cron and not change any of its behaviour. This helps avoid unexpectedly overriding WP-Cron on sites installing your plugin, which may have nothing to do with WP-Cron.
|
32 |
|
33 |
-
However, we can understand why you might want to replace WP-Cron completely in environments within
|
34 |
|
35 |
You could use the `'schedule_event'` hook in WordPress to use Action Scheduler for only newly scheduled WP-Cron jobs and map the `$event` param to Action Scheduler API functions.
|
36 |
|
@@ -96,6 +96,6 @@ It requires no setup, and won't override any WordPress APIs (unless you want it
|
|
96 |
|
97 |
### How does Action Scheduler work on WordPress Multisite?
|
98 |
|
99 |
-
Action Scheduler is designed to manage the scheduled actions on a single site. It has no special handling for running queues across multiple sites in a multisite network. That said, because
|
100 |
|
101 |
If you'd like to create a multisite plugin to do this and release it publicly to help others, [open a new issue to let us know](https://github.com/Prospress/action-scheduler/issues/new), we'd love to help you with it.
|
30 |
|
31 |
By default, Action Scheduler is designed to work alongside WP-Cron and not change any of its behaviour. This helps avoid unexpectedly overriding WP-Cron on sites installing your plugin, which may have nothing to do with WP-Cron.
|
32 |
|
33 |
+
However, we can understand why you might want to replace WP-Cron completely in environments within your control, especially as it gets you the advantages of Action Scheduler. This should be possible without too much code.
|
34 |
|
35 |
You could use the `'schedule_event'` hook in WordPress to use Action Scheduler for only newly scheduled WP-Cron jobs and map the `$event` param to Action Scheduler API functions.
|
36 |
|
96 |
|
97 |
### How does Action Scheduler work on WordPress Multisite?
|
98 |
|
99 |
+
Action Scheduler is designed to manage the scheduled actions on a single site. It has no special handling for running queues across multiple sites in a multisite network. That said, because its storage and Queue Runner are completely swappable, it would be possible to write multisite handling classes to use with it.
|
100 |
|
101 |
If you'd like to create a multisite plugin to do this and release it publicly to help others, [open a new issue to let us know](https://github.com/Prospress/action-scheduler/issues/new), we'd love to help you with it.
|
includes/lib/action-scheduler/docs/usage.md
CHANGED
@@ -22,12 +22,12 @@ require_once( plugin_dir_path( __FILE__ ) . '/libraries/action-scheduler/action-
|
|
22 |
* Schedule an action with the hook 'eg_midnight_log' to run at midnight each day
|
23 |
* so that our callback is run then.
|
24 |
*/
|
25 |
-
function
|
26 |
if ( false === as_next_scheduled_action( 'eg_midnight_log' ) ) {
|
27 |
as_schedule_recurring_action( strtotime( 'midnight tonight' ), DAY_IN_SECONDS, 'eg_midnight_log' );
|
28 |
}
|
29 |
}
|
30 |
-
add_action( 'init', '
|
31 |
|
32 |
/**
|
33 |
* A callback to run when the 'eg_midnight_log' scheduled action is run.
|
22 |
* Schedule an action with the hook 'eg_midnight_log' to run at midnight each day
|
23 |
* so that our callback is run then.
|
24 |
*/
|
25 |
+
function eg_schedule_midnight_log() {
|
26 |
if ( false === as_next_scheduled_action( 'eg_midnight_log' ) ) {
|
27 |
as_schedule_recurring_action( strtotime( 'midnight tonight' ), DAY_IN_SECONDS, 'eg_midnight_log' );
|
28 |
}
|
29 |
}
|
30 |
+
add_action( 'init', 'eg_schedule_midnight_log' );
|
31 |
|
32 |
/**
|
33 |
* A callback to run when the 'eg_midnight_log' scheduled action is run.
|
includes/lib/action-scheduler/license.txt
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
GNU GENERAL PUBLIC LICENSE
|
2 |
Version 3, 29 June 2007
|
3 |
|
4 |
-
Copyright (C) 2007 Free Software Foundation, Inc. <
|
5 |
Everyone is permitted to copy and distribute verbatim copies
|
6 |
of this license document, but changing it is not allowed.
|
7 |
|
@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
|
645 |
GNU General Public License for more details.
|
646 |
|
647 |
You should have received a copy of the GNU General Public License
|
648 |
-
along with this program. If not, see <
|
649 |
|
650 |
Also add information on how to contact you by electronic and paper mail.
|
651 |
|
@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
|
|
664 |
You should also get your employer (if you work as a programmer) or school,
|
665 |
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
666 |
For more information on this, and how to apply and follow the GNU GPL, see
|
667 |
-
<
|
668 |
|
669 |
The GNU General Public License does not permit incorporating your program
|
670 |
into proprietary programs. If your program is a subroutine library, you
|
671 |
may consider it more useful to permit linking proprietary applications with
|
672 |
the library. If this is what you want to do, use the GNU Lesser General
|
673 |
Public License instead of this License. But first, please read
|
674 |
-
<
|
1 |
GNU GENERAL PUBLIC LICENSE
|
2 |
Version 3, 29 June 2007
|
3 |
|
4 |
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
5 |
Everyone is permitted to copy and distribute verbatim copies
|
6 |
of this license document, but changing it is not allowed.
|
7 |
|
645 |
GNU General Public License for more details.
|
646 |
|
647 |
You should have received a copy of the GNU General Public License
|
648 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
649 |
|
650 |
Also add information on how to contact you by electronic and paper mail.
|
651 |
|
664 |
You should also get your employer (if you work as a programmer) or school,
|
665 |
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
666 |
For more information on this, and how to apply and follow the GNU GPL, see
|
667 |
+
<https://www.gnu.org/licenses/>.
|
668 |
|
669 |
The GNU General Public License does not permit incorporating your program
|
670 |
into proprietary programs. If your program is a subroutine library, you
|
671 |
may consider it more useful to permit linking proprietary applications with
|
672 |
the library. If this is what you want to do, use the GNU Lesser General
|
673 |
Public License instead of this License. But first, please read
|
674 |
+
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
includes/lib/action-scheduler/tests/phpunit/jobstore/ActionScheduler_wpPostStore_Test.php
CHANGED
@@ -42,7 +42,6 @@ class ActionScheduler_wpPostStore_Test extends ActionScheduler_UnitTestCase {
|
|
42 |
}
|
43 |
|
44 |
/**
|
45 |
-
* @expectedException ActionScheduler_InvalidActionException
|
46 |
* @dataProvider provide_bad_args
|
47 |
*
|
48 |
* @param string $content
|
@@ -55,7 +54,8 @@ class ActionScheduler_wpPostStore_Test extends ActionScheduler_UnitTestCase {
|
|
55 |
'post_content' => $content,
|
56 |
) );
|
57 |
|
58 |
-
$store->fetch_action( $post_id );
|
|
|
59 |
}
|
60 |
|
61 |
public function provide_bad_args() {
|
@@ -246,7 +246,7 @@ class ActionScheduler_wpPostStore_Test extends ActionScheduler_UnitTestCase {
|
|
246 |
$now = as_get_datetime_object();
|
247 |
$store->mark_complete( $action_id );
|
248 |
|
249 |
-
$this->assertEquals( $store->get_date($action_id)->getTimestamp(), $now->getTimestamp() );
|
250 |
|
251 |
$next = $action->get_schedule()->next( $now );
|
252 |
$new_action_id = $store->save_action( $action, $next );
|
42 |
}
|
43 |
|
44 |
/**
|
|
|
45 |
* @dataProvider provide_bad_args
|
46 |
*
|
47 |
* @param string $content
|
54 |
'post_content' => $content,
|
55 |
) );
|
56 |
|
57 |
+
$fetched = $store->fetch_action( $post_id );
|
58 |
+
$this->assertInstanceOf( 'ActionScheduler_NullSchedule', $fetched->get_schedule() );
|
59 |
}
|
60 |
|
61 |
public function provide_bad_args() {
|
246 |
$now = as_get_datetime_object();
|
247 |
$store->mark_complete( $action_id );
|
248 |
|
249 |
+
$this->assertEquals( $store->get_date( $action_id )->getTimestamp(), $now->getTimestamp(), '', 1 );
|
250 |
|
251 |
$next = $action->get_schedule()->next( $now );
|
252 |
$new_action_id = $store->save_action( $action, $next );
|
includes/lib/action-scheduler/tests/phpunit/runner/ActionScheduler_QueueRunner_Test.php
CHANGED
@@ -104,7 +104,7 @@ class ActionScheduler_QueueRunner_Test extends ActionScheduler_UnitTestCase {
|
|
104 |
|
105 |
|
106 |
$this->assertEquals( $random, $new_action->get_hook() );
|
107 |
-
$this->assertEquals( $schedule->next(as_get_datetime_object())->getTimestamp(), $new_action->get_schedule()->next(as_get_datetime_object())->getTimestamp() );
|
108 |
}
|
109 |
|
110 |
public function test_hooked_into_wp_cron() {
|
@@ -206,4 +206,57 @@ class ActionScheduler_QueueRunner_Test extends ActionScheduler_UnitTestCase {
|
|
206 |
public function return_6() {
|
207 |
return 6;
|
208 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
209 |
}
|
104 |
|
105 |
|
106 |
$this->assertEquals( $random, $new_action->get_hook() );
|
107 |
+
$this->assertEquals( $schedule->next( as_get_datetime_object() )->getTimestamp(), $new_action->get_schedule()->next( as_get_datetime_object() )->getTimestamp(), '', 1 );
|
108 |
}
|
109 |
|
110 |
public function test_hooked_into_wp_cron() {
|
206 |
public function return_6() {
|
207 |
return 6;
|
208 |
}
|
209 |
+
|
210 |
+
public function test_store_fetch_action_failure_schedule_next_instance() {
|
211 |
+
$random = md5( rand() );
|
212 |
+
$schedule = new ActionScheduler_IntervalSchedule( as_get_datetime_object( '12 hours ago' ), DAY_IN_SECONDS );
|
213 |
+
$action = new ActionScheduler_Action( $random, array(), $schedule );
|
214 |
+
$action_id = ActionScheduler::store()->save_action( $action );
|
215 |
+
|
216 |
+
// Set up a mock store that will throw an exception when fetching actions.
|
217 |
+
$store = $this
|
218 |
+
->getMockBuilder( 'ActionScheduler_wpPostStore' )
|
219 |
+
->setMethods( array( 'fetch_action' ) )
|
220 |
+
->getMock();
|
221 |
+
$store
|
222 |
+
->method( 'fetch_action' )
|
223 |
+
->with( $action_id )
|
224 |
+
->will( $this->throwException( new Exception() ) );
|
225 |
+
|
226 |
+
// Set up a mock queue runner to verify that schedule_next_instance()
|
227 |
+
// isn't called for an undefined $action.
|
228 |
+
$runner = $this
|
229 |
+
->getMockBuilder( 'ActionScheduler_QueueRunner' )
|
230 |
+
->setConstructorArgs( array( $store ) )
|
231 |
+
->setMethods( array( 'schedule_next_instance' ) )
|
232 |
+
->getMock();
|
233 |
+
$runner
|
234 |
+
->expects( $this->never() )
|
235 |
+
->method( 'schedule_next_instance' );
|
236 |
+
|
237 |
+
$runner->run();
|
238 |
+
|
239 |
+
// Set up a mock store that will throw an exception when fetching actions.
|
240 |
+
$store2 = $this
|
241 |
+
->getMockBuilder( 'ActionScheduler_wpPostStore' )
|
242 |
+
->setMethods( array( 'fetch_action' ) )
|
243 |
+
->getMock();
|
244 |
+
$store2
|
245 |
+
->method( 'fetch_action' )
|
246 |
+
->with( $action_id )
|
247 |
+
->willReturn( null );
|
248 |
+
|
249 |
+
// Set up a mock queue runner to verify that schedule_next_instance()
|
250 |
+
// isn't called for an undefined $action.
|
251 |
+
$runner2 = $this
|
252 |
+
->getMockBuilder( 'ActionScheduler_QueueRunner' )
|
253 |
+
->setConstructorArgs( array( $store ) )
|
254 |
+
->setMethods( array( 'schedule_next_instance' ) )
|
255 |
+
->getMock();
|
256 |
+
$runner2
|
257 |
+
->expects( $this->never() )
|
258 |
+
->method( 'schedule_next_instance' );
|
259 |
+
|
260 |
+
$runner2->run();
|
261 |
+
}
|
262 |
}
|
readme.txt
CHANGED
@@ -6,7 +6,7 @@ Requires at least: 4.7.0
|
|
6 |
Tested up to: 5.2.2
|
7 |
WC tested up to: 3.7.0
|
8 |
Requires PHP: 5.6
|
9 |
-
Stable tag: 2.3.
|
10 |
License: GPLv3
|
11 |
|
12 |
Simple and flexible Mailchimp integration for WooCommerce.
|
@@ -119,6 +119,10 @@ Also, if you enjoy using the software [we'd love it if you could give us a revie
|
|
119 |
|
120 |
== Changelog ==
|
121 |
|
|
|
|
|
|
|
|
|
122 |
#### 2.3.1 - August 26, 2019
|
123 |
- Fix tag retrieval to return tag IDs.
|
124 |
- Fix migrations.
|
6 |
Tested up to: 5.2.2
|
7 |
WC tested up to: 3.7.0
|
8 |
Requires PHP: 5.6
|
9 |
+
Stable tag: 2.3.2
|
10 |
License: GPLv3
|
11 |
|
12 |
Simple and flexible Mailchimp integration for WooCommerce.
|
119 |
|
120 |
== Changelog ==
|
121 |
|
122 |
+
#### 2.3.2 - August 27, 2019
|
123 |
+
- Upgrade ActionScheduler to 2.2.5
|
124 |
+
- Fix encoding of unicode/international characters.
|
125 |
+
|
126 |
#### 2.3.1 - August 26, 2019
|
127 |
- Fix tag retrieval to return tag IDs.
|
128 |
- Fix migrations.
|
woocommerce-mailchimp.php
CHANGED
@@ -5,7 +5,7 @@
|
|
5 |
* Description: WooCommerce Mailchimp provides simple and flexible Mailchimp integration for WooCommerce.
|
6 |
* Author: Saint Systems
|
7 |
* Author URI: https://www.saintsystems.com
|
8 |
-
* Version: 2.3.
|
9 |
* WC tested up to: 3.7.0
|
10 |
* Text Domain: woocommerce-mailchimp
|
11 |
* Domain Path: languages
|
5 |
* Description: WooCommerce Mailchimp provides simple and flexible Mailchimp integration for WooCommerce.
|
6 |
* Author: Saint Systems
|
7 |
* Author URI: https://www.saintsystems.com
|
8 |
+
* Version: 2.3.2
|
9 |
* WC tested up to: 3.7.0
|
10 |
* Text Domain: woocommerce-mailchimp
|
11 |
* Domain Path: languages
|