WP Crontrol - Version 1.12.0

Version Description

Download this release

Release Info

Developer johnbillion
Plugin Icon 128x128 WP Crontrol
Version 1.12.0
Comparing to
See all releases

Code changes from version 1.11.0 to 1.12.0

.editorconfig DELETED
@@ -1,19 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- charset = utf-8
5
- end_of_line = lf
6
- insert_final_newline = true
7
- trim_trailing_whitespace = true
8
- indent_style = tab
9
-
10
- [*.yml]
11
- indent_style = space
12
- indent_size = 2
13
-
14
- [{*.neon,*.neon.dist}]
15
- indent_style = space
16
- indent_size = 4
17
-
18
- [*.md]
19
- trim_trailing_whitespace = false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
SECURITY.md DELETED
@@ -1,5 +0,0 @@
1
- # Security Policy
2
-
3
- ## Reporting a Vulnerability
4
-
5
- If you discover a security issue in WP Crontrol, please report it to [the security program on HackerOne](https://hackerone.com/johnblackbourn). Do not report security issues on GitHub or the WordPress.org support forums. Thank you.
 
 
 
 
 
bin/test.sh ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ # -e Exit immediately if a pipeline returns a non-zero status
4
+ # -o pipefail Produce a failure return code if any command errors
5
+ set -eo pipefail
6
+
7
+ # Shorthand:
8
+ WP_PORT=`docker port wp-crontrol-wordpress | grep "[0-9]+$" -ohE | head -1`
9
+ CHROME_PORT=`docker port wp-crontrol-chrome | grep "[0-9]+$" -ohE | head -1`
10
+ WP_URL="http://host.docker.internal:${WP_PORT}"
11
+ WP="docker-compose run --rm wpcli wp --url=${WP_URL}"
12
+
13
+ # Reset or install the test database:
14
+ $WP db reset --yes
15
+
16
+ # Install WordPress:
17
+ $WP core install --title="Example" --admin_user="admin" --admin_password="admin" --admin_email="admin@example.com"
18
+
19
+ # Run the functional tests:
20
+ TEST_SITE_WEBDRIVER_PORT=$CHROME_PORT \
21
+ TEST_SITE_WP_URL=$WP_URL \
22
+ ./vendor/bin/codecept run acceptance --steps "$1"
codeception.dist.yml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ paths:
2
+ tests: tests
3
+ output: tests/_output
4
+ data: tests/_data
5
+ support: tests/_support
6
+ envs: tests/_envs
7
+ actor_suffix: Tester
8
+ extensions:
9
+ enabled:
10
+ - Codeception\Extension\RunFailed
11
+ params:
12
+ - env
css/wp-crontrol.css CHANGED
@@ -30,7 +30,7 @@ table.wp-list-table {
30
  padding: 0;
31
  }
32
 
33
- .wp-list-table td.column-status {
34
  white-space: nowrap;
35
  }
36
 
@@ -65,16 +65,16 @@ table.wp-list-table {
65
  }
66
 
67
  .status-crontrol-complete,
68
- .wp-list-table tr.crontrol-complete td.column-status {
69
  color: #080;
70
  }
71
 
72
  .status-crontrol-no-action,
73
  .status-crontrol-stalled,
74
  .status-crontrol-warning,
75
- .wp-list-table tr.crontrol-no-action td.column-status,
76
- .wp-list-table tr.crontrol-stalled td.column-status,
77
- .wp-list-table tr.crontrol-warning td.column-status {
78
  color: #c60;
79
  }
80
 
@@ -82,23 +82,23 @@ table.wp-list-table {
82
  .status-crontrol-alert,
83
  .status-crontrol-critical,
84
  .status-crontrol-error,
85
- .wp-list-table tr.crontrol-error td.column-status {
86
  color: #a00;
87
  }
88
 
89
- .wp-list-table .column-https,
90
- .wp-list-table .column-queries,
91
- .wp-list-table .column-time {
92
  text-align: end;
93
  }
94
 
95
- .column-time,
96
- .column-ran,
97
  .column-crontrol_next {
98
  white-space: nowrap;
99
  }
100
 
101
- .row-actions .in-use {
102
  color: #666;
103
  }
104
 
30
  padding: 0;
31
  }
32
 
33
+ .wp-list-table td.column-crontrol_status {
34
  white-space: nowrap;
35
  }
36
 
65
  }
66
 
67
  .status-crontrol-complete,
68
+ .wp-list-table tr.crontrol-complete td.column-crontrol_status {
69
  color: #080;
70
  }
71
 
72
  .status-crontrol-no-action,
73
  .status-crontrol-stalled,
74
  .status-crontrol-warning,
75
+ .wp-list-table tr.crontrol-no-action td.column-crontrol_status,
76
+ .wp-list-table tr.crontrol-stalled td.column-crontrol_status,
77
+ .wp-list-table tr.crontrol-warning td.column-crontrol_status {
78
  color: #c60;
79
  }
80
 
82
  .status-crontrol-alert,
83
  .status-crontrol-critical,
84
  .status-crontrol-error,
85
+ .wp-list-table tr.crontrol-error td.column-crontrol_status {
86
  color: #a00;
87
  }
88
 
89
+ .wp-list-table .column-crontrol_https,
90
+ .wp-list-table .column-crontrol_queries,
91
+ .wp-list-table .column-crontrol_time {
92
  text-align: end;
93
  }
94
 
95
+ .column-crontrol_time,
96
+ .column-crontrol_ran,
97
  .column-crontrol_next {
98
  white-space: nowrap;
99
  }
100
 
101
+ .row-actions .crontrol-in-use {
102
  color: #666;
103
  }
104
 
js/wp-crontrol.js CHANGED
@@ -34,24 +34,24 @@ function crontrolCheckHash() {
34
  }
35
 
36
  jQuery(function($){
37
- $('#next_run_date_local_custom_date,#next_run_date_local_custom_time').on('change', function() {
38
- $('#next_run_date_local_custom').prop('checked',true);
39
  });
40
 
41
  if ( $('input[value="new_php_cron"]').length ) {
42
  $('input[value="new_cron"]').on('click',function(){
43
  $('.crontrol-edit-event').removeClass('crontrol-edit-event-php').addClass('crontrol-edit-event-standard');
44
- $('#hookname').attr('required',true);
45
  });
46
  $('input[value="new_php_cron"]').on('click',function(){
47
  $('.crontrol-edit-event').removeClass('crontrol-edit-event-standard').addClass('crontrol-edit-event-php');
48
- $('#hookname').attr('required',false);
49
- if ( ! $('#hookcode').hasClass('crontrol-editor-initialized') ) {
50
- wp.codeEditor.initialize( 'hookcode', window.wpCrontrol.codeEditor );
51
  }
52
- $('#hookcode').addClass('crontrol-editor-initialized');
53
  });
54
- } else if ( $('#hookcode').length ) {
55
- wp.codeEditor.initialize( 'hookcode', window.wpCrontrol.codeEditor );
56
  }
57
  });
34
  }
35
 
36
  jQuery(function($){
37
+ $('#crontrol_next_run_date_local_custom_date,#crontrol_next_run_date_local_custom_time').on('change', function() {
38
+ $('#crontrol_next_run_date_local_custom').prop('checked',true);
39
  });
40
 
41
  if ( $('input[value="new_php_cron"]').length ) {
42
  $('input[value="new_cron"]').on('click',function(){
43
  $('.crontrol-edit-event').removeClass('crontrol-edit-event-php').addClass('crontrol-edit-event-standard');
44
+ $('#crontrol_hookname').attr('required',true);
45
  });
46
  $('input[value="new_php_cron"]').on('click',function(){
47
  $('.crontrol-edit-event').removeClass('crontrol-edit-event-standard').addClass('crontrol-edit-event-php');
48
+ $('#crontrol_hookname').attr('required',false);
49
+ if ( ! $('#crontrol_hookcode').hasClass('crontrol-editor-initialized') ) {
50
+ wp.codeEditor.initialize( 'crontrol_hookcode', window.wpCrontrol.codeEditor );
51
  }
52
+ $('#crontrol_hookcode').addClass('crontrol-editor-initialized');
53
  });
54
+ } else if ( $('#crontrol_hookcode').length ) {
55
+ wp.codeEditor.initialize( 'crontrol_hookcode', window.wpCrontrol.codeEditor );
56
  }
57
  });
phpstan.neon.dist DELETED
@@ -1,15 +0,0 @@
1
- includes:
2
- - vendor/szepeviktor/phpstan-wordpress/extension.neon
3
- parameters:
4
- level: 8
5
- inferPrivatePropertyTypeFromConstructor: true
6
- paths:
7
- - wp-crontrol.php
8
- - src/
9
- bootstrapFiles:
10
- - tests/stubs.php
11
- ignoreErrors:
12
- # Uses func_get_args()
13
- - '#^Function apply_filters invoked with [34567] parameters, 2 required\.$#'
14
- # External plugin
15
- - '#QM_Util#'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.md CHANGED
@@ -3,8 +3,8 @@
3
  Contributors: johnbillion, scompt
4
  Tags: cron, wp-cron, crontrol, debug
5
  Requires at least: 4.2
6
- Tested up to: 5.8
7
- Stable tag: 1.11.0
8
  Requires PHP: 5.3
9
  Donate link: https://github.com/sponsors/johnbillion
