WordPress.org

Make WordPress Core

Ticket #50052: 50052.diff

File 50052.diff, 62.0 KB (added by pbiron, 10 months ago)
  • src/js/_enqueues/wp/updates.js

    From 7c4e5ed067ee3532c6a4ae17c90a17a9b36f7a9b Mon Sep 17 00:00:00 2001
    From: Paul Biron <paul@sparrowhawkcomputing.com>
    Date: Wed, 13 May 2020 08:05:50 -0600
    Subject: [PATCH] Initial patch to merge the WordPress Auto-updates feature
     plugin into core.
    
    This has all the functionality of version 0.8.0, except for the help tabs, because the copy for those isn't ready yet.
    ---
     src/js/_enqueues/wp/updates.js                | 149 ++++++++++++++
     src/wp-admin/admin-ajax.php                   |   1 +
     src/wp-admin/css/common.css                   |   4 +-
     src/wp-admin/css/list-tables.css              |   4 +
     src/wp-admin/css/themes.css                   |   3 +-
     src/wp-admin/includes/ajax-actions.php        |  67 ++++++
     .../includes/class-wp-automatic-updater.php   | 192 ++++++++++++++++++
     src/wp-admin/includes/class-wp-debug-data.php |  60 +++++-
     .../class-wp-ms-themes-list-table.php         | 117 ++++++++++-
     .../includes/class-wp-plugins-list-table.php  | 106 +++++++++-
     src/wp-admin/includes/theme.php               |  15 +-
     src/wp-admin/includes/update.php              |  73 +++++++
     src/wp-admin/network/themes.php               | 100 ++++++++-
     src/wp-admin/plugins.php                      | 114 ++++++++++-
     src/wp-admin/themes.php                       |  65 ++++++
     src/wp-admin/update-core.php                  |  20 ++
     src/wp-includes/script-loader.php             |   7 +
     17 files changed, 1082 insertions(+), 15 deletions(-)
    
    diff --git a/src/js/_enqueues/wp/updates.js b/src/js/_enqueues/wp/updates.js
    index d7e0de1c39..a007ecd7d0 100644
    a b  
    409409         *
    410410         * @since 4.2.0
    411411         * @since 4.6.0 More accurately named `updatePluginSuccess`.
     412         * @since 5.5.0 Auto-update "time to next update" text cleared.
    412413         *
    413414         * @param {object} response            Response from the server.
    414415         * @param {string} response.slug       Slug of the plugin to be updated.
     
    431432                        // Update the version number in the row.
    432433                        newText = $pluginRow.find( '.plugin-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
    433434                        $pluginRow.find( '.plugin-version-author-uri' ).html( newText );
     435
     436                        // Clear the "time to next auto-update" text.
     437                        $pluginRow.find( '.auto-update-time' ).empty();
    434438                } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
    435439                        $updateMessage = $( '.plugin-card-' + response.slug ).find( '.update-now' )
    436440                                .removeClass( 'updating-message' )
     
    969973         * Updates the UI appropriately after a successful theme update.
    970974         *
    971975         * @since 4.6.0
     976         * @since 5.5.0 Auto-update "time to next update" text cleared.
    972977         *
    973978         * @param {object} response
    974979         * @param {string} response.slug       Slug of the theme to be updated.
     
    10021007                        // Update the version number in the row.
    10031008                        newText = $theme.find( '.theme-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
    10041009                        $theme.find( '.theme-version-author-uri' ).html( newText );
     1010
     1011                        // Clear the "time to next auto-update" text.
     1012                        $theme.find( '.auto-update-time' ).empty();
    10051013                } else {
    10061014                        $notice = $( '.theme-info .notice' ).add( $theme.find( '.update-message' ) );
    10071015
    10081016                        // Focus on Customize button after updating.
    10091017                        if ( isModalOpen ) {
    10101018                                $( '.load-customize:visible' ).focus();
     1019                                $( '.theme-info .theme-autoupdate' ).find( '.auto-update-time' ).empty();
    10111020                        } else {
    10121021                                $theme.find( '.load-customize' ).focus();
    10131022                        }
     
    24622471                 */
    24632472                $( window ).on( 'beforeunload', wp.updates.beforeunload );
    24642473        } );
     2474
     2475        /**
     2476         * Click handler for enabling and disabling plugin and theme auto-updates.
     2477         *
     2478         * @since 5.5.0
     2479         *
     2480         * TODO: this needs to be refactored to be like the remainder of the handlers
     2481         *       in this file.
     2482         */
     2483        $( document ).ready( function() {
     2484                $( '.column-auto-updates, .theme-overlay' ).on( 'click', '.toggle-auto-update', function( event ) {
     2485                        var data, asset, type, $parent;
     2486                        var $anchor = $( this ),
     2487                                action = $anchor.attr( 'data-wp-action' ),
     2488                                $label = $anchor.find( '.label' );
     2489
     2490                        if ( 'themes' !== pagenow ) {
     2491                                $parent = $anchor.closest( '.column-auto-updates' );
     2492                        } else {
     2493                                $parent = $anchor.closest( '.theme-autoupdate' );
     2494                        }
     2495
     2496                        event.preventDefault();
     2497
     2498                        switch ( pagenow ) {
     2499                                case 'plugins':
     2500                                case 'plugins-network':
     2501                                        type = 'plugin';
     2502                                        asset = $anchor.closest( 'tr' ).attr( 'data-plugin' );
     2503                                        break;
     2504                                case 'themes-network':
     2505                                        type = 'theme';
     2506                                        asset = $anchor.closest( 'tr' ).attr( 'data-slug' );
     2507                                        break;
     2508                                case 'themes':
     2509                                        type = 'theme';
     2510                                        asset = $anchor.attr( 'data-slug' );
     2511                                        break;
     2512                        }
     2513
     2514                        // Clear any previous errors.
     2515                        $parent.find( '.notice.error' ).addClass( 'hidden' );
     2516
     2517                        // Show loading status.
     2518                        // TODO: make it readable when network is fast, or possibly remove the interim text change.
     2519                        if ( 'enable' === action ) {
     2520                                $label.text( wp.updates.l10n.autoUpdatesEnabling );
     2521                        } else {
     2522                                $label.text( wp.updates.l10n.autoUpdatesDisabling );
     2523                        }
     2524
     2525                        // TODO: Needs design review - the link text jumps under the mouse (part may get selected).
     2526                        $anchor.find( '.dashicons-update' ).removeClass( 'hidden' );
     2527
     2528                        data = {
     2529                                action: 'toggle-auto-updates',
     2530                                _ajax_nonce: settings.ajax_nonce,
     2531                                state: action,
     2532                                type: type,
     2533                                asset: asset,
     2534                        };
     2535
     2536                        $.post( window.ajaxurl, data )
     2537                                .done( function( response ) {
     2538                                        var $enabled, $disabled, enabledNumber, disabledNumber, errorMessage;
     2539                                        var href = $anchor.attr( 'href' );
     2540
     2541                                        if ( ! response.success ) {
     2542                                                // if WP returns 0 for response (which can happen in a few cases
     2543                                                // that aren't quite failures), output the general error message,
     2544                                                // since we won't have response.data.error.
     2545                                                if ( response.data && response.data.error ) {
     2546                                                        errorMessage = response.data.error;
     2547                                                } else {
     2548                                                        errorMessage = wp.updates.l10n.autoUpdatesError;
     2549                                                }
     2550
     2551                                                $parent.find( '.notice.error' ).removeClass( 'hidden' ).find( 'p' ).text( errorMessage );
     2552                                                wp.a11y.speak( errorMessage, 'polite' );
     2553                                                return;
     2554                                        }
     2555
     2556                                        // Update the counts in the enabled/disabled views if on a screen
     2557                                        // with a list table.
     2558                                        if ( 'themes' !== pagenow ) {
     2559                                                $enabled       = $( '.auto-update-enabled span' );
     2560                                                $disabled      = $( '.auto-update-disabled span' );
     2561                                                enabledNumber  = parseInt( $enabled.text().replace( /[^\d]+/g, '' ) ) || 0;
     2562                                                disabledNumber = parseInt( $disabled.text().replace( /[^\d]+/g, '' ) ) || 0;
     2563
     2564                                                switch ( action ) {
     2565                                                        case 'enable':
     2566                                                                ++enabledNumber;
     2567                                                                --disabledNumber;
     2568                                                                break;
     2569                                                        case 'disable':
     2570                                                                --enabledNumber;
     2571                                                                ++disabledNumber;
     2572                                                                break;
     2573                                                }
     2574
     2575                                                enabledNumber = Math.max( 0, enabledNumber );
     2576                                                disabledNumber = Math.max( 0, disabledNumber );
     2577
     2578                                                $enabled.text( '(' + enabledNumber + ')' );
     2579                                                $disabled.text( '(' + disabledNumber + ')' );
     2580                                        }
     2581
     2582                                        if ( 'enable' === action ) {
     2583                                                href = href.replace( 'action=enable-auto-update', 'action=disable-auto-update' );
     2584                                                $anchor.attr( {
     2585                                                        'data-wp-action': 'disable',
     2586                                                        href: href,
     2587                                                } );
     2588
     2589                                                $label.text( wp.updates.l10n.autoUpdatesDisable );
     2590                                                $parent.find( '.auto-update-time' ).removeClass( 'hidden' );
     2591                                                wp.a11y.speak( wp.updates.l10n.autoUpdatesEnabled, 'polite' );
     2592                                        } else {
     2593                                                href = href.replace( 'action=disable-auto-update', 'action=enable-auto-update' );
     2594                                                $anchor.attr( {
     2595                                                        'data-wp-action': 'enable',
     2596                                                        href: href,
     2597                                                } );
     2598
     2599                                                $label.text( wp.updates.l10n.autoUpdatesEnable );
     2600                                                $parent.find( '.auto-update-time' ).addClass( 'hidden' );
     2601                                                wp.a11y.speak( wp.updates.l10n.autoUpdatesDisabled, 'polite' );
     2602                                        }
     2603                                } )
     2604                                .fail( function() {
     2605                                        $parent.find( '.notice.error' ).removeClass( 'hidden' ).find( 'p' ).text( wp.updates.l10n.autoUpdatesError );
     2606                                        wp.a11y.speak( wp.updates.l10n.autoUpdatesError, 'polite' );
     2607                                } )
     2608                                .always( function() {
     2609                                        $anchor.find( '.dashicons-update' ).addClass( 'hidden' );
     2610                                } );
     2611                        }
     2612                );
     2613        } );
    24652614})( jQuery, window.wp, window._wpUpdatesSettings );
  • src/wp-admin/admin-ajax.php

    diff --git a/src/wp-admin/admin-ajax.php b/src/wp-admin/admin-ajax.php
    index 4f41a31bd6..28caf3d227 100644
    a b $core_actions_post = array( 
    139139        'health-check-background-updates',
    140140        'health-check-loopback-requests',
    141141        'health-check-get-sizes',
     142        'toggle-auto-updates',
    142143);
    143144
    144145// Deprecated.
  • src/wp-admin/css/common.css

    diff --git a/src/wp-admin/css/common.css b/src/wp-admin/css/common.css
    index 3d0dacf277..fbf69fe767 100644
    a b div.error { 
    15241524.updating-message p:before,
    15251525.import-php .updating-message:before,
    15261526.button.updating-message:before,
    1527 .button.installing:before {
     1527.button.installing:before,
     1528.plugins .column-auto-updates .dashicons-update.spin,
     1529.theme-overlay .theme-autoupdate .dashicons-update.spin {
    15281530        animation: rotation 2s infinite linear;
    15291531}
    15301532
  • src/wp-admin/css/list-tables.css

    diff --git a/src/wp-admin/css/list-tables.css b/src/wp-admin/css/list-tables.css
    index f621aef2ab..9aaaefebe0 100644
    a b ul.cat-checklist { 
    12361236        width: 85px;
    12371237}
    12381238
     1239.plugins .column-auto-updates {
     1240        width: 14.2em;
     1241}
     1242
    12391243.plugins .inactive .plugin-title strong {
    12401244        font-weight: 400;
    12411245}
  • src/wp-admin/css/themes.css

    diff --git a/src/wp-admin/css/themes.css b/src/wp-admin/css/themes.css
    index a5ef219f77..7d87a06aeb 100644
    a b body.folded .theme-browser ~ .theme-overlay .theme-wrap { 
    679679        line-height: inherit;
    680680}
    681681
    682 .theme-overlay .theme-author a {
     682.theme-overlay .theme-author a,
     683.theme-overlay .theme-autoupdate a {
    683684        text-decoration: none;
    684685}
    685686
  • src/wp-admin/includes/ajax-actions.php

    diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php
    index 3b551db6c0..a560f2a941 100644
    a b function wp_ajax_health_check_get_sizes() { 
    52655265function wp_ajax_rest_nonce() {
    52665266        exit( wp_create_nonce( 'wp_rest' ) );
    52675267}
     5268
     5269/**
     5270 * Ajax handler to enable or disable plugin and theme auto-updates.
     5271 *
     5272 * @since 5.5.0
     5273 */
     5274function wp_ajax_toggle_auto_updates() {
     5275        check_ajax_referer( 'updates' );
     5276
     5277        if ( empty( $_POST['type'] ) || empty( $_POST['asset'] ) || empty( $_POST['state'] ) ) {
     5278                wp_send_json_error( array( 'error' => __( 'Invalid data. No selected item.' ) ) );
     5279        }
     5280
     5281        $asset = sanitize_text_field( urldecode( $_POST['asset'] ) );
     5282
     5283        if ( 'enable' !== $_POST['state'] && 'disable' !== $_POST['state'] ) {
     5284                wp_send_json_error( array( 'error' => __( 'Invalid data. Unknown state.' ) ) );
     5285        }
     5286        $state = $_POST['state'];
     5287
     5288        if ( 'plugin' !== $_POST['type'] && 'theme' !== $_POST['type'] ) {
     5289                wp_send_json_error( array( 'error' => __( 'Invalid data. Unknown type.' ) ) );
     5290        }
     5291        $type = $_POST['type'];
     5292
     5293        switch ( $type ) {
     5294                case 'plugin':
     5295                        if ( ! current_user_can( 'update_plugins' ) ) {
     5296                                $error_message = __( 'You do not have permission to modify plugins.' );
     5297                                wp_send_json_error( array( 'error' => $error_message ) );
     5298                        }
     5299
     5300                        $option = 'auto_update_plugins';
     5301                        /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
     5302                        $all_items = apply_filters( 'all_plugins', get_plugins() );
     5303                        break;
     5304                case 'theme':
     5305                        if ( ! current_user_can( 'update_themes' ) ) {
     5306                                $error_message = __( 'You do not have permission to modify themes.' );
     5307                                wp_send_json_error( array( 'error' => $error_message ) );
     5308                        }
     5309
     5310                        $option    = 'auto_update_themes';
     5311                        $all_items = wp_get_themes();
     5312                        break;
     5313                default:
     5314                        wp_send_json_error( array( 'error' => __( 'Invalid data. Unknown type.' ) ) );
     5315        }
     5316
     5317        if ( ! array_key_exists( $asset, $all_items ) ) {
     5318                $error_message = __( 'Invalid data. The item does not exist.' );
     5319                wp_send_json_error( array( 'error' => $error_message ) );
     5320        }
     5321
     5322        $auto_updates = (array) get_site_option( $option, array() );
     5323
     5324        if ( 'disable' === $state ) {
     5325                $auto_updates = array_diff( $auto_updates, array( $asset ) );
     5326        } else {
     5327                $auto_updates[] = $asset;
     5328                $auto_updates   = array_unique( $auto_updates );
     5329        }
     5330
     5331        update_site_option( $option, $auto_updates );
     5332
     5333        wp_send_json_success();
     5334}
  • src/wp-admin/includes/class-wp-automatic-updater.php

    diff --git a/src/wp-admin/includes/class-wp-automatic-updater.php b/src/wp-admin/includes/class-wp-automatic-updater.php
    index 10461f8f50..ec6186d6e2 100644
    a b class WP_Automatic_Updater { 
    158158                // Next up, is this an item we can update?
    159159                if ( 'core' == $type ) {
    160160                        $update = Core_Upgrader::should_update_to_version( $item->current );
     161                } elseif ( 'plugin' === $type || 'theme' === $type ) {
     162                        $update = false;
     163                        if ( wp_is_auto_update_enabled_for_type( $type ) ) {
     164                                $auto_updates = (array) get_site_option( "auto_update_{$type}s", array() );
     165                                $update      = in_array( $item->{$type}, $auto_updates, true ) || ! empty( $item->autoupdate );
     166                        }
    161167                } else {
    162168                        $update = ! empty( $item->autoupdate );
    163169                }
    class WP_Automatic_Updater { 
    497503
    498504                        if ( ! empty( $this->update_results['core'] ) ) {
    499505                                $this->after_core_update( $this->update_results['core'][0] );
     506                        } elseif ( ! empty( $this->update_results['plugin'] ) || ! empty( $this->update_results['theme'] ) ) {
     507                                $this->after_plugin_theme_update( $this->update_results );
    500508                        }
    501509
    502510                        /**
    class WP_Automatic_Updater { 
    843851                wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] );
    844852        }
    845853
     854
     855        /**
     856         * If we tried to perform plugin or theme updates, check if we should send an email.
     857         *
     858         * @since 5.5.0
     859         *
     860         * @param object $results The result of updates tasks.
     861         */
     862        protected function after_plugin_theme_update( $update_results ) {
     863                $successful_updates = array();
     864                $failed_updates     = array();
     865
     866                /**
     867                 * Filters whether to send an email following an automatic background plugin update.
     868                 *
     869                 * @since 5.5.0
     870                 *
     871                 * @param bool $enabled True if plugins notifications are enabled, false otherwise.
     872                 */
     873                $notifications_enabled = apply_filters( 'auto_plugin_update_send_email', true );
     874
     875                if ( ! empty( $update_results['plugin'] ) && $notifications_enabled ) {
     876                        foreach ( $update_results['plugin'] as $update_result ) {
     877                                if ( true === $update_result->result ) {
     878                                        $successful_updates['plugin'][] = $update_result;
     879                                } else {
     880                                        $failed_updates['plugin'][] = $update_result;
     881                                }
     882                        }
     883                }
     884
     885                /**
     886                 * Filters whether to send an email following an automatic background theme update.
     887                 *
     888                 * @since 5.5.0
     889                 *
     890                 * @param bool $enabled True if notifications are enabled, false otherwise.
     891                 */
     892                $notifications_enabled = apply_filters( 'send_theme_auto_update_email', true );
     893
     894                if ( ! empty( $update_results['theme'] ) && $notifications_enabled ) {
     895                        foreach ( $update_results['theme'] as $update_result ) {
     896                                if ( true === $update_result->result ) {
     897                                        $successful_updates['theme'][] = $update_result;
     898                                } else {
     899                                        $failed_updates['theme'][] = $update_result;
     900                                }
     901                        }
     902                }
     903
     904                if ( empty( $successful_updates ) && empty( $failed_updates ) ) {
     905                        return;
     906                }
     907
     908                if ( empty( $failed_updates ) ) {
     909                        $this->send_plugin_theme_email( 'success', $successful_updates, $failed_updates );
     910                } elseif ( empty( $successful_updates ) ) {
     911                        $this->send_plugin_theme_email( 'fail', $successful_updates, $failed_updates );
     912                } else {
     913                        $this->send_plugin_theme_email( 'mixed', $successful_updates, $failed_updates );
     914                }
     915        }
     916
     917        /**
     918         * Sends an email upon the completion or failure of a plugin or theme background update.
     919         *
     920         * @since 5.5.0
     921         *
     922         * @param string $type               The type of email to send. Can be one of 'success', 'failure', 'mixed'.
     923         * @param array  $successful_updates A list of updates that succeeded.
     924         * @param array  $failed_updates     A list of updates that failed.
     925         */
     926        protected function send_plugin_theme_email( $type, $successful_updates, $failed_updates ) {
     927                // No updates were attempted.
     928                if ( empty( $successful_updates ) && empty( $failed_updates ) ) {
     929                        return;
     930                }
     931                $body = array();
     932
     933                switch ( $type ) {
     934                        case 'success':
     935                                /* translators: %s: Site title. */
     936                                $subject = __( '[%s] Some plugins or themes were automatically updated' );
     937                                break;
     938                        case 'fail':
     939                                /* translators: %s: Site title. */
     940                                $subject = __( '[%s] Some plugins or themes have failed to update' );
     941                                $body[]  = sprintf(
     942                                        /* translators: %s: Home URL. */
     943                                        __( 'Howdy! Failures occurred when attempting to update plugins/themes on your site at %s.' ),
     944                                        home_url()
     945                                );
     946                                $body[] = "\n";
     947                                $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.' );
     948                                break;
     949                        case 'mixed':
     950                                /* translators: %s: Site title. */
     951                                $subject = __( '[%s] Some plugins or themes were automatically updated' );
     952                                $body[] = sprintf(
     953                                        /* translators: %s: Home URL. */
     954                                        __( 'Howdy! Failures occurred when attempting to update plugins/themes on your site at %s.' ),
     955                                        home_url()
     956                                );
     957                                $body[] = "\n";
     958                                $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.' );
     959                                $body[] = "\n";
     960                                break;
     961                }
     962
     963                // Get failed plugin updates
     964                if ( in_array( $type, array( 'fail', 'mixed' ), true ) && ! empty( $failed_updates['plugin'] ) ) {
     965                        $body[] = __( 'The following plugins failed to update:' );
     966                        // List failed updates.
     967                        foreach ( $failed_updates['plugin'] as $item ) {
     968                                /* translators: %s: Name of the related plugin. */
     969                                $body[] = ' ' . sprintf( __( '- %s' ), $item->name );
     970                        }
     971                        $body[] = "\n";
     972                }
     973                // Get failed theme updates
     974                if ( in_array( $type, array( 'fail', 'mixed' ), true ) && ! empty( $failed_updates['theme'] ) ) {
     975                        $body[] = __( 'The following themes failed to update:' );
     976                        // List failed updates.
     977                        foreach ( $failed_updates['theme'] as $item ) {
     978                                /* translators: %s: Name of the related plugin. */
     979                                $body[] = ' ' . sprintf( __( '- %s' ), $item->name );
     980                        }
     981                        $body[] = "\n";
     982                }
     983                // Get successful plugin updates
     984                if ( in_array( $type, array( 'success', 'mixed' ), true ) && ! empty( $successful_updates['plugin'] ) ) {
     985                        $body[] = __( 'The following plugins were successfully updated:' );
     986                        // List successful updates.
     987                        foreach ( $successful_updates['plugin'] as $item ) {
     988                                /* translators: %s: Name of the related plugin. */
     989                                $body[] = ' ' . sprintf( __( '- %s' ), $item->name );
     990                        }
     991                        $body[] = "\n";
     992                }
     993                // Get successful theme updates
     994                if ( in_array( $type, array( 'success', 'mixed' ), true ) && ! empty( $successful_updates['theme'] ) ) {
     995                        $body[] = __( 'The following themes were successfully updated:' );
     996                        // List successful updates.
     997                        foreach ( $successful_updates['theme'] as $item ) {
     998                                /* translators: %s: Name of the related plugin. */
     999                                $body[] = ' ' . sprintf( __( '- %s' ), $item->name );
     1000                        }
     1001                        $body[] = "\n";
     1002                }
     1003                $body[] = "\n";
     1004
     1005                // Add a note about the support forums.
     1006                $body[] = __( 'If you experience any issues or need support, the volunteers in the WordPress.org support forums may be able to help.' );
     1007                $body[] = __( 'https://wordpress.org/support/forums/' );
     1008                $body[] = "\n" . __( 'The WordPress Team' );
     1009
     1010                $body    = implode( "\n", $body );
     1011                $to      = get_site_option( 'admin_email' );
     1012                $subject = sprintf( $subject, wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) );
     1013                $headers = '';
     1014
     1015                $email = compact( 'to', 'subject', 'body', 'headers' );
     1016
     1017                /**
     1018                 * Filters the email sent following an automatic background plugin update.
     1019                 *
     1020                 * @param array $email {
     1021                 *     Array of email arguments that will be passed to wp_mail().
     1022                 *
     1023                 *     @type string $to      The email recipient. An array of emails
     1024                 *                           can be returned, as handled by wp_mail().
     1025                 *     @type string $subject The email's subject.
     1026                 *     @type string $body    The email message body.
     1027                 *     @type string $headers Any email headers, defaults to no headers.
     1028                 * }
     1029                 * @param string $type               The type of email being sent. Can be one of
     1030                 *                                   'success', 'fail', 'mixed'.
     1031                 * @param object $successful_updates The updates that succeeded.
     1032                 * @param object $failed_updates     The updates that failed.
     1033                 */
     1034                $email = apply_filters( 'auto_plugin_theme_update_email', $email, $type, $successful_updates, $failed_updates );
     1035                wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] );
     1036        }
     1037
    8461038        /**
    8471039         * Prepares and sends an email of a full log of background update results, useful for debugging and geekery.
    8481040         *
  • src/wp-admin/includes/class-wp-debug-data.php

    diff --git a/src/wp-admin/includes/class-wp-debug-data.php b/src/wp-admin/includes/class-wp-debug-data.php
    index 476bf55d59..b9854839fb 100644
    a b class WP_Debug_Data { 
    856856                }
    857857
    858858                // List all available plugins.
    859                 $plugins        = get_plugins();
    860                 $plugin_updates = get_plugin_updates();
     859                $plugins              = get_plugins();
     860                $plugin_updates       = get_plugin_updates();
     861                $auto_updates         = array();
     862                $auto_updates_enabled = wp_is_auto_update_enabled_for_type( 'plugin' );
     863                if ( $auto_updates_enabled ) {
     864                        $auto_updates = (array) get_site_option( 'auto_update_plugins', array() );
     865                }
    861866
    862867                foreach ( $plugins as $plugin_path => $plugin ) {
    863868                        $plugin_part = ( is_plugin_active( $plugin_path ) ) ? 'wp-plugins-active' : 'wp-plugins-inactive';
    class WP_Debug_Data { 
    892897                                $plugin_version_string_debug .= sprintf( ' (latest version: %s)', $plugin_updates[ $plugin_path ]->update->new_version );
    893898                        }
    894899
     900                        if ( $auto_updates_enabled ) {
     901                                if ( in_array( $plugin_path, $auto_updates, true ) ) {
     902                                        $plugin_version_string       .= ' | ' . __( 'Auto-updates enabled' );
     903                                        $plugin_version_string_debug .= ', ' . __( 'Auto-updates enabled' );
     904                                } else {
     905                                        $plugin_version_string       .= ' | ' . __( 'Auto-updates disabled' );
     906                                        $plugin_version_string_debug .= ', ' . __( 'Auto-updates disabled' );
     907                                }
     908                        }
     909
    895910                        $info[ $plugin_part ]['fields'][ sanitize_text_field( $plugin['Name'] ) ] = array(
    896911                                'label' => $plugin['Name'],
    897912                                'value' => $plugin_version_string,
    class WP_Debug_Data { 
    915930                $active_theme_version       = $active_theme->version;
    916931                $active_theme_version_debug = $active_theme_version;
    917932
     933                $auto_updates         = array();
     934                $auto_updates_enabled = wp_is_auto_update_enabled_for_type( 'theme' );
     935                if ( $auto_updates_enabled ) {
     936                        $auto_updates = (array) get_site_option( 'auto_update_themes', array() );
     937                }
     938
    918939                if ( array_key_exists( $active_theme->stylesheet, $theme_updates ) ) {
    919940                        $theme_update_new_version = $theme_updates[ $active_theme->stylesheet ]->update['new_version'];
    920941
    class WP_Debug_Data { 
    9801001                                'value' => get_stylesheet_directory(),
    9811002                        ),
    9821003                );
     1004                if ( $auto_updates_enabled ) {
     1005                        if ( in_array( $active_theme->stylesheet, $auto_updates ) ) {
     1006                                $theme_auto_update_string = __( 'Enabled' );
     1007                        } else {
     1008                                $theme_auto_update_string = __( 'Disabled' );
     1009                        }
    9831010
     1011                        $info['wp-active-theme']['fields']['auto_update'] = array(
     1012                                'label' => __( 'Auto-update' ),
     1013                                'value' => $theme_auto_update_string,
     1014                                'debug' => $theme_auto_update_string,
     1015                        );
     1016                }
    9841017                $parent_theme = $active_theme->parent();
    9851018
    9861019                if ( $parent_theme ) {
    class WP_Debug_Data { 
    10261059                                        'value' => get_template_directory(),
    10271060                                ),
    10281061                        );
     1062                        if ( $auto_updates_enabled ) {
     1063                                if ( in_array( $parent_theme->stylesheet, $auto_updates ) ) {
     1064                                        $parent_theme_auto_update_string = __( 'Enabled' );
     1065                                } else {
     1066                                        $parent_theme_auto_update_string = __( 'Disabled' );
     1067                                }
     1068
     1069                                $info['wp-parent-theme']['fields']['auto_update'] = array(
     1070                                        'label' => __( 'Auto-update' ),
     1071                                        'value' => $parent_theme_auto_update_string,
     1072                                        'debug' => $parent_theme_auto_update_string,
     1073                                );
     1074                        }
    10291075                }
    10301076
    10311077                // Populate a list of all themes available in the install.
    class WP_Debug_Data { 
    10751121                                $theme_version_string_debug .= sprintf( ' (latest version: %s)', $theme_updates[ $theme_slug ]->update['new_version'] );
    10761122                        }
    10771123
     1124                        if ( $auto_updates_enabled ) {
     1125                                if ( in_array( $theme_slug, $auto_updates ) ) {
     1126                                        $theme_version_string       .= ' | ' . __( 'Auto-updates enabled' );
     1127                                        $theme_version_string_debug .= ',' . __( 'Auto-updates enabled' );
     1128                                } else {
     1129                                        $theme_version_string       .= ' | ' . __( 'Auto-updates disabled' );
     1130                                        $theme_version_string_debug .= ', ' . __( 'Auto-updates disabled' );
     1131                                }
     1132                        }
     1133
    10781134                        $info['wp-themes-inactive']['fields'][ sanitize_text_field( $theme->name ) ] = array(
    10791135                                'label' => sprintf(
    10801136                                        /* translators: 1: Theme name. 2: Theme slug. */
  • src/wp-admin/includes/class-wp-ms-themes-list-table.php

    diff --git a/src/wp-admin/includes/class-wp-ms-themes-list-table.php b/src/wp-admin/includes/class-wp-ms-themes-list-table.php
    index 03441acbe6..135d844d75 100644
    a b class WP_MS_Themes_List_Table extends WP_List_Table { 
    2222
    2323        private $has_items;
    2424
     25        /**
     26         * Whether to show the auto-updates UI.
     27         *
     28         * @since 5.5.0
     29         *
     30         * @var bool True if auto-updates UI is to be shown, false otherwise.
     31         */
     32        protected $show_autoupdates = true;
     33
    2534        /**
    2635         * Constructor.
    2736         *
    class WP_MS_Themes_List_Table extends WP_List_Table { 
    4554                );
    4655
    4756                $status = isset( $_REQUEST['theme_status'] ) ? $_REQUEST['theme_status'] : 'all';
    48                 if ( ! in_array( $status, array( 'all', 'enabled', 'disabled', 'upgrade', 'search', 'broken' ), true ) ) {
     57                if ( ! in_array( $status, array( 'all', 'enabled', 'disabled', 'upgrade', 'search', 'broken', 'auto-update-enabled', 'auto-update-disabled' ), true ) ) {
    4958                        $status = 'all';
    5059                }
    5160
    class WP_MS_Themes_List_Table extends WP_List_Table { 
    5665                if ( $this->is_site_themes ) {
    5766                        $this->site_id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0;
    5867                }
     68
     69                $this->show_autoupdates = wp_is_auto_update_enabled_for_type( 'theme' ) &&
     70                        ! $this->is_site_themes && current_user_can( 'update_themes' );
    5971        }
    6072
    6173        /**
    class WP_MS_Themes_List_Table extends WP_List_Table { 
    106118                        'upgrade'  => array(),
    107119                        'broken'   => $this->is_site_themes ? array() : wp_get_themes( array( 'errors' => true ) ),
    108120                );
     121                if ( $this->show_autoupdates ) {
     122                        $auto_updates                   = (array) get_site_option( 'auto_update_themes', array() );
     123
     124                        $themes['auto-update-enabled']  = array();
     125                        $themes['auto-update-disabled'] = array();
     126                }
    109127
    110128                if ( $this->is_site_themes ) {
    111129                        $themes_per_page = $this->get_items_per_page( 'site_themes_network_per_page' );
    class WP_MS_Themes_List_Table extends WP_List_Table { 
    131149
    132150                        $filter                    = $theme->is_allowed( $allowed_where, $this->site_id ) ? 'enabled' : 'disabled';
    133151                        $themes[ $filter ][ $key ] = $themes['all'][ $key ];
     152
     153                        if ( $this->show_autoupdates ) {
     154                                if ( in_array( $key, $auto_updates, true ) ) {
     155                                        $themes['auto-update-enabled'][ $key ]  = $themes['all'][ $key ];
     156                                } else {
     157                                        $themes['auto-update-disabled'][ $key ] = $themes['all'][ $key ];
     158                                }
     159                        }
    134160                }
    135161
    136162                if ( $s ) {
    class WP_MS_Themes_List_Table extends WP_List_Table { 
    257283         * @return array
    258284         */
    259285        public function get_columns() {
    260                 return array(
     286                $columns = array(
    261287                        'cb'          => '<input type="checkbox" />',
    262288                        'name'        => __( 'Theme' ),
    263289                        'description' => __( 'Description' ),
    264290                );
     291                if ( $this->show_autoupdates ) {
     292                        $columns['auto-updates'] = __( 'Automatic Updates' );
     293                }
     294                return $columns;
    265295        }
    266296
    267297        /**
    class WP_MS_Themes_List_Table extends WP_List_Table { 
    344374                                                'themes'
    345375                                        );
    346376                                        break;
     377                                case 'auto-update-enabled':
     378                                        /* translators: %s: Number of themes. */
     379                                        $text = _n(
     380                                                'Auto-updates Enabled <span class="count">(%s)</span>',
     381                                                'Auto-updates Enabled <span class="count">(%s)</span>',
     382                                                $count
     383                                        );
     384                                        break;
     385                                case 'auto-update-disabled':
     386                                        /* translators: %s: Number of themes. */
     387                                        $text = _n(
     388                                                'Auto-updates Disabled <span class="count">(%s)</span>',
     389                                                'Auto-updates Disabled <span class="count">(%s)</span>',
     390                                                $count
     391                                        );
     392                                        break;
    347393                        }
    348394
    349395                        if ( $this->is_site_themes ) {
    class WP_MS_Themes_List_Table extends WP_List_Table { 
    388434                                $actions['delete-selected'] = __( 'Delete' );
    389435                        }
    390436                }
     437                if ( $this->show_autoupdates ) {
     438                        if ( 'auto-update-enabled' !== $status ) {
     439                                $actions['enable-auto-update-selected']  = __( 'Enable Auto-updates' );
     440                        }
     441                        if ( 'auto-update-disabled' !== $status ) {
     442                                $actions['disable-auto-update-selected'] = __( 'Disable Auto-updates' );
     443                        }
     444                }
    391445                return $actions;
    392446        }
    393447
    class WP_MS_Themes_List_Table extends WP_List_Table { 
    639693                echo '</div>';
    640694        }
    641695
     696        /**
     697         * Handles the auto-updates column output.
     698         *
     699         * @since 5.5.0
     700         *
     701         * @global string $status
     702         * @global int  $page
     703         *
     704         * @param WP_Theme $theme The current WP_Theme object.
     705         */
     706        function column_autoupdates( $theme ) {
     707                global $status, $page;
     708
     709                static $auto_updates, $available_updates;
     710
     711                if ( ! $auto_updates ) {
     712                        $auto_updates = (array) get_site_option( 'auto_update_themes', array() );
     713                }
     714                if ( ! $available_updates ) {
     715                        $available_updates = get_site_transient( 'update_themes' );
     716                }
     717
     718                $stylesheet = $theme->get_stylesheet();
     719
     720                if ( in_array( $stylesheet, $auto_updates, true ) ) {
     721                        $text                   = __( 'Disable auto-updates', 'wp-autoupdates' );
     722                        $auto_update_time_class = '';
     723                        $action                 = 'disable';
     724                } else {
     725                        $text                   = __( 'Enable auto-updates', 'wp-autoupdates' );
     726                        $action                 = 'enable';
     727                        $auto_update_time_class = ' hidden';
     728                }
     729
     730                printf(
     731                        '<a href="%s" class="toggle-auto-update" data-wp-action="%s"><span class="dashicons dashicons-update spin hidden"></span><span class="label">%s</span></a>',
     732                        wp_nonce_url( 'themes.php?action=' . $action . '-auto-update&amp;theme=' . urlencode( $stylesheet ) . '&amp;paged=' . $page . '&amp;theme_status=' . $status, 'updates' ),
     733                        $action,
     734                        $text
     735                );
     736
     737                $available_updates = get_site_transient( 'update_themes' );
     738                if ( isset( $available_updates->response[ $stylesheet ] ) ) {
     739                        printf(
     740                                '<div class="auto-update-time%s">%s</div>',
     741                                $auto_update_time_class,
     742                                wp_get_auto_update_message()
     743                        );
     744                }
     745                echo '<div class="auto-updates-error inline notice error hidden"><p></p></div>';
     746        }
     747
    642748        /**
    643749         * Handles default column output.
    644750         *
    class WP_MS_Themes_List_Table extends WP_List_Table { 
    721827                                        echo '</td>';
    722828                                        break;
    723829
     830                                case 'auto-updates':
     831                                        echo "<td class='column-auto-updates{$extra_classes}'>";
     832
     833                                        $this->column_autoupdates( $item );
     834
     835                                        echo '</td>';
     836                                        break;
    724837                                default:
    725838                                        echo "<td class='$column_name column-$column_name{$extra_classes}'>";
    726839
  • src/wp-admin/includes/class-wp-plugins-list-table.php

    diff --git a/src/wp-admin/includes/class-wp-plugins-list-table.php b/src/wp-admin/includes/class-wp-plugins-list-table.php
    index 9334f86347..68efa93385 100644
    a b  
    1616 * @see WP_List_Table
    1717 */
    1818class WP_Plugins_List_Table extends WP_List_Table {
     19        /**
     20         * Whether to show the auto-updates UI.
     21         *
     22         * @since 5.5.0
     23         *
     24         * @var bool True if auto-updates UI is to be shown, false otherwise.
     25         */
     26        protected $show_autoupdates = true;
    1927
    2028        /**
    2129         * Constructor.
    class WP_Plugins_List_Table extends WP_List_Table { 
    3947                        )
    4048                );
    4149
    42                 $status_whitelist = array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search', 'paused' );
     50                $status_whitelist = array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search', 'paused', 'auto-update-enabled', 'auto-update-disabled' );
    4351
    4452                $status = 'all';
    4553                if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], $status_whitelist, true ) ) {
    class WP_Plugins_List_Table extends WP_List_Table { 
    5159                }
    5260
    5361                $page = $this->get_pagenum();
     62
     63                $this->show_autoupdates = wp_is_auto_update_enabled_for_type( 'plugin' ) &&
     64                        current_user_can( 'update_plugins' ) &&
     65                        ( ! is_multisite() || $this->screen->in_admin( 'network' ) );
    5466        }
    5567
    5668        /**
    class WP_Plugins_List_Table extends WP_List_Table { 
    103115                        'dropins'            => array(),
    104116                        'paused'             => array(),
    105117                );
     118                if ( $this->show_autoupdates ) {
     119                        $auto_updates                    = (array) get_site_option( 'auto_update_plugins', array() );
     120
     121                        $plugins['auto-update-enabled']  = array();
     122                        $plugins['auto-update-disabled'] = array();
     123                }
    106124
    107125                $screen = $this->screen;
    108126
    class WP_Plugins_List_Table extends WP_List_Table { 
    233251                                // Populate the inactive list with plugins that aren't activated.
    234252                                $plugins['inactive'][ $plugin_file ] = $plugin_data;
    235253                        }
     254
     255                        if ( $this->show_autoupdates ) {
     256                                if ( in_array( $plugin_file, $auto_updates, true ) ) {
     257                                        $plugins['auto-update-enabled'][ $plugin_file]  = $plugins['all'][ $plugin_file ];
     258                                } else {
     259                                        $plugins['auto-update-disabled'][ $plugin_file] = $plugins['all'][ $plugin_file ];
     260                                }
     261                        }
    236262                }
    237263
    238264                if ( strlen( $s ) ) {
    class WP_Plugins_List_Table extends WP_List_Table { 
    399425        public function get_columns() {
    400426                global $status;
    401427
    402                 return array(
     428                $columns = array(
    403429                        'cb'          => ! in_array( $status, array( 'mustuse', 'dropins' ), true ) ? '<input type="checkbox" />' : '',
    404430                        'name'        => __( 'Plugin' ),
    405431                        'description' => __( 'Description' ),
    406432                );
     433                if ( $this->show_autoupdates ) {
     434                        $columns['auto-updates'] = __( 'Automatic Updates' );
     435                }
     436
     437                return $columns;
    407438        }
    408439
    409440        /**
    class WP_Plugins_List_Table extends WP_List_Table { 
    493524                                                $count
    494525                                        );
    495526                                        break;
     527                                case 'auto-update-enabled':
     528                                        /* translators: %s: Number of plugins. */
     529                                        $text = _n(
     530                                                'Auto-updates Enabled <span class="count">(%s)</span>',
     531                                                'Auto-updates Enabled <span class="count">(%s)</span>',
     532                                                $count
     533                                        );
     534                                        break;
     535                                case 'auto-update-disabled':
     536                                        /* translators: %s: Number of plugins. */
     537                                        $text = _n(
     538                                                'Auto-updates Disabled <span class="count">(%s)</span>',
     539                                                'Auto-updates Disabled <span class="count">(%s)</span>',
     540                                                $count
     541                                        );
     542                                        break;
    496543                        }
    497544
    498545                        if ( 'search' !== $type ) {
    class WP_Plugins_List_Table extends WP_List_Table { 
    533580                        if ( current_user_can( 'delete_plugins' ) && ( 'active' != $status ) ) {
    534581                                $actions['delete-selected'] = __( 'Delete' );
    535582                        }
     583
     584                        if ( $this->show_autoupdates ) {
     585                                if ( 'auto-update-enabled' !== $status ) {
     586                                        $actions['enable-auto-update-selected']  = __( 'Enable Auto-updates' );
     587                                }
     588                                if ( 'auto-update-disabled' !== $status ) {
     589                                        $actions['disable-auto-update-selected'] = __( 'Disable Auto-updates' );
     590                                }
     591                        }
    536592                }
    537593
    538594                return $actions;
    class WP_Plugins_List_Table extends WP_List_Table { 
    882938
    883939                list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
    884940
     941                $auto_updates      = (array) get_site_option( 'auto_update_plugins', array() );
     942                $available_updates = get_site_transient( 'update_plugins' );
     943
    885944                foreach ( $columns as $column_name => $column_display_name ) {
    886945                        $extra_classes = '';
    887946                        if ( in_array( $column_name, $hidden, true ) ) {
    class WP_Plugins_List_Table extends WP_List_Table { 
    9721031                                                }
    9731032                                        }
    9741033
     1034                                        echo '</td>';
     1035                                        break;
     1036                                case 'auto-updates':
     1037                                        if ( ! $this->show_autoupdates ) {
     1038                                                break;
     1039                                        }
     1040
     1041                                        echo "<td class='column-auto-updates{$extra_classes}'>";
     1042
     1043                                        if ( in_array( $plugin_file, $auto_updates, true ) ) {
     1044                                                $text                   = __( 'Disable auto-updates', 'wp-autoupdates' );
     1045                                                $action                 = 'disable';
     1046                                                $auto_update_time_class = '';
     1047                                        } else {
     1048                                                $text                   = __( 'Enable auto-updates', 'wp-autoupdates' );
     1049                                                $action                 = 'enable';
     1050                                                $auto_update_time_class = ' hidden';
     1051                                        }
     1052
     1053                                        printf(
     1054                                                '<a href="%s" class="toggle-auto-update" data-wp-action="%s"><span class="dashicons dashicons-update spin hidden"></span><span class="label">%s</span></a>',
     1055                                                wp_nonce_url( 'plugins.php?action=' . $action . '-auto-update&amp;plugin=' . urlencode( $plugin_file ) . '&amp;paged=' . $page . '&amp;plugin_status=' . $status, 'updates' ),
     1056                                                $action,
     1057                                                $text
     1058                                        );
     1059
     1060                                        $available_updates = get_site_transient( 'update_plugins' );
     1061                                        if ( isset( $available_updates->response[ $plugin_file ] ) ) {
     1062                                                printf(
     1063                                                        '<div class="auto-update-time%s">%s</div>',
     1064                                                        $auto_update_time_class,
     1065                                                        wp_get_auto_update_message()
     1066                                                );
     1067                                        }
     1068                                        echo '<div class="inline notice error hidden"><p></p></div>';
    9751069                                        echo '</td>';
    9761070                                        break;
    9771071                                default:
    class WP_Plugins_List_Table extends WP_List_Table { 
    10001094                 * Fires after each row in the Plugins list table.
    10011095                 *
    10021096                 * @since 2.3.0
     1097                 * @since 5.5.0 Added 'Auto-updates Enabled' and 'Auto-updates Disabled' `$status`.
    10031098                 *
    10041099                 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
    10051100                 * @param array  $plugin_data An array of plugin data.
    10061101                 * @param string $status      Status of the plugin. Defaults are 'All', 'Active',
    10071102                 *                            'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
    1008                  *                            'Drop-ins', 'Search', 'Paused'.
     1103                 *                            'Drop-ins', 'Search', 'Paused', 'Auto-updates Enabled',
     1104                 *                            'Auto-updates Disabled'.
    10091105                 */
    10101106                do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status );
    10111107
    class WP_Plugins_List_Table extends WP_List_Table { 
    10161112                 * to the plugin file, relative to the plugins directory.
    10171113                 *
    10181114                 * @since 2.7.0
     1115                 * @since 5.5.0 Added 'Auto-updates Enabled' and 'Auto-updates Disabled' `$status`.
    10191116                 *
    10201117                 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
    10211118                 * @param array  $plugin_data An array of plugin data.
    10221119                 * @param string $status      Status of the plugin. Defaults are 'All', 'Active',
    10231120                 *                            'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
    1024                  *                            'Drop-ins', 'Search', 'Paused'.
     1121                 *                            'Drop-ins', 'Search', 'Paused', 'Auto-updates Enabled',
     1122                 *                            'Auto-updates Disabled'.
    10251123                 */
    10261124                do_action( "after_plugin_row_{$plugin_file}", $plugin_file, $plugin_data, $status );
    10271125        }
  • src/wp-admin/includes/theme.php

    diff --git a/src/wp-admin/includes/theme.php b/src/wp-admin/includes/theme.php
    index 47168057e0..65cdd8091e 100644
    a b function wp_prepare_themes_for_js( $themes = null ) { 
    630630
    631631        $parents = array();
    632632
     633        $auto_updates = (array) get_site_option( 'auto_update_themes', array() );
     634
    633635        foreach ( $themes as $theme ) {
    634636                $slug         = $theme->get_stylesheet();
    635637                $encoded_slug = urlencode( $slug );
    function wp_prepare_themes_for_js( $themes = null ) { 
    653655                        );
    654656                }
    655657
     658                $auto_update        = in_array( $slug, $auto_updates, true );
     659                $auto_update_action = $auto_update ? 'disable-auto-update' : 'enable-auto-update';
     660
    656661                $prepared_themes[ $slug ] = array(
    657662                        'id'           => $slug,
    658663                        'name'         => $theme->display( 'Name' ),
    function wp_prepare_themes_for_js( $themes = null ) { 
    667672                        'hasUpdate'    => isset( $updates[ $slug ] ),
    668673                        'hasPackage'   => isset( $updates[ $slug ] ) && ! empty( $updates[ $slug ]['package'] ),
    669674                        'update'       => get_theme_update_available( $theme ),
     675                        'autoupdate'   => $auto_update,
    670676                        'actions'      => array(
    671                                 'activate'  => current_user_can( 'switch_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=activate&amp;stylesheet=' . $encoded_slug ), 'switch-theme_' . $slug ) : null,
    672                                 'customize' => $customize_action,
    673                                 'delete'    => current_user_can( 'delete_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=delete&amp;stylesheet=' . $encoded_slug ), 'delete-theme_' . $slug ) : null,
     677                                'activate'   => current_user_can( 'switch_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=activate&amp;stylesheet=' . $encoded_slug ), 'switch-theme_' . $slug ) : null,
     678                                'customize'  => $customize_action,
     679                                'delete'     => current_user_can( 'delete_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=delete&amp;stylesheet=' . $encoded_slug ), 'delete-theme_' . $slug ) : null,
     680                                'autoupdate' => wp_is_auto_update_enabled_for_type( 'theme' ) && ! is_multisite() && current_user_can( 'update_themes' )
     681                                        ? wp_nonce_url( admin_url( 'themes.php?action=' . $auto_update_action . '&amp;stylesheet=' . $encoded_slug ), 'updates' )
     682                                        : null,
    674683                        ),
    675684                );
    676685        }
  • src/wp-admin/includes/update.php

    diff --git a/src/wp-admin/includes/update.php b/src/wp-admin/includes/update.php
    index 464ae93d22..d5caebe36c 100644
    a b function wp_recovery_mode_nag() { 
    933933        </div>
    934934        <?php
    935935}
     936
     937/**
     938 * Checks whether auto-updates are enabled.
     939 *
     940 * @since 5.5.0
     941 *
     942 * @param string $type    The type of update being checked: 'theme' or 'plugin'.
     943 * @return bool True if auto-updates are enabled for `$type`, false otherwise.
     944 */
     945function wp_is_auto_update_enabled_for_type( $type ) {
     946        switch ( $type ) {
     947                case 'plugin':
     948                        /**
     949                         * Filters whether plugins manual auto-update is enabled.
     950                         *
     951                         * @since 5.5.0
     952                         *
     953                         * @param bool $enabled True if plugins auto-update is enabled, false otherwise.
     954                         */
     955                        return apply_filters( 'wp_plugins_auto_update_enabled', true );
     956                case 'theme':
     957                        /**
     958                         * Filters whether plugins manual auto-update is enabled.
     959                         *
     960                         * @since 5.5.0
     961                         *
     962                         * @param bool True if themes auto-update is enabled, false otherwise.
     963                         */
     964                        return apply_filters( 'wp_themes_auto_update_enabled', true );
     965        }
     966
     967        return false;
     968}
     969
     970/**
     971 * Determines the appropriate update message to be displayed.
     972 *
     973 * @since 5.5.0
     974 *
     975 * @return string The update message to be shown.
     976 */
     977function wp_get_auto_update_message() {
     978        $next_update_time = wp_next_scheduled( 'wp_version_check' );
     979
     980        // Check if event exists.
     981        if ( false === $next_update_time ) {
     982                return __( 'There may be a problem with WP-Cron. Automatic update not scheduled.' );
     983        }
     984
     985        // See if cron is disabled
     986        $cron_disabled = defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON;
     987        if ( $cron_disabled ) {
     988                return __( 'WP-Cron is disabled. Automatic updates not available.' );
     989        }
     990
     991        $time_to_next_update = human_time_diff( intval( $next_update_time ) );
     992
     993        // See if cron is overdue.
     994        $overdue = ( time() - $next_update_time ) > 0;
     995        if ( $overdue ) {
     996                return sprintf(
     997                        /* translators: Duration that WP-Cron has been overdue. */
     998                        __( 'There may be a problem with WP-Cron. Automatic update overdue by %s.' ),
     999                        $time_to_next_update
     1000                );
     1001        } else {
     1002                return sprintf(
     1003                        /* translators: Time until the next update. */
     1004                        __( 'Auto-update scheduled in %s.' ),
     1005                        $time_to_next_update
     1006                );
     1007        }
     1008}
  • src/wp-admin/network/themes.php

    diff --git a/src/wp-admin/network/themes.php b/src/wp-admin/network/themes.php
    index e8027b939c..9090ba0f55 100644
    a b $action = $wp_list_table->current_action(); 
    2222$s = isset( $_REQUEST['s'] ) ? $_REQUEST['s'] : '';
    2323
    2424// Clean up request URI from temporary args for screen options/paging uri's to work as expected.
    25 $temp_args              = array( 'enabled', 'disabled', 'deleted', 'error' );
     25$temp_args              = array( 'enabled', 'disabled', 'deleted', 'error', 'enabled-auto-update', 'disabled-auto-update' );
    2626$_SERVER['REQUEST_URI'] = remove_query_arg( $temp_args, $_SERVER['REQUEST_URI'] );
    2727$referer                = remove_query_arg( $temp_args, wp_get_referer() );
    2828
    if ( $action ) { 
    208208                                )
    209209                        );
    210210                        exit;
     211                case 'enable-auto-update':
     212                        if ( ! ( current_user_can( 'update_themes' ) && wp_is_auto_update_enabled_for_type( 'theme' ) ) ) {
     213                                wp_die( __( 'Sorry, you are not allowed to enable themes automatic updates.' ) );
     214                        }
     215
     216                        check_admin_referer( 'updates' );
     217
     218                        $auto_updates   = (array) get_site_option( 'auto_update_themes', array() );
     219
     220                        $auto_updates[] = $_GET['theme'];
     221                        $auto_updates   = array_unique( $auto_updates );
     222
     223                        update_site_option( 'auto_update_themes', $auto_updates );
     224
     225                        wp_safe_redirect( add_query_arg( 'enabled-auto-update', 1, $referer ) );
     226                        exit;
     227                case 'disable-auto-update':
     228                        if ( ! ( current_user_can( 'update_themes' ) && wp_is_auto_update_enabled_for_type( 'theme' ) ) ) {
     229                                wp_die( __( 'Sorry, you are not allowed to disable themes automatic updates.' ) );
     230                        }
     231
     232                        check_admin_referer( 'updates' );
     233
     234                        $auto_updates = (array) get_site_option( 'auto_update_themes', array() );
     235                        $auto_updates = array_diff( $auto_updates, array( $_GET['theme'] ) );
     236
     237                        update_site_option( 'auto_update_themes', $auto_updates );
     238
     239                        wp_safe_redirect( add_query_arg( 'disabled-auto-update', 1, $referer ) );
     240                        exit;
     241                case 'enable-auto-update-selected':
     242                        if ( ! ( current_user_can( 'update_themes' ) && wp_is_auto_update_enabled_for_type( 'theme' ) ) ) {
     243                                wp_die( __( 'Sorry, you are not allowed to enable themes automatic updates.' ) );
     244                        }
     245
     246                        check_admin_referer( 'bulk-themes' );
     247
     248                        $themes = isset( $_POST['checked'] ) ? (array) wp_unslash( $_POST['checked'] ) : array();
     249
     250                        $auto_updates     = (array) get_site_option( 'auto_update_themes', array() );
     251                        $new_auto_updates = array_merge( $auto_updates, $themes );
     252                        $new_auto_updates = array_unique( $new_auto_updates );
     253
     254                        update_site_option( 'auto_update_themes', $new_auto_updates );
     255
     256                        wp_safe_redirect( add_query_arg( 'enabled-auto-update', count( $themes ), $referer ) );
     257                        exit;
     258                case 'disable-auto-update-selected':
     259                        if ( ! ( current_user_can( 'update_themes' ) && wp_is_auto_update_enabled_for_type( 'theme' ) ) ) {
     260                                wp_die( __( 'Sorry, you are not allowed to disable themes automatic updates.' ) );
     261                        }
     262
     263                        check_admin_referer( 'bulk-themes' );
     264
     265                        $themes = isset( $_POST['checked'] ) ? (array) wp_unslash( $_POST['checked'] ) : array();
     266
     267                        $auto_updates     = (array) get_site_option( 'auto_update_themes', array() );
     268                        $new_auto_updates = array_diff( $auto_updates, $themes );
     269
     270                        if ( $new_auto_updates == $auto_updates ) {
     271                                if ( false === strpos( $referer, '/network/themes.php' ) ) {
     272                                        wp_redirect( network_admin_url( 'themes.php?disabled-auto-update=1' ) );
     273                                } else {
     274                                        wp_safe_redirect( $referer );
     275                                }
     276                                exit;
     277                        }
     278
     279                        update_site_option( 'auto_update_themes', $new_auto_updates );
     280
     281                        wp_safe_redirect( add_query_arg( 'disabled-auto-update', count( $themes ), $referer ) );
     282                        exit;
    211283                default:
    212284                        $themes = isset( $_POST['checked'] ) ? (array) $_POST['checked'] : array();
    213285                        if ( empty( $themes ) ) {
    if ( isset( $_GET['enabled'] ) ) { 
    309381                $message = _n( '%s theme deleted.', '%s themes deleted.', $deleted );
    310382        }
    311383        echo '<div id="message" class="updated notice is-dismissible"><p>' . sprintf( $message, number_format_i18n( $deleted ) ) . '</p></div>';
     384} elseif ( isset( $_GET['enabled-auto-update'] ) ) {
     385        $enabled = absint( $_GET['enabled-auto-update'] );
     386        if ( 1 == $enabled ) {
     387                $message = __( 'Theme will be auto-updated.' );
     388        } else {
     389                /* translators: %s: Number of themes. */
     390                $message = _n( '%s theme will be auto-updated.', '%s themes will be auto-updated.', $enabled );
     391        }
     392        echo '<div id="message" class="updated notice is-dismissible"><p>' . sprintf( $message, number_format_i18n( $enabled ) ) . '</p></div>';
     393} elseif ( isset( $_GET['disabled-auto-update'] ) ) {
     394        $disabled = absint( $_GET['disabled-auto-update'] );
     395        if ( 1 == $disabled ) {
     396                $message = __( 'Theme will no longer be auto-updated.' );
     397        } else {
     398                /* translators: %s: Number of themes. */
     399                $message = _n( '%s theme will no longer be auto-updated.', '%s themes will no longer be auto-updated.', $disabled );
     400        }
     401        echo '<div id="message" class="updated notice is-dismissible"><p>' . sprintf( $message, number_format_i18n( $disabled ) ) . '</p></div>';
    312402} elseif ( isset( $_GET['error'] ) && 'none' == $_GET['error'] ) {
    313403        echo '<div id="message" class="error notice is-dismissible"><p>' . __( 'No theme selected.' ) . '</p></div>';
    314404} elseif ( isset( $_GET['error'] ) && 'main' == $_GET['error'] ) {
    315405        echo '<div class="error notice is-dismissible"><p>' . __( 'You cannot delete a theme while it is active on the main site.' ) . '</p></div>';
     406} elseif ( isset( $_GET['error'] ) && 'none' == $_GET['error'] ) {
     407        echo '<div id="message" class="error notice is-dismissible"><p>' . __( 'No theme selected.' ) . '</p></div>';
     408} elseif ( isset( $_GET['disable-auto-update'] ) ) {
     409        echo '<div id="message" class="updated notice is-dismissible"><p>' . __( 'Theme will no longer be auto-updated.' ) . '</p></div>';
     410} elseif ( isset( $_GET['enable-autoupdate-multi'] ) ) {
     411        echo '<div id="message" class="updated notice is-dismissible"><p>' . __( 'Themes will be auto-updated.' ) . '</p></div>';
     412} elseif ( isset( $_GET['disable-autoupdate-multi'] ) ) {
     413        echo '<div id="message" class="updated notice is-dismissible"><p>' . __( 'Themes will no longer be auto-updated.' ) . '</p></div>';
    316414}
    317415
    318416?>
  • src/wp-admin/plugins.php

    diff --git a/src/wp-admin/plugins.php b/src/wp-admin/plugins.php
    index b7d1b2ddfc..b9e33b6052 100644
    a b $plugin = isset( $_REQUEST['plugin'] ) ? wp_unslash( $_REQUEST['plugin'] ) : ''; 
    2222$s      = isset( $_REQUEST['s'] ) ? urlencode( wp_unslash( $_REQUEST['s'] ) ) : '';
    2323
    2424// Clean up request URI from temporary args for screen options/paging uri's to work as expected.
    25 $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'error', 'deleted', 'activate', 'activate-multi', 'deactivate', 'deactivate-multi', '_error_nonce' ), $_SERVER['REQUEST_URI'] );
     25$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'error', 'deleted', 'activate', 'activate-multi', 'deactivate', 'deactivate-multi', 'enable-auto-update', 'disable-auto-update', 'enable-auto-update-multi', 'disable-auto-update-multi', '_error_nonce' ), $_SERVER['REQUEST_URI'] );
    2626
    2727wp_enqueue_script( 'updates' );
    2828
    if ( $action ) { 
    414414                        wp_redirect( self_admin_url( "plugins.php?resume=true&plugin_status=$status&paged=$page&s=$s" ) );
    415415                        exit;
    416416
     417                case 'enable-auto-update':
     418                        if ( ! current_user_can( 'update_plugins' ) || ! wp_is_auto_update_enabled_for_type( 'plugin' ) ) {
     419                                wp_die( __( 'Sorry, you are not allowed to enable plugins automatic updates.' ) );
     420                        }
     421
     422                        if ( is_multisite() && ! is_network_admin() ) {
     423                                wp_die( __( 'Please connect to your network admin to manage plugins automatic updates.' ) );
     424                        }
     425
     426                        check_admin_referer( 'updates' );
     427
     428                        if ( empty( $plugin ) ) {
     429                                wp_redirect( self_admin_url( "plugins.php?plugin_status=$status&paged=$page&s=$s" ) );
     430                                exit;
     431                        }
     432
     433                        $auto_updates = (array) get_site_option( 'auto_update_plugins', array() );
     434
     435                        $auto_updates[] = $plugin;
     436                        $auto_updates   = array_unique( $auto_updates );
     437
     438                        update_site_option( 'auto_update_plugins', $auto_updates );
     439                        wp_redirect( self_admin_url( "plugins.php?enable-auto-update=true&plugin_status=$status&paged=$page&s=$s" ) );
     440                        exit;
     441
     442                case 'disable-auto-update':
     443                        if ( ! current_user_can( 'update_plugins' ) || ! wp_is_auto_update_enabled_for_type( 'plugin' ) ) {
     444                                wp_die( __( 'Sorry, you are not allowed to disable plugins automatic updates.' ) );
     445                        }
     446
     447                        if ( is_multisite() && ! is_network_admin() ) {
     448                                wp_die( __( 'Please connect to your network admin to manage plugins automatic updates.' ) );
     449                        }
     450
     451                        check_admin_referer( 'updates' );
     452
     453                        if ( empty( $plugin ) ) {
     454                                wp_redirect( self_admin_url( "plugins.php?plugin_status=$status&paged=$page&s=$s" ) );
     455                                exit;
     456                        }
     457
     458                        $auto_updates = (array) get_site_option( 'auto_update_plugins', array() );
     459                        $auto_updates = array_diff( $auto_updates, array( $plugin ) );
     460
     461                        update_site_option( 'auto_update_plugins', $auto_updates );
     462                        wp_redirect( self_admin_url( "plugins.php?disable-auto-update=true&plugin_status=$status&paged=$page&s=$s" ) );
     463                        exit;
     464
     465                case 'enable-auto-update-selected':
     466                        if ( ! ( current_user_can( 'update_plugins' ) && wp_is_auto_update_enabled_for_type( 'plugin' ) ) ) {
     467                                wp_die( __( 'Sorry, you are not allowed to enable plugins automatic updates.' ) );
     468                        }
     469
     470                        if ( is_multisite() && ! is_network_admin() ) {
     471                                wp_die( __( 'Please connect to your network admin to manage plugins automatic updates.' ) );
     472                        }
     473
     474                        check_admin_referer( 'bulk-plugins' );
     475
     476                        $plugins = isset( $_POST['checked'] ) ? (array) wp_unslash( $_POST['checked'] ) : array();
     477
     478                        $auto_updates     = (array) get_site_option( 'auto_update_plugins', array() );
     479                        $new_auto_updates = array_merge( $auto_updates, $plugins );
     480                        $new_auto_updates = array_unique( $new_auto_updates );
     481
     482                        // return early if all selected plugins already have auto-updates enabled.
     483                        // must use non-strict comparison, so that array order is not treated as significant.
     484                        if ( $new_auto_updates == $auto_updates ) {
     485                                wp_redirect( self_admin_url( "plugins.php?plugin_status=$status&paged=$page&s=$s" ) );
     486                                exit;
     487                        }
     488
     489                        update_site_option( 'auto_update_plugins', $new_auto_updates );
     490
     491                        wp_redirect( self_admin_url( "plugins.php?enable-auto-update-multi=true&plugin_status=$status&paged=$page&s=$s" ) );
     492                        exit;
     493                case 'disable-auto-update-selected':
     494                        if ( ! ( current_user_can( 'update_plugins' ) && wp_is_auto_update_enabled_for_type( 'plugin' ) ) ) {
     495                                wp_die( __( 'Sorry, you are not allowed to disable plugins automatic updates.' ) );
     496                        }
     497
     498                        if ( is_multisite() && ! is_network_admin() ) {
     499                                wp_die( __( 'Please connect to your network admin to manage plugins automatic updates.' ) );
     500                        }
     501
     502                        check_admin_referer( 'bulk-plugins' );
     503
     504                        $plugins = isset( $_POST['checked'] ) ? (array) wp_unslash( $_POST['checked'] ) : array();
     505
     506                        $auto_updates     = (array) get_site_option( 'auto_update_plugins', array() );
     507                        $new_auto_updates = array_diff( $auto_updates, $plugins );
     508                        $new_auto_updates = array_unique( $new_auto_updates );
     509
     510                        // return early if all selected plugins already have auto-updates enabled.
     511                        // must use non-strict comparison, so that array order is not treated as significant.
     512                        if ( $new_auto_updates == $auto_updates ) {
     513                                wp_redirect( self_admin_url( "plugins.php?plugin_status=$status&paged=$page&s=$s" ) );
     514                                exit;
     515                        }
     516
     517                        update_site_option( 'auto_update_plugins', $new_auto_updates );
     518
     519                        wp_redirect( self_admin_url( "plugins.php?disable-auto-update-multi=true&plugin_status=$status&paged=$page&s=$s" ) );
     520                        exit;
    417521                default:
    418522                        if ( isset( $_POST['checked'] ) ) {
    419523                                check_admin_referer( 'bulk-plugins' );
    elseif ( isset( $_GET['deleted'] ) ) : 
    583687        <div id="message" class="updated notice is-dismissible"><p><?php _e( 'All selected plugins are up to date.' ); ?></p></div>
    584688<?php elseif ( isset( $_GET['resume'] ) ) : ?>
    585689        <div id="message" class="updated notice is-dismissible"><p><?php _e( 'Plugin resumed.' ); ?></p></div>
     690<?php elseif ( isset( $_GET['enable-auto-update'] ) ) : ?>
     691        <div id="message" class="updated notice is-dismissible"><p><?php _e( 'Plugin will be auto-updated.' ); ?></p></div>
     692<?php elseif ( isset( $_GET['disable-auto-update'] ) ) : ?>
     693        <div id="message" class="updated notice is-dismissible"><p><?php _e( 'Plugin will no longer be auto-updated.' ); ?></p></div>
     694<?php elseif ( isset( $_GET['enable-auto-update-multi'] ) ) : ?>
     695        <div id="message" class="updated notice is-dismissible"><p><?php _e( 'Selected plugins will be auto-updated.' ); ?></p></div>
     696<?php elseif ( isset( $_GET['disable-auto-update-multi'] ) ) : ?>
     697        <div id="message" class="updated notice is-dismissible"><p><?php _e( 'Selected plugins will no longer be auto-updated.' ); ?></p></div>
    586698<?php endif; ?>
    587699
    588700<div class="wrap">
  • src/wp-admin/themes.php

    diff --git a/src/wp-admin/themes.php b/src/wp-admin/themes.php
    index 485c7674c3..eec6ca3e53 100644
    a b if ( current_user_can( 'switch_themes' ) && isset( $_GET['action'] ) ) { 
    8080                        delete_theme( $_GET['stylesheet'] );
    8181                        wp_redirect( admin_url( 'themes.php?deleted=true' ) );
    8282                }
     83                exit;
     84        } elseif ( 'enable-auto-update' === $_GET['action'] ) {
     85                if ( ! ( current_user_can( 'update_themes' ) && wp_is_auto_update_enabled_for_type( 'theme' ) ) ) {
     86                        wp_die( __( 'Sorry, you are not allowed to enable themes automatic updates.' ) );
     87                }
     88
     89                check_admin_referer( 'updates' );
     90
     91                $auto_updates = (array) get_site_option( 'auto_update_themes', array() );
     92
     93                $auto_updates[] = $_GET['stylesheet'];
     94                $auto_updates   = array_unique( $auto_updates );
     95
     96                update_site_option( 'auto_update_themes', $auto_updates );
     97
     98                wp_redirect( admin_url( "themes.php?enabled-auto-update=true" ) );
     99
     100                exit;
     101        } elseif ( 'disable-auto-update' === $_GET['action'] ) {
     102                if ( ! ( current_user_can( 'update_themes' ) && wp_is_auto_update_enabled_for_type( 'theme' ) ) ) {
     103                        wp_die( __( 'Sorry, you are not allowed to disable themes automatic updates.' ) );
     104                }
     105
     106                check_admin_referer( 'updates' );
     107
     108                $auto_updates = (array) get_site_option( 'auto_update_themes', array() );
     109
     110                $auto_updates = array_diff( $auto_updates, array( $_GET['stylesheet'] ) );
     111
     112                update_site_option( 'auto_update_themes', $auto_updates );
     113
     114                wp_redirect( admin_url( "themes.php?disabled-auto-update=true" ) );
     115
    83116                exit;
    84117        }
    85118}
    if ( ! validate_current_theme() || isset( $_GET['broken'] ) ) { 
    228261        ?>
    229262        <div id="message6" class="error"><p><?php _e( 'Theme could not be resumed because it triggered a <strong>fatal error</strong>.' ); ?></p></div>
    230263        <?php
     264} elseif ( isset( $_GET['enabled-auto-update'] ) ) {
     265        ?>
     266        <div id="message7" class="updated notice is-dismissible"><p><?php _e( 'Theme will be auto-updated.' ); ?></p></div>
     267        <?php
     268} elseif ( isset( $_GET['disabled-auto-update'] ) ) {
     269        ?>
     270        <div id="message8" class="updated notice is-dismissible"><p><?php _e( 'Theme will no longer be auto-updated.' ); ?></p></div>
     271        <?php
    231272}
    232273
    233274$ct = wp_get_theme();
    if ( ! is_multisite() && $broken_themes ) { 
    563604                                        ?>
    564605                                </p>
    565606
     607                                <# if ( data.actions.autoupdate ) { #>
     608                                <p class="theme-autoupdate">
     609                                <# if ( data.autoupdate ) { #>
     610                                        <a href="{{{ data.actions.autoupdate }}}" class="toggle-auto-update" data-slug="{{ data.id }}" data-wp-action="disable">
     611                                                <span class="dashicons dashicons-update spin hidden"></span>
     612                                                <span class="label"><?php _e( 'Disable auto-updates' ) ?></span>
     613                                        </a>
     614                                <# } else { #>
     615                                        <a href="{{{ data.actions.autoupdate }}}" class="toggle-auto-update" data-slug="{{ data.id }}" data-wp-action="enable">
     616                                                <span class="dashicons dashicons-update spin hidden"></span>
     617                                                <span class="label"><?php _e( 'Enable auto-updates' ) ?></span>
     618                                        </a>
     619                                <# } #>
     620                                <# if ( data.hasUpdate ) { #>
     621                                        <# if ( data.autoupdate) { #>
     622                                        <span class="auto-update-time"><br /><?php echo wp_get_auto_update_message() ?></span>
     623                                        <# } else { #>
     624                                        <span class="auto-update-time hidden"><br /><?php echo wp_get_auto_update_message() ?></span>
     625                                        <# } #>
     626                                <# } #>
     627                                        <span class="auto-updates-error hidden"><p></p></span>
     628                                </p>
     629                                <# } #>
     630
    566631                                <# if ( data.hasUpdate ) { #>
    567632                                <div class="notice notice-warning notice-alt notice-large">
    568633                                        <h3 class="notice-title"><?php _e( 'Update Available' ); ?></h3>
  • src/wp-admin/update-core.php

    diff --git a/src/wp-admin/update-core.php b/src/wp-admin/update-core.php
    index 9d21ac8c2c..49d21bb4b3 100644
    a b function list_plugin_updates() { 
    328328
    329329        <tbody class="plugins">
    330330        <?php
     331
     332        $auto_updates = array();
     333        if ( wp_is_auto_update_enabled_for_type( 'plugin' ) ) {
     334                $auto_updates       = (array) get_site_option( 'auto_update_plugins', array() );
     335                $auto_update_notice = ' | ' . wp_get_auto_update_message();
     336        }
     337
    331338        foreach ( (array) $plugins as $plugin_file => $plugin_data ) {
    332339                $plugin_data = (object) _get_plugin_data_markup_translate( $plugin_file, (array) $plugin_data, false, true );
    333340
    function list_plugin_updates() { 
    419426                                $plugin_data->update->new_version
    420427                        );
    421428                        echo ' ' . $details . $compat . $upgrade_notice;
     429                        if ( in_array( $plugin_file, $auto_updates, true ) ) {
     430                                echo $auto_update_notice;
     431                        }
    422432                        ?>
    423433                </p></td>
    424434        </tr>
    function list_theme_updates() { 
    478488
    479489        <tbody class="plugins">
    480490        <?php
     491        $auto_updates = array();
     492        if ( wp_is_auto_update_enabled_for_type( 'theme' ) ) {
     493                $auto_updates = (array) get_site_option( 'auto_update_themes', array() );
     494                $auto_update_notice = ' | ' . wp_get_auto_update_message();
     495        }
     496
    481497        foreach ( $themes as $stylesheet => $theme ) {
    482498                $checkbox_id = 'checkbox_' . md5( $theme->get( 'Name' ) );
     499
    483500                ?>
    484501        <tr>
    485502                <td class="check-column">
    function list_theme_updates() { 
    501518                                $theme->display( 'Version' ),
    502519                                $theme->update['new_version']
    503520                        );
     521                        if ( in_array( $stylesheet, $auto_updates, true ) ) {
     522                                echo $auto_update_notice;
     523                        }
    504524                        ?>
    505525                </p></td>
    506526        </tr>
  • src/wp-includes/script-loader.php

    diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php
    index 59aed70a22..e765a03163 100644
    a b function wp_default_scripts( $scripts ) { 
    15201520                                        /* translators: %s: Number of plugins. */
    15211521                                        'pluginsFound'             => __( 'Number of plugins found: %d' ),
    15221522                                        'noPluginsFound'           => __( 'No plugins found. Try a different search.' ),
     1523                                        'autoUpdatesEnable'        => __( 'Enable auto-updates' ),
     1524                                        'autoUpdatesEnabling'      => __( 'Enabling...' ),
     1525                                        'autoUpdatesEnabled'       => __( 'Auto-updates enabled' ),
     1526                                        'autoUpdatesDisable'       => __( 'Disable auto-updates' ),
     1527                                        'autoUpdatesDisabling'     => __( 'Disabling...' ),
     1528                                        'autoUpdatesDisabled'      => __( 'Auto-updates disabled' ),
     1529                                        'autoUpdatesError'         => __( 'The request could not be completed.' ),
    15231530                                ),
    15241531                        )
    15251532                );