PluginProbe ʕ •ᴥ•ʔ
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin / 2.1.1
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin v2.1.1
4.9.0 0.9.6 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.2 1.7.0 1.7.1 1.8.0 1.8.1 1.9.0 2.0.0 2.0.1 2.1.1 2.2.1 2.3.1 2.4.0 2.5.0 2.5.1 2.6.0 2.7.0 2.8.0 2.9.0 3.0.1 3.0.2 3.0.3 3.1.0 3.10.0 3.11.0 3.11.1 3.2.0 3.2.1 3.3.0 3.4.0 3.5.0 3.5.1 3.5.2 3.6.1 3.7.0 3.8.0 3.8.2 3.9.0 4.0.1 4.1.0 4.1.1 4.2.0 4.3.0 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.8.0 trunk 0.10.0 0.10.1 0.11.1 0.11.2 0.3.1 0.3.2 0.4 0.4.1 0.4.2 0.5.0 0.5.1 0.5.2 0.6 0.7 0.8 0.8.2 0.8.3 0.8.4 0.8.5 0.8.6 0.8.7 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5
wp-mail-smtp / vendor / woocommerce / action-scheduler / classes / ActionScheduler_ListTable.php
wp-mail-smtp / vendor / woocommerce / action-scheduler / classes Last commit date
WP_CLI 6 years ago abstracts 6 years ago actions 6 years ago data-stores 6 years ago migration 6 years ago schedules 6 years ago schema 6 years ago ActionScheduler_ActionClaim.php 6 years ago ActionScheduler_ActionFactory.php 6 years ago ActionScheduler_AdminView.php 6 years ago ActionScheduler_AsyncRequest_QueueRunner.php 6 years ago ActionScheduler_Compatibility.php 6 years ago ActionScheduler_DataController.php 6 years ago ActionScheduler_DateTime.php 6 years ago ActionScheduler_Exception.php 6 years ago ActionScheduler_FatalErrorMonitor.php 6 years ago ActionScheduler_InvalidActionException.php 6 years ago ActionScheduler_ListTable.php 6 years ago ActionScheduler_LogEntry.php 6 years ago ActionScheduler_NullLogEntry.php 6 years ago ActionScheduler_OptionLock.php 6 years ago ActionScheduler_QueueCleaner.php 6 years ago ActionScheduler_QueueRunner.php 6 years ago ActionScheduler_Versions.php 6 years ago ActionScheduler_WPCommentCleaner.php 6 years ago ActionScheduler_wcSystemStatus.php 6 years ago
ActionScheduler_ListTable.php
613 lines
1 <?php
2
3 /**
4 * Implements the admin view of the actions.
5 * @codeCoverageIgnore
6 */
7 class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
8
9 /**
10 * The package name.
11 *
12 * @var string
13 */
14 protected $package = 'action-scheduler';
15
16 /**
17 * Columns to show (name => label).
18 *
19 * @var array
20 */
21 protected $columns = array();
22
23 /**
24 * Actions (name => label).
25 *
26 * @var array
27 */
28 protected $row_actions = array();
29
30 /**
31 * The active data stores
32 *
33 * @var ActionScheduler_Store
34 */
35 protected $store;
36
37 /**
38 * A logger to use for getting action logs to display
39 *
40 * @var ActionScheduler_Logger
41 */
42 protected $logger;
43
44 /**
45 * A ActionScheduler_QueueRunner runner instance (or child class)
46 *
47 * @var ActionScheduler_QueueRunner
48 */
49 protected $runner;
50
51 /**
52 * Bulk actions. The key of the array is the method name of the implementation:
53 *
54 * bulk_<key>(array $ids, string $sql_in).
55 *
56 * See the comments in the parent class for further details
57 *
58 * @var array
59 */
60 protected $bulk_actions = array();
61
62 /**
63 * Flag variable to render our notifications, if any, once.
64 *
65 * @var bool
66 */
67 protected static $did_notification = false;
68
69 /**
70 * Array of seconds for common time periods, like week or month, alongside an internationalised string representation, i.e. "Day" or "Days"
71 *
72 * @var array
73 */
74 private static $time_periods;
75
76 /**
77 * Sets the current data store object into `store->action` and initialises the object.
78 *
79 * @param ActionScheduler_Store $store
80 * @param ActionScheduler_Logger $logger
81 * @param ActionScheduler_QueueRunner $runner
82 */
83 public function __construct( ActionScheduler_Store $store, ActionScheduler_Logger $logger, ActionScheduler_QueueRunner $runner ) {
84
85 $this->store = $store;
86 $this->logger = $logger;
87 $this->runner = $runner;
88
89 $this->table_header = __( 'Scheduled Actions', 'action-scheduler' );
90
91 $this->bulk_actions = array(
92 'delete' => __( 'Delete', 'action-scheduler' ),
93 );
94
95 $this->columns = array(
96 'hook' => __( 'Hook', 'action-scheduler' ),
97 'status' => __( 'Status', 'action-scheduler' ),
98 'args' => __( 'Arguments', 'action-scheduler' ),
99 'group' => __( 'Group', 'action-scheduler' ),
100 'recurrence' => __( 'Recurrence', 'action-scheduler' ),
101 'schedule' => __( 'Scheduled Date', 'action-scheduler' ),
102 'log_entries' => __( 'Log', 'action-scheduler' ),
103 );
104
105 $this->sort_by = array(
106 'schedule',
107 'hook',
108 'group',
109 );
110
111 $this->search_by = array(
112 'hook',
113 'args',
114 'claim_id',
115 );
116
117 $request_status = $this->get_request_status();
118
119 if ( empty( $request_status ) ) {
120 $this->sort_by[] = 'status';
121 } elseif ( in_array( $request_status, array( 'in-progress', 'failed' ) ) ) {
122 $this->columns += array( 'claim_id' => __( 'Claim ID', 'action-scheduler' ) );
123 $this->sort_by[] = 'claim_id';
124 }
125
126 $this->row_actions = array(
127 'hook' => array(
128 'run' => array(
129 'name' => __( 'Run', 'action-scheduler' ),
130 'desc' => __( 'Process the action now as if it were run as part of a queue', 'action-scheduler' ),
131 ),
132 'cancel' => array(
133 'name' => __( 'Cancel', 'action-scheduler' ),
134 'desc' => __( 'Cancel the action now to avoid it being run in future', 'action-scheduler' ),
135 'class' => 'cancel trash',
136 ),
137 ),
138 );
139
140 self::$time_periods = array(
141 array(
142 'seconds' => YEAR_IN_SECONDS,
143 /* translators: %s: amount of time */
144 'names' => _n_noop( '%s year', '%s years', 'action-scheduler' ),
145 ),
146 array(
147 'seconds' => MONTH_IN_SECONDS,
148 /* translators: %s: amount of time */
149 'names' => _n_noop( '%s month', '%s months', 'action-scheduler' ),
150 ),
151 array(
152 'seconds' => WEEK_IN_SECONDS,
153 /* translators: %s: amount of time */
154 'names' => _n_noop( '%s week', '%s weeks', 'action-scheduler' ),
155 ),
156 array(
157 'seconds' => DAY_IN_SECONDS,
158 /* translators: %s: amount of time */
159 'names' => _n_noop( '%s day', '%s days', 'action-scheduler' ),
160 ),
161 array(
162 'seconds' => HOUR_IN_SECONDS,
163 /* translators: %s: amount of time */
164 'names' => _n_noop( '%s hour', '%s hours', 'action-scheduler' ),
165 ),
166 array(
167 'seconds' => MINUTE_IN_SECONDS,
168 /* translators: %s: amount of time */
169 'names' => _n_noop( '%s minute', '%s minutes', 'action-scheduler' ),
170 ),
171 array(
172 'seconds' => 1,
173 /* translators: %s: amount of time */
174 'names' => _n_noop( '%s second', '%s seconds', 'action-scheduler' ),
175 ),
176 );
177
178 parent::__construct( array(
179 'singular' => 'action-scheduler',
180 'plural' => 'action-scheduler',
181 'ajax' => false,
182 ) );
183 }
184
185 /**
186 * Convert an interval of seconds into a two part human friendly string.
187 *
188 * The WordPress human_time_diff() function only calculates the time difference to one degree, meaning
189 * even if an action is 1 day and 11 hours away, it will display "1 day". This function goes one step
190 * further to display two degrees of accuracy.
191 *
192 * Inspired by the Crontrol::interval() function by Edward Dale: https://wordpress.org/plugins/wp-crontrol/
193 *
194 * @param int $interval A interval in seconds.
195 * @param int $periods_to_include Depth of time periods to include, e.g. for an interval of 70, and $periods_to_include of 2, both minutes and seconds would be included. With a value of 1, only minutes would be included.
196 * @return string A human friendly string representation of the interval.
197 */
198 private static function human_interval( $interval, $periods_to_include = 2 ) {
199
200 if ( $interval <= 0 ) {
201 return __( 'Now!', 'action-scheduler' );
202 }
203
204 $output = '';
205
206 for ( $time_period_index = 0, $periods_included = 0, $seconds_remaining = $interval; $time_period_index < count( self::$time_periods ) && $seconds_remaining > 0 && $periods_included < $periods_to_include; $time_period_index++ ) {
207
208 $periods_in_interval = floor( $seconds_remaining / self::$time_periods[ $time_period_index ]['seconds'] );
209
210 if ( $periods_in_interval > 0 ) {
211 if ( ! empty( $output ) ) {
212 $output .= ' ';
213 }
214 $output .= sprintf( _n( self::$time_periods[ $time_period_index ]['names'][0], self::$time_periods[ $time_period_index ]['names'][1], $periods_in_interval, 'action-scheduler' ), $periods_in_interval );
215 $seconds_remaining -= $periods_in_interval * self::$time_periods[ $time_period_index ]['seconds'];
216 $periods_included++;
217 }
218 }
219
220 return $output;
221 }
222
223 /**
224 * Returns the recurrence of an action or 'Non-repeating'. The output is human readable.
225 *
226 * @param ActionScheduler_Action $action
227 *
228 * @return string
229 */
230 protected function get_recurrence( $action ) {
231 $schedule = $action->get_schedule();
232 if ( $schedule->is_recurring() ) {
233 $recurrence = $schedule->get_recurrence();
234
235 if ( is_numeric( $recurrence ) ) {
236 /* translators: %s: time interval */
237 return sprintf( __( 'Every %s', 'action-scheduler' ), self::human_interval( $recurrence ) );
238 } else {
239 return $recurrence;
240 }
241 }
242
243 return __( 'Non-repeating', 'action-scheduler' );
244 }
245
246 /**
247 * Serializes the argument of an action to render it in a human friendly format.
248 *
249 * @param array $row The array representation of the current row of the table
250 *
251 * @return string
252 */
253 public function column_args( array $row ) {
254 if ( empty( $row['args'] ) ) {
255 return apply_filters( 'action_scheduler_list_table_column_args', '', $row );
256 }
257
258 $row_html = '<ul>';
259 foreach ( $row['args'] as $key => $value ) {
260 $row_html .= sprintf( '<li><code>%s => %s</code></li>', esc_html( var_export( $key, true ) ), esc_html( var_export( $value, true ) ) );
261 }
262 $row_html .= '</ul>';
263
264 return apply_filters( 'action_scheduler_list_table_column_args', $row_html, $row );
265 }
266
267 /**
268 * Prints the logs entries inline. We do so to avoid loading Javascript and other hacks to show it in a modal.
269 *
270 * @param array $row Action array.
271 * @return string
272 */
273 public function column_log_entries( array $row ) {
274
275 $log_entries_html = '<ol>';
276
277 $timezone = new DateTimezone( 'UTC' );
278
279 foreach ( $row['log_entries'] as $log_entry ) {
280 $log_entries_html .= $this->get_log_entry_html( $log_entry, $timezone );
281 }
282
283 $log_entries_html .= '</ol>';
284
285 return $log_entries_html;
286 }
287
288 /**
289 * Prints the logs entries inline. We do so to avoid loading Javascript and other hacks to show it in a modal.
290 *
291 * @param ActionScheduler_LogEntry $log_entry
292 * @param DateTimezone $timezone
293 * @return string
294 */
295 protected function get_log_entry_html( ActionScheduler_LogEntry $log_entry, DateTimezone $timezone ) {
296 $date = $log_entry->get_date();
297 $date->setTimezone( $timezone );
298 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() ) );
299 }
300
301 /**
302 * Only display row actions for pending actions.
303 *
304 * @param array $row Row to render
305 * @param string $column_name Current row
306 *
307 * @return string
308 */
309 protected function maybe_render_actions( $row, $column_name ) {
310 if ( 'pending' === strtolower( $row[ 'status_name' ] ) ) {
311 return parent::maybe_render_actions( $row, $column_name );
312 }
313
314 return '';
315 }
316
317 /**
318 * Renders admin notifications
319 *
320 * Notifications:
321 * 1. When the maximum number of tasks are being executed simultaneously.
322 * 2. Notifications when a task is manually executed.
323 * 3. Tables are missing.
324 */
325 public function display_admin_notices() {
326 global $wpdb;
327
328 if ( ( is_a( $this->store, 'ActionScheduler_HybridStore' ) || is_a( $this->store, 'ActionScheduler_DBStore' ) ) && apply_filters( 'action_scheduler_enable_recreate_data_store', true ) ) {
329 $table_list = array(
330 'actionscheduler_actions',
331 'actionscheduler_logs',
332 'actionscheduler_groups',
333 'actionscheduler_claims',
334 );
335
336 $found_tables = $wpdb->get_col( "SHOW TABLES LIKE '{$wpdb->prefix}actionscheduler%'" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
337 foreach ( $table_list as $table_name ) {
338 if ( ! in_array( $wpdb->prefix . $table_name, $found_tables ) ) {
339 $this->admin_notices[] = array(
340 'class' => 'error',
341 'message' => __( 'It appears one or more database tables were missing. Attempting to re-create the missing table(s).' , 'action-scheduler' ),
342 );
343 $this->recreate_tables();
344 parent::display_admin_notices();
345
346 return;
347 }
348 }
349 }
350
351 if ( $this->runner->has_maximum_concurrent_batches() ) {
352 $claim_count = $this->store->get_claim_count();
353 $this->admin_notices[] = array(
354 'class' => 'updated',
355 'message' => sprintf(
356 /* translators: %s: amount of claims */
357 _n(
358 'Maximum simultaneous queues already in progress (%s queue). No additional queues will begin processing until the current queues are complete.',
359 'Maximum simultaneous queues already in progress (%s queues). No additional queues will begin processing until the current queues are complete.',
360 $claim_count,
361 'action-scheduler'
362 ),
363 $claim_count
364 ),
365 );
366 } elseif ( $this->store->has_pending_actions_due() ) {
367
368 $async_request_lock_expiration = ActionScheduler::lock()->get_expiration( 'async-request-runner' );
369
370 // No lock set or lock expired
371 if ( false === $async_request_lock_expiration || $async_request_lock_expiration < time() ) {
372 $in_progress_url = add_query_arg( 'status', 'in-progress', remove_query_arg( 'status' ) );
373 /* translators: %s: process URL */
374 $async_request_message = sprintf( __( 'A new queue has begun processing. <a href="%s">View actions in-progress &raquo;</a>', 'action-scheduler' ), esc_url( $in_progress_url ) );
375 } else {
376 /* translators: %d: seconds */
377 $async_request_message = sprintf( __( 'The next queue will begin processing in approximately %d seconds.', 'action-scheduler' ), $async_request_lock_expiration - time() );
378 }
379
380 $this->admin_notices[] = array(
381 'class' => 'notice notice-info',
382 'message' => $async_request_message,
383 );
384 }
385
386 $notification = get_transient( 'action_scheduler_admin_notice' );
387
388 if ( is_array( $notification ) ) {
389 delete_transient( 'action_scheduler_admin_notice' );
390
391 $action = $this->store->fetch_action( $notification['action_id'] );
392 $action_hook_html = '<strong><code>' . $action->get_hook() . '</code></strong>';
393 if ( 1 == $notification['success'] ) {
394 $class = 'updated';
395 switch ( $notification['row_action_type'] ) {
396 case 'run' :
397 /* translators: %s: action HTML */
398 $action_message_html = sprintf( __( 'Successfully executed action: %s', 'action-scheduler' ), $action_hook_html );
399 break;
400 case 'cancel' :
401 /* translators: %s: action HTML */
402 $action_message_html = sprintf( __( 'Successfully canceled action: %s', 'action-scheduler' ), $action_hook_html );
403 break;
404 default :
405 /* translators: %s: action HTML */
406 $action_message_html = sprintf( __( 'Successfully processed change for action: %s', 'action-scheduler' ), $action_hook_html );
407 break;
408 }
409 } else {
410 $class = 'error';
411 /* translators: 1: action HTML 2: action ID 3: error message */
412 $action_message_html = sprintf( __( 'Could not process change for action: "%1$s" (ID: %2$d). Error: %3$s', 'action-scheduler' ), $action_hook_html, esc_html( $notification['action_id'] ), esc_html( $notification['error_message'] ) );
413 }
414
415 $action_message_html = apply_filters( 'action_scheduler_admin_notice_html', $action_message_html, $action, $notification );
416
417 $this->admin_notices[] = array(
418 'class' => $class,
419 'message' => $action_message_html,
420 );
421 }
422
423 parent::display_admin_notices();
424 }
425
426 /**
427 * Prints the scheduled date in a human friendly format.
428 *
429 * @param array $row The array representation of the current row of the table
430 *
431 * @return string
432 */
433 public function column_schedule( $row ) {
434 return $this->get_schedule_display_string( $row['schedule'] );
435 }
436
437 /**
438 * Get the scheduled date in a human friendly format.
439 *
440 * @param ActionScheduler_Schedule $schedule
441 * @return string
442 */
443 protected function get_schedule_display_string( ActionScheduler_Schedule $schedule ) {
444
445 $schedule_display_string = '';
446
447 if ( ! $schedule->get_date() ) {
448 return '0000-00-00 00:00:00';
449 }
450
451 $next_timestamp = $schedule->get_date()->getTimestamp();
452
453 $schedule_display_string .= $schedule->get_date()->format( 'Y-m-d H:i:s O' );
454 $schedule_display_string .= '<br/>';
455
456 if ( gmdate( 'U' ) > $next_timestamp ) {
457 /* translators: %s: date interval */
458 $schedule_display_string .= sprintf( __( ' (%s ago)', 'action-scheduler' ), self::human_interval( gmdate( 'U' ) - $next_timestamp ) );
459 } else {
460 /* translators: %s: date interval */
461 $schedule_display_string .= sprintf( __( ' (%s)', 'action-scheduler' ), self::human_interval( $next_timestamp - gmdate( 'U' ) ) );
462 }
463
464 return $schedule_display_string;
465 }
466
467 /**
468 * Bulk delete
469 *
470 * Deletes actions based on their ID. This is the handler for the bulk delete. It assumes the data
471 * properly validated by the callee and it will delete the actions without any extra validation.
472 *
473 * @param array $ids
474 * @param string $ids_sql Inherited and unused
475 */
476 protected function bulk_delete( array $ids, $ids_sql ) {
477 foreach ( $ids as $id ) {
478 $this->store->delete_action( $id );
479 }
480 }
481
482 /**
483 * Implements the logic behind running an action. ActionScheduler_Abstract_ListTable validates the request and their
484 * parameters are valid.
485 *
486 * @param int $action_id
487 */
488 protected function row_action_cancel( $action_id ) {
489 $this->process_row_action( $action_id, 'cancel' );
490 }
491
492 /**
493 * Implements the logic behind running an action. ActionScheduler_Abstract_ListTable validates the request and their
494 * parameters are valid.
495 *
496 * @param int $action_id
497 */
498 protected function row_action_run( $action_id ) {
499 $this->process_row_action( $action_id, 'run' );
500 }
501
502 /**
503 * Force the data store schema updates.
504 */
505 protected function recreate_tables() {
506 if ( is_a( $this->store, 'ActionScheduler_HybridStore' ) ) {
507 $store = $this->store;
508 } else {
509 $store = new ActionScheduler_HybridStore();
510 }
511 add_action( 'action_scheduler/created_table', array( $store, 'set_autoincrement' ), 10, 2 );
512
513 $store_schema = new ActionScheduler_StoreSchema();
514 $logger_schema = new ActionScheduler_LoggerSchema();
515 $store_schema->register_tables( true );
516 $logger_schema->register_tables( true );
517
518 remove_action( 'action_scheduler/created_table', array( $store, 'set_autoincrement' ), 10 );
519 }
520 /**
521 * Implements the logic behind processing an action once an action link is clicked on the list table.
522 *
523 * @param int $action_id
524 * @param string $row_action_type The type of action to perform on the action.
525 */
526 protected function process_row_action( $action_id, $row_action_type ) {
527 try {
528 switch ( $row_action_type ) {
529 case 'run' :
530 $this->runner->process_action( $action_id, 'Admin List Table' );
531 break;
532 case 'cancel' :
533 $this->store->cancel_action( $action_id );
534 break;
535 }
536 $success = 1;
537 $error_message = '';
538 } catch ( Exception $e ) {
539 $success = 0;
540 $error_message = $e->getMessage();
541 }
542
543 set_transient( 'action_scheduler_admin_notice', compact( 'action_id', 'success', 'error_message', 'row_action_type' ), 30 );
544 }
545
546 /**
547 * {@inheritDoc}
548 */
549 public function prepare_items() {
550 $this->prepare_column_headers();
551
552 $per_page = $this->get_items_per_page( $this->package . '_items_per_page', $this->items_per_page );
553 $query = array(
554 'per_page' => $per_page,
555 'offset' => $this->get_items_offset(),
556 'status' => $this->get_request_status(),
557 'orderby' => $this->get_request_orderby(),
558 'order' => $this->get_request_order(),
559 'search' => $this->get_request_search_query(),
560 );
561
562 $this->items = array();
563
564 $total_items = $this->store->query_actions( $query, 'count' );
565
566 $status_labels = $this->store->get_status_labels();
567
568 foreach ( $this->store->query_actions( $query ) as $action_id ) {
569 try {
570 $action = $this->store->fetch_action( $action_id );
571 } catch ( Exception $e ) {
572 continue;
573 }
574 if ( is_a( $action, 'ActionScheduler_NullAction' ) ) {
575 continue;
576 }
577 $this->items[ $action_id ] = array(
578 'ID' => $action_id,
579 'hook' => $action->get_hook(),
580 'status_name' => $this->store->get_status( $action_id ),
581 'status' => $status_labels[ $this->store->get_status( $action_id ) ],
582 'args' => $action->get_args(),
583 'group' => $action->get_group(),
584 'log_entries' => $this->logger->get_logs( $action_id ),
585 'claim_id' => $this->store->get_claim_id( $action_id ),
586 'recurrence' => $this->get_recurrence( $action ),
587 'schedule' => $action->get_schedule(),
588 );
589 }
590
591 $this->set_pagination_args( array(
592 'total_items' => $total_items,
593 'per_page' => $per_page,
594 'total_pages' => ceil( $total_items / $per_page ),
595 ) );
596 }
597
598 /**
599 * Prints the available statuses so the user can click to filter.
600 */
601 protected function display_filter_by_status() {
602 $this->status_counts = $this->store->action_counts();
603 parent::display_filter_by_status();
604 }
605
606 /**
607 * Get the text to display in the search box on the list table.
608 */
609 protected function get_search_box_button_text() {
610 return __( 'Search hook, args and claim ID', 'action-scheduler' );
611 }
612 }
613