Skip to content

Commit ac7991d

Browse files
authored
Merge pull request #1071 from crstauf/wpcli
Add additional WP-CLI commands to make it easier to manage actions on the terminal and via scripts, etc.
2 parents 06fd6f7 + e702c92 commit ac7991d

14 files changed

+1660
-3
lines changed

classes/ActionScheduler_AdminView.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,14 @@ public function add_help_tabs() {
260260
'<h2>' . sprintf( __( 'About Action Scheduler %s', 'action-scheduler' ), $as_version ) . '</h2>' .
261261
'<p>' .
262262
__( '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' ) .
263+
'</p>' .
264+
'<h3>' . esc_html__( 'WP CLI', 'action-scheduler' ) . '</h3>' .
265+
'<p>' .
266+
sprintf(
267+
/* translators: %1$s is WP CLI command (not translatable) */
268+
esc_html__( 'WP CLI commands are available: execute %1$s for a list of available commands.', 'action-scheduler' ),
269+
'<code>wp help action-scheduler</code>'
270+
) .
263271
'</p>',
264272
)
265273
);
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
namespace Action_Scheduler\WP_CLI\Action;
4+
5+
use function \WP_CLI\Utils\get_flag_value;
6+
7+
/**
8+
* WP-CLI command: action-scheduler action cancel
9+
*/
10+
class Cancel_Command extends \ActionScheduler_WPCLI_Command {
11+
12+
/**
13+
* Execute command.
14+
*
15+
* @return void
16+
*/
17+
public function execute() {
18+
$hook = '';
19+
$group = get_flag_value( $this->assoc_args, 'group', '' );
20+
$callback_args = get_flag_value( $this->assoc_args, 'args', null );
21+
$all = get_flag_value( $this->assoc_args, 'all', false );
22+
23+
if ( ! empty( $this->args[0] ) ) {
24+
$hook = $this->args[0];
25+
}
26+
27+
if ( ! empty( $callback_args ) ) {
28+
$callback_args = json_decode( $callback_args, true );
29+
}
30+
31+
if ( $all ) {
32+
$this->cancel_all( $hook, $callback_args, $group );
33+
return;
34+
}
35+
36+
$this->cancel_single( $hook, $callback_args, $group );
37+
}
38+
39+
/**
40+
* Cancel single action.
41+
*
42+
* @param string $hook The hook that the job will trigger.
43+
* @param array $callback_args Args that would have been passed to the job.
44+
* @param string $group The group the job is assigned to.
45+
* @return void
46+
*/
47+
protected function cancel_single( $hook, $callback_args, $group ) {
48+
if ( empty( $hook ) ) {
49+
\WP_CLI::error( __( 'Please specify hook of action to cancel.', 'action-scheduler' ) );
50+
}
51+
52+
try {
53+
$result = as_unschedule_action( $hook, $callback_args, $group );
54+
} catch ( \Exception $e ) {
55+
$this->print_error( $e, false );
56+
}
57+
58+
if ( null === $result ) {
59+
$e = new \Exception( __( 'Unable to cancel scheduled action: check the logs.', 'action-scheduler' ) );
60+
$this->print_error( $e, false );
61+
}
62+
63+
$this->print_success( false );
64+
}
65+
66+
/**
67+
* Cancel all actions.
68+
*
69+
* @param string $hook The hook that the job will trigger.
70+
* @param array $callback_args Args that would have been passed to the job.
71+
* @param string $group The group the job is assigned to.
72+
* @return void
73+
*/
74+
protected function cancel_all( $hook, $callback_args, $group ) {
75+
if ( empty( $hook ) && empty( $group ) ) {
76+
\WP_CLI::error( __( 'Please specify hook and/or group of actions to cancel.', 'action-scheduler' ) );
77+
}
78+
79+
try {
80+
$result = as_unschedule_all_actions( $hook, $callback_args, $group );
81+
} catch ( \Exception $e ) {
82+
$this->print_error( $e, $multiple );
83+
}
84+
85+
/**
86+
* Because as_unschedule_all_actions() does not provide a result,
87+
* neither confirm or deny actions cancelled.
88+
*/
89+
\WP_CLI::success( __( 'Request to cancel scheduled actions completed.', 'action-scheduler' ) );
90+
}
91+
92+
/**
93+
* Print a success message.
94+
*
95+
* @return void
96+
*/
97+
protected function print_success() {
98+
\WP_CLI::success( __( 'Scheduled action cancelled.', 'action-scheduler' ) );
99+
}
100+
101+
/**
102+
* Convert an exception into a WP CLI error.
103+
*
104+
* @param \Exception $e The error object.
105+
* @param bool $multiple Boolean if multiple actions.
106+
* @throws \WP_CLI\ExitException When an error occurs.
107+
* @return void
108+
*/
109+
protected function print_error( \Exception $e, $multiple ) {
110+
\WP_CLI::error(
111+
sprintf(
112+
/* translators: %1$s: singular or plural %2$s: refers to the exception error message. */
113+
__( 'There was an error cancelling the %1$s: %2$s', 'action-scheduler' ),
114+
$multiple ? __( 'scheduled actions', 'action-scheduler' ) : __( 'scheduled action', 'action-scheduler' ),
115+
$e->getMessage()
116+
)
117+
);
118+
}
119+
120+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?php
2+
3+
namespace Action_Scheduler\WP_CLI\Action;
4+
5+
/**
6+
* WP-CLI command: action-scheduler action create
7+
*/
8+
class Create_Command extends \ActionScheduler_WPCLI_Command {
9+
10+
const ASYNC_OPTS = array( 'async', 0 );
11+
12+
/**
13+
* Execute command.
14+
*
15+
* @return void
16+
*/
17+
public function execute() {
18+
$hook = $this->args[0];
19+
$schedule_start = $this->args[1];
20+
$callback_args = get_flag_value( $this->assoc_args, 'args', array() );
21+
$group = get_flag_value( $this->assoc_args, 'group', '' );
22+
$interval = absint( get_flag_value( $this->assoc_args, 'interval', 0 ) );
23+
$cron = get_flag_value( $this->assoc_args, 'cron', '' );
24+
$unique = get_flag_value( $this->assoc_args, 'unique', false );
25+
$priority = absint( get_flag_value( $this->assoc_args, 'priority', 10 ) );
26+
27+
if ( ! empty( $callback_args ) ) {
28+
$callback_args = json_decode( $callback_args, true );
29+
}
30+
31+
$function_args = array(
32+
'start' => $schedule_start,
33+
'cron' => $cron,
34+
'interval' => $interval,
35+
'hook' => $hook,
36+
'callback_args' => $callback_args,
37+
'group' => $group,
38+
'unique' => $unique,
39+
'priority' => $priority,
40+
);
41+
42+
try {
43+
// Generate schedule start if appropriate.
44+
if ( ! in_array( $schedule_start, static::ASYNC_OPTS, true ) ) {
45+
$schedule_start = as_get_datetime_object( $schedule_start );
46+
$function_args['start'] = $schedule_start->format( 'U' );
47+
}
48+
} catch ( \Exception $e ) {
49+
\WP_CLI::error( $e->getMessage() );
50+
}
51+
52+
// Default to creating single action.
53+
$action_type = 'single';
54+
$function = 'as_schedule_single_action';
55+
56+
if ( ! empty( $interval ) ) { // Creating recurring action.
57+
$action_type = 'recurring';
58+
$function = 'as_schedule_recurring_action';
59+
60+
$function_args = array_filter(
61+
$function_args,
62+
static function( $key ) {
63+
return in_array( $key, array( 'start', 'interval', 'hook', 'callback_args', 'group', 'unique', 'priority' ), true );
64+
},
65+
ARRAY_FILTER_USE_KEY
66+
);
67+
} elseif ( ! empty( $cron ) ) { // Creating cron action.
68+
$action_type = 'cron';
69+
$function = 'as_schedule_cron_action';
70+
71+
$function_args = array_filter(
72+
$function_args,
73+
static function( $key ) {
74+
return in_array( $key, array( 'start', 'cron', 'hook', 'callback_args', 'group', 'unique', 'priority' ), true );
75+
},
76+
ARRAY_FILTER_USE_KEY
77+
);
78+
} elseif ( in_array( $function_args['start'], static::ASYNC_OPTS, true ) ) { // Enqueue async action.
79+
$action_type = 'async';
80+
$function = 'as_enqueue_async_action';
81+
82+
$function_args = array_filter(
83+
$function_args,
84+
static function( $key ) {
85+
return in_array( $key, array( 'hook', 'callback_args', 'group', 'unique', 'priority' ), true );
86+
},
87+
ARRAY_FILTER_USE_KEY
88+
);
89+
} else { // Enqueue single action.
90+
$function_args = array_filter(
91+
$function_args,
92+
static function( $key ) {
93+
return in_array( $key, array( 'start', 'hook', 'callback_args', 'group', 'unique', 'priority' ), true );
94+
},
95+
ARRAY_FILTER_USE_KEY
96+
);
97+
}
98+
99+
$function_args = array_values( $function_args );
100+
101+
try {
102+
$action_id = call_user_func_array( $function, $function_args );
103+
} catch ( \Exception $e ) {
104+
$this->print_error( $e );
105+
}
106+
107+
if ( 0 === $action_id ) {
108+
$e = new \Exception( __( 'Unable to create a scheduled action.', 'action-scheduler' ) );
109+
$this->print_error( $e );
110+
}
111+
112+
$this->print_success( $action_id, $action_type );
113+
}
114+
115+
/**
116+
* Print a success message with the action ID.
117+
*
118+
* @param int $action_id Created action ID.
119+
* @param string $action_type Type of action.
120+
*
121+
* @return void
122+
*/
123+
protected function print_success( $action_id, $action_type ) {
124+
\WP_CLI::success(
125+
sprintf(
126+
/* translators: %1$s: type of action, %2$d: ID of the created action */
127+
__( '%1$s action (%2$d) scheduled.', 'action-scheduler' ),
128+
ucfirst( $action_type ),
129+
$action_id
130+
)
131+
);
132+
}
133+
134+
/**
135+
* Convert an exception into a WP CLI error.
136+
*
137+
* @param \Exception $e The error object.
138+
* @throws \WP_CLI\ExitException When an error occurs.
139+
* @return void
140+
*/
141+
protected function print_error( \Exception $e ) {
142+
\WP_CLI::error(
143+
sprintf(
144+
/* translators: %s refers to the exception error message. */
145+
__( 'There was an error creating the scheduled action: %s', 'action-scheduler' ),
146+
$e->getMessage()
147+
)
148+
);
149+
}
150+
151+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
namespace Action_Scheduler\WP_CLI\Action;
4+
5+
/**
6+
* WP-CLI command: action-scheduler action delete
7+
*/
8+
class Delete_Command extends \ActionScheduler_WPCLI_Command {
9+
10+
/**
11+
* Array of action IDs to delete.
12+
*
13+
* @var int[]
14+
*/
15+
protected $action_ids = array();
16+
17+
/**
18+
* Number of deleted, failed, and total actions deleted.
19+
*
20+
* @var array<string, int>
21+
*/
22+
protected $action_counts = array(
23+
'deleted' => 0,
24+
'failed' => 0,
25+
'total' => 0,
26+
);
27+
28+
/**
29+
* Construct.
30+
*
31+
* @param string[] $args Positional arguments.
32+
* @param array<string, string> $assoc_args Keyed arguments.
33+
*/
34+
public function __construct( array $args, array $assoc_args ) {
35+
parent::__construct( $args, $assoc_args );
36+
37+
$this->action_ids = array_map( 'absint', $args );
38+
$this->action_counts['total'] = count( $this->action_ids );
39+
40+
add_action( 'action_scheduler_deleted_action', array( $this, 'on_action_deleted' ) );
41+
}
42+
43+
/**
44+
* Execute.
45+
*
46+
* @return void
47+
*/
48+
public function execute() {
49+
$store = \ActionScheduler::store();
50+
51+
$progress_bar = \WP_CLI\Utils\make_progress_bar(
52+
sprintf(
53+
/* translators: %d: number of actions to be deleted */
54+
_n( 'Deleting %d action', 'Deleting %d actions', $this->action_counts['total'], 'action-scheduler' ),
55+
number_format_i18n( $this->action_counts['total'] )
56+
),
57+
$this->action_counts['total']
58+
);
59+
60+
foreach ( $this->action_ids as $action_id ) {
61+
try {
62+
$store->delete_action( $action_id );
63+
} catch ( \Exception $e ) {
64+
$this->action_counts['failed']++;
65+
\WP_CLI::warning( $e->getMessage() );
66+
}
67+
68+
$progress_bar->tick();
69+
}
70+
71+
$progress_bar->finish();
72+
73+
/* translators: %1$d: number of actions deleted */
74+
$format = _n( 'Deleted %1$d action', 'Deleted %1$d actions', $this->action_counts['deleted'], 'action-scheduler' ) . ', ';
75+
/* translators: %2$d: number of actions deletions failed */
76+
$format .= _n( '%2$d failure.', '%2$d failures.', $this->action_counts['failed'], 'action-scheduler' );
77+
78+
\WP_CLI::success(
79+
sprintf(
80+
$format,
81+
number_format_i18n( $this->action_counts['deleted'] ),
82+
number_format_i18n( $this->action_counts['failed'] )
83+
)
84+
);
85+
}
86+
87+
/**
88+
* Action: action_scheduler_deleted_action
89+
*
90+
* @param int $action_id Action ID.
91+
* @return void
92+
*/
93+
public function on_action_deleted( $action_id ) {
94+
if ( 'action_scheduler_deleted_action' !== current_action() ) {
95+
return;
96+
}
97+
98+
$action_id = absint( $action_id );
99+
100+
if ( ! in_array( $action_id, $this->action_ids, true ) ) {
101+
return;
102+
}
103+
104+
$this->action_counts['deleted']++;
105+
\WP_CLI::debug( sprintf( 'Action %d was deleted.', $action_id ) );
106+
}
107+
108+
}

0 commit comments

Comments
 (0)