no wp
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,698 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* List table for cron events.
|
||||
*/
|
||||
|
||||
namespace Crontrol\Event;
|
||||
|
||||
use stdClass;
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||
|
||||
/**
|
||||
* Cron event list table class.
|
||||
*/
|
||||
class Table extends \WP_List_Table {
|
||||
|
||||
/**
|
||||
* Array of cron event hooks that are persistently added by WordPress core.
|
||||
*
|
||||
* @var array<int,string> Array of hook names.
|
||||
*/
|
||||
protected static $persistent_core_hooks;
|
||||
|
||||
/**
|
||||
* Whether the current user has the capability to create or edit PHP cron events.
|
||||
*
|
||||
* @var bool Whether the user can create or edit PHP cron events.
|
||||
*/
|
||||
protected static $can_manage_php_crons;
|
||||
|
||||
/**
|
||||
* Array of the count of each hook.
|
||||
*
|
||||
* @var array<string,int> Array of count of each hooked, keyed by hook name.
|
||||
*/
|
||||
protected static $count_by_hook;
|
||||
|
||||
/**
|
||||
* Array of all cron events.
|
||||
*
|
||||
* @var array<string,stdClass> Array of event objects.
|
||||
*/
|
||||
protected $all_events = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct( array(
|
||||
'singular' => 'crontrol-event',
|
||||
'plural' => 'crontrol-events',
|
||||
'ajax' => false,
|
||||
'screen' => 'crontrol-events',
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the list table items and arguments.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function prepare_items() {
|
||||
self::$persistent_core_hooks = \Crontrol\get_persistent_core_hooks();
|
||||
self::$can_manage_php_crons = current_user_can( 'edit_files' );
|
||||
self::$count_by_hook = count_by_hook();
|
||||
|
||||
$events = get();
|
||||
$this->all_events = $events;
|
||||
|
||||
if ( ! empty( $_GET['s'] ) ) {
|
||||
$s = sanitize_text_field( wp_unslash( $_GET['s'] ) );
|
||||
|
||||
$events = array_filter( $events, function( $event ) use ( $s ) {
|
||||
return ( false !== strpos( $event->hook, $s ) );
|
||||
} );
|
||||
}
|
||||
|
||||
if ( ! empty( $_GET['crontrol_hooks_type'] ) ) {
|
||||
$hooks_type = sanitize_text_field( $_GET['crontrol_hooks_type'] );
|
||||
$filtered = self::get_filtered_events( $events );
|
||||
|
||||
if ( isset( $filtered[ $hooks_type ] ) ) {
|
||||
$events = $filtered[ $hooks_type ];
|
||||
}
|
||||
}
|
||||
|
||||
$count = count( $events );
|
||||
$per_page = 50;
|
||||
$offset = ( $this->get_pagenum() - 1 ) * $per_page;
|
||||
|
||||
$this->items = array_slice( $events, $offset, $per_page );
|
||||
|
||||
$has_late = (bool) array_filter( array_map( __NAMESPACE__ . '\\is_late', $this->items ) );
|
||||
|
||||
if ( $has_late ) {
|
||||
add_action( 'admin_notices', function() {
|
||||
printf(
|
||||
'<div id="crontrol-late-message" class="notice notice-warning"><p>%1$s</p><p><a href="%2$s">%3$s</a></p></div>',
|
||||
/* translators: %s: Help page URL. */
|
||||
esc_html__( 'One or more cron events have missed their schedule.', 'wp-crontrol' ),
|
||||
'https://github.com/johnbillion/wp-crontrol/wiki/Cron-events-that-have-missed-their-schedule',
|
||||
esc_html__( 'More information', 'wp-crontrol' )
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
$this->set_pagination_args( array(
|
||||
'total_items' => $count,
|
||||
'per_page' => $per_page,
|
||||
'total_pages' => (int) ceil( $count / $per_page ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns events filtered by various parameters
|
||||
*
|
||||
* @param array<string,stdClass> $events The list of all events.
|
||||
* @return array<string,array<string,stdClass>> Array of filtered events keyed by filter name.
|
||||
*/
|
||||
public static function get_filtered_events( array $events ) {
|
||||
$all_core_hooks = \Crontrol\get_all_core_hooks();
|
||||
$filtered = array(
|
||||
'all' => $events,
|
||||
);
|
||||
|
||||
$filtered['noaction'] = array_filter( $events, function( $event ) {
|
||||
$hook_callbacks = \Crontrol\get_hook_callbacks( $event->hook );
|
||||
return empty( $hook_callbacks );
|
||||
} );
|
||||
|
||||
$filtered['core'] = array_filter( $events, function( $event ) use ( $all_core_hooks ) {
|
||||
return ( in_array( $event->hook, $all_core_hooks, true ) );
|
||||
} );
|
||||
|
||||
$filtered['custom'] = array_filter( $events, function( $event ) use ( $all_core_hooks ) {
|
||||
return ( ! in_array( $event->hook, $all_core_hooks, true ) );
|
||||
} );
|
||||
|
||||
$paused = array_filter( $events, function( $event ) {
|
||||
return ( is_paused( $event ) );
|
||||
} );
|
||||
|
||||
if ( count( $paused ) > 0 ) {
|
||||
$filtered['paused'] = $paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the available filtered events on the cron event listing screen.
|
||||
*
|
||||
* See the corresponding `crontrol/filter-types` filter to adjust the list of filter types.
|
||||
*
|
||||
* @since 1.11.0
|
||||
*
|
||||
* @param array[] $filtered Array of filtered event arrays keyed by filter name.
|
||||
* @param stdClass[] $events Array of all events.
|
||||
*/
|
||||
$filtered = apply_filters( 'crontrol/filtered-events', $filtered, $events );
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of column names for the table.
|
||||
*
|
||||
* @return array<string,string> Array of column names keyed by their ID.
|
||||
*/
|
||||
public function get_columns() {
|
||||
return array(
|
||||
'cb' => '<input type="checkbox" />',
|
||||
'crontrol_hook' => __( 'Hook', 'wp-crontrol' ),
|
||||
'crontrol_args' => __( 'Arguments', 'wp-crontrol' ),
|
||||
'crontrol_next' => sprintf(
|
||||
/* translators: %s: UTC offset */
|
||||
__( 'Next Run (%s)', 'wp-crontrol' ),
|
||||
\Crontrol\get_utc_offset()
|
||||
),
|
||||
'crontrol_actions' => __( 'Action', 'wp-crontrol' ),
|
||||
'crontrol_recurrence' => __( 'Recurrence', 'wp-crontrol' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Columns to make sortable.
|
||||
*
|
||||
* @return array<string,array<int,mixed>>
|
||||
* @phpstan-return array<string,array{
|
||||
* 0: string,
|
||||
* 1: bool,
|
||||
* 2?: ?string,
|
||||
* 3?: ?string,
|
||||
* 4?: 'asc'|'desc',
|
||||
* }>
|
||||
*/
|
||||
public function get_sortable_columns() {
|
||||
return array(
|
||||
'crontrol_hook' => array( 'crontrol_hook', false ),
|
||||
'crontrol_next' => array( 'crontrol_next', false, null, null, 'asc' ),
|
||||
'crontrol_recurrence' => array( 'crontrol_recurrence', false ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of CSS class names for the table.
|
||||
*
|
||||
* @return array<int,string> Array of class names.
|
||||
*/
|
||||
protected function get_table_classes() {
|
||||
return array( 'widefat', 'striped', 'table-view-list', $this->_args['plural'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an associative array ( option_name => option_title ) with the list
|
||||
* of bulk actions available on this table.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @return array<string,string>
|
||||
*/
|
||||
protected function get_bulk_actions() {
|
||||
return array(
|
||||
'crontrol_delete_crons' => esc_html__( 'Delete', 'wp-crontrol' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the list of hook types.
|
||||
*
|
||||
* @return array<string,string>
|
||||
*/
|
||||
public function get_views() {
|
||||
$filtered = self::get_filtered_events( $this->all_events );
|
||||
|
||||
$views = array();
|
||||
$hooks_type = ( ! empty( $_GET['crontrol_hooks_type'] ) ? $_GET['crontrol_hooks_type'] : 'all' );
|
||||
|
||||
$types = array(
|
||||
'all' => __( 'All events', 'wp-crontrol' ),
|
||||
'noaction' => __( 'Events with no action', 'wp-crontrol' ),
|
||||
'core' => __( 'WordPress core events', 'wp-crontrol' ),
|
||||
'custom' => __( 'Custom events', 'wp-crontrol' ),
|
||||
'paused' => __( 'Paused events', 'wp-crontrol' ),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the filter types on the cron event listing screen.
|
||||
*
|
||||
* See the corresponding `crontrol/filtered-events` filter to adjust the filtered events.
|
||||
*
|
||||
* @since 1.11.0
|
||||
*
|
||||
* @param string[] $types Array of filter names keyed by filter name.
|
||||
* @param string $hooks_type The current filter name.
|
||||
*/
|
||||
$types = apply_filters( 'crontrol/filter-types', $types, $hooks_type );
|
||||
|
||||
$url = admin_url( 'tools.php?page=crontrol_admin_manage_page' );
|
||||
|
||||
/**
|
||||
* @var array<string,string> $types
|
||||
*/
|
||||
foreach ( $types as $key => $type ) {
|
||||
if ( ! isset( $filtered[ $key ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$link = ( 'all' === $key ) ? $url : add_query_arg( 'crontrol_hooks_type', $key, $url );
|
||||
|
||||
$views[ $key ] = sprintf(
|
||||
'<a href="%1$s"%2$s>%3$s <span class="count">(%4$s)</span></a>',
|
||||
esc_url( $link ),
|
||||
$hooks_type === $key ? ' class="current"' : '',
|
||||
esc_html( $type ),
|
||||
number_format_i18n( count( $filtered[ $key ] ) )
|
||||
);
|
||||
}
|
||||
|
||||
return $views;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra controls to be displayed between bulk actions and pagination.
|
||||
*
|
||||
* @param string $which One of 'top' or 'bottom' to indicate the position on the screen.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function extra_tablenav( $which ) {
|
||||
wp_nonce_field( 'crontrol-export-event-csv', 'crontrol_nonce' );
|
||||
printf(
|
||||
'<input type="hidden" name="crontrol_hooks_type" value="%s"/>',
|
||||
esc_attr( isset( $_GET['crontrol_hooks_type'] ) ? sanitize_text_field( wp_unslash( $_GET['crontrol_hooks_type'] ) ) : 'all' )
|
||||
);
|
||||
printf(
|
||||
'<input type="hidden" name="s" value="%s"/>',
|
||||
esc_attr( isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '' )
|
||||
);
|
||||
printf(
|
||||
'<button class="button" type="submit" name="crontrol_action" value="export-event-csv">%s</button>',
|
||||
esc_html__( 'Export', 'wp-crontrol' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates content for a single row of the table.
|
||||
*
|
||||
* @param stdClass $event The current event.
|
||||
* @return void
|
||||
*/
|
||||
public function single_row( $event ) {
|
||||
$classes = array();
|
||||
|
||||
if ( ( 'crontrol_cron_job' === $event->hook ) && ! empty( $event->args['syntax_error_message'] ) ) {
|
||||
$classes[] = 'crontrol-error';
|
||||
}
|
||||
|
||||
$schedule_name = ( $event->interval ? get_schedule_name( $event ) : false );
|
||||
|
||||
if ( is_wp_error( $schedule_name ) ) {
|
||||
$classes[] = 'crontrol-error';
|
||||
}
|
||||
|
||||
$callbacks = \Crontrol\get_hook_callbacks( $event->hook );
|
||||
|
||||
if ( ! $callbacks ) {
|
||||
$classes[] = 'crontrol-no-action';
|
||||
} else {
|
||||
foreach ( $callbacks as $callback ) {
|
||||
if ( ! empty( $callback['callback']['error'] ) ) {
|
||||
$classes[] = 'crontrol-error';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_late( $event ) || is_too_frequent( $event ) ) {
|
||||
$classes[] = 'crontrol-warning';
|
||||
}
|
||||
|
||||
if ( is_paused( $event ) ) {
|
||||
$classes[] = 'crontrol-paused';
|
||||
}
|
||||
|
||||
printf(
|
||||
'<tr class="%s">',
|
||||
esc_attr( implode( ' ', $classes ) )
|
||||
);
|
||||
|
||||
$this->single_row_columns( $event );
|
||||
echo '</tr>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and displays row action links for the table.
|
||||
*
|
||||
* @param stdClass $event The cron event for the current row.
|
||||
* @param string $column_name Current column name.
|
||||
* @param string $primary Primary column name.
|
||||
* @return string The row actions HTML.
|
||||
*/
|
||||
protected function handle_row_actions( $event, $column_name, $primary ) {
|
||||
if ( $primary !== $column_name ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$links = array();
|
||||
|
||||
if ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_manage_php_crons ) {
|
||||
$link = array(
|
||||
'page' => 'crontrol_admin_manage_page',
|
||||
'crontrol_action' => 'edit-cron',
|
||||
'crontrol_id' => rawurlencode( $event->hook ),
|
||||
'crontrol_sig' => rawurlencode( $event->sig ),
|
||||
'crontrol_next_run_utc' => rawurlencode( $event->timestamp ),
|
||||
);
|
||||
$link = add_query_arg( $link, admin_url( 'tools.php' ) );
|
||||
|
||||
$links[] = "<a href='" . esc_url( $link ) . "'>" . esc_html__( 'Edit', 'wp-crontrol' ) . '</a>';
|
||||
}
|
||||
|
||||
if ( ! is_paused( $event ) ) {
|
||||
$link = array(
|
||||
'page' => 'crontrol_admin_manage_page',
|
||||
'crontrol_action' => 'run-cron',
|
||||
'crontrol_id' => rawurlencode( $event->hook ),
|
||||
'crontrol_sig' => rawurlencode( $event->sig ),
|
||||
'crontrol_next_run_utc' => rawurlencode( $event->timestamp ),
|
||||
);
|
||||
$link = add_query_arg( $link, admin_url( 'tools.php' ) );
|
||||
$link = wp_nonce_url( $link, "crontrol-run-cron_{$event->hook}_{$event->sig}" );
|
||||
|
||||
$links[] = "<a href='" . esc_url( $link ) . "'>" . esc_html__( 'Run now', 'wp-crontrol' ) . '</a>';
|
||||
}
|
||||
|
||||
if ( is_paused( $event ) ) {
|
||||
$link = array(
|
||||
'page' => 'crontrol_admin_manage_page',
|
||||
'crontrol_action' => 'resume-hook',
|
||||
'crontrol_id' => rawurlencode( $event->hook ),
|
||||
);
|
||||
$link = add_query_arg( $link, admin_url( 'tools.php' ) );
|
||||
$link = wp_nonce_url( $link, "crontrol-resume-hook_{$event->hook}" );
|
||||
|
||||
/* translators: Resume is a verb */
|
||||
$links[] = "<a href='" . esc_url( $link ) . "'>" . esc_html__( 'Resume this hook', 'wp-crontrol' ) . '</a>';
|
||||
} elseif ( 'crontrol_cron_job' !== $event->hook ) {
|
||||
$link = array(
|
||||
'page' => 'crontrol_admin_manage_page',
|
||||
'crontrol_action' => 'pause-hook',
|
||||
'crontrol_id' => rawurlencode( $event->hook ),
|
||||
);
|
||||
$link = add_query_arg( $link, admin_url( 'tools.php' ) );
|
||||
$link = wp_nonce_url( $link, "crontrol-pause-hook_{$event->hook}" );
|
||||
|
||||
/* translators: Pause is a verb */
|
||||
$links[] = "<a href='" . esc_url( $link ) . "'>" . esc_html__( 'Pause this hook', 'wp-crontrol' ) . '</a>';
|
||||
}
|
||||
|
||||
if ( ! in_array( $event->hook, self::$persistent_core_hooks, true ) && ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_manage_php_crons ) ) {
|
||||
$link = array(
|
||||
'page' => 'crontrol_admin_manage_page',
|
||||
'crontrol_action' => 'delete-cron',
|
||||
'crontrol_id' => rawurlencode( $event->hook ),
|
||||
'crontrol_sig' => rawurlencode( $event->sig ),
|
||||
'crontrol_next_run_utc' => rawurlencode( $event->timestamp ),
|
||||
);
|
||||
$link = add_query_arg( $link, admin_url( 'tools.php' ) );
|
||||
$link = wp_nonce_url( $link, "crontrol-delete-cron_{$event->hook}_{$event->sig}_{$event->timestamp}" );
|
||||
|
||||
$links[] = "<span class='delete'><a href='" . esc_url( $link ) . "'>" . esc_html__( 'Delete', 'wp-crontrol' ) . '</a></span>';
|
||||
}
|
||||
|
||||
if ( function_exists( 'wp_unschedule_hook' ) && ( 'crontrol_cron_job' !== $event->hook ) ) {
|
||||
if ( self::$count_by_hook[ $event->hook ] > 1 ) {
|
||||
$link = array(
|
||||
'page' => 'crontrol_admin_manage_page',
|
||||
'crontrol_action' => 'delete-hook',
|
||||
'crontrol_id' => rawurlencode( $event->hook ),
|
||||
);
|
||||
$link = add_query_arg( $link, admin_url( 'tools.php' ) );
|
||||
$link = wp_nonce_url(
|
||||
$link,
|
||||
sprintf(
|
||||
'crontrol-delete-hook_%1$s',
|
||||
$event->hook
|
||||
)
|
||||
);
|
||||
$text = sprintf(
|
||||
/* translators: %s: The number of events with this hook */
|
||||
__( 'Delete all events with this hook (%s)', 'wp-crontrol' ),
|
||||
number_format_i18n( self::$count_by_hook[ $event->hook ] )
|
||||
);
|
||||
|
||||
$links[] = sprintf(
|
||||
'<span class="delete"><a href="%1$s">%2$s</a></span>',
|
||||
esc_url( $link ),
|
||||
esc_html( $text )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->row_actions( $links );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the checkbox cell of a table row.
|
||||
*
|
||||
* @param stdClass $event The cron event for the current row.
|
||||
* @return string The cell output.
|
||||
*/
|
||||
protected function column_cb( $event ) {
|
||||
$id = sprintf(
|
||||
'crontrol-delete-%1$s-%2$s-%3$s',
|
||||
$event->timestamp,
|
||||
rawurlencode( $event->hook ),
|
||||
$event->sig
|
||||
);
|
||||
|
||||
if ( in_array( $event->hook, self::$persistent_core_hooks, true ) ) {
|
||||
return sprintf(
|
||||
'<span class="dashicons dashicons-wordpress" aria-hidden="true"></span>
|
||||
<span class="screen-reader-text">%s</span>',
|
||||
esc_html__( 'This is a WordPress core event and cannot be deleted', 'wp-crontrol' )
|
||||
);
|
||||
} elseif ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_manage_php_crons ) {
|
||||
return sprintf(
|
||||
'<label class="screen-reader-text" for="%1$s">%2$s</label>
|
||||
<input type="checkbox" name="crontrol_delete[%3$s][%4$s]" value="%5$s" id="%1$s">',
|
||||
esc_attr( $id ),
|
||||
esc_html__( 'Select this row', 'wp-crontrol' ),
|
||||
esc_attr( $event->timestamp ),
|
||||
esc_attr( rawurlencode( $event->hook ) ),
|
||||
esc_attr( $event->sig )
|
||||
);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for the hook name cell of a table row.
|
||||
*
|
||||
* @param stdClass $event The cron event for the current row.
|
||||
* @return string The cell output.
|
||||
*/
|
||||
protected function column_crontrol_hook( $event ) {
|
||||
if ( 'crontrol_cron_job' === $event->hook ) {
|
||||
if ( ! empty( $event->args['name'] ) ) {
|
||||
/* translators: 1: The name of the PHP cron event. */
|
||||
return '<em>' . esc_html( sprintf( __( 'PHP Cron (%s)', 'wp-crontrol' ), $event->args['name'] ) ) . '</em>';
|
||||
} else {
|
||||
return '<em>' . esc_html__( 'PHP Cron', 'wp-crontrol' ) . '</em>';
|
||||
}
|
||||
}
|
||||
|
||||
$output = esc_html( $event->hook );
|
||||
|
||||
if ( is_paused( $event ) ) {
|
||||
$output .= sprintf(
|
||||
' — <strong class="status-crontrol-paused post-state"><span class="dashicons dashicons-controls-pause" aria-hidden="true"></span> %s</strong>',
|
||||
/* translators: State of a cron event, adjective */
|
||||
esc_html__( 'Paused', 'wp-crontrol' )
|
||||
);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for the arguments cell of a table row.
|
||||
*
|
||||
* @param stdClass $event The cron event for the current row.
|
||||
* @return string The cell output.
|
||||
*/
|
||||
protected function column_crontrol_args( $event ) {
|
||||
if ( 'crontrol_cron_job' === $event->hook ) {
|
||||
$return = '<em>' . esc_html__( 'PHP Code', 'wp-crontrol' ) . '</em>';
|
||||
|
||||
if ( ! empty( $event->args['syntax_error_message'] ) ) {
|
||||
$return .= '<br><span class="status-crontrol-error"><span class="dashicons dashicons-warning" aria-hidden="true"></span> ';
|
||||
$return .= sprintf(
|
||||
/* translators: 1: Line number, 2: Error message text */
|
||||
esc_html__( 'Line %1$s: %2$s', 'wp-crontrol' ),
|
||||
esc_html( number_format_i18n( $event->args['syntax_error_line'] ) ),
|
||||
esc_html( $event->args['syntax_error_message'] )
|
||||
);
|
||||
$return .= '</span>';
|
||||
}
|
||||
|
||||
if ( ! empty( $event->args['code'] ) ) {
|
||||
$lines = explode( "\n", trim( $event->args['code'] ) );
|
||||
$code = reset( $lines );
|
||||
$code = substr( $code, 0, 50 );
|
||||
|
||||
$return .= '<br>';
|
||||
$return .= sprintf(
|
||||
'<code>%s</code>…',
|
||||
esc_html( $code )
|
||||
);
|
||||
}
|
||||
|
||||
return $return;
|
||||
} else {
|
||||
if ( empty( $event->args ) ) {
|
||||
return sprintf(
|
||||
'<em>%s</em>',
|
||||
esc_html__( 'None', 'wp-crontrol' )
|
||||
);
|
||||
} else {
|
||||
return sprintf(
|
||||
'<pre>%s</pre>',
|
||||
esc_html( \Crontrol\json_output( $event->args ) )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for the actions cell of a table row.
|
||||
*
|
||||
* @param stdClass $event The cron event for the current row.
|
||||
* @return string The cell output.
|
||||
*/
|
||||
protected function column_crontrol_actions( $event ) {
|
||||
$hook_callbacks = \Crontrol\get_hook_callbacks( $event->hook );
|
||||
|
||||
if ( 'crontrol_cron_job' === $event->hook ) {
|
||||
return '<em>' . esc_html__( 'WP Crontrol', 'wp-crontrol' ) . '</em>';
|
||||
} elseif ( ! empty( $hook_callbacks ) ) {
|
||||
$callbacks = array();
|
||||
|
||||
foreach ( $hook_callbacks as $callback ) {
|
||||
$callbacks[] = \Crontrol\output_callback( $callback );
|
||||
}
|
||||
|
||||
return implode( '<br>', $callbacks ); // WPCS:: XSS ok.
|
||||
} else {
|
||||
return sprintf(
|
||||
'<span class="status-crontrol-warning"><span class="dashicons dashicons-warning" aria-hidden="true"></span> %s</span>',
|
||||
esc_html__( 'None', 'wp-crontrol' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for the next run cell of a table row.
|
||||
*
|
||||
* @param stdClass $event The cron event for the current row.
|
||||
* @return string The cell output.
|
||||
*/
|
||||
protected function column_crontrol_next( $event ) {
|
||||
$date_local_format = 'Y-m-d H:i:s';
|
||||
$offset_site = get_date_from_gmt( 'now', 'P' );
|
||||
$offset_event = get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $event->timestamp ), 'P' );
|
||||
|
||||
if ( $offset_site !== $offset_event ) {
|
||||
$date_local_format .= ' P';
|
||||
}
|
||||
|
||||
$date_utc = gmdate( 'c', $event->timestamp );
|
||||
$date_local = get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $event->timestamp ), $date_local_format );
|
||||
|
||||
$time = sprintf(
|
||||
'<time datetime="%1$s">%2$s</time>',
|
||||
esc_attr( $date_utc ),
|
||||
esc_html( $date_local )
|
||||
);
|
||||
|
||||
$until = $event->timestamp - time();
|
||||
$late = is_late( $event );
|
||||
|
||||
if ( $late ) {
|
||||
// Show a warning for events that are late.
|
||||
$ago = sprintf(
|
||||
/* translators: %s: Time period, for example "8 minutes" */
|
||||
__( '%s ago', 'wp-crontrol' ),
|
||||
\Crontrol\interval( abs( $until ) )
|
||||
);
|
||||
return sprintf(
|
||||
'%s<br><span class="status-crontrol-warning"><span class="dashicons dashicons-warning" aria-hidden="true"></span> %s</span>',
|
||||
$time,
|
||||
esc_html( $ago )
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%s<br>%s',
|
||||
$time,
|
||||
esc_html( \Crontrol\interval( $until ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for the recurrence cell of a table row.
|
||||
*
|
||||
* @param stdClass $event The cron event for the current row.
|
||||
* @return string The cell output.
|
||||
*/
|
||||
protected function column_crontrol_recurrence( $event ) {
|
||||
if ( $event->schedule ) {
|
||||
$schedule_name = get_schedule_name( $event );
|
||||
if ( is_wp_error( $schedule_name ) ) {
|
||||
return sprintf(
|
||||
'<span class="status-crontrol-error"><span class="dashicons dashicons-warning" aria-hidden="true"></span> %s</span>',
|
||||
esc_html( $schedule_name->get_error_message() )
|
||||
);
|
||||
} elseif ( is_too_frequent( $event ) ) {
|
||||
return sprintf(
|
||||
'%1$s<span class="status-crontrol-warning"><br><span class="dashicons dashicons-warning" aria-hidden="true"></span> %2$s</span>',
|
||||
esc_html( $schedule_name ),
|
||||
sprintf(
|
||||
/* translators: 1: The name of the configuration constant, 2: The value of the configuration constant */
|
||||
esc_html__( 'This interval is less than the %1$s constant which is set to %2$s seconds. Events that use it may not run on time.', 'wp-crontrol' ),
|
||||
'<code>WP_CRON_LOCK_TIMEOUT</code>',
|
||||
intval( WP_CRON_LOCK_TIMEOUT )
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return esc_html( $schedule_name );
|
||||
}
|
||||
} else {
|
||||
return esc_html__( 'Non-repeating', 'wp-crontrol' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a message when there are no items to show in the table.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function no_items() {
|
||||
if ( empty( $_GET['s'] ) && empty( $_GET['crontrol_hooks_type'] ) ) {
|
||||
esc_html_e( 'There are currently no scheduled cron events.', 'wp-crontrol' );
|
||||
} else {
|
||||
esc_html_e( 'No matching cron events.', 'wp-crontrol' );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,551 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Functions related to cron events.
|
||||
*/
|
||||
|
||||
namespace Crontrol\Event;
|
||||
|
||||
use stdClass;
|
||||
use Crontrol\Schedule;
|
||||
use WP_Error;
|
||||
|
||||
use const Crontrol\PAUSED_OPTION;
|
||||
|
||||
/**
|
||||
* Executes a cron event immediately.
|
||||
*
|
||||
* Executes an event by scheduling a new single event with the same arguments.
|
||||
*
|
||||
* @param string $hookname The hook name of the cron event to run.
|
||||
* @param string $sig The cron event signature.
|
||||
* @return true|WP_Error True if the execution was successful, WP_Error if not.
|
||||
*/
|
||||
function run( $hookname, $sig ) {
|
||||
$crons = get_core_cron_array();
|
||||
|
||||
foreach ( $crons as $time => $cron ) {
|
||||
if ( isset( $cron[ $hookname ][ $sig ] ) ) {
|
||||
$event = $cron[ $hookname ][ $sig ];
|
||||
|
||||
$event['hook'] = $hookname;
|
||||
$event['timestamp'] = $time;
|
||||
|
||||
$event = (object) $event;
|
||||
|
||||
delete_transient( 'doing_cron' );
|
||||
$scheduled = force_schedule_single_event( $hookname, $event->args ); // UTC
|
||||
|
||||
if ( is_wp_error( $scheduled ) ) {
|
||||
return $scheduled;
|
||||
}
|
||||
|
||||
add_filter( 'cron_request', function( array $cron_request_array ) {
|
||||
$cron_request_array['url'] = add_query_arg( 'crontrol-single-event', 1, $cron_request_array['url'] );
|
||||
return $cron_request_array;
|
||||
} );
|
||||
|
||||
spawn_cron();
|
||||
|
||||
sleep( 1 );
|
||||
|
||||
/**
|
||||
* Fires after a cron event is scheduled to run manually.
|
||||
*
|
||||
* @param stdClass $event {
|
||||
* An object containing the event's data.
|
||||
*
|
||||
* @type string $hook Action hook to execute when the event is run.
|
||||
* @type int $timestamp Unix timestamp (UTC) for when to next run the event.
|
||||
* @type string|false $schedule How often the event should subsequently recur.
|
||||
* @type array $args Array containing each separate argument to pass to the hook's callback function.
|
||||
* @type int $interval The interval time in seconds for the schedule. Only present for recurring events.
|
||||
* }
|
||||
*/
|
||||
do_action( 'crontrol/ran_event', $event );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'not_found',
|
||||
sprintf(
|
||||
/* translators: 1: The name of the cron event. */
|
||||
__( 'The cron event %s could not be found.', 'wp-crontrol' ),
|
||||
$hookname
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcibly schedules a single event for the purpose of manually running it.
|
||||
*
|
||||
* This is used instead of `wp_schedule_single_event()` to avoid the duplicate check that's otherwise performed.
|
||||
*
|
||||
* @param string $hook Action hook to execute when the event is run.
|
||||
* @param mixed[] $args Optional. Array containing each separate argument to pass to the hook's callback function.
|
||||
* @return true|WP_Error True if event successfully scheduled. WP_Error on failure.
|
||||
*/
|
||||
function force_schedule_single_event( $hook, $args = array() ) {
|
||||
$event = (object) array(
|
||||
'hook' => $hook,
|
||||
'timestamp' => 1,
|
||||
'schedule' => false,
|
||||
'args' => $args,
|
||||
);
|
||||
$crons = get_core_cron_array();
|
||||
$key = md5( serialize( $event->args ) );
|
||||
|
||||
$crons[ $event->timestamp ][ $event->hook ][ $key ] = array(
|
||||
'schedule' => $event->schedule,
|
||||
'args' => $event->args,
|
||||
);
|
||||
ksort( $crons );
|
||||
|
||||
$result = _set_cron_array( $crons );
|
||||
|
||||
// Not using the WP_Error from `_set_cron_array()` here so we can provide a more specific error message.
|
||||
if ( false === $result ) {
|
||||
return new WP_Error(
|
||||
'could_not_add',
|
||||
sprintf(
|
||||
/* translators: 1: The name of the cron event. */
|
||||
__( 'Failed to schedule the cron event %s.', 'wp-crontrol' ),
|
||||
$hook
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new cron event.
|
||||
*
|
||||
* @param string $next_run_local The time that the event should be run at, in the site's timezone.
|
||||
* @param string $schedule The recurrence of the cron event.
|
||||
* @param string $hook The name of the hook to execute.
|
||||
* @param mixed[] $args Arguments to add to the cron event.
|
||||
* @return true|WP_error True if the addition was successful, WP_Error otherwise.
|
||||
*/
|
||||
function add( $next_run_local, $schedule, $hook, array $args ) {
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
$current_time = current_time( 'timestamp' );
|
||||
$next_run_local = strtotime( $next_run_local, $current_time );
|
||||
|
||||
if ( false === $next_run_local ) {
|
||||
return new WP_Error(
|
||||
'invalid_timestamp',
|
||||
__( 'Invalid timestamp provided.', 'wp-crontrol' )
|
||||
);
|
||||
}
|
||||
|
||||
$next_run_utc = (int) get_gmt_from_date( gmdate( 'Y-m-d H:i:s', $next_run_local ), 'U' );
|
||||
|
||||
if ( ! is_array( $args ) ) {
|
||||
$args = array();
|
||||
}
|
||||
|
||||
if ( 'crontrol_cron_job' === $hook && ! empty( $args['code'] ) && class_exists( '\ParseError' ) ) {
|
||||
try {
|
||||
// phpcs:ignore Squiz.PHP.Eval.Discouraged
|
||||
eval( sprintf(
|
||||
'return true; %s',
|
||||
$args['code']
|
||||
) );
|
||||
// phpcs:ignore PHPCompatibility.Classes.NewClasses.parseerrorFound
|
||||
} catch ( \ParseError $e ) {
|
||||
$args['syntax_error_message'] = $e->getMessage();
|
||||
$args['syntax_error_line'] = $e->getLine();
|
||||
}
|
||||
}
|
||||
|
||||
if ( '_oneoff' === $schedule || '' === $schedule ) {
|
||||
/**
|
||||
* @var bool|null|\WP_Error $result
|
||||
*/
|
||||
$result = wp_schedule_single_event( $next_run_utc, $hook, $args, true );
|
||||
} else {
|
||||
/**
|
||||
* @var bool|null|\WP_Error $result
|
||||
*/
|
||||
$result = wp_schedule_event( $next_run_utc, $schedule, $hook, $args, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible return values of `wp_schedule_*()` as called above:
|
||||
*
|
||||
* - 5.7+ Success: true, Failure: WP_Error
|
||||
* - 5.1+ Success: true, Failure: false
|
||||
* - <5.1 Success: null, Failure: false
|
||||
*/
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ( false === $result ) {
|
||||
return new WP_Error(
|
||||
'could_not_add',
|
||||
sprintf(
|
||||
/* translators: 1: The name of the cron event. */
|
||||
__( 'Failed to schedule the cron event %s.', 'wp-crontrol' ),
|
||||
$hook
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a cron event.
|
||||
*
|
||||
* @param string $hook The hook name of the event to delete.
|
||||
* @param string $sig The cron event signature.
|
||||
* @param string $next_run_utc The UTC time that the event would be run at.
|
||||
* @return true|WP_Error True if the deletion was successful, WP_Error otherwise.
|
||||
*/
|
||||
function delete( $hook, $sig, $next_run_utc ) {
|
||||
$event = get_single( $hook, $sig, $next_run_utc );
|
||||
|
||||
if ( is_wp_error( $event ) ) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var bool|null|\WP_Error $unscheduled
|
||||
*/
|
||||
$unscheduled = wp_unschedule_event( $event->timestamp, $event->hook, $event->args, true );
|
||||
|
||||
/**
|
||||
* Possible return values of `wp_unschedule_*()` as called above:
|
||||
*
|
||||
* - 5.7+ Success: true, Failure: WP_Error
|
||||
* - 5.1+ Success: true, Failure: false
|
||||
* - <5.1 Success: null, Failure: false
|
||||
*/
|
||||
|
||||
if ( is_wp_error( $unscheduled ) ) {
|
||||
return $unscheduled;
|
||||
}
|
||||
|
||||
if ( false === $unscheduled ) {
|
||||
return new WP_Error(
|
||||
'could_not_delete',
|
||||
sprintf(
|
||||
/* translators: 1: The name of the cron event. */
|
||||
__( 'Failed to the delete the cron event %s.', 'wp-crontrol' ),
|
||||
$hook
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses a cron event.
|
||||
*
|
||||
* @param string $hook The hook name of the event to pause.
|
||||
* @return true|WP_Error True if the pause was successful, WP_Error otherwise.
|
||||
*/
|
||||
function pause( $hook ) {
|
||||
$paused = get_option( PAUSED_OPTION, array() );
|
||||
|
||||
if ( ! is_array( $paused ) ) {
|
||||
$paused = array();
|
||||
}
|
||||
|
||||
$paused[ $hook ] = true;
|
||||
|
||||
$result = update_option( PAUSED_OPTION, $paused, true );
|
||||
|
||||
if ( false === $result ) {
|
||||
return new WP_Error(
|
||||
'could_not_pause',
|
||||
sprintf(
|
||||
/* translators: 1: The name of the cron event. */
|
||||
__( 'Failed to pause the cron event %s.', 'wp-crontrol' ),
|
||||
$hook
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes a paused cron event.
|
||||
*
|
||||
* @param string $hook The hook name of the event to resume.
|
||||
* @return true|WP_Error True if the resumption was successful, WP_Error otherwise.
|
||||
*/
|
||||
function resume( $hook ) {
|
||||
$paused = get_option( PAUSED_OPTION );
|
||||
|
||||
if ( ! is_array( $paused ) || ( count( $paused ) === 0 ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
unset( $paused[ $hook ] );
|
||||
|
||||
$result = update_option( PAUSED_OPTION, $paused, true );
|
||||
|
||||
if ( false === $result ) {
|
||||
return new WP_Error(
|
||||
'could_not_resume',
|
||||
sprintf(
|
||||
/* translators: 1: The name of the cron event. */
|
||||
__( 'Failed to resume the cron event %s.', 'wp-crontrol' ),
|
||||
$hook
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flattened array of cron events.
|
||||
*
|
||||
* @return array<string,stdClass> An array of cron event objects keyed by unique signature.
|
||||
*/
|
||||
function get() {
|
||||
$crons = get_core_cron_array();
|
||||
$events = array();
|
||||
|
||||
if ( empty( $crons ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
foreach ( $crons as $time => $cron ) {
|
||||
foreach ( $cron as $hook => $dings ) {
|
||||
foreach ( $dings as $sig => $data ) {
|
||||
|
||||
// This is a prime candidate for a Crontrol_Event class but I'm not bothering currently.
|
||||
$events[ "$hook-$sig-$time" ] = (object) array(
|
||||
'hook' => $hook,
|
||||
'timestamp' => $time, // UTC
|
||||
'sig' => $sig,
|
||||
'args' => $data['args'],
|
||||
'schedule' => $data['schedule'],
|
||||
'interval' => isset( $data['interval'] ) ? $data['interval'] : null,
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure events are always returned in date descending order.
|
||||
// External cron runners such as Cavalcade don't guarantee events are returned in order of time.
|
||||
uasort( $events, 'Crontrol\Event\uasort_order_events' );
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a single cron event.
|
||||
*
|
||||
* @param string $hook The hook name of the event.
|
||||
* @param string $sig The event signature.
|
||||
* @param string|int $next_run_utc The UTC time that the event would be run at.
|
||||
* @return stdClass|WP_Error A cron event object, or a WP_Error if it's not found.
|
||||
*/
|
||||
function get_single( $hook, $sig, $next_run_utc ) {
|
||||
$crons = get_core_cron_array();
|
||||
$next_run_utc = (int) $next_run_utc;
|
||||
|
||||
if ( isset( $crons[ $next_run_utc ][ $hook ][ $sig ] ) ) {
|
||||
$event = $crons[ $next_run_utc ][ $hook ][ $sig ];
|
||||
|
||||
$event['hook'] = $hook;
|
||||
$event['timestamp'] = $next_run_utc;
|
||||
|
||||
$event = (object) $event;
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'not_found',
|
||||
sprintf(
|
||||
/* translators: 1: The name of the cron event. */
|
||||
__( 'The cron event %s could not be found.', 'wp-crontrol' ),
|
||||
$hook
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the number of events for each hook.
|
||||
*
|
||||
* @return array<string,int> Array of number of events for each hook, keyed by the hook name.
|
||||
*/
|
||||
function count_by_hook() {
|
||||
$crons = get_core_cron_array();
|
||||
$events = array();
|
||||
|
||||
if ( empty( $crons ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
foreach ( $crons as $time => $cron ) {
|
||||
foreach ( $cron as $hook => $dings ) {
|
||||
if ( ! isset( $events[ $hook ] ) ) {
|
||||
$events[ $hook ] = 0;
|
||||
}
|
||||
|
||||
$events[ $hook ] += count( $dings );
|
||||
}
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the schedule display name for a given event.
|
||||
*
|
||||
* @param stdClass $event A WP-Cron event.
|
||||
* @return string|WP_Error The interval display name, or a WP_Error object if no such schedule exists.
|
||||
*/
|
||||
function get_schedule_name( stdClass $event ) {
|
||||
$schedules = Schedule\get();
|
||||
|
||||
if ( isset( $schedules[ $event->schedule ] ) ) {
|
||||
return isset( $schedules[ $event->schedule ]['display'] ) ? $schedules[ $event->schedule ]['display'] : $schedules[ $event->schedule ]['name'];
|
||||
}
|
||||
|
||||
return new WP_Error( 'unknown_schedule', sprintf(
|
||||
/* translators: %s: Schedule name */
|
||||
__( 'Unknown (%s)', 'wp-crontrol' ),
|
||||
$event->schedule
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the schedule for an event means it runs too frequently to be reliable.
|
||||
*
|
||||
* @param stdClass $event A WP-Cron event.
|
||||
* @return bool Whether the event scheduled is too frequent.
|
||||
*/
|
||||
function is_too_frequent( stdClass $event ) {
|
||||
$schedules = Schedule\get();
|
||||
|
||||
if ( ! isset( $schedules[ $event->schedule ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $schedules[ $event->schedule ]['is_too_frequent'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an event is late.
|
||||
*
|
||||
* An event which has missed its schedule by more than 10 minutes is considered late.
|
||||
*
|
||||
* @param stdClass $event The event.
|
||||
* @return bool Whether the event is late.
|
||||
*/
|
||||
function is_late( stdClass $event ) {
|
||||
$until = $event->timestamp - time();
|
||||
|
||||
return ( $until < ( 0 - ( 10 * MINUTE_IN_SECONDS ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an event is paused.
|
||||
*
|
||||
* @param stdClass $event The event.
|
||||
* @return bool Whether the event is paused.
|
||||
*/
|
||||
function is_paused( stdClass $event ) {
|
||||
$paused = get_option( PAUSED_OPTION );
|
||||
|
||||
if ( ! is_array( $paused ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_key_exists( $event->hook, $paused );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises and returns the list table for events.
|
||||
*
|
||||
* @return Table The list table.
|
||||
*/
|
||||
function get_list_table() {
|
||||
static $table = null;
|
||||
|
||||
if ( ! $table ) {
|
||||
$table = new Table();
|
||||
$table->prepare_items();
|
||||
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Order events function.
|
||||
*
|
||||
* The comparison function returns an integer less than, equal to, or greater than zero if the first argument is
|
||||
* considered to be respectively less than, equal to, or greater than the second.
|
||||
*
|
||||
* @param stdClass $a The first event to compare.
|
||||
* @param stdClass $b The second event to compare.
|
||||
* @return int
|
||||
*/
|
||||
function uasort_order_events( $a, $b ) {
|
||||
$orderby = ( ! empty( $_GET['orderby'] ) ) ? sanitize_text_field( $_GET['orderby'] ) : 'crontrol_next';
|
||||
$order = ( ! empty( $_GET['order'] ) ) ? sanitize_text_field( $_GET['order'] ) : 'asc';
|
||||
$compare = 0;
|
||||
|
||||
switch ( $orderby ) {
|
||||
case 'crontrol_hook':
|
||||
if ( 'asc' === $order ) {
|
||||
$compare = strcmp( $a->hook, $b->hook );
|
||||
} else {
|
||||
$compare = strcmp( $b->hook, $a->hook );
|
||||
}
|
||||
break;
|
||||
case 'crontrol_recurrence':
|
||||
if ( 'asc' === $order ) {
|
||||
$compare = ( $a->interval ?? 0 ) <=> ( $b->interval ?? 0 );
|
||||
} else {
|
||||
$compare = ( $b->interval ?? 0 ) <=> ( $a->interval ?? 0 );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ( 'asc' === $order ) {
|
||||
$compare = $a->timestamp <=> $b->timestamp;
|
||||
} else {
|
||||
$compare = $b->timestamp <=> $a->timestamp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $compare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the list of cron events from WordPress core.
|
||||
*
|
||||
* @return array<int,array<string,array<string,array<string,mixed[]>>>>
|
||||
* @phpstan-return array<int,array<string,array<string,array<string,array{
|
||||
* args: mixed[],
|
||||
* schedule: string|false,
|
||||
* interval?: int,
|
||||
* }>>>>
|
||||
*/
|
||||
function get_core_cron_array() {
|
||||
$crons = _get_cron_array();
|
||||
|
||||
if ( empty( $crons ) ) {
|
||||
$crons = array();
|
||||
}
|
||||
|
||||
return $crons;
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Request handler.
|
||||
*/
|
||||
|
||||
namespace Crontrol;
|
||||
|
||||
/**
|
||||
* Class Request
|
||||
*/
|
||||
class Request {
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $args = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $next_run_date_local = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $next_run_date_local_custom_date = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $next_run_date_local_custom_time = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $schedule = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $hookname = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $hookcode = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $eventname = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $original_hookname = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $original_sig = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $original_next_run_utc = '';
|
||||
|
||||
/**
|
||||
* Crontrol_Request constructor.
|
||||
*
|
||||
* @param array<string,mixed> $props Properties.
|
||||
* @return Request This object.
|
||||
*/
|
||||
public function init( array $props ) {
|
||||
foreach ( $props as $name => $value ) {
|
||||
$prop = (string) preg_replace( '#^crontrol_#', '', $name );
|
||||
|
||||
if ( property_exists( $this, $prop ) ) {
|
||||
$this->$prop = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* List table for cron schedules.
|
||||
*/
|
||||
|
||||
namespace Crontrol;
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||
|
||||
/**
|
||||
* Cron schedule list table class.
|
||||
*/
|
||||
class Schedule_List_Table extends \WP_List_Table {
|
||||
|
||||
/**
|
||||
* Array of cron event schedules that are added by WordPress core.
|
||||
*
|
||||
* @var array<int,string> Array of schedule names.
|
||||
*/
|
||||
protected static $core_schedules;
|
||||
|
||||
/**
|
||||
* Array of cron event schedule names that are in use by events.
|
||||
*
|
||||
* @var array<int,string> Array of schedule names.
|
||||
*/
|
||||
protected static $used_schedules;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct( array(
|
||||
'singular' => 'crontrol-schedule',
|
||||
'plural' => 'crontrol-schedules',
|
||||
'ajax' => false,
|
||||
'screen' => 'crontrol-schedules',
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the primary column.
|
||||
*
|
||||
* @return string The name of the primary column.
|
||||
*/
|
||||
protected function get_primary_column_name() {
|
||||
return 'crontrol_name';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the list table items and arguments.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function prepare_items() {
|
||||
$schedules = Schedule\get();
|
||||
$count = count( $schedules );
|
||||
|
||||
self::$core_schedules = get_core_schedules();
|
||||
self::$used_schedules = array_unique( wp_list_pluck( Event\get(), 'schedule' ) );
|
||||
|
||||
$this->items = $schedules;
|
||||
|
||||
$this->set_pagination_args( array(
|
||||
'total_items' => $count,
|
||||
'per_page' => -1,
|
||||
'total_pages' => 1,
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of column names for the table.
|
||||
*
|
||||
* @return array<string,string> Array of column names keyed by their ID.
|
||||
*/
|
||||
public function get_columns() {
|
||||
return array(
|
||||
'crontrol_icon' => '',
|
||||
'crontrol_name' => __( 'Internal Name', 'wp-crontrol' ),
|
||||
'crontrol_interval' => __( 'Interval', 'wp-crontrol' ),
|
||||
'crontrol_display' => __( 'Display Name', 'wp-crontrol' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of CSS class names for the table.
|
||||
*
|
||||
* @return array<int,string> Array of class names.
|
||||
*/
|
||||
protected function get_table_classes() {
|
||||
return array( 'widefat', 'fixed', 'striped', 'table-view-list', $this->_args['plural'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and displays row action links for the table.
|
||||
*
|
||||
* @phpstan-param array{
|
||||
* interval: int,
|
||||
* display?: string,
|
||||
* name: string,
|
||||
* is_too_frequent: bool,
|
||||
* } $schedule
|
||||
* @param mixed[] $schedule The schedule for the current row.
|
||||
* @param string $column_name Current column name.
|
||||
* @param string $primary Primary column name.
|
||||
* @return string The row actions HTML.
|
||||
*/
|
||||
protected function handle_row_actions( $schedule, $column_name, $primary ) {
|
||||
if ( $primary !== $column_name ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$links = array();
|
||||
/** @var array<string,int|string> */
|
||||
$new_scheds = get_option( 'crontrol_schedules', array() );
|
||||
|
||||
if ( in_array( $schedule['name'], self::$core_schedules, true ) ) {
|
||||
$links[] = "<span class='crontrol-in-use'>" . esc_html__( 'This is a WordPress core schedule and cannot be deleted', 'wp-crontrol' ) . '</span>';
|
||||
} elseif ( ! isset( $new_scheds[ $schedule['name'] ] ) ) {
|
||||
$links[] = "<span class='crontrol-in-use'>" . esc_html__( 'This schedule is added by another plugin and cannot be deleted', 'wp-crontrol' ) . '</span>';
|
||||
} elseif ( in_array( $schedule['name'], self::$used_schedules, true ) ) {
|
||||
$links[] = "<span class='crontrol-in-use'>" . esc_html__( 'This custom schedule is in use and cannot be deleted', 'wp-crontrol' ) . '</span>';
|
||||
} else {
|
||||
$link = add_query_arg( array(
|
||||
'page' => 'crontrol_admin_options_page',
|
||||
'crontrol_action' => 'delete-schedule',
|
||||
'crontrol_id' => rawurlencode( $schedule['name'] ),
|
||||
), admin_url( 'options-general.php' ) );
|
||||
$link = wp_nonce_url( $link, 'crontrol-delete-schedule_' . $schedule['name'] );
|
||||
|
||||
$links[] = "<span class='delete'><a href='" . esc_url( $link ) . "'>" . esc_html__( 'Delete', 'wp-crontrol' ) . '</a></span>';
|
||||
}
|
||||
|
||||
return $this->row_actions( $links );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for the icon cell of a table row.
|
||||
*
|
||||
* @phpstan-param array{
|
||||
* interval: int,
|
||||
* display?: string,
|
||||
* name: string,
|
||||
* is_too_frequent: bool,
|
||||
* } $schedule
|
||||
* @param mixed[] $schedule The schedule for the current row.
|
||||
* @return string The cell output.
|
||||
*/
|
||||
protected function column_crontrol_icon( array $schedule ) {
|
||||
if ( in_array( $schedule['name'], self::$core_schedules, true ) ) {
|
||||
return sprintf(
|
||||
'<span class="dashicons dashicons-wordpress" aria-hidden="true"></span>
|
||||
<span class="screen-reader-text">%s</span>',
|
||||
esc_html__( 'This is a WordPress core schedule and cannot be deleted', 'wp-crontrol' )
|
||||
);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for the schdule name cell of a table row.
|
||||
*
|
||||
* @phpstan-param array{
|
||||
* interval: int,
|
||||
* display?: string,
|
||||
* name: string,
|
||||
* is_too_frequent: bool,
|
||||
* } $schedule
|
||||
* @param mixed[] $schedule The schedule for the current row.
|
||||
* @return string The cell output.
|
||||
*/
|
||||
protected function column_crontrol_name( array $schedule ) {
|
||||
return esc_html( $schedule['name'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for the interval cell of a table row.
|
||||
*
|
||||
* @phpstan-param array{
|
||||
* interval: int,
|
||||
* display?: string,
|
||||
* name: string,
|
||||
* is_too_frequent: bool,
|
||||
* } $schedule
|
||||
* @param mixed[] $schedule The schedule for the current row.
|
||||
* @return string The cell output.
|
||||
*/
|
||||
protected function column_crontrol_interval( array $schedule ) {
|
||||
$interval = sprintf(
|
||||
'%s (%s)',
|
||||
esc_html( "{$schedule['interval']}" ),
|
||||
esc_html( interval( $schedule['interval'] ) )
|
||||
);
|
||||
|
||||
if ( $schedule['is_too_frequent'] ) {
|
||||
$interval .= sprintf(
|
||||
'<span class="status-crontrol-warning"><br><span class="dashicons dashicons-warning" aria-hidden="true"></span> %s</span>',
|
||||
sprintf(
|
||||
/* translators: 1: The name of the configuration constant, 2: The value of the configuration constant */
|
||||
esc_html__( 'This interval is less than the %1$s constant which is set to %2$s seconds. Events that use it may not run on time.', 'wp-crontrol' ),
|
||||
'<code>WP_CRON_LOCK_TIMEOUT</code>',
|
||||
intval( WP_CRON_LOCK_TIMEOUT )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output for the display name cell of a table row.
|
||||
*
|
||||
* @param mixed[] $schedule The schedule for the current row.
|
||||
*
|
||||
* @phpstan-param array{
|
||||
* interval: int,
|
||||
* display?: string,
|
||||
* name: string,
|
||||
* is_too_frequent: bool,
|
||||
* } $schedule
|
||||
* @return string The cell output.
|
||||
*/
|
||||
protected function column_crontrol_display( array $schedule ) {
|
||||
return esc_html( isset( $schedule['display'] ) ? $schedule['display'] : $schedule['name'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a message when there are no items to show in the table.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function no_items() {
|
||||
esc_html_e( 'There are no schedules.', 'wp-crontrol' );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Functions related to schedules.
|
||||
*/
|
||||
|
||||
namespace Crontrol\Schedule;
|
||||
|
||||
/**
|
||||
* Adds a new custom cron schedule.
|
||||
*
|
||||
* @param string $name The internal name of the schedule.
|
||||
* @param int $interval The interval between executions of the new schedule.
|
||||
* @param string $display The display name of the schedule.
|
||||
* @return void
|
||||
*/
|
||||
function add( $name, $interval, $display ) {
|
||||
/** @var array<string,int|string> */
|
||||
$old_scheds = get_option( 'crontrol_schedules', array() );
|
||||
|
||||
$old_scheds[ $name ] = array(
|
||||
'interval' => $interval,
|
||||
'display' => $display,
|
||||
);
|
||||
update_option( 'crontrol_schedules', $old_scheds );
|
||||
|
||||
/**
|
||||
* Fires after a new cron schedule is added.
|
||||
*
|
||||
* @param string $name The internal name of the schedule.
|
||||
* @param int $interval The interval between executions of the new schedule.
|
||||
* @param string $display The display name of the schedule.
|
||||
*/
|
||||
do_action( 'crontrol/added_new_schedule', $name, $interval, $display );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a custom cron schedule.
|
||||
*
|
||||
* @param string $name The internal name of the schedule to delete.
|
||||
* @return void
|
||||
*/
|
||||
function delete( $name ) {
|
||||
/** @var array<string,int|string> */
|
||||
$scheds = get_option( 'crontrol_schedules', array() );
|
||||
unset( $scheds[ $name ] );
|
||||
update_option( 'crontrol_schedules', $scheds );
|
||||
|
||||
/**
|
||||
* Fires after a cron schedule is deleted.
|
||||
*
|
||||
* @param string $name The internal name of the schedule.
|
||||
*/
|
||||
do_action( 'crontrol/deleted_schedule', $name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a sorted (according to interval) list of the cron schedules
|
||||
*
|
||||
* @return array<string,array<string,(int|string)>> Array of cron schedule arrays.
|
||||
* @phpstan-return array<string,array{
|
||||
* interval: int,
|
||||
* display?: string,
|
||||
* name: string,
|
||||
* is_too_frequent: bool,
|
||||
* }>
|
||||
*/
|
||||
function get() {
|
||||
/**
|
||||
* @phpstan-var array<string,array{
|
||||
* interval: int,
|
||||
* display?: string,
|
||||
* }> $schedules
|
||||
*/
|
||||
$schedules = wp_get_schedules();
|
||||
uasort( $schedules, function( array $a, array $b ) {
|
||||
return ( $a['interval'] - $b['interval'] );
|
||||
} );
|
||||
|
||||
array_walk( $schedules, function( array &$schedule, $name ) {
|
||||
$schedule['name'] = $name;
|
||||
$schedule['is_too_frequent'] = ( $schedule['interval'] < WP_CRON_LOCK_TIMEOUT );
|
||||
} );
|
||||
|
||||
/**
|
||||
* @phpstan-var array<string,array{
|
||||
* interval: int,
|
||||
* display?: string,
|
||||
* name: string,
|
||||
* is_too_frequent: bool,
|
||||
* }> $schedules
|
||||
*/
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a dropdown filled with the possible schedules, including non-repeating.
|
||||
*
|
||||
* @param string|false $current The currently selected schedule, or false for none.
|
||||
* @return void
|
||||
*/
|
||||
function dropdown( $current = false ) {
|
||||
$schedules = get();
|
||||
?>
|
||||
<select class="postform" name="crontrol_schedule" id="crontrol_schedule" required>
|
||||
<option <?php selected( $current, '_oneoff' ); ?> value="_oneoff"><?php esc_html_e( 'Non-repeating', 'wp-crontrol' ); ?></option>
|
||||
<?php foreach ( $schedules as $sched_name => $sched_data ) { ?>
|
||||
<option <?php selected( $current, $sched_name ); ?> value="<?php echo esc_attr( $sched_name ); ?>">
|
||||
<?php
|
||||
printf(
|
||||
'%s (%s)',
|
||||
esc_html( isset( $sched_data['display'] ) ? $sched_data['display'] : $sched_data['name'] ),
|
||||
esc_html( $sched_name )
|
||||
);
|
||||
?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<?php
|
||||
}
|
||||
Reference in New Issue
Block a user