├── css
├── index.php
└── wp-autoupdates.css
├── index.php
├── license.txt
├── readme.md
├── readme.txt
└── wp-autoupdates.php
/css/index.php:
--------------------------------------------------------------------------------
1 | ';
43 | $autoupdate_text .= $update_message;
44 | $autoupdate_text .= ' ';
45 | $script .= 'jQuery(".check-column input[value=\'' . $theme . '\']").closest("tr").find(".plugin-title > p").append(\'' . $autoupdate_text . '\');';
46 | }
47 | }
48 |
49 | if ( wp_autoupdates_is_plugins_auto_update_enabled() ) {
50 | $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() );
51 |
52 | $update_message = wp_autoupdates_get_update_message();
53 | foreach ( $wp_auto_update_plugins as $plugin ) {
54 | $autoupdate_text = ' ';
55 | $autoupdate_text .= $update_message;
56 | $autoupdate_text .= ' ';
57 | $script .= 'jQuery(".check-column input[value=\'' . $plugin . '\']").closest("tr").find(".plugin-title > p").append(\'' . $autoupdate_text . '\');';
58 | }
59 | }
60 | $script .= '});';
61 | wp_add_inline_script( 'jquery', $script );
62 | }
63 |
64 | // When manually updating a plugin the 'time until next update' text needs to be hidden.
65 | // Doesn't need to be done on the update-core.php page since that page refreshes after an update.
66 | if ( 'plugins.php' === $hook ) {
67 | $script = 'jQuery( document ).ready(function() {
68 | jQuery( ".update-link" ).click( function() {
69 | var plugin = jQuery( this ).closest("tr").data("plugin");
70 | var plugin_row = jQuery( "tr.update[data-plugin=\'" + plugin + "\']" );
71 | var plugin_auto_update_time_text = plugin_row.find("span.plugin-autoupdate-time");
72 | plugin_auto_update_time_text.remove();
73 | });
74 | });';
75 | wp_add_inline_script( 'jquery', $script );
76 | }
77 |
78 | if ( 'themes.php' === $hook ) {
79 | if ( wp_autoupdates_is_themes_auto_update_enabled() ) {
80 | $script = 'jQuery( document ).ready( function() {';
81 |
82 | /* translators: %s: Theme name. */
83 | $aria_label_enable = sprintf( _x( 'Enable automatic update for %s', 'theme' ), '{{ data.name }}' );
84 | $aria_label_disable = sprintf( _x( 'Disable automatic update for %s', 'theme' ), '{{ data.name }}' );
85 |
86 | // Put the enable/disable link below the author and before the update box.
87 | $autoupdate_text = '
<# if ( data.autoupdate ) { #>';
88 | $autoupdate_text .= '';
89 | $autoupdate_text .= ' ' . __( 'Disable automatic updates' ) . '';
90 | $autoupdate_text .= '';
91 | $autoupdate_text .= '<# } else { #>';
92 | $autoupdate_text .= '';
93 | $autoupdate_text .= ' ' . __( 'Enable automatic updates' ) . '';
94 | $autoupdate_text .= '';
95 | $autoupdate_text .= '<# } #>
';
96 |
97 | $script .= ' const theme_template_single = jQuery( "#tmpl-theme-single" );
98 |
99 | // Pull template into new html element, manipulate, then put back.
100 | // Props https://stackoverflow.com/a/42248980.
101 | function insert_into_template(positioning_text, added_text, insert_before) {
102 | var template_text = theme_template_single.text();
103 | var position = template_text.search(positioning_text);
104 | if ( -1 !== position ) {
105 | if ( true !== insert_before ) {
106 | position += positioning_text.length;
107 | }
108 |
109 | const new_template_text = template_text.substr(0, position) + added_text + template_text.substr(position);
110 | theme_template_single.text( new_template_text );
111 | }
112 | }
113 |
114 | const position_beginning_of_update_box = "<# if \\\\( data.hasUpdate \\\\) { #>";
115 | insert_into_template(position_beginning_of_update_box, "' . str_replace('"', '\"', $autoupdate_text) . '", true);
116 | ';
117 |
118 | // Put the time until next update within the data.hasUpdate block.
119 | $update_message = wp_autoupdates_get_update_message();
120 | $autoupdate_time_text = '<# if ( data.autoupdate ) { #>';
121 | $autoupdate_time_text .= '' . $update_message . '
';
122 | $autoupdate_time_text .= '<# } #>';
123 |
124 | $script .= '
125 | const position_data_update = "{{{ data.update }}}";
126 | insert_into_template(position_data_update, "' . str_replace('"', '\"', $autoupdate_time_text) . '", false);
127 | ';
128 |
129 | $script .= '});';
130 | wp_add_inline_script( 'jquery', $script );
131 | }
132 | }
133 | }
134 | add_action( 'admin_enqueue_scripts', 'wp_autoupdates_enqueues' );
135 |
136 |
137 | /**
138 | * Filter the themes prepared for JavaScript, for themes.php.
139 | */
140 | function wp_autoupdates_prepare_themes_for_js( $prepared_themes ) {
141 | $wp_auto_update_themes = get_option( 'wp_auto_update_themes', array() );
142 | foreach( $prepared_themes as $theme ) {
143 | // Set extra data for use in the template.
144 | $slug = $theme['id'];
145 | $encoded_slug = urlencode( $slug );
146 |
147 | $theme['autoupdate'] = in_array( $slug, $wp_auto_update_themes, true );
148 | $theme['actions']['autoupdate'] = current_user_can( 'update_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=autoupdate&theme=' . $encoded_slug ), 'autoupdate-theme_' . $slug ) : null;
149 |
150 | $prepared_themes[ $slug ] = $theme;
151 | }
152 |
153 | return $prepared_themes;
154 | }
155 | add_action( 'wp_prepare_themes_for_js', 'wp_autoupdates_prepare_themes_for_js' );
156 |
157 |
158 | /**
159 | * Checks whether plugins manual auto-update is enabled.
160 | */
161 | function wp_autoupdates_is_plugins_auto_update_enabled() {
162 | $enabled = ! defined( 'WP_DISABLE_PLUGINS_AUTO_UPDATE' ) || ! WP_DISABLE_PLUGINS_AUTO_UPDATE;
163 |
164 | /**
165 | * Filters whether plugins manual auto-update is enabled.
166 | *
167 | * @param bool $enabled True if plugins auto-update is enabled, false otherwise.
168 | */
169 | return apply_filters( 'wp_plugins_auto_update_enabled', $enabled );
170 | }
171 |
172 |
173 | /**
174 | * Checks whether themes manual auto-update is enabled.
175 | */
176 | function wp_autoupdates_is_themes_auto_update_enabled() {
177 | $enabled = ! defined( 'WP_DISABLE_THEMES_AUTO_UPDATE' ) || ! WP_DISABLE_THEMES_AUTO_UPDATE;
178 |
179 | /**
180 | * Filters whether themes manual auto-update is enabled.
181 | *
182 | * @param bool $enabled True if themes auto-update is enabled, false otherwise.
183 | */
184 | return apply_filters( 'wp_themes_auto_update_enabled', $enabled );
185 | }
186 |
187 |
188 | /**
189 | * Autoupdate selected plugins.
190 | */
191 | function wp_autoupdates_selected_plugins( $update, $item ) {
192 | $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() );
193 | if ( in_array( $item->plugin, $wp_auto_update_plugins, true ) && wp_autoupdates_is_plugins_auto_update_enabled() ) {
194 | return true;
195 | } else {
196 | return $update;
197 | }
198 | }
199 | add_filter( 'auto_update_plugin', 'wp_autoupdates_selected_plugins', 10, 2 );
200 |
201 |
202 | /**
203 | * Autoupdate selected themes.
204 | */
205 | function wp_autoupdates_selected_themes( $update, $item ) {
206 | $wp_auto_update_themes = get_site_option( 'wp_auto_update_themes', array() );
207 | if ( in_array( $item->theme, $wp_auto_update_themes, true ) && wp_autoupdates_is_themes_auto_update_enabled() ) {
208 | return true;
209 | } else {
210 | return $update;
211 | }
212 | }
213 | add_filter( 'auto_update_theme', 'wp_autoupdates_selected_themes', 10, 2 );
214 |
215 |
216 | /**
217 | * Add autoupdate column to plugins screen.
218 | */
219 | function wp_autoupdates_add_plugins_autoupdates_column( $columns ) {
220 | if ( ! current_user_can( 'update_plugins' ) || ! wp_autoupdates_is_plugins_auto_update_enabled() ) {
221 | return $columns;
222 | }
223 | if ( ! isset( $_GET['plugin_status'] ) || ( 'mustuse' !== $_GET['plugin_status'] && 'dropins' !== $_GET['plugin_status'] ) ) {
224 | $columns['autoupdates_column'] = __( 'Automatic updates', 'wp-autoupdates' );
225 | }
226 | return $columns;
227 | }
228 | add_filter( is_multisite() ? 'manage_plugins-network_columns' : 'manage_plugins_columns', 'wp_autoupdates_add_plugins_autoupdates_column' );
229 |
230 | /**
231 | * Render autoupdate column’s content.
232 | */
233 | function wp_autoupdates_add_plugins_autoupdates_column_content( $column_name, $plugin_file, $plugin_data ) {
234 | if ( ! current_user_can( 'update_plugins' ) || ! wp_autoupdates_is_plugins_auto_update_enabled() ) {
235 | return;
236 | }
237 | if ( 'autoupdates_column' !== $column_name ) {
238 | return;
239 | }
240 | $plugins = get_plugins();
241 | $plugins_updates = get_site_transient( 'update_plugins' );
242 | $page = isset( $_GET['paged'] ) && ! empty( $_GET['paged'] ) ? wp_unslash( esc_html( $_GET['paged'] ) ) : '';
243 | $plugin_status = isset( $_GET['plugin_status'] ) && ! empty( $_GET['plugin_status'] ) ? wp_unslash( esc_html( $_GET['plugin_status'] ) ) : '';
244 | if ( wp_autoupdates_is_plugins_auto_update_enabled() ) {
245 | if ( ! isset( $plugins[ $plugin_file ] ) ) {
246 | return;
247 | }
248 | $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() );
249 | if ( in_array( $plugin_file, $wp_auto_update_plugins, true ) ) {
250 | $aria_label = esc_attr(
251 | sprintf(
252 | /* translators: Plugin name. */
253 | _x( 'Disable automatic updates for %s', 'plugin', 'wp-autoupdates' ),
254 | esc_html( $plugins[ $plugin_file ]['Name'] )
255 | )
256 | );
257 | echo '';
258 | echo '' . __( 'Auto-updates enabled', 'wp-autoupdates' ) . '';
259 | echo '
';
260 |
261 | $update_message = wp_autoupdates_get_update_message();
262 | if ( isset( $plugins_updates->response[$plugin_file] ) ) {
263 | echo '';
264 | echo $update_message;
265 | echo '
';
266 | echo '';
267 | }
268 | if ( current_user_can( 'update_plugins', $plugin_file ) ) {
269 | echo sprintf(
270 | '%s',
271 | wp_nonce_url( 'plugins.php?action=autoupdate&plugin=' . urlencode( $plugin_file ) . '&paged=' . $page . '&plugin_status=' . $plugin_status, 'autoupdate-plugin_' . $plugin_file ),
272 | $aria_label,
273 | __( 'Disable', 'wp-autoupdates' )
274 | );
275 | }
276 | echo '
';
277 | } else {
278 | if ( current_user_can( 'update_plugins', $plugin_file ) ) {
279 | $aria_label = esc_attr(
280 | sprintf(
281 | /* translators: Plugin name. */
282 | _x( 'Enable automatic updates for %s', 'plugin', 'wp-autoupdates' ),
283 | esc_html( $plugins[ $plugin_file ]['Name'] )
284 | )
285 | );
286 | echo '';
287 | echo sprintf(
288 | ' %s',
289 | wp_nonce_url( 'plugins.php?action=autoupdate&plugin=' . urlencode( $plugin_file ) . '&paged=' . $page . '&plugin_status=' . $plugin_status, 'autoupdate-plugin_' . $plugin_file ),
290 | $aria_label,
291 | __( 'Enable', 'wp-autoupdates' )
292 | );
293 | echo '
';
294 | }
295 | }
296 | }
297 | }
298 | add_action( 'manage_plugins_custom_column' , 'wp_autoupdates_add_plugins_autoupdates_column_content', 10, 3 );
299 |
300 |
301 | /**
302 | * Add plugins autoupdates bulk actions
303 | */
304 | function wp_autoupdates_plugins_bulk_actions( $actions ) {
305 | $actions['enable-autoupdate-selected'] = __( 'Enable auto-updates', 'wp-autoupdates' );
306 | $actions['disable-autoupdate-selected'] = __( 'Disable auto-updates', 'wp-autoupdates' );
307 | return $actions;
308 | }
309 | add_action( 'bulk_actions-plugins', 'wp_autoupdates_plugins_bulk_actions' );
310 | add_action( 'bulk_actions-plugins-network', 'wp_autoupdates_plugins_bulk_actions' );
311 |
312 |
313 | /**
314 | * Handles auto-updates enabling for plugins
315 | */
316 | function wp_autoupdates_plugins_enabler() {
317 | $action = isset( $_GET['action'] ) && ! empty( esc_html( $_GET['action'] ) ) ? wp_unslash( esc_html( $_GET['action'] ) ) : '';
318 | if ( 'autoupdate' === $action ) {
319 | if ( ! current_user_can( 'update_plugins' ) || ! wp_autoupdates_is_plugins_auto_update_enabled() ) {
320 | wp_die( __( 'Sorry, you are not allowed to enable plugins automatic updates.', 'wp-autoupdates' ) );
321 | }
322 |
323 | if ( is_multisite() && ! is_network_admin() ) {
324 | wp_die( __( 'Please connect to your network admin to manage plugins automatic updates.', 'wp-autoupdates' ) );
325 | }
326 |
327 | $plugin = ! empty( esc_html( $_GET['plugin'] ) ) ? wp_unslash( esc_html( $_GET['plugin'] ) ) : '';
328 | $page = isset( $_GET['paged'] ) && ! empty( esc_html( $_GET['paged'] ) ) ? wp_unslash( esc_html( $_GET['paged'] ) ) : '';
329 | $status = isset( $_GET['plugin_status'] ) && ! empty( esc_html( $_GET['plugin_status'] ) ) ? wp_unslash( esc_html( $_GET['plugin_status'] ) ) : '';
330 | $s = isset( $_GET['s'] ) && ! empty( esc_html( $_GET['s'] ) ) ? wp_unslash( esc_html( $_GET['s'] ) ) : '';
331 |
332 | if ( empty( $plugin ) ) {
333 | wp_redirect( self_admin_url( "plugins.php?plugin_status=$status&paged=$page&s=$s" ) );
334 | exit;
335 | }
336 |
337 | check_admin_referer( 'autoupdate-plugin_' . $plugin );
338 | $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() );
339 |
340 | if ( in_array( $plugin, $wp_auto_update_plugins, true ) ) {
341 | $wp_auto_update_plugins = array_diff( $wp_auto_update_plugins, array( $plugin ) );
342 | $action_type = 'disable-autoupdate=true';
343 | } else {
344 | array_push( $wp_auto_update_plugins, $plugin );
345 | $action_type = 'enable-autoupdate=true';
346 | }
347 | update_site_option( 'wp_auto_update_plugins', $wp_auto_update_plugins );
348 | wp_redirect( self_admin_url( "plugins.php?$action_type&plugin_status=$status&paged=$page&s=$s" ) );
349 | exit;
350 | }
351 | }
352 |
353 |
354 | /**
355 | * Handles auto-updates enabling for themes
356 | */
357 | function wp_autoupdates_themes_enabler() {
358 | $action = isset( $_GET['action'] ) && ! empty( esc_html( $_GET['action'] ) ) ? wp_unslash( esc_html( $_GET['action'] ) ) : '';
359 | if ( 'autoupdate' === $action ) {
360 | if ( ! current_user_can( 'update_themes' ) || ! wp_autoupdates_is_themes_auto_update_enabled() ) {
361 | wp_die( __( 'Sorry, you are not allowed to enable themes automatic updates.', 'wp-autoupdates' ) );
362 | }
363 |
364 | if ( is_multisite() && ! is_network_admin() ) {
365 | wp_die( __( 'Please connect to your network admin to manage themes automatic updates.', 'wp-autoupdates' ) );
366 | }
367 |
368 | $theme = ! empty( esc_html( $_GET['theme'] ) ) ? wp_unslash( esc_html( $_GET['theme'] ) ) : '';
369 | if ( empty( $theme ) ) {
370 | wp_redirect( self_admin_url( 'themes.php' ) );
371 | exit;
372 | }
373 |
374 | check_admin_referer( 'autoupdate-theme_' . $theme );
375 | $wp_auto_update_themes = get_site_option( 'wp_auto_update_themes', array() );
376 |
377 | if ( in_array( $theme, $wp_auto_update_themes, true ) ) {
378 | $wp_auto_update_themes = array_diff( $wp_auto_update_themes, array( $theme ) );
379 | $action_type = 'disable-autoupdate=true';
380 | } else {
381 | array_push( $wp_auto_update_themes, $theme );
382 | $action_type = 'enable-autoupdate=true';
383 | }
384 |
385 | update_site_option( 'wp_auto_update_themes', $wp_auto_update_themes );
386 | wp_redirect( self_admin_url( "themes.php?$action_type" ) );
387 | exit;
388 | }
389 | }
390 |
391 |
392 | /**
393 | * Handle autoupdates enabling
394 | */
395 | function wp_autoupdates_enabler() {
396 | $pagenow = $GLOBALS['pagenow'];
397 | if ( 'plugins.php' === $pagenow ) {
398 | wp_autoupdates_plugins_enabler();
399 | }
400 | else if ( 'themes.php' === $pagenow ) {
401 | wp_autoupdates_themes_enabler();
402 | }
403 | }
404 | add_action( 'admin_init', 'wp_autoupdates_enabler' );
405 |
406 |
407 | /**
408 | * Handle plugins autoupdates bulk actions
409 | */
410 | function wp_autoupdates_plugins_bulk_actions_handle( $redirect_to, $doaction, $items ) {
411 | if ( 'enable-autoupdate-selected' === $doaction ) {
412 | if ( ! current_user_can( 'update_plugins' ) || ! wp_autoupdates_is_plugins_auto_update_enabled() ) {
413 | wp_die( __( 'Sorry, you are not allowed to enable plugins automatic updates.', 'wp-autoupdates' ) );
414 | }
415 |
416 | if ( is_multisite() && ! is_network_admin() ) {
417 | wp_die( __( 'Please connect to your network admin to manage plugins automatic updates.', 'wp-autoupdates' ) );
418 | }
419 |
420 | check_admin_referer( 'bulk-plugins' );
421 |
422 | $plugins = ! empty( $items ) ? (array) wp_unslash( $items ) : array();
423 | $page = isset( $_GET['paged'] ) && ! empty( esc_html( $_GET['paged'] ) ) ? wp_unslash( esc_html( $_GET['paged'] ) ) : '';
424 | $status = isset( $_GET['plugin_status'] ) && ! empty( esc_html( $_GET['plugin_status'] ) ) ? wp_unslash( esc_html( $_GET['plugin_status'] ) ) : '';
425 | $s = isset( $_GET['s'] ) && ! empty( esc_html( $_GET['s'] ) ) ? wp_unslash( esc_html( $_GET['s'] ) ) : '';
426 |
427 | if ( empty( $plugins ) ) {
428 | $redirect_to = self_admin_url( "plugins.php?plugin_status=$status&paged=$page&s=$s" );
429 | return $redirect_to;
430 | }
431 |
432 | $previous_autoupdated_plugins = get_site_option( 'wp_auto_update_plugins', array() );
433 |
434 | $new_autoupdated_plugins = array_merge( $previous_autoupdated_plugins, $plugins );
435 | $new_autoupdated_plugins = array_unique( $new_autoupdated_plugins );
436 |
437 | update_site_option( 'wp_auto_update_plugins', $new_autoupdated_plugins );
438 |
439 | $redirect_to = self_admin_url( "plugins.php?enable-autoupdate=true&plugin_status=$status&paged=$page&s=$s" );
440 | return $redirect_to;
441 | }
442 |
443 | if ( 'disable-autoupdate-selected' === $doaction ) {
444 | if ( ! current_user_can( 'update_plugins' ) || ! wp_autoupdates_is_plugins_auto_update_enabled() ) {
445 | wp_die( __( 'Sorry, you are not allowed to enable plugins automatic updates.', 'wp-autoupdates' ) );
446 | }
447 |
448 | if ( is_multisite() && ! is_network_admin() ) {
449 | wp_die( __( 'Please connect to your network admin to manage plugins automatic updates.', 'wp-autoupdates' ) );
450 | }
451 |
452 | check_admin_referer( 'bulk-plugins' );
453 |
454 | $plugins = ! empty( $items ) ? (array) wp_unslash( $items ) : array();
455 | $page = isset( $_GET['paged'] ) && ! empty( esc_html( $_GET['paged'] ) ) ? wp_unslash( esc_html( $_GET['paged'] ) ) : '';
456 | $status = isset( $_GET['plugin_status'] ) && ! empty( esc_html( $_GET['plugin_status'] ) ) ? wp_unslash( esc_html( $_GET['plugin_status'] ) ) : '';
457 | $s = isset( $_GET['s'] ) && ! empty( esc_html( $_GET['s'] ) ) ? wp_unslash( esc_html( $_GET['s'] ) ) : '';
458 |
459 | if ( empty( $plugins ) ) {
460 | $redirect_to = self_admin_url( "plugins.php?plugin_status=$status&paged=$page&s=$s" );
461 | return $redirect_to;
462 | }
463 |
464 | $previous_autoupdated_plugins = get_site_option( 'wp_auto_update_plugins', array() );
465 |
466 | $new_autoupdated_plugins = array_diff( $previous_autoupdated_plugins, $plugins );
467 | $new_autoupdated_plugins = array_unique( $new_autoupdated_plugins );
468 |
469 | update_site_option( 'wp_auto_update_plugins', $new_autoupdated_plugins );
470 |
471 | $redirect_to = self_admin_url( "plugins.php?disable-autoupdate=true&plugin_status=$status&paged=$page&s=$s" );
472 | return $redirect_to;
473 | }
474 |
475 | }
476 | add_action( 'handle_bulk_actions-plugins', 'wp_autoupdates_plugins_bulk_actions_handle', 10, 3 );
477 | add_action( 'handle_bulk_actions-plugins-network', 'wp_autoupdates_plugins_bulk_actions_handle', 10, 3 );
478 |
479 |
480 | /**
481 | * Handle cleanup when plugin deleted
482 | */
483 | function wp_autoupdates_plugin_deleted( $plugin_file, $deleted ) {
484 | // Do nothing if the plugin wasn't deleted
485 | if ( ! $deleted ) {
486 | return;
487 | }
488 |
489 | // Remove settings
490 | $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() );
491 | if ( in_array( $plugin_file, $wp_auto_update_plugins, true ) ) {
492 | $wp_auto_update_plugins = array_diff( $wp_auto_update_plugins, array( $plugin_file ) );
493 | update_site_option( 'wp_auto_update_plugins', $wp_auto_update_plugins );
494 | }
495 | }
496 | add_action( 'deleted_plugin', 'wp_autoupdates_plugin_deleted', 10, 2 );
497 |
498 |
499 | /**
500 | * Auto-update notices for plugins
501 | */
502 | function wp_autoupdates_plugins_notices() {
503 | if ( isset( $_GET['enable-autoupdate'] ) ) {
504 | echo '';
505 | _e( 'The selected plugins will now update automatically.', 'wp-autoupdates' );
506 | echo '
';
507 | }
508 | if ( isset( $_GET['disable-autoupdate'] ) ) {
509 | echo '';
510 | _e( 'The selected plugins won’t automatically update anymore.', 'wp-autoupdates' );
511 | echo '
';
512 | }
513 | }
514 |
515 |
516 | /**
517 | * Auto-update notices for themes
518 | */
519 | function wp_autoupdates_themes_notices() {
520 | if ( isset( $_GET['enable-autoupdate'] ) ) {
521 | echo '';
522 | _e( 'The selected themes will now update automatically.', 'wp-autoupdates' );
523 | echo '
';
524 | }
525 | if ( isset( $_GET['disable-autoupdate'] ) ) {
526 | echo '';
527 | _e( 'The selected themes won’t automatically update anymore.', 'wp-autoupdates' );
528 | echo '
';
529 | }
530 | }
531 |
532 |
533 | /**
534 | * Auto-update notices
535 | */
536 | function wp_autoupdates_notices() {
537 | // Plugins screen
538 | $pagenow = $GLOBALS['pagenow'];
539 | if ( 'plugins.php' === $pagenow ) {
540 | wp_autoupdates_plugins_notices();
541 | }
542 | else if ( 'themes.php' === $pagenow ) {
543 | wp_autoupdates_themes_notices();
544 | }
545 | }
546 | add_action( 'admin_notices', 'wp_autoupdates_notices' );
547 |
548 | /**
549 | * Add views for auto-update enabled/disabled.
550 | *
551 | * This is modeled on `WP_Plugins_List_Table::get_views()`. If this is merged into core,
552 | * then this should be encorporated there.
553 | *
554 | * @global array $totals Counts by plugin_status, set in `WP_Plugins_List_Table::prepare_items()`.
555 | */
556 | function wp_autoupdates_plugins_status_links( $status_links ) {
557 | global $totals;
558 |
559 | if ( ! current_user_can( 'update_plugins' ) || ! wp_autoupdates_is_plugins_auto_update_enabled() ) {
560 | return $status_links;
561 | }
562 |
563 | /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
564 | $all_plugins = apply_filters( 'all_plugins', get_plugins() );
565 | $wp_autoupdate_plugins = get_site_option( 'wp_auto_update_plugins', array() );
566 | $wp_autoupdate_plugins = array_intersect( $wp_autoupdate_plugins, array_keys( $all_plugins ) );
567 | $enabled_count = count( $wp_autoupdate_plugins );
568 |
569 | // when merged, these counts will need to be set in WP_Plugins_List_Table::prepare_items().
570 | $counts = array(
571 | 'autoupdate_enabled' => $enabled_count,
572 | 'autoupdate_disabled' => $totals['all'] - $enabled_count,
573 | );
574 |
575 | // we can't use the global $status set in WP_Plugin_List_Table::__construct() because
576 | // it will be 'all' for our "custom statuses".
577 | $status = isset( $_REQUEST['plugin_status'] ) ? $_REQUEST['plugin_status'] : 'all';
578 |
579 | foreach ( $counts as $type => $count ) {
580 | if ( 0 === $count ) {
581 | continue;
582 | }
583 | switch( $type ) {
584 | case 'autoupdate_enabled':
585 | /* translators: %s: Number of plugins. */
586 | $text = _n(
587 | 'Auto-updates Enabled (%s)',
588 | 'Auto-updates Enabled (%s)',
589 | $count,
590 | 'wp-autoupdates'
591 | );
592 |
593 | break;
594 | case 'autoupdate_disabled':
595 | /* translators: %s: Number of plugins. */
596 | $text = _n(
597 | 'Auto-updates Disabled (%s)',
598 | 'Auto-updates Disabled (%s)',
599 | $count,
600 | 'wp-autoupdates'
601 | );
602 | }
603 |
604 | $status_links[ $type ] = sprintf(
605 | "%s",
606 | add_query_arg( 'plugin_status', $type, 'plugins.php' ),
607 | ( $type === $status ) ? ' class="current" aria-current="page"' : '',
608 | sprintf( $text, number_format_i18n( $count ) )
609 | );
610 | }
611 |
612 | // make the 'all' status link not current if one of our "custom statuses" is current.
613 | if ( in_array( $status, array_keys( $counts ) ) ) {
614 | $status_links['all'] = str_replace( ' class="current" aria-current="page"', '', $status_links['all'] );
615 | }
616 |
617 | return $status_links;
618 | }
619 | add_action( is_multisite() ? 'views_plugins-network' : 'views_plugins', 'wp_autoupdates_plugins_status_links' );
620 |
621 | /**
622 | * Filter plugins shown in the list table when status is 'auto-update-enabled' or 'auto-update-disabled'.
623 | *
624 | * This is modeled on `WP_Plugins_List_Table::prepare_items()`. If this is merged into core,
625 | * then this should be encorporated there.
626 | *
627 | * This action this is hooked to is fired in `wp-admin/plugins.php`.
628 | *
629 | * @global WP_Plugins_List_Table $wp_list_table The global list table object. Set in `wp-admin/plugins.php`.
630 | * @global int $page The current page of plugins displayed. Set in WP_Plugins_List_Table::__construct().
631 | */
632 | function wp_autoupdates_plugins_filter_plugins_by_status( $plugins ) {
633 | global $wp_list_table, $page;
634 |
635 | $custom_statuses = array(
636 | 'autoupdate_enabled',
637 | 'autoupdate_disabled',
638 | );
639 |
640 | if ( ! ( isset( $_REQUEST['plugin_status'] ) &&
641 | in_array( $_REQUEST['plugin_status'], $custom_statuses ) ) ) {
642 | // current request is not for one of our statuses.
643 | // nothing to do, so bail.
644 | return;
645 | }
646 |
647 | $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() );
648 | $_plugins = array();
649 | foreach ( $plugins as $plugin_file => $plugin_data ) {
650 | switch ( $_REQUEST['plugin_status'] ) {
651 | case 'autoupdate_enabled':
652 | if ( in_array( $plugin_file, $wp_auto_update_plugins ) ) {
653 | $_plugins[ $plugin_file ] = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, false, true );
654 | }
655 | break;
656 | case 'autoupdate_disabled':
657 | if ( ! in_array( $plugin_file, $wp_auto_update_plugins ) ) {
658 | $_plugins[ $plugin_file ] = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, false, true );
659 | }
660 | break;
661 | }
662 | }
663 |
664 | // set the list table's items array to just those plugins with our custom status.
665 | $wp_list_table->items = $_plugins;
666 |
667 | // now, update the pagination properties of the list table accordingly.
668 | $total_this_page = count( $_plugins );
669 |
670 | $plugins_per_page = $wp_list_table->get_items_per_page( str_replace( '-', '_', $wp_list_table->screen->id . '_per_page' ), 999 );
671 |
672 | $start = ( $page - 1 ) * $plugins_per_page;
673 |
674 | if ( $total_this_page > $plugins_per_page ) {
675 | $wp_list_table->items = array_slice( $wp_list_table->items, $start, $plugins_per_page );
676 | }
677 |
678 | $wp_list_table->set_pagination_args(
679 | array(
680 | 'total_items' => $total_this_page,
681 | 'per_page' => $plugins_per_page,
682 | )
683 | );
684 |
685 | return;
686 | }
687 | add_action( 'pre_current_active_plugins', 'wp_autoupdates_plugins_filter_plugins_by_status' );
688 |
689 | /*
690 | * Populate site health informations
691 | */
692 | function wp_autoupdates_debug_information( $info ) {
693 | // Plugins
694 | if ( wp_autoupdates_is_plugins_auto_update_enabled() ) {
695 | // Populate plugins informations
696 | $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() );
697 |
698 | $plugins = get_plugins();
699 | $plugin_updates = get_plugin_updates();
700 |
701 | foreach ( $plugins as $plugin_path => $plugin ) {
702 | $plugin_part = ( is_plugin_active( $plugin_path ) ) ? 'wp-plugins-active' : 'wp-plugins-inactive';
703 |
704 | $plugin_version = $plugin['Version'];
705 | $plugin_author = $plugin['Author'];
706 |
707 | $plugin_version_string = __( 'No version or author information is available.', 'wp-autoupdates' );
708 | $plugin_version_string_debug = __( 'author: (undefined), version: (undefined)', 'wp-autoupdates' );
709 |
710 | if ( ! empty( $plugin_version ) && ! empty( $plugin_author ) ) {
711 | /* translators: 1: Plugin version number. 2: Plugin author name. */
712 | $plugin_version_string = sprintf( __( 'Version %1$s by %2$s', 'wp-autoupdates' ), $plugin_version, $plugin_author );
713 | /* translators: 1: Plugin version number. 2: Plugin author name. */
714 | $plugin_version_string_debug = sprintf( __( 'version: %1$s, author: %2$s', 'wp-autoupdates' ), $plugin_version, $plugin_author );
715 | } else {
716 | if ( ! empty( $plugin_author ) ) {
717 | /* translators: %s: Plugin author name. */
718 | $plugin_version_string = sprintf( __( 'By %s', 'wp-autoupdates' ), $plugin_author );
719 | /* translators: %s: Plugin author name. */
720 | $plugin_version_string_debug = sprintf( __( 'author: %s, version: (undefined)', 'wp-autoupdates' ), $plugin_author );
721 | }
722 | if ( ! empty( $plugin_version ) ) {
723 | /* translators: %s: Plugin version number. */
724 | $plugin_version_string = sprintf( __( 'Version %s', 'wp-autoupdates' ), $plugin_version );
725 | /* translators: %s: Plugin version number. */
726 | $plugin_version_string_debug = sprintf( __( 'author: (undefined), version: %s', 'wp-autoupdates' ), $plugin_version );
727 | }
728 | }
729 |
730 | if ( array_key_exists( $plugin_path, $plugin_updates ) ) {
731 | /* translators: %s: Latest plugin version number. */
732 | $plugin_version_string .= ' ' . sprintf( __( '(Latest version: %s)', 'wp-autoupdates' ), $plugin_updates[ $plugin_path ]->update->new_version );
733 | /* translators: %s: Latest plugin version number. */
734 | $plugin_version_string_debug .= ' ' . sprintf( __( '(latest version: %s)', 'wp-autoupdates' ), $plugin_updates[ $plugin_path ]->update->new_version );
735 | }
736 |
737 | if ( in_array( $plugin_path, $wp_auto_update_plugins ) ) {
738 | $plugin_version_string .= ' | ' . sprintf( __( 'Auto-updates enabled', 'wp-autoupdates' ) );
739 | $plugin_version_string_debug .= sprintf( __( 'auto-updates enabled', 'wp-autoupdates' ) );
740 | } else {
741 | $plugin_version_string .= ' | ' . sprintf( __( 'Auto-updates disabled', 'wp-autoupdates' ) );
742 | $plugin_version_string_debug .= sprintf( __( 'auto-updates disabled', 'wp-autoupdates' ) );
743 | }
744 |
745 | $info[ $plugin_part ]['fields'][ sanitize_text_field( $plugin['Name'] ) ] = array(
746 | 'label' => $plugin['Name'],
747 | 'value' => $plugin_version_string,
748 | 'debug' => $plugin_version_string_debug,
749 | );
750 | }
751 | }
752 |
753 | if ( wp_autoupdates_is_themes_auto_update_enabled() ) {
754 | // Populate themes informations
755 | $wp_auto_update_themes = get_site_option( 'wp_auto_update_themes', array() );
756 |
757 | $themes = wp_get_themes();
758 | $active_theme = wp_get_theme();
759 | foreach ( $themes as $theme_path => $theme ) {
760 | $theme_version = sanitize_text_field( $theme['Version'] );
761 | $theme_author = sanitize_text_field( $theme['Author'] );
762 |
763 | $is_active_theme = $theme->name === $active_theme->name;
764 | if ($is_active_theme) {
765 | $theme_part = 'wp-active-theme';
766 |
767 | if ( in_array( $theme_path, $wp_auto_update_themes ) ) {
768 | $theme_auto_update_string = sprintf( __( 'Enabled', 'wp-autoupdates' ) );
769 | } else {
770 | $theme_auto_update_string = sprintf( __( 'Disabled', 'wp-autoupdates' ) );
771 | }
772 |
773 | $info[ $theme_part ]['fields']['Auto-update'] = array(
774 | 'label' => __( 'Auto-update', 'wp-autoupdates' ),
775 | 'value' => $theme_auto_update_string,
776 | 'debug' => $theme_auto_update_string,
777 | );
778 | } else {
779 | $theme_part = 'wp-themes-inactive';
780 |
781 | $theme_version_string = __( 'No version or author information is available.', 'wp-autoupdates' );
782 | $theme_version_string_debug = __( 'author: (undefined), version: (undefined)', 'wp-autoupdates' );
783 |
784 | if ( ! empty( $theme_version ) && ! empty( $theme_author ) ) {
785 | /* translators: 1: Theme version number. 2: Theme author name. */
786 | $theme_version_string = sprintf( __( 'Version %1$s by %2$s', 'wp-autoupdates' ), $theme_version, $theme_author );
787 | /* translators: 1: Theme version number. 2: Theme author name. */
788 | $theme_version_string_debug = sprintf( __( 'version: %1$s, author: %2$s', 'wp-autoupdates' ), $theme_version, $theme_author );
789 | } else {
790 | if ( ! empty( $theme_author ) ) {
791 | /* translators: %s: Theme author name. */
792 | $theme_version_string = sprintf( __( 'By %s', 'wp-autoupdates' ), $theme_author );
793 | /* translators: %s: Theme author name. */
794 | $theme_version_string_debug = sprintf( __( 'author: %s, version: (undefined)', 'wp-autoupdates' ), $theme_author );
795 | }
796 | if ( ! empty( $theme_version ) ) {
797 | /* translators: %s: Theme version number. */
798 | $theme_version_string = sprintf( __( 'Version %s', 'wp-autoupdates' ), $theme_version );
799 | /* translators: %s: Theme version number. */
800 | $theme_version_string_debug = sprintf( __( 'author: (undefined), version: %s', 'wp-autoupdates' ), $theme_version );
801 | }
802 | }
803 |
804 | if ( in_array( $theme_path, $wp_auto_update_themes ) ) {
805 | $theme_version_string .= ' | ' . sprintf( __( 'Auto-updates enabled', 'wp-autoupdates' ) );
806 | $theme_version_string_debug .= sprintf( __( 'auto-updates enabled', 'wp-autoupdates' ) );
807 | } else {
808 | $theme_version_string .= ' | ' . sprintf( __( 'Auto-updates disabled', 'wp-autoupdates' ) );
809 | $theme_version_string_debug .= sprintf( __( 'auto-updates disabled', 'wp-autoupdates' ) );
810 | }
811 |
812 | $theme_name = sanitize_text_field( $theme['Name'] );
813 | $label_name = sprintf( __( '%1$s (%2$s)', 'wp-autoupdates' ), $theme_name, $theme_path);
814 | $info[ $theme_part ]['fields'][ $theme_name ] = array(
815 | 'label' => $label_name,
816 | 'value' => $theme_version_string,
817 | 'debug' => $theme_version_string_debug,
818 | );
819 | }
820 | }
821 | }
822 |
823 | // Populate constants informations
824 | $plugins_enabled = defined( 'WP_DISABLE_PLUGINS_AUTO_UPDATE' ) ? WP_DISABLE_PLUGINS_AUTO_UPDATE : __( 'Undefined', 'wp-autoupdates' );
825 | $info['wp-constants']['fields']['WP_DISABLE_PLUGINS_AUTO_UPDATE'] = array(
826 | 'label' => 'WP_DISABLE_PLUGINS_AUTO_UPDATE',
827 | 'value' => $plugins_enabled,
828 | 'debug' => strtolower( $plugins_enabled ),
829 | );
830 |
831 | $themes_enabled = defined( 'WP_DISABLE_THEMES_AUTO_UPDATE' ) ? WP_DISABLE_THEMES_AUTO_UPDATE : __( 'Undefined', 'wp-autoupdates' );
832 | $info['wp-constants']['fields']['WP_DISABLE_THEMES_AUTO_UPDATE'] = array(
833 | 'label' => 'WP_DISABLE_THEMES_AUTO_UPDATE',
834 | 'value' => $themes_enabled,
835 | 'debug' => strtolower( $themes_enabled ),
836 | );
837 |
838 | return $info;
839 | }
840 | add_filter( 'debug_information', 'wp_autoupdates_debug_information' );
841 |
842 |
843 | /**
844 | * If we tried to perform plugin updates, check if we should send an email.
845 | *
846 | * @param object $results The result of the plugin updates.
847 | */
848 | function wp_autoupdates_automatic_updates_complete_notification( $results ) {
849 | $successful_updates = array();
850 | $failed_updates = array();
851 | if ( isset( $results['plugin'] ) ) {
852 | foreach ( $results['plugin'] as $update_result ) {
853 | if ( true === $update_result->result ) {
854 | $successful_updates[] = $update_result;
855 | } else {
856 | $failed_updates[] = $update_result;
857 | }
858 | }
859 | if ( empty( $successful_updates ) && empty( $failed_updates ) ) {
860 | return;
861 | }
862 | if ( empty( $failed_updates ) ) {
863 | wp_autoupdates_send_email_notification( 'success', $successful_updates, $failed_updates );
864 | } elseif ( empty( $successful_updates ) ) {
865 | wp_autoupdates_send_email_notification( 'fail', $successful_updates, $failed_updates );
866 | } else {
867 | wp_autoupdates_send_email_notification( 'mixed', $successful_updates, $failed_updates );
868 | }
869 | }
870 | }
871 | add_action( 'automatic_updates_complete', 'wp_autoupdates_automatic_updates_complete_notification' );
872 |
873 |
874 | /**
875 | * Sends an email upon the completion or failure of a plugin background update.
876 | *
877 | * @param string $type The type of email to send. Can be one of 'success', 'failure', 'mixed'.
878 | * @param array $successful_updates A list of plugin updates that succeeded.
879 | * @param array $failed_updates A list of plugin updates that failed.
880 | */
881 | function wp_autoupdates_send_email_notification( $type, $successful_updates, $failed_updates ) {
882 | // No updates were attempted.
883 | if ( empty( $successful_updates ) && empty( $failed_updates ) ) {
884 | return;
885 | }
886 | $body = array();
887 |
888 | switch ( $type ) {
889 | case 'success':
890 | /* translators: %s: Site title. */
891 | $subject = __( '[%s] Plugins have automatically updated', 'wp-autoupdates' );
892 | break;
893 | case 'fail':
894 | /* translators: %s: Site title. */
895 | $subject = __( '[%s] Plugins have failed to update', 'wp-autoupdates' );
896 | $body[] = sprintf(
897 | /* translators: %s: Home URL. */
898 | __( 'Howdy! Failures occurred when attempting to update plugins on your site at %s.', 'wp-autoupdates' ),
899 | home_url()
900 | );
901 | $body[] = "\n";
902 | $body[] = __( 'Please check out your site now. It’s possible that everything is working. If it says you need to update, you should do so.', 'wp-autoupdates' );
903 | break;
904 | case 'mixed':
905 | /* translators: %s: Site title. */
906 | $subject = __( '[%s] Some plugins have automatically updated', 'wp-autoupdates' );
907 | $body[] = sprintf(
908 | /* translators: %s: Home URL. */
909 | __( 'Howdy! There were some failures while attempting to update plugins on your site at %s.', 'wp-autoupdates' ),
910 | home_url()
911 | );
912 | $body[] = "\n";
913 | $body[] = __( 'Please check out your site now. It’s possible that everything is working. If it says you need to update, you should do so.', 'wp-autoupdates' );
914 | $body[] = "\n";
915 | break;
916 | }
917 |
918 | if ( in_array( $type, array( 'fail', 'mixed' ), true ) && ! empty( $failed_updates ) ) {
919 | $body[] = __( 'The following plugins failed to update:' );
920 | // List failed updates.
921 | foreach ( $failed_updates as $item ) {
922 | /* translators: %s: Name of the related plugin. */
923 | $body[] = ' ' . sprintf( __( '- %s', 'wp-autoupdates' ), $item->name );
924 | }
925 | $body[] = "\n";
926 | }
927 | if ( in_array( $type, array( 'success', 'mixed' ), true ) && ! empty( $successful_updates ) ) {
928 | $body[] = __( 'The following plugins were successfully updated:' );
929 | // List successful updates.
930 | foreach ( $successful_updates as $plugin ) {
931 | /* translators: %s: Name of the related plugin. */
932 | $body[] = ' ' . sprintf( __( '- %s', 'wp-autoupdates' ), $plugin->name );
933 | }
934 | }
935 | $body[] = "\n";
936 |
937 | // Add a note about the support forums.
938 | $body[] = __( 'If you experience any issues or need support, the volunteers in the WordPress.org support forums may be able to help.', 'wp-autoupdates' );
939 | $body[] = __( 'https://wordpress.org/support/forums/', 'wp-autoupdates' );
940 | $body[] = "\n" . __( 'The WordPress Team', 'wp-autoupdates' );
941 |
942 | $body = implode( "\n", $body );
943 | $to = get_site_option( 'admin_email' );
944 | $subject = sprintf( $subject, wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) );
945 | $headers = '';
946 |
947 | $email = compact( 'to', 'subject', 'body', 'headers' );
948 |
949 | /**
950 | * Filters the email sent following an automatic background plugin update.
951 | * @param array $email {
952 | * Array of email arguments that will be passed to wp_mail().
953 | *
954 | * @type string $to The email recipient. An array of emails
955 | * can be returned, as handled by wp_mail().
956 | * @type string $subject The email's subject.
957 | * @type string $body The email message body.
958 | * @type string $headers Any email headers, defaults to no headers.
959 | * }
960 | * @param string $type The type of email being sent. Can be one of
961 | * 'success', 'fail', 'mixed'.
962 | * @param object $successful_updates The updates that succeded.
963 | * @param object $failed_updates The updates that failed.
964 | */
965 | $email = apply_filters( 'wp_autoupdates_notifications_email', $email, $type, $successful_updates, $failed_updates );
966 | wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] );
967 | }
968 |
969 |
970 | /**
971 | * Determines the appropriate update message to be displayed.
972 | *
973 | * @return string The update message to be shown.
974 | */
975 | function wp_autoupdates_get_update_message() {
976 | $next_update_time = wp_next_scheduled( 'wp_version_check' );
977 |
978 | // Check if event exists.
979 | if ( false === $next_update_time ) {
980 | return __( 'There may be a problem with WP-Cron. Automatic update not scheduled.', 'wp-autoupdates' );
981 | }
982 |
983 | // See if cron is disabled
984 | $cron_disabled = defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON;
985 | if ( $cron_disabled ) {
986 | return __( 'WP-Cron is disabled. Automatic updates not available.', 'wp-autoupdates' );
987 | }
988 |
989 | $time_to_next_update = human_time_diff( intval( $next_update_time ) );
990 |
991 | // See if cron is overdue.
992 | $overdue = (time() - $next_update_time) > 0;
993 | if ( $overdue ) {
994 | return sprintf(
995 | /* translators: Duration that WP-Cron has been overdue. */
996 | __( 'There may be a problem with WP-Cron. Automatic update overdue by %s.', 'wp-autoupdates' ),
997 | $time_to_next_update
998 | );
999 | } else {
1000 | return sprintf(
1001 | /* translators: Time until the next update. */
1002 | __( 'Automatic update scheduled in %s.', 'wp-autoupdates' ),
1003 | $time_to_next_update
1004 | );
1005 | }
1006 | }
1007 |
--------------------------------------------------------------------------------