10
 
@@ -157,6 +157,11 @@ The photo was taken by <a href="https://www.flickr.com/photos/michaelpardo/21453
157
 
158
  ## Changelog ##
159
 
 
 
 
 
 
160
  ### 1.11.0 ###
161
 
162
  * Introduced an `Export` feature to the event listing screen for exporting the list of events as a CSV file.
@@ -276,12 +281,6 @@ The photo was taken by <a href="https://www.flickr.com/photos/michaelpardo/21453
276
  - Terminology and i18n improvements.
277
 
278
 
279
- ### 1.3.1 ###
280
-
281
- - Display a less scary looking message when `DISABLE_WP_CRON` is defined.
282
- - Correct the example code for cron event arguments.
283
-
284
-
285
  ### 1.3 ###
286
 
287
  - Improvements to the UI.
3
  Contributors: johnbillion, scompt
4
  Tags: cron, wp-cron, crontrol, debug
5
  Requires at least: 4.2
6
+ Tested up to: 5.9
7
+ Stable tag: 1.12.0
8
  Requires PHP: 5.3
9
  Donate link: https://github.com/sponsors/johnbillion
10
 
157
 
158
  ## Changelog ##
159
 
160
+ ### 1.12.0 ###
161
+
162
+ * Fix the PHP cron event management.
163
+ * More "namespacing" of query variables to avoid conflicts with other cron management plugins.
164
+
165
  ### 1.11.0 ###
166
 
167
  * Introduced an `Export` feature to the event listing screen for exporting the list of events as a CSV file.
281
  - Terminology and i18n improvements.
282
 
283
 
 
 
 
 
 
 
284
  ### 1.3 ###
285
 
286
  - Improvements to the UI.
src/event-list-table.php CHANGED
@@ -77,8 +77,8 @@ class Table extends \WP_List_Table {
77
  } );
78
  }
79
 
