'crontrol-event',
'plural' => 'crontrol-events',
'ajax' => false,
'screen' => 'crontrol-events',
) );
}
/**
* Prepares the list table items and arguments.
*/
public function prepare_items() {
self::$persistent_core_hooks = \Crontrol\get_persistent_core_hooks();
self::$can_edit_files = 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['hooks_type'] ) ) {
$hooks_type = sanitize_text_field( $_GET['hooks_type'] );
$filtered = $this->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(
'
',
/* 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' => ceil( $count / $per_page ),
) );
}
/**
* Returns events filtered by various parameters
*
* @param array $events The list of all events.
* @return array Array of filtered events keyed by parameter.
*/
protected 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 ) );
} );
return $filtered;
}
/**
* Returns an array of column names for the table.
*
* @return string[] Array of column names keyed by their ID.
*/
public function get_columns() {
return array(
'cb' => '',
'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
*/
public function get_sortable_columns() {
return array(
'crontrol_hook' => array( 'crontrol_hook', true ),
'crontrol_next' => array( 'crontrol_next', false ),
);
}
/**
* Returns an array of CSS class names for the table.
*
* @return string[] Array of class names.
*/
protected function get_table_classes() {
return array( 'widefat', 'striped', $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
*/
protected function get_bulk_actions() {
return array(
'delete_crons' => esc_html__( 'Delete', 'wp-crontrol' ),
);
}
/**
* Display the list of hook types.
*
* @return string[]
*/
public function get_views() {
$filtered = $this->get_filtered_events( $this->all_events );
$views = array();
$hooks_type = ( ! empty( $_GET['hooks_type'] ) ? $_GET['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' ),
);
$url = admin_url( 'tools.php?page=crontrol_admin_manage_page' );
foreach ( $types as $key => $type ) {
$views[ $key ] = sprintf(
'%3$s (%4$s)',
'all' === $key ? $url : add_query_arg( 'hooks_type', $key, $url ),
$hooks_type === $key ? ' class="current"' : '',
$type,
count( $filtered[ $key ] )
);
}
return $views;
}
/**
* Generates content for a single row of the table.
*
* @param object $event The current event.
*/
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-warning';
} 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';
}
printf(
'',
esc_attr( implode( ' ', $classes ) )
);
$this->single_row_columns( $event );
echo '
';
}
/**
* 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_edit_files ) {
$link = array(
'page' => 'crontrol_admin_manage_page',
'action' => 'edit-cron',
'id' => rawurlencode( $event->hook ),
'sig' => rawurlencode( $event->sig ),
'next_run_utc' => rawurlencode( $event->time ),
);
$link = add_query_arg( $link, admin_url( 'tools.php' ) );
$links[] = "" . esc_html__( 'Edit', 'wp-crontrol' ) . '';
}
$link = array(
'page' => 'crontrol_admin_manage_page',
'action' => 'run-cron',
'id' => rawurlencode( $event->hook ),
'sig' => rawurlencode( $event->sig ),
'next_run_utc' => rawurlencode( $event->time ),
);
$link = add_query_arg( $link, admin_url( 'tools.php' ) );
$link = wp_nonce_url( $link, "run-cron_{$event->hook}_{$event->sig}" );
$links[] = "" . esc_html__( 'Run Now', 'wp-crontrol' ) . '';
if ( ! in_array( $event->hook, self::$persistent_core_hooks, true ) && ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_edit_files ) ) {
$link = array(
'page' => 'crontrol_admin_manage_page',
'action' => 'delete-cron',
'id' => rawurlencode( $event->hook ),
'sig' => rawurlencode( $event->sig ),
'next_run_utc' => rawurlencode( $event->time ),
);
$link = add_query_arg( $link, admin_url( 'tools.php' ) );
$link = wp_nonce_url( $link, "delete-cron_{$event->hook}_{$event->sig}_{$event->time}" );
$links[] = "" . esc_html__( 'Delete', 'wp-crontrol' ) . '';
}
if ( function_exists( 'wp_unschedule_hook' ) && ! in_array( $event->hook, self::$persistent_core_hooks, true ) && ( 'crontrol_cron_job' !== $event->hook ) ) {
if ( self::$count_by_hook[ $event->hook ] > 1 ) {
$link = array(
'page' => 'crontrol_admin_manage_page',
'action' => 'delete-hook',
'id' => rawurlencode( $event->hook ),
);
$link = add_query_arg( $link, admin_url( 'tools.php' ) );
$link = wp_nonce_url( $link, "delete-hook_{$event->hook}" );
$text = sprintf(
/* translators: %s: Number of events with a given name */
_n( 'Delete All %s', 'Delete All %s', self::$count_by_hook[ $event->hook ], 'wp-crontrol' ),
number_format_i18n( self::$count_by_hook[ $event->hook ] )
);
$links[] = "" . 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(
'wp-crontrol-delete-%1$s-%2$s-%3$s',
$event->time,
rawurlencode( $event->hook ),
$event->sig
);
if ( in_array( $event->hook, self::$persistent_core_hooks, true ) ) {
return sprintf(
'
%s',
esc_html__( 'This is a WordPress core event and cannot be deleted', 'wp-crontrol' )
);
} elseif ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_edit_files ) {
return sprintf(
'
',
esc_attr( $id ),
esc_html__( 'Select this row', 'wp-crontrol' ),
esc_attr( $event->time ),
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 '' . esc_html( sprintf( __( 'PHP Cron (%s)', 'wp-crontrol' ), $event->args['name'] ) ) . '';
} else {
return '' . esc_html__( 'PHP Cron', 'wp-crontrol' ) . '';
}
}
return esc_html( $event->hook );
}
/**
* 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 ( ! empty( $event->args ) ) {
$args = \Crontrol\json_output( $event->args );
}
if ( 'crontrol_cron_job' === $event->hook ) {
$return = '' . esc_html__( 'PHP Code', 'wp-crontrol' ) . '';
if ( ! empty( $event->args['syntax_error_message'] ) ) {
$return .= '
';
$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 .= '';
}
if ( ! empty( $event->args['code'] ) ) {
$lines = explode( "\n", trim( $event->args['code'] ) );
$code = reset( $lines );
$code = substr( $code, 0, 50 );
$return .= '
';
$return .= sprintf(
'%s
…',
esc_html( $code )
);
}
return $return;
} else {
if ( empty( $event->args ) ) {
return sprintf(
'%s',
esc_html__( 'None', 'wp-crontrol' )
);
} else {
return sprintf(
'%s
',
esc_html( $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 '' . esc_html__( 'WP Crontrol', 'wp-crontrol' ) . '';
} elseif ( ! empty( $hook_callbacks ) ) {
$callbacks = array();
foreach ( $hook_callbacks as $callback ) {
$callbacks[] = \Crontrol\output_callback( $callback );
}
return implode( '
', $callbacks ); // WPCS:: XSS ok.
} else {
return sprintf(
' %s',
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_utc = gmdate( 'Y-m-d\TH:i:s+00:00', $event->time );
$date_local = get_date_from_gmt( date( 'Y-m-d H:i:s', $event->time ), 'Y-m-d H:i:s' );
$time = sprintf(
'',
esc_attr( $date_utc ),
esc_html( $date_local )
);
$until = $event->time - 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
%s',
$time,
esc_html( $ago )
);
}
return sprintf(
'%s
%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(
' %s',
esc_html( $schedule_name->get_error_message() )
);
} elseif ( is_too_frequent( $event ) ) {
return sprintf(
'%1$s
%2$s',
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' ),
'WP_CRON_LOCK_TIMEOUT
',
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.
*/
public function no_items() {
if ( empty( $_GET['s'] ) && empty( $_GET['hooks_type'] ) ) {
esc_html_e( 'There are currently no scheduled cron events.', 'wp-crontrol' );
} else {
esc_html_e( 'No matching cron events.', 'wp-crontrol' );
}
}
}