WP Crontrol - Version 1.13.0

Version Description

Download this release

Release Info

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

Code changes from version 1.12.1 to 1.13.0

Files changed (6) hide show
  1. composer.json +90 -0
  2. css/wp-crontrol.css +27 -7
  3. readme.md +71 -62
  4. src/event-list-table.php +61 -12
  5. src/event.php +84 -2
  6. wp-crontrol.php +118 -7
composer.json ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "johnbillion/wp-crontrol",
3
+ "description": "WP Crontrol lets you view and control what's happening in the WP-Cron system.",
4
+ "homepage": "https://github.com/johnbillion/wp-crontrol/",
5
+ "license": "GPL-2.0-or-later",
6
+ "type": "wordpress-plugin",
7
+ "authors": [
8
+ {
9
+ "name": "John Blackbourn",
10
+ "homepage": "https://johnblackbourn.com/"
11
+ },
12
+ {
13
+ "name": "Edward Dale",
14
+ "homepage": "http://scompt.com/"
15
+ }
16
+ ],
17
+ "config": {
18
+ "sort-packages": true,
19
+ "preferred-install": "dist",
20
+ "allow-plugins": {
21
+ "composer/installers": true,
22
+ "dealerdirect/phpcodesniffer-composer-installer": true,
23
+ "roots/wordpress-core-installer": true
24
+ }
25
+ },
26
+ "require-dev": {
27
+ "codeception/module-asserts": "^1.0",
28
+ "codeception/module-filesystem": "^1.0",
29
+ "codeception/module-webdriver": "^1.0",
30
+ "codeception/util-universalframework": "^1.0",
31
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
32
+ "lucatume/wp-browser": "^3.0",
33
+ "phpcompatibility/phpcompatibility-wp": "2.1.2",
34
+ "phpstan/phpstan": "^1.7",
35
+ "phpunit/phpunit": "^9.0",
36
+ "roots/wordpress-core-installer": "^1.0.0",
37
+ "roots/wordpress-full": "*",
38
+ "szepeviktor/phpstan-wordpress": "^1.0",
39
+ "wp-coding-standards/wpcs": "2.3.0"
40
+ },
41
+ "require": {
42
+ "php": ">=5.6",
43
+ "composer/installers": "^1.0 || ^2.0"
44
+ },
45
+ "autoload": {
46
+ "classmap": [
47
+ "src"
48
+ ]
49
+ },
50
+ "extra": {
51
+ "wordpress-install-dir": "tests/wordpress"
52
+ },
53
+ "scripts": {
54
+ "test:cs": [
55
+ "phpcs -nps --colors --report-code --report-summary --report-width=80 --cache=tests/cache/phpcs --basepath=./ ."
56
+ ],
57
+ "test:cs2pr": [
58
+ "phpcs -nsq --report=checkstyle --cache=tests/cache/phpcs . | cs2pr"
59
+ ],
60
+ "test:analyze": [
61
+ "phpstan analyze --memory-limit=1024M"
62
+ ],
63
+ "test:start": [
64
+ "docker compose up -d"
65
+ ],
66
+ "test:acceptance": [
67
+ "bin/test.sh"
68
+ ],
69
+ "test:stop": [
70
+ "docker compose down"
71
+ ],
72
+ "test": [
73
+ "composer validate --strict",
74
+ "@test:cs",
75
+ "@test:analyze",
76
+ "@test:acceptance"
77
+ ]
78
+ },
79
+ "support": {
80
+ "issues": "https://github.com/johnbillion/wp-crontrol/issues",
81
+ "forum": "https://wordpress.org/support/plugin/wp-crontrol",
82
+ "source": "https://github.com/johnbillion/wp-crontrol"
83
+ },
84
+ "funding": [
85
+ {
86
+ "type": "github",
87
+ "url": "https://github.com/sponsors/johnbillion"
88
+ }
89
+ ]
90
+ }
css/wp-crontrol.css CHANGED
@@ -42,18 +42,26 @@ table.wp-list-table {
42
  .wp-list-table tr.crontrol-no-action th,
43
  .wp-list-table tr.crontrol-stalled th,
44
  .wp-list-table tr.crontrol-warning th {
45
- border-color: #ffb900;
46
  }
47
 
48
  .wp-list-table tr.crontrol-error th {
49
- border-color: #dc3232;
 
 
 
 
 
 
 
 
50
  }
51
 
52
  .wp-list-table .column-crontrol_icon .dashicons,
53
  .wp-list-table .check-column .dashicons {
54
  margin-left: 6px;
55
  font-size: 14px;
56
- color: #aaa;
57
  }
58
 
59
  .wp-list-table .column-crontrol_icon .dashicons {
@@ -66,7 +74,7 @@ table.wp-list-table {
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,
@@ -75,7 +83,7 @@ table.wp-list-table {
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
 
81
  .status-crontrol-emergency,
@@ -83,7 +91,19 @@ table.wp-list-table {
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,
@@ -99,7 +119,7 @@ table.wp-list-table {
99
  }
100
 
101
  .row-actions .crontrol-in-use {
102
- color: #666;
103
  }
104
 
105
  .form-field input[type="number"] {
42
  .wp-list-table tr.crontrol-no-action th,
43
  .wp-list-table tr.crontrol-stalled th,
44
  .wp-list-table tr.crontrol-warning th {
45
+ border-color: #dba617;
46
  }
47
 
48
  .wp-list-table tr.crontrol-error th {
49
+ border-color: #d63638;
50
+ }
51
+
52
+ .wp-list-table tr.crontrol-paused th {
53
+ border-color: #8c8f94;
54
+ }
55
+
56
+ .wp-list-table tr.crontrol-paused:not(.crontrol-no-action) .column-crontrol_actions {
57
+ text-decoration: line-through;
58
  }
59
 
60
  .wp-list-table .column-crontrol_icon .dashicons,
61
  .wp-list-table .check-column .dashicons {
62
  margin-left: 6px;
63
  font-size: 14px;
64
+ color: #a7aaad;
65
  }
66
 
67
  .wp-list-table .column-crontrol_icon .dashicons {
74
 
75
  .status-crontrol-complete,
76
  .wp-list-table tr.crontrol-complete td.column-crontrol_status {
77
+ color: #00a32a;
78
  }
79
 
80
  .status-crontrol-no-action,
83
  .wp-list-table tr.crontrol-no-action td.column-crontrol_status,
84
  .wp-list-table tr.crontrol-stalled td.column-crontrol_status,
85
  .wp-list-table tr.crontrol-warning td.column-crontrol_status {
86
+ color: #bd8600;
87
  }
88
 
89
  .status-crontrol-emergency,
91
  .status-crontrol-critical,
92
  .status-crontrol-error,
93
  .wp-list-table tr.crontrol-error td.column-crontrol_status {
94
+ color: #d63638;
95
+ }
96
+
97
+ .status-crontrol-paused .dashicons {
98
+ background: #646970;
99
+ border-radius: 50%;
100
+ color: #fff;
101
+ font-size: 14px;
102
+ height: 14px;
103
+ line-height: 14px;
104
+ margin-top: 2px;
105
+ padding: 1px;
106
+ width: 14px;
107
  }
108
 
109
  .wp-list-table .column-crontrol_https,
119
  }
120
 
121
  .row-actions .crontrol-in-use {
122
+ color: #646970;
123
  }
124
 
125
  .form-field input[type="number"] {
readme.md CHANGED
@@ -4,8 +4,8 @@ Contributors: johnbillion, scompt
4
  Tags: cron, wp-cron, crontrol, debug
5
  Requires at least: 4.2
6
  Tested up to: 6.0
7
- Stable tag: 1.12.1
8
- Requires PHP: 5.3
9
  Donate link: https://github.com/sponsors/johnbillion
10
 
11
  WP Crontrol enables you to view and control what's happening in the WP-Cron system.
@@ -15,7 +15,7 @@ WP Crontrol enables you to view and control what's happening in the WP-Cron syst
15
  WP Crontrol enables you to view and control what's happening in the WP-Cron system. From the admin screens you can:
16
 
17
  * View all cron events along with their arguments, recurrence, callback functions, and when they are next due.
18
- * Edit, delete, and immediately run any cron events.
19
  * Add new cron events.
20
  * Bulk delete cron events.
21
  * Add and remove custom cron schedules.
@@ -51,7 +51,7 @@ Yes, it's actively tested and working up to PHP 8.1.
51
 
52
  ### Why do some cron events reappear shortly after I delete them?
53
 
54
- If the event is added by a plugin then the plugin most likely rescheduled the event as soon as it saw that the event was missing. Unfortunately there's nothing that WP Crontrol can do about this - you should contact the author of the related plugin and ask for advice.
55
 
56
  ### Is it safe to delete cron events?
57
 
@@ -63,6 +63,18 @@ If the event shows "None" as its action then it's usually safe to delete. Please
63
 
64
  The WordPress core software uses cron events for some of its functionality and removing these events is not possible because WordPress would immediately reschedule them if you did delete them. For this reason, WP Crontrol doesn't let you delete these persistent events from WordPress core in the first place.
65
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  ### What does it mean when "None" is shown for the Action of a cron event?
67
 
68
  This means the cron event is scheduled to run at the specified time but there is no corresponding functionality that will be triggered when the event runs, therefore the event is useless.
@@ -157,31 +169,37 @@ The photo was taken by <a href="https://www.flickr.com/photos/michaelpardo/21453
157
 
158
  ## Changelog ##
159
 
 
 
 
 
 
 
160
  ### 1.12.1 ###
161
 
162
- * Corrects an issue where an invalid hook callback isn't always identified
163
  * Various code quality improvements
164
 
165
  ### 1.12.0 ###
166
 
167
- * Fix the PHP cron event management.
168
  * More "namespacing" of query variables to avoid conflicts with other cron management plugins.
169
 
170
  ### 1.11.0 ###
171
 
172
- * Introduced an `Export` feature to the event listing screen for exporting the list of events as a CSV file.
173
- * Added the timezone offset to the date displayed for events that are due to run after the next DST change, for extra clarity.
174
- * Introduced the `crontrol/filter-types` and `crontrol/filtered-events` filters for adjusting the available event filters on the event listing screen.
175
- * Lots of code quality improvements (thanks, PHPStan!).
176
 
177
 
178
  ### 1.10.0 ###
179
 
180
- * Support for more granular cron-related error messages in WordPress 5.7
181
- * Several accessibility improvements
182
- * Warning for events that are attached to [a schedule that is too frequent](https://github.com/johnbillion/wp-crontrol/wiki/This-interval-is-less-than-the-WP_CRON_LOCK_TIMEOUT-constant)
183
- * More clarity around events and schedules that are built in to WordPress core
184
- * Add a Help tab with links to the wiki and FAQs
185
 
186
 
187
  ### 1.9.1 ###
@@ -190,12 +208,12 @@ The photo was taken by <a href="https://www.flickr.com/photos/michaelpardo/21453
190
 
191
  ### 1.9.0 ###
192
 
193
- * Add filters and sorting to the event listing screen. Props @yuriipavlov.
194
- * Replace the "Add New" tabs with a more standard "Add New" button on the cron event listing page.
195
- * Switch back to using browser-native controls for the date and time inputs.
196
- * Add an error message when trying to edit a non-existent event.
197
- * Introduce an informational message which appears when there are events that have missed their schedule.
198
- * Fire actions when cron events and schedules are added, updated, and deleted.
199
 
200
 
201
  ### 1.8.5 ###
@@ -204,7 +222,7 @@ The photo was taken by <a href="https://www.flickr.com/photos/michaelpardo/21453
204
 
205
  ### 1.8.4 ###
206
 
207
- * Add a warning message if the default timezone has been changed. <a href="https://github.com/johnbillion/wp-crontrol/wiki/PHP-default-timezone-is-not-set-to-UTC">More information</a>.
208
  * Fixed string being passed to `strtotime()` function when the `Now` option is chosen when adding or editing an event.
209
 
210
  ### 1.8.3 ###
@@ -213,27 +231,27 @@ The photo was taken by <a href="https://www.flickr.com/photos/michaelpardo/21453
213
 
214
  ### 1.8.2 ###
215
 
216
- * Bypass the duplicate event check when manually running an event. This allows an event to manually run even if it's due within ten minutes or if it's overdue.
217
- * Force only one event to fire when manually running a cron event.
218
- * Introduce polling of the events list in order to show a warning when the event listing screen is out of date.
219
- * Add a warning for cron schedules which are shorter than `WP_CRON_LOCK_TIMEOUT`.
220
- * Add the Site Health check event to the list of persistent core hooks.
221
 
222
 
223
  ### 1.8.1 ###
224
 
225
- * Fix the bottom bulk action menu on the event listing screen.
226
  * Make the timezone more prominent when adding or editing a cron event.
227
 
228
  ### 1.8.0 ###
229
 
230
- * Searching and pagination for cron events
231
- * Ability to delete all cron events with a given hook
232
- * More accurate response messages when managing events (in WordPress 5.1+)
233
- * Visual warnings for events without actions, and PHP events with syntax errors
234
- * Timezone-related clarifications and fixes
235
- * A more unified UI
236
- * Modernised codebase
237
 
238
 
239
  ### 1.7.1 ###
@@ -242,16 +260,16 @@ The photo was taken by <a href="https://www.flickr.com/photos/michaelpardo/21453
242
 
243
  ### 1.7.0 ###
244
 
245
- * Remove the `date` and `time` inputs and replace with a couple of preset options and a plain text field. Fixes #24 .
246
- * Ensure the schedule name is always correct when multiple schedules exist with the same interval. Add error handling. Fixes #25.
247
- * Re-introduce the display of the current site time.
248
- * Use a more appropriate HTTP response code for unauthorised request errors.
249
 
250
 
251
  ### 1.6.2 ###
252
 
253
- * Remove the ability to delete a PHP cron event if the user cannot edit files.
254
- * Remove the `Edit` link for PHP cron events when the user cannot edit the event.
255
  * Avoid a PHP notice due to an undefined variable when adding a new cron event.
256
 
257
  ### 1.6.1 ###
@@ -260,22 +278,22 @@ The photo was taken by <a href="https://www.flickr.com/photos/michaelpardo/21453
260
 
261
  ### 1.6 ###
262
 
263
- * Introduce bulk deletion of cron events. Yay!
264
- * Show the schedule name instead of the schedule interval next to each event.
265
- * Add core's new `delete_expired_transients` event to the list of core events.
266
- * Don't allow custom cron schedules to be deleted if they're in use.
267
- * Add links between the Events and Schedules admin screens.
268
- * Add syntax highlighting to the PHP code editor for a PHP cron event.
269
- * Styling fixes for events with many arguments or long arguments.
270
- * Improvements to help text.
271
- * Remove usage of `create_function()`.
272
  * Fix some translator comments, improve i18n, improve coding standards.
273
 
274
  ### 1.5.0 ###
275
 
276
- * Show the hooked actions for each cron event.
277
- * Don't show the `Delete` link for core's built-in cron events, as they get re-populated immediately.
278
- * Correct the success message after adding or editing PHP cron events.
279
  * Correct the translations directory name.
280
 
281
  ### 1.4 ###
@@ -341,12 +359,3 @@ The photo was taken by <a href="https://www.flickr.com/photos/michaelpardo/21453
341
  - Non-repeating cron events
342
  - Handles cron events with arguments
343
 
344
-
345
- ### 0.3 ###
346
-
347
- - Internationalization
348
- - Editing/deleting/execution of cron events
349
- - More text, status messages, etc.
350
- - Allow a user to enter a schedule event in a human manner
351
- - Looks better on WordPress 2.5
352
-
4
  Tags: cron, wp-cron, crontrol, debug
5
  Requires at least: 4.2
6
  Tested up to: 6.0
7
+ Stable tag: 1.13.0
8
+ Requires PHP: 5.6
9
  Donate link: https://github.com/sponsors/johnbillion
10
 
11
  WP Crontrol enables you to view and control what's happening in the WP-Cron system.
15
  WP Crontrol enables you to view and control what's happening in the WP-Cron system. From the admin screens you can:
16
 
17
  * View all cron events along with their arguments, recurrence, callback functions, and when they are next due.
18
+ * Edit, delete, pause, resume, and immediately run cron events.
19
  * Add new cron events.
20
  * Bulk delete cron events.
21
  * Add and remove custom cron schedules.
51
 
52
  ### Why do some cron events reappear shortly after I delete them?
53
 
54
+ If the event is added by a plugin then the plugin most likely rescheduled the event as soon as it saw that the event was missing. To get around this you can instead use the "Pause" option for the event which means it'll remain in place but won't perform any action when it runs.
55
 
56
  ### Is it safe to delete cron events?
57
 
63
 
64
  The WordPress core software uses cron events for some of its functionality and removing these events is not possible because WordPress would immediately reschedule them if you did delete them. For this reason, WP Crontrol doesn't let you delete these persistent events from WordPress core in the first place.
65
 
66
+ If you don't want these events to run, you can "Pause" them instead.
67
+
68
+ ### What happens when I pause an event?
69
+
70
+ Pausing an event will disable all actions attached to the event's hook. The event itself will remain in place and will run according to its schedule, but all actions attached to its hook will be disabled. This renders the event inoperative but keeps it scheduled so as to remain fully compatible with events which would otherwise get automatically rescheduled when they're missing.
71
+
72
+ As pausing an event actually pauses its hook, all events that use the same hook will be paused or resumed when pausing and resuming an event. This is much more useful and reliable than pausing individual events separately.
73
+
74
+ ### What happens when I resume an event?
75
+
76
+ Resuming an event re-enables all actions attached to the event's hook. All events that use the same hook will be resumed.
77
+
78
  ### What does it mean when "None" is shown for the Action of a cron event?
79
 
80
  This means the cron event is scheduled to run at the specified time but there is no corresponding functionality that will be triggered when the event runs, therefore the event is useless.
169
 
170
  ## Changelog ##
171
 
172
+ ### 1.13.0 ###
173
+
174
+ * Introduces the ability to pause and resume cron events from the event listing screen; see the FAQ for full details
175
+ * Implements an autoloader to reduce memory usage
176
+ * Bumps the minimum supported version of PHP to 5.6
177
+
178
  ### 1.12.1 ###
179
 
180
+ * Corrects an issue where an invalid hook callback isn't always identified
181
  * Various code quality improvements
182
 
183
  ### 1.12.0 ###
184
 
185
+ * Fix the PHP cron event management.
186
  * More "namespacing" of query variables to avoid conflicts with other cron management plugins.
187
 
188
  ### 1.11.0 ###
189
 
190
+ * Introduced an `Export` feature to the event listing screen for exporting the list of events as a CSV file.
191
+ * Added the timezone offset to the date displayed for events that are due to run after the next DST change, for extra clarity.
192
+ * Introduced the `crontrol/filter-types` and `crontrol/filtered-events` filters for adjusting the available event filters on the event listing screen.
193
+ * Lots of code quality improvements (thanks, PHPStan!).
194
 
195
 
196
  ### 1.10.0 ###
197
 
198
+ * Support for more granular cron-related error messages in WordPress 5.7
199
+ * Several accessibility improvements
200
+ * Warning for events that are attached to [a schedule that is too frequent](https://github.com/johnbillion/wp-crontrol/wiki/This-interval-is-less-than-the-WP_CRON_LOCK_TIMEOUT-constant)
201
+ * More clarity around events and schedules that are built in to WordPress core
202
+ * Add a Help tab with links to the wiki and FAQs
203
 
204
 
205
  ### 1.9.1 ###
208
 
209
  ### 1.9.0 ###
210
 
211
+ * Add filters and sorting to the event listing screen. Props @yuriipavlov.
212
+ * Replace the "Add New" tabs with a more standard "Add New" button on the cron event listing page.
213
+ * Switch back to using browser-native controls for the date and time inputs.
214
+ * Add an error message when trying to edit a non-existent event.
215
+ * Introduce an informational message which appears when there are events that have missed their schedule.
216
+ * Fire actions when cron events and schedules are added, updated, and deleted.
217
 
218
 
219
  ### 1.8.5 ###
222
 
223
  ### 1.8.4 ###
224
 
225
+ * Add a warning message if the default timezone has been changed. <a href="https://github.com/johnbillion/wp-crontrol/wiki/PHP-default-timezone-is-not-set-to-UTC">More information</a>.
226
  * Fixed string being passed to `strtotime()` function when the `Now` option is chosen when adding or editing an event.
227
 
228
  ### 1.8.3 ###
231
 
232
  ### 1.8.2 ###
233
 
234
+ * Bypass the duplicate event check when manually running an event. This allows an event to manually run even if it's due within ten minutes or if it's overdue.
235
+ * Force only one event to fire when manually running a cron event.
236
+ * Introduce polling of the events list in order to show a warning when the event listing screen is out of date.
237
+ * Add a warning for cron schedules which are shorter than `WP_CRON_LOCK_TIMEOUT`.
238
+ * Add the Site Health check event to the list of persistent core hooks.
239
 
240
 
241
  ### 1.8.1 ###
242
 
243
+ * Fix the bottom bulk action menu on the event listing screen.
244
  * Make the timezone more prominent when adding or editing a cron event.
245
 
246
  ### 1.8.0 ###
247
 
248
+ * Searching and pagination for cron events
249
+ * Ability to delete all cron events with a given hook
250
+ * More accurate response messages when managing events (in WordPress 5.1+)
251
+ * Visual warnings for events without actions, and PHP events with syntax errors
252
+ * Timezone-related clarifications and fixes
253
+ * A more unified UI
254
+ * Modernised codebase
255
 
256
 
257
  ### 1.7.1 ###
260
 
261
  ### 1.7.0 ###
262
 
263
+ * Remove the `date` and `time` inputs and replace with a couple of preset options and a plain text field. Fixes #24 .
264
+ * Ensure the schedule name is always correct when multiple schedules exist with the same interval. Add error handling. Fixes #25.
265
+ * Re-introduce the display of the current site time.
266
+ * Use a more appropriate HTTP response code for unauthorised request errors.
267
 
268
 
269
  ### 1.6.2 ###
270
 
271
+ * Remove the ability to delete a PHP cron event if the user cannot edit files.
272
+ * Remove the `Edit` link for PHP cron events when the user cannot edit the event.
273
  * Avoid a PHP notice due to an undefined variable when adding a new cron event.
274
 
275
  ### 1.6.1 ###
278
 
279
  ### 1.6 ###
280
 
281
+ * Introduce bulk deletion of cron events. Yay!
282
+ * Show the schedule name instead of the schedule interval next to each event.
283
+ * Add core's new `delete_expired_transients` event to the list of core events.
284
+ * Don't allow custom cron schedules to be deleted if they're in use.
285
+ * Add links between the Events and Schedules admin screens.
286
+ * Add syntax highlighting to the PHP code editor for a PHP cron event.
287
+ * Styling fixes for events with many arguments or long arguments.
288
+ * Improvements to help text.
289
+ * Remove usage of `create_function()`.
290
  * Fix some translator comments, improve i18n, improve coding standards.
291
 
292
  ### 1.5.0 ###
293
 
294
+ * Show the hooked actions for each cron event.
295
+ * Don't show the `Delete` link for core's built-in cron events, as they get re-populated immediately.
296
+ * Correct the success message after adding or editing PHP cron events.
297
  * Correct the translations directory name.
298
 
299
  ### 1.4 ###
359
  - Non-repeating cron events
360
  - Handles cron events with arguments
361
 
 
 
 
 
 
 
 
 
 
src/event-list-table.php CHANGED
@@ -136,6 +136,14 @@ class Table extends \WP_List_Table {
136
  return ( ! in_array( $event->hook, $all_core_hooks, true ) );
137
  } );
138
 
 
 
 
 
 
 
 
 
139
  /**
140
  * Filters the available filtered events on the cron event listing screen.
141
  *
@@ -222,6 +230,7 @@ class Table extends \WP_List_Table {
222
  'noaction' => __( 'Events with no action', 'wp-crontrol' ),
223
  'core' => __( 'WordPress core events', 'wp-crontrol' ),
224
  'custom' => __( 'Custom events', 'wp-crontrol' ),
 
225
  );
226
 
227
  /**
@@ -305,7 +314,7 @@ class Table extends \WP_List_Table {
305
  $callbacks = \Crontrol\get_hook_callbacks( $event->hook );
306
 
307
  if ( ! $callbacks ) {
308
- $classes[] = 'crontrol-warning';
309
  } else {
310
  foreach ( $callbacks as $callback ) {
311
  if ( ! empty( $callback['callback']['error'] ) ) {
@@ -319,6 +328,10 @@ class Table extends \WP_List_Table {
319
  $classes[] = 'crontrol-warning';
320
  }
321
 
 
 
 
 
322
  printf(
323
  '<tr class="%s">',
324
  esc_attr( implode( ' ', $classes ) )
@@ -356,17 +369,43 @@ class Table extends \WP_List_Table {
356
  $links[] = "<a href='" . esc_url( $link ) . "'>" . esc_html__( 'Edit', 'wp-crontrol' ) . '</a>';
357
  }
358
 
359
- $link = array(
360
- 'page' => 'crontrol_admin_manage_page',
361
- 'crontrol_action' => 'run-cron',
362
- 'crontrol_id' => rawurlencode( $event->hook ),
363
- 'crontrol_sig' => rawurlencode( $event->sig ),
364
- 'crontrol_next_run_utc' => rawurlencode( $event->time ),
365
- );
366
- $link = add_query_arg( $link, admin_url( 'tools.php' ) );
367
- $link = wp_nonce_url( $link, "crontrol-run-cron_{$event->hook}_{$event->sig}" );
 
368
 
369
- $links[] = "<a href='" . esc_url( $link ) . "'>" . esc_html__( 'Run Now', 'wp-crontrol' ) . '</a>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
 
371
  if ( ! in_array( $event->hook, self::$persistent_core_hooks, true ) && ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_manage_php_crons ) ) {
372
  $link = array(
@@ -455,7 +494,17 @@ class Table extends \WP_List_Table {
455
  }
456
  }
457
 
458
- return esc_html( $event->hook );
 
 
 
 
 
 
 
 
 
 
459
  }
460
 
461
  /**
136
  return ( ! in_array( $event->hook, $all_core_hooks, true ) );
137
  } );
138
 
139
+ $paused = array_filter( $events, function( $event ) {
140
+ return ( is_paused( $event ) );
141
+ } );
142
+
143
+ if ( count( $paused ) > 0 ) {
144
+ $filtered['paused'] = $paused;
145
+ }
146
+
147
  /**
148
  * Filters the available filtered events on the cron event listing screen.
149
  *
230
  'noaction' => __( 'Events with no action', 'wp-crontrol' ),
231
  'core' => __( 'WordPress core events', 'wp-crontrol' ),
232
  'custom' => __( 'Custom events', 'wp-crontrol' ),
233
+ 'paused' => __( 'Paused events', 'wp-crontrol' ),
234
  );
235
 
236
  /**
314
  $callbacks = \Crontrol\get_hook_callbacks( $event->hook );
315
 
316
  if ( ! $callbacks ) {
317
+ $classes[] = 'crontrol-no-action';
318
  } else {
319
  foreach ( $callbacks as $callback ) {
320
  if ( ! empty( $callback['callback']['error'] ) ) {
328
  $classes[] = 'crontrol-warning';
329
  }
330
 
331
+ if ( is_paused( $event ) ) {
332
+ $classes[] = 'crontrol-paused';
333
+ }
334
+
335
  printf(
336
  '<tr class="%s">',
337
  esc_attr( implode( ' ', $classes ) )
369
  $links[] = "<a href='" . esc_url( $link ) . "'>" . esc_html__( 'Edit', 'wp-crontrol' ) . '</a>';
370
  }
371
 
372
+ if ( ! is_paused( $event ) ) {
373
+ $link = array(
374
+ 'page' => 'crontrol_admin_manage_page',
375
+ 'crontrol_action' => 'run-cron',
376
+ 'crontrol_id' => rawurlencode( $event->hook ),
377
+ 'crontrol_sig' => rawurlencode( $event->sig ),
378
+ 'crontrol_next_run_utc' => rawurlencode( $event->time ),
379
+ );
380
+ $link = add_query_arg( $link, admin_url( 'tools.php' ) );
381
+ $link = wp_nonce_url( $link, "crontrol-run-cron_{$event->hook}_{$event->sig}" );
382
 
383
+ $links[] = "<a href='" . esc_url( $link ) . "'>" . esc_html__( 'Run Now', 'wp-crontrol' ) . '</a>';
384
+ }
385
+
386
+ if ( is_paused( $event ) ) {
387
+ $link = array(
388
+ 'page' => 'crontrol_admin_manage_page',
389
+ 'crontrol_action' => 'resume-hook',
390
+ 'crontrol_id' => rawurlencode( $event->hook ),
391
+ );
392
+ $link = add_query_arg( $link, admin_url( 'tools.php' ) );
393
+ $link = wp_nonce_url( $link, "crontrol-resume-hook_{$event->hook}" );
394
+
395
+ /* translators: Verb */
396
+ $links[] = "<a href='" . esc_url( $link ) . "'>" . esc_html__( 'Resume', 'wp-crontrol' ) . '</a>';
397
+ } elseif ( 'crontrol_cron_job' !== $event->hook ) {
398
+ $link = array(
399
+ 'page' => 'crontrol_admin_manage_page',
400
+ 'crontrol_action' => 'pause-hook',
401
+ 'crontrol_id' => rawurlencode( $event->hook ),
402
+ );
403
+ $link = add_query_arg( $link, admin_url( 'tools.php' ) );
404
+ $link = wp_nonce_url( $link, "crontrol-pause-hook_{$event->hook}" );
405
+
406
+ /* translators: Verb */
407
+ $links[] = "<a href='" . esc_url( $link ) . "'>" . esc_html__( 'Pause', 'wp-crontrol' ) . '</a>';
408
+ }
409
 
410
  if ( ! in_array( $event->hook, self::$persistent_core_hooks, true ) && ( ( 'crontrol_cron_job' !== $event->hook ) || self::$can_manage_php_crons ) ) {
411
  $link = array(
494
  }
495
  }
496
 
497
+ $output = esc_html( $event->hook );
498
+
499
+ if ( is_paused( $event ) ) {
500
+ $output .= sprintf(
501
+ ' &mdash; <strong class="status-crontrol-paused post-state"><span class="dashicons dashicons-controls-pause" aria-hidden="true"></span> %s</strong>',
502
+ /* translators: State of a cron event, adjective */
503
+ esc_html__( 'Paused', 'wp-crontrol' )
504
+ );
505
+ }
506
+
507
+ return $output;
508
  }
509
 
510
  /**
src/event.php CHANGED
@@ -9,6 +9,8 @@ use stdClass;
9
  use Crontrol\Schedule;
10
  use WP_Error;
11
 
 
 
12
  /**
13
  * Executes a cron event immediately.
14
  *
@@ -244,6 +246,72 @@ function delete( $hook, $sig, $next_run_utc ) {
244
  return true;
245
  }
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  /**
248
  * Returns a flattened array of cron events.
249
  *
@@ -391,6 +459,22 @@ function is_late( stdClass $event ) {
391
  return ( $until < ( 0 - ( 10 * MINUTE_IN_SECONDS ) ) );
392
  }
393
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  /**
395
  * Initialises and returns the list table for events.
396
  *
@@ -400,8 +484,6 @@ function get_list_table() {
400
  static $table = null;
401
 
402
  if ( ! $table ) {
403
- require_once __DIR__ . '/event-list-table.php';
404
-
405
  $table = new Table();
406
  $table->prepare_items();
407
 
9
  use Crontrol\Schedule;
10
  use WP_Error;
11
 
12
+ use const Crontrol\PAUSED_OPTION;
13
+
14
  /**
15
  * Executes a cron event immediately.
16
  *
246
  return true;
247
  }
248
 
249
+ /**
250
+ * Pauses a cron event.
251
+ *
252
+ * @param string $hook The hook name of the event to pause.
253
+ * @return true|WP_Error True if the pause was successful, WP_Error otherwise.
254
+ */
255
+ function pause( $hook ) {
256
+ $paused = get_option( PAUSED_OPTION, array() );
257
+
258
+ if ( ! is_array( $paused ) ) {
259
+ $paused = array();
260
+ }
261
+
262
+ $paused[ $hook ] = true;
263
+
264
+ $result = update_option( PAUSED_OPTION, $paused, false );
265
+
266
+ if ( false === $result ) {
267
+ return new WP_Error(
268
+ 'could_not_pause',
269
+ sprintf(
270
+ /* translators: 1: The name of the cron event. */
271
+ __( 'Failed to the pause the cron event %s.', 'wp-crontrol' ),
272
+ $hook
273
+ )
274
+ );
275
+ }
276
+
277
+ return true;
278
+ }
279
+
280
+ /**
281
+ * Resumes a paused cron event.
282
+ *
283
+ * @param string $hook The hook name of the event to resume.
284
+ * @return true|WP_Error True if the resumption was successful, WP_Error otherwise.
285
+ */
286
+ function resume( $hook ) {
287
+ $paused = get_option( PAUSED_OPTION );
288
+
289
+ if ( ! is_array( $paused ) || ( count( $paused ) === 0 ) ) {
290
+ return true;
291
+ }
292
+
293
+ unset( $paused[ $hook ] );
294
+
295
+ if ( count( $paused ) === 0 ) {
296
+ $result = delete_option( PAUSED_OPTION );
297
+ } else {
298
+ $result = update_option( PAUSED_OPTION, $paused, false );
299
+ }
300
+
301
+ if ( false === $result ) {
302
+ return new WP_Error(
303
+ 'could_not_resume',
304
+ sprintf(
305
+ /* translators: 1: The name of the cron event. */
306
+ __( 'Failed to the resume the cron event %s.', 'wp-crontrol' ),
307
+ $hook
308
+ )
309
+ );
310
+ }
311
+
312
+ return true;
313
+ }
314
+
315
  /**
316
  * Returns a flattened array of cron events.
317
  *
459
  return ( $until < ( 0 - ( 10 * MINUTE_IN_SECONDS ) ) );
460
  }
461
 
462
+ /**
463
+ * Determines whether an event is paused.
464
+ *
465
+ * @param stdClass $event The event.
466
+ * @return bool Whether the event is paused.
467
+ */
468
+ function is_paused( stdClass $event ) {
469
+ $paused = get_option( PAUSED_OPTION );
470
+
471
+ if ( ! is_array( $paused ) ) {
472
+ return false;
473
+ }
474
+
475
+ return array_key_exists( $event->hook, $paused );
476
+ }
477
+
478
  /**
479
  * Initialises and returns the list table for events.
480
  *
484
  static $table = null;
485
 
486
  if ( ! $table ) {
 
 
487
  $table = new Table();
488
  $table->prepare_items();
489
 
wp-crontrol.php CHANGED
@@ -5,10 +5,10 @@
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.1
9
  * Text Domain: wp-crontrol
10
  * Domain Path: /languages/
11
- * Requires PHP: 5.3.6
12
  * License: GPL v2 or later
13
  *
14
  * LICENSE
@@ -42,11 +42,12 @@ if ( ! defined( 'ABSPATH' ) ) {
42
  exit;
43
  }
44
 
 
45
  require_once __DIR__ . '/src/event.php';
46
- require_once __DIR__ . '/src/request.php';
47
  require_once __DIR__ . '/src/schedule.php';
48
 
49
  const TRANSIENT = 'crontrol-message-%d';
 
50
 
51
  /**
52
  * Hook onto all of the actions and filters needed by the plugin.
@@ -128,6 +129,22 @@ function filter_plugin_row_meta( array $plugin_meta, $plugin_file ) {
128
  */
129
  function action_init() {
130
  load_plugin_textdomain( 'wp-crontrol', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
132
 
133
  /**
@@ -639,13 +656,95 @@ function action_handle_posts() {
639
  $redirect['crontrol_message'] = 'error';
640
  }
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',
@@ -886,8 +985,6 @@ function admin_options_page() {
886
  );
887
  }
888
 
889
- require_once __DIR__ . '/src/schedule-list-table.php';
890
-
891
  $table = new Schedule_List_Table();
892
 
893
  $table->prepare_items();
@@ -1546,6 +1643,16 @@ function admin_manage_page() {
1546
  __( 'Failed to save the cron event %s.', 'wp-crontrol' ),
1547
  'error',
1548
  ),
 
 
 
 
 
 
 
 
 
 
1549
  'error' => array(
1550
  __( 'An unknown error occurred.', 'wp-crontrol' ),
1551
  'error',
@@ -1742,6 +1849,10 @@ function get_hook_callbacks( $name ) {
1742
  foreach ( $callbacks as $callback ) {
1743
  $callback = populate_callback( $callback );
1744
 
 
 
 
 
1745
  $actions[] = array(
1746
  'priority' => $priority,
1747
  'callback' => $callback,
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.13.0
9
  * Text Domain: wp-crontrol
10
  * Domain Path: /languages/
11
+ * Requires PHP: 5.6
12
  * License: GPL v2 or later
13
  *
14
  * LICENSE
42
  exit;
43
  }
44
 
45
+ require_once __DIR__ . '/vendor/autoload.php';
46
  require_once __DIR__ . '/src/event.php';
 
47
  require_once __DIR__ . '/src/schedule.php';
48
 
49
  const TRANSIENT = 'crontrol-message-%d';
50
+ const PAUSED_OPTION = 'wp_crontrol_paused';
51
 
52
  /**
53
  * Hook onto all of the actions and filters needed by the plugin.
129
  */
130
  function action_init() {
131
  load_plugin_textdomain( 'wp-crontrol', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
132
+
133
+ /** @var array<string, true>|false $paused */
134
+ $paused = get_option( PAUSED_OPTION, array() );
135
+
136
+ if ( is_array( $paused ) ) {
137
+ foreach ( $paused as $hook => $value ) {
138
+ add_action( $hook, __NAMESPACE__ . '\\pauser', -99999 );
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * @return void
145
+ */
146
+ function pauser() {
147
+ remove_all_actions( current_filter() );
148
  }
149
 
150
  /**
656
  $redirect['crontrol_message'] = 'error';
657
  }
658
 
659
+ wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
660
+ exit;
661
+ } elseif ( isset( $_GET['crontrol_action'] ) && 'pause-hook' === $_GET['crontrol_action'] ) {
662
+ if ( ! current_user_can( 'manage_options' ) ) {
663
+ wp_die( esc_html__( 'You are not allowed to pause or resume cron events.', 'wp-crontrol' ), 401 );
664
+ }
665
+
666
+ $hook = wp_unslash( $_GET['crontrol_id'] );
667
+ check_admin_referer( "crontrol-pause-hook_{$hook}" );
668
+
669
+ $paused = Event\pause( $hook );
670
+
671
+ $redirect = array(
672
+ 'page' => 'crontrol_admin_manage_page',
673
+ 'crontrol_message' => '11',
674
+ 'crontrol_name' => rawurlencode( $hook ),
675
+ );
676
+
677
+ if ( is_wp_error( $paused ) ) {
678
+ $set = set_message( $paused->get_error_message() );
679
+
680
+ // If we can't store the error message in a transient, just display it.
681
+ if ( ! $set ) {
682
+ wp_die(
683
+ esc_html( $paused->get_error_message() ),
684
+ '',
685
+ array(
686
+ 'response' => 500,
687
+ 'back_link' => true,
688
+ )
689
+ );
690
+ }
691
+ $redirect['crontrol_message'] = 'error';
692
+ } else {
693
+ /**
694
+ * Fires after a cron event hook is paused.
695
+ *
696
+ * @param string $hook The event hook name.
697
+ */
698
+ do_action( 'crontrol/paused_hook', $hook );
699
+ }
700
+
701
+ wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
702
+ exit;
703
+ } elseif ( isset( $_GET['crontrol_action'] ) && 'resume-hook' === $_GET['crontrol_action'] ) {
704
+ if ( ! current_user_can( 'manage_options' ) ) {
705
+ wp_die( esc_html__( 'You are not allowed to pause or resume cron events.', 'wp-crontrol' ), 401 );
706
+ }
707
+
708
+ $hook = wp_unslash( $_GET['crontrol_id'] );
709
+ check_admin_referer( "crontrol-resume-hook_{$hook}" );
710
+
711
+ $resumed = Event\resume( $hook );
712
+
713
+ $redirect = array(
714
+ 'page' => 'crontrol_admin_manage_page',
715
+ 'crontrol_message' => '12',
716
+ 'crontrol_name' => rawurlencode( $hook ),
717
+ );
718
+
719
+ if ( is_wp_error( $resumed ) ) {
720
+ $set = set_message( $resumed->get_error_message() );
721
+
722
+ // If we can't store the error message in a transient, just display it.
723
+ if ( ! $set ) {
724
+ wp_die(
725
+ esc_html( $resumed->get_error_message() ),
726
+ '',
727
+ array(
728
+ 'response' => 500,
729
+ 'back_link' => true,
730
+ )
731
+ );
732
+ }
733
+ $redirect['crontrol_message'] = 'error';
734
+ } else {
735
+ /**
736
+ * Fires after a paused cron event hook is resumed.
737
+ *
738
+ * @param string $hook The event hook name.
739
+ */
740
+ do_action( 'crontrol/resumed_hook', $hook );
741
+ }
742
+
743
  wp_safe_redirect( add_query_arg( $redirect, admin_url( 'tools.php' ) ) );
744
  exit;
745
  } elseif ( isset( $_POST['crontrol_action'] ) && 'export-event-csv' === $_POST['crontrol_action'] ) {
746
  check_admin_referer( 'crontrol-export-event-csv', 'crontrol_nonce' );
747
 
 
 
748
  $type = isset( $_POST['crontrol_hooks_type'] ) ? $_POST['crontrol_hooks_type'] : 'all';
749
  $headers = array(
750
  'hook',
985
  );
986
  }
987
 
 
 
988
  $table = new Schedule_List_Table();
989
 
990
  $table->prepare_items();
1643
  __( 'Failed to save the cron event %s.', 'wp-crontrol' ),
1644
  'error',
1645
  ),
1646
+ '11' => array(
1647
+ /* translators: 1: The name of the cron event. */
1648
+ __( 'Paused the %s hook.', 'wp-crontrol' ),
1649
+ 'success',
1650
+ ),
1651
+ '12' => array(
1652
+ /* translators: 1: The name of the cron event. */
1653
+ __( 'Resumed the %s hook.', 'wp-crontrol' ),
1654
+ 'success',
1655
+ ),
1656
  'error' => array(
1657
  __( 'An unknown error occurred.', 'wp-crontrol' ),
1658
  'error',
1849
  foreach ( $callbacks as $callback ) {
1850
  $callback = populate_callback( $callback );
1851
 
1852
+ if ( __NAMESPACE__ . '\\pauser' === $callback['function'] ) {
1853
+ continue;
1854
+ }
1855
+
1856
  $actions[] = array(
1857
  'priority' => $priority,
1858
  'callback' => $callback,