80
- if ( ! empty( $_GET['hooks_type'] ) ) {
81
- $hooks_type = sanitize_text_field( $_GET['hooks_type'] );
82
  $filtered = self::get_filtered_events( $events );
83
 
84
  if ( isset( $filtered[ $hooks_type ] ) ) {
@@ -217,7 +217,7 @@ class Table extends \WP_List_Table {
217
  $filtered = self::get_filtered_events( $this->all_events );
218
 
219
  $views = array();
220
- $hooks_type = ( ! empty( $_GET['hooks_type'] ) ? $_GET['hooks_type'] : 'all' );
221
 
222
  $types = array(
223
  'all' => __( 'All events', 'wp-crontrol' ),
@@ -248,7 +248,7 @@ class Table extends \WP_List_Table {
248
  continue;
249
  }
250
 
251
- $link = ( 'all' === $key ) ? $url : add_query_arg( 'hooks_type', $key, $url );
252
 
253
  $views[ $key ] = sprintf(
254
  '<a href="%1$s"%2$s>%3$s <span class="count">(%4$s)</span></a>',
@@ -272,15 +272,15 @@ class Table extends \WP_List_Table {
272
  protected function extra_tablenav( $which ) {
273
  wp_nonce_field( 'crontrol-export-event-csv', 'crontrol_nonce' );
274
  printf(
275
- '<input type="hidden" name="hooks_type" value="%s"/>',
276
- esc_attr( isset( $_GET['hooks_type'] ) ? sanitize_text_field( wp_unslash( $_GET['hooks_type'] ) ) : 'all' )
277
  );
278
  printf(
279
  '<input type="hidden" name="s" value="%s"/>',
280
  esc_attr( isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '' )
281
  );
282
  printf(
283
- '<button class="button" type="submit" name="action" value="crontrol-export-event-csv">%s</button>',
284
  esc_html__( 'Export', 'wp-crontrol' )
285
  );
286
  }
@@ -347,11 +347,11 @@ class Table extends \WP_List_Table {
347
 
348
  if ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_edit_files ) {
349
  $link = array(
350
- 'page' => 'crontrol_admin_manage_page',
351
- 'action' => 'crontrol-edit-cron',
352
- 'id' => rawurlencode( $event->hook ),
353
- 'sig' => rawurlencode( $event->sig ),
354
- 'next_run_utc' => rawurlencode( $event->time ),
355
  );
356
  $link = add_query_arg( $link, admin_url( 'tools.php' ) );
357
 
@@ -359,11 +359,11 @@ class Table extends \WP_List_Table {
359
  }
360
 
361
  $link = array(
362
- 'page' => 'crontrol_admin_manage_page',
363
- 'action' => 'crontrol-run-cron',
364
- 'id' => rawurlencode( $event->hook ),
365
- 'sig' => rawurlencode( $event->sig ),
366
- 'next_run_utc' => rawurlencode( $event->time ),
367
  );
368
  $link = add_query_arg( $link, admin_url( 'tools.php' ) );
369
  $link = wp_nonce_url( $link, "crontrol-run-cron_{$event->hook}_{$event->sig}" );
@@ -372,11 +372,11 @@ class Table extends \WP_List_Table {
372
 
373
  if ( ! in_array( $event->hook, self::$persistent_core_hooks, true ) && ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_edit_files ) ) {
374
  $link = array(
375
- 'page' => 'crontrol_admin_manage_page',
376
- 'action' => 'crontrol-delete-cron',
377
- 'id' => rawurlencode( $event->hook ),
378
- 'sig' => rawurlencode( $event->sig ),
379
- 'next_run_utc' => rawurlencode( $event->time ),
380
  );
381
  $link = add_query_arg( $link, admin_url( 'tools.php' ) );
382
  $link = wp_nonce_url( $link, "crontrol-delete-cron_{$event->hook}_{$event->sig}_{$event->time}" );
@@ -387,9 +387,9 @@ class Table extends \WP_List_Table {
387
  if ( function_exists( 'wp_unschedule_hook' ) && ! in_array( $event->hook, self::$persistent_core_hooks, true ) && ( 'crontrol_cron_job' !== $event->hook ) ) {
388
  if ( self::$count_by_hook[ $event->hook ] > 1 ) {
389
  $link = array(
390
- 'page' => 'crontrol_admin_manage_page',
391
- 'action' => 'crontrol-delete-hook',
392
- 'id' => rawurlencode( $event->hook ),
393
  );
394
  $link = add_query_arg( $link, admin_url( 'tools.php' ) );
395
  $link = wp_nonce_url( $link, "crontrol-delete-hook_{$event->hook}" );
@@ -414,7 +414,7 @@ class Table extends \WP_List_Table {
414
  */
415
  protected function column_cb( $event ) {
416
  $id = sprintf(
417
- 'wp-crontrol-delete-%1$s-%2$s-%3$s',
418
  $event->time,
419
  rawurlencode( $event->hook ),
420
  $event->sig
@@ -429,7 +429,7 @@ class Table extends \WP_List_Table {
429
  } elseif ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_edit_files ) {
430
  return sprintf(
431
  '<label class="screen-reader-text" for="%1$s">%2$s</label>
432
- <input type="checkbox" name="delete[%3$s][%4$s]" value="%5$s" id="%1$s">',
433
  esc_attr( $id ),
434
  esc_html__( 'Select this row', 'wp-crontrol' ),
435
  esc_attr( $event->time ),
@@ -623,7 +623,7 @@ class Table extends \WP_List_Table {
623
  * @return void
624
  */
625
  public function no_items() {
626
- if ( empty( $_GET['s'] ) && empty( $_GET['hooks_type'] ) ) {
627
  esc_html_e( 'There are currently no scheduled cron events.', 'wp-crontrol' );
628
  } else {
629
  esc_html_e( 'No matching cron events.', 'wp-crontrol' );
77
  } );
78
  }
79
 
80
+ if ( ! empty( $_GET['crontrol_hooks_type'] ) ) {
81
+ $hooks_type = sanitize_text_field( $_GET['crontrol_hooks_type'] );
82
  $filtered = self::get_filtered_events( $events );
83
 
84
  if ( isset( $filtered[ $hooks_type ] ) ) {
217
  $filtered = self::get_filtered_events( $this->all_events );
218
 
219
  $views = array();
220
+ $hooks_type = ( ! empty( $_GET['crontrol_hooks_type'] ) ? $_GET['crontrol_hooks_type'] : 'all' );
221
 
222
  $types = array(
223
  'all' => __( 'All events', 'wp-crontrol' ),
248
  continue;
249
  }
250
 
251
+ $link = ( 'all' === $key ) ? $url : add_query_arg( 'crontrol_hooks_type', $key, $url );
252
 
253
  $views[ $key ] = sprintf(
254
  '<a href="%1$s"%2$s>%3$s <span class="count">(%4$s)</span></a>',
272
  protected function extra_tablenav( $which ) {
273
  wp_nonce_field( 'crontrol-export-event-csv', 'crontrol_nonce' );
274
  printf(
275
+ '<input type="hidden" name="crontrol_hooks_type" value="%s"/>',
276
+ esc_attr( isset( $_GET['crontrol_hooks_type'] ) ? sanitize_text_field( wp_unslash( $_GET['crontrol_hooks_type'] ) ) : 'all' )
277
  );
278
  printf(
279
  '<input type="hidden" name="s" value="%s"/>',
280
  esc_attr( isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '' )
281
  );
282
  printf(
283
+ '<button class="button" type="submit" name="crontrol_action" value="export-event-csv">%s</button>',
284
  esc_html__( 'Export', 'wp-crontrol' )
285
  );
286
  }
347
 
348
  if ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_edit_files ) {
349
  $link = array(
350
+ 'page' => 'crontrol_admin_manage_page',
351
+ 'crontrol_action' => 'edit-cron',
352
+ 'crontrol_id' => rawurlencode( $event->hook ),
353
+ 'crontrol_sig' => rawurlencode( $event->sig ),
354
+ 'crontrol_next_run_utc' => rawurlencode( $event->time ),
355
  );
356
  $link = add_query_arg( $link, admin_url( 'tools.php' ) );
357
 
359
  }
360
 
361
  $link = array(
362
+ 'page' => 'crontrol_admin_manage_page',
363
+ 'crontrol_action' => 'run-cron',
364
+ 'crontrol_id' => rawurlencode( $event->hook ),
365
+ 'crontrol_sig' => rawurlencode( $event->sig ),
366
+ 'crontrol_next_run_utc' => rawurlencode( $event->time ),
367
  );
368
  $link = add_query_arg( $link, admin_url( 'tools.php' ) );
369
  $link = wp_nonce_url( $link, "crontrol-run-cron_{$event->hook}_{$event->sig}" );
372
 
373
  if ( ! in_array( $event->hook, self::$persistent_core_hooks, true ) && ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_edit_files ) ) {
374
  $link = array(
375
+ 'page' => 'crontrol_admin_manage_page',
376
+ 'crontrol_action' => 'delete-cron',
377
+ 'crontrol_id' => rawurlencode( $event->hook ),
378
+ 'crontrol_sig' => rawurlencode( $event->sig ),
379
+ 'crontrol_next_run_utc' => rawurlencode( $event->time ),
380
  );
381
  $link = add_query_arg( $link, admin_url( 'tools.php' ) );
382
  $link = wp_nonce_url( $link, "crontrol-delete-cron_{$event->hook}_{$event->sig}_{$event->time}" );
387
  if ( function_exists( 'wp_unschedule_hook' ) && ! in_array( $event->hook, self::$persistent_core_hooks, true ) && ( 'crontrol_cron_job' !== $event->hook ) ) {
388
  if ( self::$count_by_hook[ $event->hook ] > 1 ) {
389
  $link = array(
390
+ 'page' => 'crontrol_admin_manage_page',
391
+ 'crontrol_action' => 'delete-hook',
392
+ 'crontrol_id' => rawurlencode( $event->hook ),
393
  );
394
  $link = add_query_arg( $link, admin_url( 'tools.php' ) );
395
  $link = wp_nonce_url( $link, "crontrol-delete-hook_{$event->hook}" );
414
  */
415
  protected function column_cb( $event ) {
416
  $id = sprintf(
417
+ 'crontrol-delete-%1$s-%2$s-%3$s',
418
  $event->time,
419
  rawurlencode( $event->hook ),
420
  $event->sig
429
  } elseif ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_edit_files ) {
430
  return sprintf(
431
  '<label class="screen-reader-text" for="%1$s">%2$s</label>
432
+ <input type="checkbox" name="crontrol_delete[%3$s][%4$s]" value="%5$s" id="%1$s">',
433
  esc_attr( $id ),
434
  esc_html__( 'Select this row', 'wp-crontrol' ),
435
  esc_attr( $event->time ),
623
  * @return void
624
  */
625
  public function no_items() {
626
+ if ( empty( $_GET['s'] ) && empty( $_GET['crontrol_hooks_type'] ) ) {
627
  esc_html_e( 'There are currently no scheduled cron events.', 'wp-crontrol' );
628
  } else {
629
  esc_html_e( 'No matching cron events.', 'wp-crontrol' );
src/event.php CHANGED
@@ -278,13 +278,14 @@ function get() {
278
  /**
279
  * Gets a single cron event.
280
  *
281
- * @param string $hook The hook name of the event.
282
- * @param string $sig The event signature.
283
- * @param string $next_run_utc The UTC time that the event would be run at.
284
  * @return stdClass|WP_Error A cron event object, or a WP_Error if it's not found.
285
  */
286
  function get_single( $hook, $sig, $next_run_utc ) {
287
  $crons = get_core_cron_array();
 
288
 
289
  if ( isset( $crons[ $next_run_utc ][ $hook ][ $sig ] ) ) {
290
  $event = $crons[ $next_run_utc ][ $hook ][ $sig ];
278
  /**
279
  * Gets a single cron event.
280
  *
281
+ * @param string $hook The hook name of the event.
282
+ * @param string $sig The event signature.
283
+ * @param string|int $next_run_utc The UTC time that the event would be run at.
284
  * @return stdClass|WP_Error A cron event object, or a WP_Error if it's not found.
285
  */
286
  function get_single( $hook, $sig, $next_run_utc ) {
287
  $crons = get_core_cron_array();
288
+ $next_run_utc = (int) $next_run_utc;
289
 
290
  if ( isset( $crons[ $next_run_utc ][ $hook ][ $sig ] ) ) {
291
  $event = $crons[ $next_run_utc ][ $hook ][ $sig ];
src/request.php CHANGED
@@ -97,8 +97,10 @@ class Request {
97
  */
98
  public function init( array $props ) {
99
  foreach ( $props as $name => $value ) {
100
- if ( property_exists( $this, $name ) ) {
101
- $this->$name = $value;
 
 
102
  }
103
  }
104
 
97
  */
98
  public function init( array $props ) {
99
  foreach ( $props as $name => $value ) {
100
+ $prop = (string) preg_replace( '#^crontrol_#', '', $name );
101
+
102
+ if ( property_exists( $this, $prop ) ) {
103
+ $this->$prop = $value;
104
  }
105
  }
106
 
src/schedule-list-table.php CHANGED
@@ -96,6 +96,12 @@ class Schedule_List_Table extends \WP_List_Table {
96
  /**
97
  * Generates and displays row action links for the table.
98
  *
 
 
 
 
 
 
99
  * @param mixed[] $schedule The schedule for the current row.
100
  * @param string $column_name Current column name.
101
  * @param string $primary Primary column name.
@@ -107,19 +113,20 @@ class Schedule_List_Table extends \WP_List_Table {
107
  }
108
 
109
  $links = array();
 
110
  $new_scheds = get_option( 'crontrol_schedules', array() );
111
 
112
  if ( in_array( $schedule['name'], self::$core_schedules, true ) ) {
113
- $links[] = "<span class='in-use'>" . esc_html__( 'This is a WordPress core schedule and cannot be deleted', 'wp-crontrol' ) . '</span>';
114
  } elseif ( ! isset( $new_scheds[ $schedule['name'] ] ) ) {
115
- $links[] = "<span class='in-use'>" . esc_html__( 'This schedule is added by another plugin and cannot be deleted', 'wp-crontrol' ) . '</span>';
116
  } elseif ( in_array( $schedule['name'], self::$used_schedules, true ) ) {
117
- $links[] = "<span class='in-use'>" . esc_html__( 'This custom schedule is in use and cannot be deleted', 'wp-crontrol' ) . '</span>';
118
  } else {
119
  $link = add_query_arg( array(
120
- 'page' => 'crontrol_admin_options_page',
121
- 'action' => 'crontrol-delete-schedule',
122
- 'id' => rawurlencode( $schedule['name'] ),
123
  ), admin_url( 'options-general.php' ) );
124
  $link = wp_nonce_url( $link, 'crontrol-delete-schedule_' . $schedule['name'] );
125
 
@@ -132,6 +139,12 @@ class Schedule_List_Table extends \WP_List_Table {
132
  /**
133
  * Returns the output for the icon cell of a table row.
134
  *
 
 
 
 
 
 
135
  * @param mixed[] $schedule The schedule for the current row.
136
  * @return string The cell output.
137
  */
@@ -150,6 +163,12 @@ class Schedule_List_Table extends \WP_List_Table {
150
  /**
151
  * Returns the output for the schdule name cell of a table row.
152
  *
 
 
 
 
 
 
153
  * @param mixed[] $schedule The schedule for the current row.
154
  * @return string The cell output.
155
  */
@@ -160,13 +179,19 @@ class Schedule_List_Table extends \WP_List_Table {
160
  /**
161
  * Returns the output for the interval cell of a table row.
162
  *
 
 
 
 
 
 
163
  * @param mixed[] $schedule The schedule for the current row.
164
  * @return string The cell output.
165
  */
166
  protected function column_crontrol_interval( array $schedule ) {
167
  $interval = sprintf(
168
  '%s (%s)',
169
- esc_html( $schedule['interval'] ),
170
  esc_html( interval( $schedule['interval'] ) )
171
  );
172
 
@@ -189,6 +214,13 @@ class Schedule_List_Table extends \WP_List_Table {
189
  * Returns the output for the display name cell of a table row.
190
  *
191
  * @param mixed[] $schedule The schedule for the current row.
 
 
 
 
 
 
 
192
  * @return string The cell output.
193
  */
194
  protected function column_crontrol_display( array $schedule ) {
96
  /**
97
  * Generates and displays row action links for the table.
98
  *
99
+ * @phpstan-param array{
100
+ * interval: int,
101
+ * display: string,
102
+ * name: string,
103
+ * is_too_frequent: bool,
104
+ * } $schedule
105
  * @param mixed[] $schedule The schedule for the current row.
106
  * @param string $column_name Current column name.
107
  * @param string $primary Primary column name.
113
  }
114
 
115
  $links = array();
116
+ /** @var array<string,int|string> */
117
  $new_scheds = get_option( 'crontrol_schedules', array() );
118
 
119
  if ( in_array( $schedule['name'], self::$core_schedules, true ) ) {
120
+ $links[] = "<span class='crontrol-in-use'>" . esc_html__( 'This is a WordPress core schedule and cannot be deleted', 'wp-crontrol' ) . '</span>';
121
  } elseif ( ! isset( $new_scheds[ $schedule['name'] ] ) ) {
122
+ $links[] = "<span class='crontrol-in-use'>" . esc_html__( 'This schedule is added by another plugin and cannot be deleted', 'wp-crontrol' ) . '</span>';
123
  } elseif ( in_array( $schedule['name'], self::$used_schedules, true ) ) {
124
+ $links[] = "<span class='crontrol-in-use'>" . esc_html__( 'This custom schedule is in use and cannot be deleted', 'wp-crontrol' ) . '</span>';
125
  } else {
126
  $link = add_query_arg( array(
127
+ 'page' => 'crontrol_admin_options_page',
128
+ 'crontrol_action' => 'delete-schedule',
129
+ 'crontrol_id' => rawurlencode( $schedule['name'] ),
130
  ), admin_url( 'options-general.php' ) );
131
  $link = wp_nonce_url( $link, 'crontrol-delete-schedule_' . $schedule['name'] );
132
 
139
  /**
140
  * Returns the output for the icon cell of a table row.
141
  *
142
+ * @phpstan-param array{
143
+ * interval: int,
144
+ * display: string,
145
+ * name: string,
146
+ * is_too_frequent: bool,
147
+ * } $schedule
148
  * @param mixed[] $schedule The schedule for the current row.
149
  * @return string The cell output.
150
  */
163
  /**
164
  * Returns the output for the schdule name cell of a table row.
165
  *
166
+ * @phpstan-param array{
167
+ * interval: int,
168
+ * display: string,
169
+ * name: string,
170
+ * is_too_frequent: bool,
171
+ * } $schedule
172
  * @param mixed[] $schedule The schedule for the current row.
173
  * @return string The cell output.
174
  */
179
  /**
180
  * Returns the output for the interval cell of a table row.
181
  *
182
+ * @phpstan-param array{
183
+ * interval: int,
184
+ * display: string,
185
+ * name: string,
186
+ * is_too_frequent: bool,
187
+ * } $schedule
188
  * @param mixed[] $schedule The schedule for the current row.
189
  * @return string The cell output.
190
  */
191
  protected function column_crontrol_interval( array $schedule ) {
192
  $interval = sprintf(
193
  '%s (%s)',
194
+ esc_html( "{$schedule['interval']}" ),
195
  esc_html( interval( $schedule['interval'] ) )
196
  );
197
 
214
  * Returns the output for the display name cell of a table row.
215
  *
216
  * @param mixed[] $schedule The schedule for the current row.
217
+ *
218
+ * @phpstan-param array{
219
+ * interval: int,
220
+ * display: string,
221
+ * name: string,
222
+ * is_too_frequent: bool,
223
+ * } $schedule
224
  * @return string The cell output.
225
  */
226
  protected function column_crontrol_display( array $schedule ) {
src/schedule.php CHANGED
@@ -16,6 +16,7 @@ namespace Crontrol\Schedule;
16
  * @return void
17
  */
18
  function add( $name, $interval, $display ) {
 
19
  $old_scheds = get_option( 'crontrol_schedules', array() );
20
 
21
  $old_scheds[ $name ] = array(
@@ -41,6 +42,7 @@ function add( $name, $interval, $display ) {
41
  * @return void
42
  */
43
  function delete( $name ) {
 
44
  $scheds = get_option( 'crontrol_schedules', array() );
45
  unset( $scheds[ $name ] );
46
  update_option( 'crontrol_schedules', $scheds );
@@ -87,7 +89,7 @@ function get() {
87
  function dropdown( $current = false ) {
88
  $schedules = get();
89
  ?>
90
- <select class="postform" name="schedule" id="schedule" required>
91
  <option <?php selected( $current, '_oneoff' ); ?> value="_oneoff"><?php esc_html_e( 'Non-repeating', 'wp-crontrol' ); ?></option>
92
  <?php foreach ( $schedules as $sched_name => $sched_data ) { ?>
93
  <option <?php selected( $current, $sched_name ); ?> value="<?php echo esc_attr( $sched_name ); ?>">
16
  * @return void
17
  */
18
  function add( $name, $interval, $display ) {
19
+ /** @var array<string,int|string> */
20
  $old_scheds = get_option( 'crontrol_schedules', array() );
21
 
22
  $old_scheds[ $name ] = array(
42
  * @return void
43
  */
44
  function delete( $name ) {
45
+ /** @var array<string,int|string> */
46
  $scheds = get_option( 'crontrol_schedules', array() );
47
  unset( $scheds[ $name ] );
48
  update_option( 'crontrol_schedules', $scheds );
89
  function dropdown( $current = false ) {
90
  $schedules = get();
91
  ?>
92
+ <select class="postform" name="crontrol_schedule" id="crontrol_schedule" required>
93
  <option <?php selected( $current, '_oneoff' ); ?> value="_oneoff"><?php esc_html_e( 'Non-repeating', 'wp-crontrol' ); ?></option>
94
  <?php foreach ( $schedules as $sched_name => $sched_data ) { ?>
95
  <option <?php selected( $current, $sched_name ); ?> value="<?php echo esc_attr( $sched_name ); ?>">
wp-crontrol.php CHANGED
@@ -5,7 +5,7 @@
5
  * Description: WP Crontrol enables you to view and control what's happening in the WP-Cron system.
6
  * Author: John Blackbourn & crontributors
7
  * Author URI: https://github.com/johnbillion/wp-crontrol/graphs/contributors
8
- * Version: 1.11.0
9
  * Text Domain: wp-crontrol
10
  * Domain Path: /languages/
11
  * Requires PHP: 5.3.6
@@ -138,7 +138,7 @@ function action_init() {
138
  function action_handle_posts() {
139
  $request = new Request();
140
 
141
- if ( isset( $_POST['action'] ) && ( 'crontrol_new_cron' === $_POST['action'] ) ) {
142
  if ( ! current_user_can( 'manage_options' ) ) {
143
  wp_die( esc_html__( 'You are not allowed to add new cron events.', 'wp-crontrol' ), 401 );
144
  }
@@ -151,7 +151,7 @@ function action_handle_posts() {
151
  }
152
  $args = json_decode( $cr->args, true );
153
 
154
- if ( empty( $args ) ) {
155
  $args = array();
156
  }
157
 
@@ -196,7 +196,7 @@ function action_handle_posts() {
196
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
197
  exit;
198
 
199
- } elseif ( isset( $_POST['action'] ) && ( 'crontrol_new_php_cron' === $_POST['action'] ) ) {
200
  if ( ! current_user_can( 'edit_files' ) ) {
201
  wp_die( esc_html__( 'You are not allowed to add new PHP cron events.', 'wp-crontrol' ), 401 );
202
  }
@@ -250,7 +250,7 @@ function action_handle_posts() {
250
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
251
  exit;
252
 
253
- } elseif ( isset( $_POST['action'] ) && ( 'crontrol_edit_cron' === $_POST['action'] ) ) {
254
  if ( ! current_user_can( 'manage_options' ) ) {
255
  wp_die( esc_html__( 'You are not allowed to edit cron events.', 'wp-crontrol' ), 401 );
256
  }
@@ -265,7 +265,7 @@ function action_handle_posts() {
265
 
266
  $args = json_decode( $cr->args, true );
267
 
268
- if ( empty( $args ) ) {
269
  $args = array();
270
  }
271
 
@@ -342,7 +342,7 @@ function action_handle_posts() {
342
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
343
  exit;
344
 
345
- } elseif ( isset( $_POST['action'] ) && ( 'crontrol_edit_php_cron' === $_POST['action'] ) ) {
346
  if ( ! current_user_can( 'edit_files' ) ) {
347
  wp_die( esc_html__( 'You are not allowed to edit PHP cron events.', 'wp-crontrol' ), 401 );
348
  }
@@ -433,9 +433,9 @@ function action_handle_posts() {
433
  wp_die( esc_html__( 'You are not allowed to add new cron schedules.', 'wp-crontrol' ), 401 );
434
  }
435
  check_admin_referer( 'crontrol-new-schedule' );
436
- $name = wp_unslash( $_POST['internal_name'] );
437
- $interval = absint( $_POST['interval'] );
438
- $display = wp_unslash( $_POST['display_name'] );
439
 
440
  Schedule\add( $name, $interval, $display );
441
  $redirect = array(
@@ -446,11 +446,11 @@ function action_handle_posts() {
446
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'options-general.php' ) ) );
447
  exit;
448
 
449
- } elseif ( isset( $_GET['action'] ) && 'crontrol-delete-schedule' === $_GET['action'] ) {
450
  if ( ! current_user_can( 'manage_options' ) ) {
451
  wp_die( esc_html__( 'You are not allowed to delete cron schedules.', 'wp-crontrol' ), 401 );
452
  }
453
- $schedule = wp_unslash( $_GET['id'] );
454
  check_admin_referer( "crontrol-delete-schedule_{$schedule}" );
455
  Schedule\delete( $schedule );
456
  $redirect = array(
@@ -467,14 +467,14 @@ function action_handle_posts() {
467
  }
468
  check_admin_referer( 'bulk-crontrol-events' );
469
 
470
- if ( empty( $_POST['delete'] ) ) {
471
  return;
472
  }
473
 
474
  /**
475
  * @var array<string,array<string,string>>
476
  */
477
- $delete = (array) wp_unslash( $_POST['delete'] );
478
  $deleted = 0;
479
 
480
  foreach ( $delete as $next_run_utc => $events ) {
@@ -503,13 +503,13 @@ function action_handle_posts() {
503
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
504
  exit;
505
 
506
- } elseif ( isset( $_GET['action'] ) && 'crontrol-delete-cron' === $_GET['action'] ) {
507
  if ( ! current_user_can( 'manage_options' ) ) {
508
  wp_die( esc_html__( 'You are not allowed to delete cron events.', 'wp-crontrol' ), 401 );
509
  }
510
- $hook = wp_unslash( $_GET['id'] );
511
- $sig = wp_unslash( $_GET['sig'] );
512
- $next_run_utc = wp_unslash( $_GET['next_run_utc'] );
513
  check_admin_referer( "crontrol-delete-cron_{$hook}_{$sig}_{$next_run_utc}" );
514
 
515
  if ( 'crontrol_cron_job' === $hook && ! current_user_can( 'edit_files' ) ) {
@@ -556,11 +556,11 @@ function action_handle_posts() {
556
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
557
  exit;
558
 
559
- } elseif ( isset( $_GET['action'] ) && 'crontrol-delete-hook' === $_GET['action'] ) {
560
  if ( ! current_user_can( 'manage_options' ) ) {
561
  wp_die( esc_html__( 'You are not allowed to delete cron events.', 'wp-crontrol' ), 401 );
562
  }
563
- $hook = wp_unslash( $_GET['id'] );
564
  $deleted = false;
565
  check_admin_referer( "crontrol-delete-hook_{$hook}" );
566
 
@@ -569,6 +569,7 @@ function action_handle_posts() {
569
  }
570
 
571
  if ( function_exists( 'wp_unschedule_hook' ) ) {
 
572
  $deleted = wp_unschedule_hook( $hook );
573
  }
574
 
@@ -605,12 +606,12 @@ function action_handle_posts() {
605
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
606
  exit;
607
  }
608
- } elseif ( isset( $_GET['action'] ) && 'crontrol-run-cron' === $_GET['action'] ) {
609
  if ( ! current_user_can( 'manage_options' ) ) {
610
  wp_die( esc_html__( 'You are not allowed to run cron events.', 'wp-crontrol' ), 401 );
611
  }
612
- $hook = wp_unslash( $_GET['id'] );
613
- $sig = wp_unslash( $_GET['sig'] );
614
  check_admin_referer( "crontrol-run-cron_{$hook}_{$sig}" );
615
 
616
  $ran = Event\run( $hook, $sig );
@@ -640,12 +641,12 @@ function action_handle_posts() {
640
 
641
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
642
  exit;
643
- } elseif ( isset( $_POST['action'] ) && 'crontrol-export-event-csv' === $_POST['action'] ) {
644
  check_admin_referer( 'crontrol-export-event-csv', 'crontrol_nonce' );
645
 
646
  require_once __DIR__ . '/src/event-list-table.php';
647
 
648
- $type = isset( $_POST['hooks_type'] ) ? $_POST['hooks_type'] : 'all';
649
  $headers = array(
650
  'hook',
651
  'arguments',
@@ -908,25 +909,25 @@ function admin_options_page() {
908
  <p><?php esc_html_e( 'Adding a new cron schedule will allow you to schedule events that re-occur at the given interval.', 'wp-crontrol' ); ?></p>
909
  <form method="post" action="options-general.php?page=crontrol_admin_options_page">
910
  <div class="form-field form-required">
911
- <label for="cron_internal_name">
912
  <?php esc_html_e( 'Internal Name', 'wp-crontrol' ); ?>
913
  </label>
914
- <input type="text" value="" id="cron_internal_name" name="internal_name" required/>
915
  </div>
916
  <div class="form-field form-required">
917
- <label for="cron_interval">
918
  <?php esc_html_e( 'Interval (seconds)', 'wp-crontrol' ); ?>
919
  </label>
920
- <input type="number" value="" id="cron_interval" name="interval" min="1" step="1" required/>
921
  </div>
922
  <div class="form-field form-required">
923
- <label for="cron_display_name">
924
  <?php esc_html_e( 'Display Name', 'wp-crontrol' ); ?>
925
  </label>
926
- <input type="text" value="" id="cron_display_name" name="display_name" required/>
927
  </div>
928
  <p class="submit">
929
- <input id="schedadd-submit" type="submit" class="button button-primary" value="<?php esc_attr_e( 'Add Cron Schedule', 'wp-crontrol' ); ?>" name="crontrol_new_schedule"/>
930
  </p>
931
  <?php wp_nonce_field( 'crontrol-new-schedule' ); ?>
932
  </form>
@@ -1006,8 +1007,9 @@ function test_cron_spawn( $cache = true ) {
1006
  global $wp_version;
1007
 
1008
  $cron_runner_plugins = array(
1009
- '\HM\Cavalcade\Plugin\Job' => 'Cavalcade',
1010
- '\Automattic\WP\Cron_Control' => 'Cron Control',
 
1011
  );
1012
 
1013
  foreach ( $cron_runner_plugins as $class => $plugin ) {
@@ -1102,13 +1104,13 @@ function show_cron_status( $tab ) {
1102
  if ( is_wp_error( $status ) ) {
1103
  if ( 'crontrol_info' === $status->get_error_code() ) {
1104
  ?>
1105
- <div id="cron-status-notice" class="notice notice-info">
1106
  <p><?php echo esc_html( $status->get_error_message() ); ?></p>
1107
  </div>
1108
  <?php
1109
  } else {
1110
  ?>
1111
- <div id="cron-status-error" class="error">
1112
  <?php
1113
  printf(
1114
  '<p>%1$s</p><p><a href="%2$s">%3$s</a></p>',
@@ -1133,6 +1135,7 @@ function show_cron_status( $tab ) {
1133
  * @return string The name and UTC offset for the site's timezone.
1134
  */
1135
  function get_timezone_name() {
 
1136
  $timezone_string = get_option( 'timezone_string', '' );
1137
  $gmt_offset = get_option( 'gmt_offset', 0 );
1138
 
@@ -1192,11 +1195,11 @@ function show_cron_form( $editing ) {
1192
  $edit_id = null;
1193
  $existing = false;
1194
 
1195
- if ( $editing && ! empty( $_GET['id'] ) ) {
1196
- $edit_id = wp_unslash( $_GET['id'] );
1197
 
1198
  foreach ( Event\get() as $event ) {
1199
- if ( $edit_id === $event->hook && intval( $_GET['next_run_utc'] ) === $event->time && $event->sig === $_GET['sig'] ) {
1200
  $existing = array(
1201
  'hookname' => $event->hook,
1202
  'next_run' => $event->time, // UTC
@@ -1237,13 +1240,13 @@ function show_cron_form( $editing ) {
1237
 
1238
  if ( is_array( $existing ) ) {
1239
  $other_fields = wp_nonce_field( "crontrol-edit-cron_{$existing['hookname']}_{$existing['sig']}_{$existing['next_run']}", '_wpnonce', true, false );
1240
- $other_fields .= sprintf( '<input name="original_hookname" type="hidden" value="%s" />',
1241
  esc_attr( $existing['hookname'] )
1242
  );
1243
- $other_fields .= sprintf( '<input name="original_sig" type="hidden" value="%s" />',
1244
  esc_attr( $existing['sig'] )
1245
  );
1246
- $other_fields .= sprintf( '<input name="original_next_run_utc" type="hidden" value="%s" />',
1247
  esc_attr( (string) $existing['next_run'] )
1248
  );
1249
  if ( ! empty( $existing['args'] ) ) {
@@ -1312,9 +1315,9 @@ function show_cron_form( $editing ) {
1312
  <table class="form-table"><tbody>
1313
  <?php
1314
  if ( $editing ) {
1315
- $action = $is_editing_php ? 'crontrol_edit_php_cron' : 'crontrol_edit_cron';
1316
  printf(
1317
- '<input type="hidden" name="action" value="%s"/>',
1318
  esc_attr( $action )
1319
  );
1320
  } elseif ( $can_add_php ) {
@@ -1324,14 +1327,14 @@ function show_cron_form( $editing ) {
1324
  <?php esc_html_e( 'Event Type', 'wp-crontrol' ); ?>
1325
  </th>
1326
  <td>
1327
- <p><label><input type="radio" name="action" value="crontrol_new_cron" checked>Standard cron event</label></p>
1328
- <p><label><input type="radio" name="action" value="crontrol_new_php_cron">PHP cron event</label></p>
1329
  </td>
1330
  </tr>
1331
  <?php
1332
  } else {
1333
  ?>
1334
- <input type="hidden" name="action" value="crontrol_new_cron"/>
1335
  <?php
1336
  }
1337
 
@@ -1339,7 +1342,7 @@ function show_cron_form( $editing ) {
1339
  ?>
1340
  <tr class="crontrol-event-php">
1341
  <th valign="top" scope="row">
1342
- <label for="hookcode">
1343
  <?php esc_html_e( 'PHP Code', 'wp-crontrol' ); ?>
1344
  </label>
1345
  </th>
@@ -1353,18 +1356,18 @@ function show_cron_form( $editing ) {
1353
  );
1354
  ?>
1355
  </p>
1356
- <p><textarea class="large-text code" rows="10" cols="50" id="hookcode" name="hookcode"><?php echo esc_textarea( $editing ? $existing['args']['code'] : '' ); ?></textarea></p>
1357
  <?php do_action( 'crontrol/manage/hookcode', $existing ); ?>
1358
  </td>
1359
  </tr>
1360
  <tr class="crontrol-event-php">
1361
  <th valign="top" scope="row">
1362
- <label for="eventname">
1363
  <?php esc_html_e( 'Event Name (optional)', 'wp-crontrol' ); ?>
1364
  </label>
1365
  </th>
1366
  <td>
1367
- <input type="text" class="regular-text" id="eventname" name="eventname" value="<?php echo esc_attr( $editing ? $existing['args']['name'] : '' ); ?>"/>
1368
  <?php do_action( 'crontrol/manage/eventname', $existing ); ?>
1369
  </td>
1370
  </tr>
@@ -1375,23 +1378,23 @@ function show_cron_form( $editing ) {
1375
  ?>
1376
  <tr class="crontrol-event-standard">
1377
  <th valign="top" scope="row">
1378
- <label for="hookname">
1379
  <?php esc_html_e( 'Hook Name', 'wp-crontrol' ); ?>
1380
  </label>
1381
  </th>
1382
  <td>
1383
- <input type="text" autocorrect="off" autocapitalize="off" spellcheck="false" class="regular-text" id="hookname" name="hookname" value="<?php echo esc_attr( $existing['hookname'] ); ?>" required />
1384
  <?php do_action( 'crontrol/manage/hookname', $existing ); ?>
1385
  </td>
1386
  </tr>
1387
  <tr class="crontrol-event-standard">
1388
  <th valign="top" scope="row">
1389
- <label for="args">
1390
  <?php esc_html_e( 'Arguments (optional)', 'wp-crontrol' ); ?>
1391
  </label>
1392
  </th>
1393
  <td>
1394
- <input type="text" autocorrect="off" autocapitalize="off" spellcheck="false" class="regular-text code" id="args" name="args" value="<?php echo esc_attr( $display_args ); ?>"/>
1395
  <?php do_action( 'crontrol/manage/args', $existing ); ?>
1396
  <p class="description">
1397
  <?php
@@ -1411,7 +1414,7 @@ function show_cron_form( $editing ) {
1411
  ?>
1412
  <tr>
1413
  <th valign="top" scope="row">
1414
- <label for="next_run_date_local">
1415
  <?php esc_html_e( 'Next Run', 'wp-crontrol' ); ?>
1416
  </label>
1417
  </th>
@@ -1419,27 +1422,27 @@ function show_cron_form( $editing ) {
1419
  <ul>
1420
  <li>
1421
  <label>
1422
- <input type="radio" name="next_run_date_local" value="now" checked>
1423
  <?php esc_html_e( 'Now', 'wp-crontrol' ); ?>
1424
  </label>
1425
  </li>
1426
  <li>
1427
  <label>
1428
- <input type="radio" name="next_run_date_local" value="+1 day">
1429
  <?php esc_html_e( 'Tomorrow', 'wp-crontrol' ); ?>
1430
  </label>
1431
  </li>
1432
  <li>
1433
  <label>
1434
- <input type="radio" name="next_run_date_local" value="custom" id="next_run_date_local_custom" <?php checked( $editing ); ?>>
1435
  <?php
1436
  printf(
1437
  /* translators: %s: An input field for specifying a date and time */
1438
  esc_html__( 'At: %s', 'wp-crontrol' ),
1439
  sprintf(
1440
  '<br>
1441
- <input type="date" autocorrect="off" autocapitalize="off" spellcheck="false" name="next_run_date_local_custom_date" id="next_run_date_local_custom_date" value="%1$s" placeholder="yyyy-mm-dd" pattern="\d{4}-\d{2}-\d{2}" />
1442
- <input type="time" autocorrect="off" autocapitalize="off" spellcheck="false" name="next_run_date_local_custom_time" id="next_run_date_local_custom_time" value="%2$s" step="1" placeholder="hh:mm:ss" pattern="\d{2}:\d{2}:\d{2}" />',
1443
  esc_attr( $next_run_date_local ),
1444
  esc_attr( $next_run_time_local )
1445
  )
@@ -1464,7 +1467,7 @@ function show_cron_form( $editing ) {
1464
  </tr>
1465
  <tr>
1466
  <th valign="top" scope="row">
1467
- <label for="schedule">
1468
  <?php esc_html_e( 'Recurrence', 'wp-crontrol' ); ?>
1469
  </label>
1470
  </th>
@@ -1585,7 +1588,7 @@ function admin_manage_page() {
1585
 
1586
  <h1 class="wp-heading-inline"><?php esc_html_e( 'Cron Events', 'wp-crontrol' ); ?></h1>
1587
 
1588
- <?php echo '<a href="' . esc_url( admin_url( 'tools.php?page=crontrol_admin_manage_page&action=crontrol-new-cron' ) ) . '" class="page-title-action">' . esc_html__( 'Add New', 'wp-crontrol' ) . '</a>'; ?>
1589
 
1590
  <hr class="wp-header-end">
1591
 
@@ -1636,10 +1639,10 @@ function admin_manage_page() {
1636
  */
1637
  function get_tab_states() {
1638
  $tabs = array(
1639
- 'events' => ( ! empty( $_GET['page'] ) && 'crontrol_admin_manage_page' === $_GET['page'] && empty( $_GET['action'] ) ),
1640
- 'schedules' => ( ! empty( $_GET['page'] ) && 'crontrol_admin_options_page' === $_GET['page'] ),
1641
- 'add-event' => ( ! empty( $_GET['action'] ) && 'crontrol-new-cron' === $_GET['action'] ),
1642
- 'edit-event' => ( ! empty( $_GET['action'] ) && 'crontrol-edit-cron' === $_GET['action'] ),
1643
  );
1644
 
1645
  $tabs = apply_filters( 'crontrol/tabs', $tabs );
@@ -1805,19 +1808,19 @@ function output_callback( array $callback ) {
1805
  $qm = WP_PLUGIN_DIR . '/query-monitor/query-monitor.php';
1806
  $html = plugin_dir_path( $qm ) . 'output/Html.php';
1807
 
 
 
 
 
 
 
 
 
1808
  // If Query Monitor is installed, use its rich callback output.
1809
  if ( class_exists( '\QueryMonitor' ) && file_exists( $html ) ) {
1810
  require_once $html;
1811
 
1812
  if ( class_exists( '\QM_Output_Html' ) ) {
1813
- if ( ! empty( $callback['callback']['error'] ) ) {
1814
- $return = '<code>' . $callback['callback']['name'] . '</code>';
1815
- $return .= '<br><span class="status-crontrol-error"><span class="dashicons dashicons-warning" aria-hidden="true"></span> ';
1816
- $return .= esc_html( $callback['callback']['error']->get_error_message() );
1817
- $return .= '</span>';
1818
- return $return;
1819
- }
1820
-
1821
  return \QM_Output_Html::output_filename(
1822
  $callback['callback']['name'],
1823
  $callback['callback']['file'],
@@ -2024,7 +2027,6 @@ function get_all_core_hooks() {
2024
  'wp_maybe_auto_update', // 3.7.0
2025
  'wp_split_shared_term_batch', // 4.3.0
2026
  'wp_update_comment_type_batch', // 5.5.0
2027
- 'delete_temp_updater_backups', // 5.9.0
2028
  'wp_delete_temp_updater_backups', // 5.9.0
2029
  )
2030
  );
5
  * Description: WP Crontrol enables you to view and control what's happening in the WP-Cron system.
6
  * Author: John Blackbourn & crontributors
7
  * Author URI: https://github.com/johnbillion/wp-crontrol/graphs/contributors
8
+ * Version: 1.12.0
9
  * Text Domain: wp-crontrol
10
  * Domain Path: /languages/
11
  * Requires PHP: 5.3.6
138
  function action_handle_posts() {
139
  $request = new Request();
140
 
141
+ if ( isset( $_POST['crontrol_action'] ) && ( 'new_cron' === $_POST['crontrol_action'] ) ) {
142
  if ( ! current_user_can( 'manage_options' ) ) {
143
  wp_die( esc_html__( 'You are not allowed to add new cron events.', 'wp-crontrol' ), 401 );
144
  }
151
  }
152
  $args = json_decode( $cr->args, true );
153
 
154
+ if ( empty( $args ) || ! is_array( $args ) ) {
155
  $args = array();
156
  }
157
 
196
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
197
  exit;
198
 
199
+ } elseif ( isset( $_POST['crontrol_action'] ) && ( 'new_php_cron' === $_POST['crontrol_action'] ) ) {
200
  if ( ! current_user_can( 'edit_files' ) ) {
201
  wp_die( esc_html__( 'You are not allowed to add new PHP cron events.', 'wp-crontrol' ), 401 );
202
  }
250
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
251
  exit;
252
 
253
+ } elseif ( isset( $_POST['crontrol_action'] ) && ( 'edit_cron' === $_POST['crontrol_action'] ) ) {
254
  if ( ! current_user_can( 'manage_options' ) ) {
255
  wp_die( esc_html__( 'You are not allowed to edit cron events.', 'wp-crontrol' ), 401 );
256
  }
265
 
266
  $args = json_decode( $cr->args, true );
267
 
268
+ if ( empty( $args ) || ! is_array( $args ) ) {
269
  $args = array();
270
  }
271
 
342
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
343
  exit;
344
 
345
+ } elseif ( isset( $_POST['crontrol_action'] ) && ( 'edit_php_cron' === $_POST['crontrol_action'] ) ) {
346
  if ( ! current_user_can( 'edit_files' ) ) {
347
  wp_die( esc_html__( 'You are not allowed to edit PHP cron events.', 'wp-crontrol' ), 401 );
348
  }
433
  wp_die( esc_html__( 'You are not allowed to add new cron schedules.', 'wp-crontrol' ), 401 );
434
  }
435
  check_admin_referer( 'crontrol-new-schedule' );
436
+ $name = wp_unslash( $_POST['crontrol_schedule_internal_name'] );
437
+ $interval = absint( $_POST['crontrol_schedule_interval'] );
438
+ $display = wp_unslash( $_POST['crontrol_schedule_display_name'] );
439
 
440
  Schedule\add( $name, $interval, $display );
441
  $redirect = array(
446
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'options-general.php' ) ) );
447
  exit;
448
 
449
+ } elseif ( isset( $_GET['crontrol_action'] ) && 'delete-schedule' === $_GET['crontrol_action'] ) {
450
  if ( ! current_user_can( 'manage_options' ) ) {
451
  wp_die( esc_html__( 'You are not allowed to delete cron schedules.', 'wp-crontrol' ), 401 );
452
  }
453
+ $schedule = wp_unslash( $_GET['crontrol_id'] );
454
  check_admin_referer( "crontrol-delete-schedule_{$schedule}" );
455
  Schedule\delete( $schedule );
456
  $redirect = array(
467
  }
468
  check_admin_referer( 'bulk-crontrol-events' );
469
 
470
+ if ( empty( $_POST['crontrol_delete'] ) ) {
471
  return;
472
  }
473
 
474
  /**
475
  * @var array<string,array<string,string>>
476
  */
477
+ $delete = (array) wp_unslash( $_POST['crontrol_delete'] );
478
  $deleted = 0;
479
 
480
  foreach ( $delete as $next_run_utc => $events ) {
503
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
504
  exit;
505
 
506
+ } elseif ( isset( $_GET['crontrol_action'] ) && 'delete-cron' === $_GET['crontrol_action'] ) {
507
  if ( ! current_user_can( 'manage_options' ) ) {
508
  wp_die( esc_html__( 'You are not allowed to delete cron events.', 'wp-crontrol' ), 401 );
509
  }
510
+ $hook = wp_unslash( $_GET['crontrol_id'] );
511
+ $sig = wp_unslash( $_GET['crontrol_sig'] );
512
+ $next_run_utc = wp_unslash( $_GET['crontrol_next_run_utc'] );
513
  check_admin_referer( "crontrol-delete-cron_{$hook}_{$sig}_{$next_run_utc}" );
514
 
515
  if ( 'crontrol_cron_job' === $hook && ! current_user_can( 'edit_files' ) ) {
556
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
557
  exit;
558
 
559
+ } elseif ( isset( $_GET['crontrol_action'] ) && 'delete-hook' === $_GET['crontrol_action'] ) {
560
  if ( ! current_user_can( 'manage_options' ) ) {
561
  wp_die( esc_html__( 'You are not allowed to delete cron events.', 'wp-crontrol' ), 401 );
562
  }
563
+ $hook = wp_unslash( $_GET['crontrol_id'] );
564
  $deleted = false;
565
  check_admin_referer( "crontrol-delete-hook_{$hook}" );
566
 
569
  }
570
 
571
  if ( function_exists( 'wp_unschedule_hook' ) ) {
572
+ /** @var int|false */
573
  $deleted = wp_unschedule_hook( $hook );
574
  }
575
 
606
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
607
  exit;
608
  }
609
+ } elseif ( isset( $_GET['crontrol_action'] ) && 'run-cron' === $_GET['crontrol_action'] ) {
610
  if ( ! current_user_can( 'manage_options' ) ) {
611
  wp_die( esc_html__( 'You are not allowed to run cron events.', 'wp-crontrol' ), 401 );
612
  }
613
+ $hook = wp_unslash( $_GET['crontrol_id'] );
614
+ $sig = wp_unslash( $_GET['crontrol_sig'] );
615
  check_admin_referer( "crontrol-run-cron_{$hook}_{$sig}" );
616
 
617
  $ran = Event\run( $hook, $sig );
641
 
642
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
643
  exit;
644
+ } elseif ( isset( $_POST['crontrol_action'] ) && 'export-event-csv' === $_POST['crontrol_action'] ) {
645
  check_admin_referer( 'crontrol-export-event-csv', 'crontrol_nonce' );
646
 
647
  require_once __DIR__ . '/src/event-list-table.php';
648
 
649
+ $type = isset( $_POST['crontrol_hooks_type'] ) ? $_POST['crontrol_hooks_type'] : 'all';
650
  $headers = array(
651
  'hook',
652
  'arguments',
909
  <p><?php esc_html_e( 'Adding a new cron schedule will allow you to schedule events that re-occur at the given interval.', 'wp-crontrol' ); ?></p>
910
  <form method="post" action="options-general.php?page=crontrol_admin_options_page">
911
  <div class="form-field form-required">
912
+ <label for="crontrol_schedule_internal_name">
913
  <?php esc_html_e( 'Internal Name', 'wp-crontrol' ); ?>
914
  </label>
915
+ <input type="text" value="" id="crontrol_schedule_internal_name" name="crontrol_schedule_internal_name" required/>
916
  </div>
917
  <div class="form-field form-required">
918
+ <label for="crontrol_schedule_interval">
919
  <?php esc_html_e( 'Interval (seconds)', 'wp-crontrol' ); ?>
920
  </label>
921
+ <input type="number" value="" id="crontrol_schedule_interval" name="crontrol_schedule_interval" min="1" step="1" required/>
922
  </div>
923
  <div class="form-field form-required">
924
+ <label for="crontrol_schedule_display_name">
925
  <?php esc_html_e( 'Display Name', 'wp-crontrol' ); ?>
926
  </label>
927
+ <input type="text" value="" id="crontrol_schedule_display_name" name="crontrol_schedule_display_name" required/>
928
  </div>
929
  <p class="submit">
930
+ <input type="submit" class="button button-primary" value="<?php esc_attr_e( 'Add Cron Schedule', 'wp-crontrol' ); ?>" name="crontrol_new_schedule"/>
931
  </p>
932
  <?php wp_nonce_field( 'crontrol-new-schedule' ); ?>
933
  </form>
1007
  global $wp_version;
1008
 
1009
  $cron_runner_plugins = array(
1010
+ '\HM\Cavalcade\Plugin\Job' => 'Cavalcade',
1011
+ '\Automattic\WP\Cron_Control\Main' => 'Cron Control',
1012
+ '\KMM\KRoN\Core' => 'KMM KRoN',
1013
  );
1014
 
1015
  foreach ( $cron_runner_plugins as $class => $plugin ) {
1104
  if ( is_wp_error( $status ) ) {
1105
  if ( 'crontrol_info' === $status->get_error_code() ) {
1106
  ?>
1107
+ <div id="crontrol-status-notice" class="notice notice-info">
1108
  <p><?php echo esc_html( $status->get_error_message() ); ?></p>
1109
  </div>
1110
  <?php
1111
  } else {
1112
  ?>
1113
+ <div id="crontrol-status-error" class="error">
1114
  <?php
1115
  printf(
1116
  '<p>%1$s</p><p><a href="%2$s">%3$s</a></p>',
1135
  * @return string The name and UTC offset for the site's timezone.
1136
  */
1137
  function get_timezone_name() {
1138
+ /** @var string */
1139
  $timezone_string = get_option( 'timezone_string', '' );
1140
  $gmt_offset = get_option( 'gmt_offset', 0 );
1141
 
1195
  $edit_id = null;
1196
  $existing = false;
1197
 
1198
+ if ( $editing && ! empty( $_GET['crontrol_id'] ) ) {
1199
+ $edit_id = wp_unslash( $_GET['crontrol_id'] );
1200
 
1201
  foreach ( Event\get() as $event ) {
1202
+ if ( $edit_id === $event->hook && intval( $_GET['crontrol_next_run_utc'] ) === $event->time && $event->sig === $_GET['crontrol_sig'] ) {
1203
  $existing = array(
1204
  'hookname' => $event->hook,
1205
  'next_run' => $event->time, // UTC
1240
 
1241
  if ( is_array( $existing ) ) {
1242
  $other_fields = wp_nonce_field( "crontrol-edit-cron_{$existing['hookname']}_{$existing['sig']}_{$existing['next_run']}", '_wpnonce', true, false );
1243
+ $other_fields .= sprintf( '<input name="crontrol_original_hookname" type="hidden" value="%s" />',
1244
  esc_attr( $existing['hookname'] )
1245
  );
1246
+ $other_fields .= sprintf( '<input name="crontrol_original_sig" type="hidden" value="%s" />',
1247
  esc_attr( $existing['sig'] )
1248
  );
1249
+ $other_fields .= sprintf( '<input name="crontrol_original_next_run_utc" type="hidden" value="%s" />',
1250
  esc_attr( (string) $existing['next_run'] )
1251
  );
1252
  if ( ! empty( $existing['args'] ) ) {
1315
  <table class="form-table"><tbody>
1316
  <?php
1317
  if ( $editing ) {
1318
+ $action = $is_editing_php ? 'edit_php_cron' : 'edit_cron';
1319
  printf(
1320
+ '<input type="hidden" name="crontrol_action" value="%s"/>',
1321
  esc_attr( $action )
1322
  );
1323
  } elseif ( $can_add_php ) {
1327
  <?php esc_html_e( 'Event Type', 'wp-crontrol' ); ?>
1328
  </th>
1329
  <td>
1330
+ <p><label><input type="radio" name="crontrol_action" value="new_cron" checked>Standard cron event</label></p>
1331
+ <p><label><input type="radio" name="crontrol_action" value="new_php_cron">PHP cron event</label></p>
1332
  </td>
1333
  </tr>
1334
  <?php
1335
  } else {
1336
  ?>
1337
+ <input type="hidden" name="crontrol_action" value="new_cron"/>
1338
  <?php
1339
  }
1340
 
1342
  ?>
1343
  <tr class="crontrol-event-php">
1344
  <th valign="top" scope="row">
1345
+ <label for="crontrol_hookcode">
1346
  <?php esc_html_e( 'PHP Code', 'wp-crontrol' ); ?>
1347
  </label>
1348
  </th>
1356
  );
1357
  ?>
1358
  </p>
1359
+ <p><textarea class="large-text code" rows="10" cols="50" id="crontrol_hookcode" name="crontrol_hookcode"><?php echo esc_textarea( $editing ? $existing['args']['code'] : '' ); ?></textarea></p>
1360
  <?php do_action( 'crontrol/manage/hookcode', $existing ); ?>
1361
  </td>
1362
  </tr>
1363
  <tr class="crontrol-event-php">
1364
  <th valign="top" scope="row">
1365
+ <label for="crontrol_eventname">
1366
  <?php esc_html_e( 'Event Name (optional)', 'wp-crontrol' ); ?>
1367
  </label>
1368
  </th>
1369
  <td>
1370
+ <input type="text" class="regular-text" id="crontrol_eventname" name="crontrol_eventname" value="<?php echo esc_attr( $editing ? $existing['args']['name'] : '' ); ?>"/>
1371
  <?php do_action( 'crontrol/manage/eventname', $existing ); ?>
1372
  </td>
1373
  </tr>
1378
  ?>
1379
  <tr class="crontrol-event-standard">
1380
  <th valign="top" scope="row">
1381
+ <label for="crontrol_hookname">
1382
  <?php esc_html_e( 'Hook Name', 'wp-crontrol' ); ?>
1383
  </label>
1384
  </th>
1385
  <td>
1386
+ <input type="text" autocorrect="off" autocapitalize="off" spellcheck="false" class="regular-text" id="crontrol_hookname" name="crontrol_hookname" value="<?php echo esc_attr( $existing['hookname'] ); ?>" required />
1387
  <?php do_action( 'crontrol/manage/hookname', $existing ); ?>
1388
  </td>
1389
  </tr>
1390
  <tr class="crontrol-event-standard">
1391
  <th valign="top" scope="row">
1392
+ <label for="crontrol_args">
1393
  <?php esc_html_e( 'Arguments (optional)', 'wp-crontrol' ); ?>
1394
  </label>
1395
  </th>
1396
  <td>
1397
+ <input type="text" autocorrect="off" autocapitalize="off" spellcheck="false" class="regular-text code" id="crontrol_args" name="crontrol_args" value="<?php echo esc_attr( $display_args ); ?>"/>
1398
  <?php do_action( 'crontrol/manage/args', $existing ); ?>
1399
  <p class="description">
1400
  <?php
1414
  ?>
1415
  <tr>
1416
  <th valign="top" scope="row">
1417
+ <label for="crontrol_next_run_date_local">
1418
  <?php esc_html_e( 'Next Run', 'wp-crontrol' ); ?>
1419
  </label>
1420
  </th>
1422
  <ul>
1423
  <li>
1424
  <label>
1425
+ <input type="radio" name="crontrol_next_run_date_local" value="now" checked>
1426
  <?php esc_html_e( 'Now', 'wp-crontrol' ); ?>
1427
  </label>
1428
  </li>
1429
  <li>
1430
  <label>
1431
+ <input type="radio" name="crontrol_next_run_date_local" value="+1 day">
1432
  <?php esc_html_e( 'Tomorrow', 'wp-crontrol' ); ?>
1433
  </label>
1434
  </li>
1435
  <li>
1436
  <label>
1437
+ <input type="radio" name="crontrol_next_run_date_local" value="custom" id="crontrol_next_run_date_local_custom" <?php checked( $editing ); ?>>
1438
  <?php
1439
  printf(
1440
  /* translators: %s: An input field for specifying a date and time */
1441
  esc_html__( 'At: %s', 'wp-crontrol' ),
1442
  sprintf(
1443
  '<br>
1444
+ <input type="date" autocorrect="off" autocapitalize="off" spellcheck="false" name="crontrol_next_run_date_local_custom_date" id="crontrol_next_run_date_local_custom_date" value="%1$s" placeholder="yyyy-mm-dd" pattern="\d{4}-\d{2}-\d{2}" />
1445
+ <input type="time" autocorrect="off" autocapitalize="off" spellcheck="false" name="crontrol_next_run_date_local_custom_time" id="crontrol_next_run_date_local_custom_time" value="%2$s" step="1" placeholder="hh:mm:ss" pattern="\d{2}:\d{2}:\d{2}" />',
1446
  esc_attr( $next_run_date_local ),
1447
  esc_attr( $next_run_time_local )
1448
  )
1467
  </tr>
1468
  <tr>
1469
  <th valign="top" scope="row">
1470
+ <label for="crontrol_schedule">
1471
  <?php esc_html_e( 'Recurrence', 'wp-crontrol' ); ?>
1472
  </label>
1473
  </th>
1588
 
1589
  <h1 class="wp-heading-inline"><?php esc_html_e( 'Cron Events', 'wp-crontrol' ); ?></h1>
1590
 
1591
+ <?php echo '<a href="' . esc_url( admin_url( 'tools.php?page=crontrol_admin_manage_page&crontrol_action=new-cron' ) ) . '" class="page-title-action">' . esc_html__( 'Add New', 'wp-crontrol' ) . '</a>'; ?>
1592
 
1593
  <hr class="wp-header-end">
1594
 
1639
  */
1640
  function get_tab_states() {
1641
  $tabs = array(
1642
+ 'events' => ( ! empty( $_GET['page'] ) && 'crontrol_admin_manage_page' === $_GET['page'] && empty( $_GET['crontrol_action'] ) ),
1643
+ 'schedules' => ( ! empty( $_GET['page'] ) && 'crontrol_admin_options_page' === $_GET['page'] ),
1644
+ 'add-event' => ( ! empty( $_GET['crontrol_action'] ) && 'new-cron' === $_GET['crontrol_action'] ),
1645
+ 'edit-event' => ( ! empty( $_GET['crontrol_action'] ) && 'edit-cron' === $_GET['crontrol_action'] ),
1646
  );
1647
 
1648
  $tabs = apply_filters( 'crontrol/tabs', $tabs );
1808
  $qm = WP_PLUGIN_DIR . '/query-monitor/query-monitor.php';
1809
  $html = plugin_dir_path( $qm ) . 'output/Html.php';
1810
 
1811
+ if ( ! empty( $callback['callback']['error'] ) ) {
1812
+ $return = '<code>' . $callback['callback']['name'] . '</code>';
1813
+ $return .= '<br><span class="status-crontrol-error"><span class="dashicons dashicons-warning" aria-hidden="true"></span> ';
1814
+ $return .= esc_html( $callback['callback']['error']->get_error_message() );
1815
+ $return .= '</span>';
1816
+ return $return;
1817
+ }
1818
+
1819
  // If Query Monitor is installed, use its rich callback output.
1820
  if ( class_exists( '\QueryMonitor' ) && file_exists( $html ) ) {
1821
  require_once $html;
1822
 
1823
  if ( class_exists( '\QM_Output_Html' ) ) {
 
 
 
 
 
 
 
 
1824
  return \QM_Output_Html::output_filename(
1825
  $callback['callback']['name'],
1826
  $callback['callback']['file'],
2027
  'wp_maybe_auto_update', // 3.7.0
2028
  'wp_split_shared_term_batch', // 4.3.0
2029
  'wp_update_comment_type_batch', // 5.5.0
 
2030
  'wp_delete_temp_updater_backups', // 5.9.0
2031
  )
2032
  );