diff -r 3d4e9c994f10 -r a86126ab1dd4 wp/wp-admin/includes/class-wp-ms-themes-list-table.php --- a/wp/wp-admin/includes/class-wp-ms-themes-list-table.php Tue Oct 22 16:11:46 2019 +0200 +++ b/wp/wp-admin/includes/class-wp-ms-themes-list-table.php Tue Dec 15 13:49:49 2020 +0100 @@ -23,6 +23,15 @@ private $has_items; /** + * Whether to show the auto-updates UI. + * + * @since 5.5.0 + * + * @var bool True if auto-updates UI is to be shown, false otherwise. + */ + protected $show_autoupdates = true; + + /** * Constructor. * * @since 3.1.0 @@ -45,7 +54,7 @@ ); $status = isset( $_REQUEST['theme_status'] ) ? $_REQUEST['theme_status'] : 'all'; - if ( ! in_array( $status, array( 'all', 'enabled', 'disabled', 'upgrade', 'search', 'broken' ) ) ) { + if ( ! in_array( $status, array( 'all', 'enabled', 'disabled', 'upgrade', 'search', 'broken', 'auto-update-enabled', 'auto-update-disabled' ), true ) ) { $status = 'all'; } @@ -56,13 +65,16 @@ if ( $this->is_site_themes ) { $this->site_id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; } + + $this->show_autoupdates = wp_is_auto_update_enabled_for_type( 'theme' ) && + ! $this->is_site_themes && current_user_can( 'update_themes' ); } /** * @return array */ protected function get_table_classes() { - // todo: remove and add CSS for .themes + // @todo Remove and add CSS for .themes. return array( 'widefat', 'plugins' ); } @@ -107,6 +119,13 @@ 'broken' => $this->is_site_themes ? array() : wp_get_themes( array( 'errors' => true ) ), ); + if ( $this->show_autoupdates ) { + $auto_updates = (array) get_site_option( 'auto_update_themes', array() ); + + $themes['auto-update-enabled'] = array(); + $themes['auto-update-disabled'] = array(); + } + if ( $this->is_site_themes ) { $themes_per_page = $this->get_items_per_page( 'site_themes_network_per_page' ); $allowed_where = 'site'; @@ -115,7 +134,8 @@ $allowed_where = 'network'; } - $maybe_update = current_user_can( 'update_themes' ) && ! $this->is_site_themes && $current = get_site_transient( 'update_themes' ); + $current = get_site_transient( 'update_themes' ); + $maybe_update = current_user_can( 'update_themes' ) && ! $this->is_site_themes && $current; foreach ( (array) $themes['all'] as $key => $theme ) { if ( $this->is_site_themes && $theme->is_allowed( 'network' ) ) { @@ -130,6 +150,57 @@ $filter = $theme->is_allowed( $allowed_where, $this->site_id ) ? 'enabled' : 'disabled'; $themes[ $filter ][ $key ] = $themes['all'][ $key ]; + + $theme_data = array( + 'update_supported' => isset( $theme->update_supported ) ? $theme->update_supported : true, + ); + + // Extra info if known. array_merge() ensures $theme_data has precedence if keys collide. + if ( isset( $current->response[ $key ] ) ) { + $theme_data = array_merge( (array) $current->response[ $key ], $theme_data ); + } elseif ( isset( $current->no_update[ $key ] ) ) { + $theme_data = array_merge( (array) $current->no_update[ $key ], $theme_data ); + } else { + $theme_data['update_supported'] = false; + } + + $theme->update_supported = $theme_data['update_supported']; + + /* + * Create the expected payload for the auto_update_theme filter, this is the same data + * as contained within $updates or $no_updates but used when the Theme is not known. + */ + $filter_payload = array( + 'theme' => $key, + 'new_version' => '', + 'url' => '', + 'package' => '', + 'requires' => '', + 'requires_php' => '', + ); + + $filter_payload = array_merge( $filter_payload, array_intersect_key( $theme_data, $filter_payload ) ); + + $type = 'theme'; + /** This filter is documented in wp-admin/includes/class-wp-automatic-updater.php */ + $auto_update_forced = apply_filters( "auto_update_{$type}", null, (object) $filter_payload ); + + if ( ! is_null( $auto_update_forced ) ) { + $theme->auto_update_forced = $auto_update_forced; + } + + if ( $this->show_autoupdates ) { + $enabled = in_array( $key, $auto_updates, true ) && $theme->update_supported; + if ( isset( $theme->auto_update_forced ) ) { + $enabled = (bool) $theme->auto_update_forced; + } + + if ( $enabled ) { + $themes['auto-update-enabled'][ $key ] = $theme; + } else { + $themes['auto-update-disabled'][ $key ] = $theme; + } + } } if ( $s ) { @@ -142,7 +213,7 @@ $totals[ $type ] = count( $list ); } - if ( empty( $themes[ $status ] ) && ! in_array( $status, array( 'all', 'search' ) ) ) { + if ( empty( $themes[ $status ] ) && ! in_array( $status, array( 'all', 'search' ), true ) ) { $status = 'all'; } @@ -165,7 +236,7 @@ $orderby = ucfirst( $orderby ); $order = strtoupper( $order ); - if ( $orderby === 'Name' ) { + if ( 'Name' === $orderby ) { if ( 'ASC' === $order ) { $this->items = array_reverse( $this->items ); } @@ -189,7 +260,6 @@ } /** - * @staticvar string $term * @param WP_Theme $theme * @return bool */ @@ -248,7 +318,7 @@ if ( $this->has_items ) { _e( 'No themes found.' ); } else { - _e( 'You do not appear to have any themes available at this time.' ); + _e( 'No themes are currently available.' ); } } @@ -256,11 +326,17 @@ * @return array */ public function get_columns() { - return array( + $columns = array( 'cb' => '', 'name' => __( 'Theme' ), 'description' => __( 'Description' ), ); + + if ( $this->show_autoupdates ) { + $columns['auto-updates'] = __( 'Automatic Updates' ); + } + + return $columns; } /** @@ -299,19 +375,65 @@ switch ( $type ) { case 'all': - $text = _nx( 'All (%s)', 'All (%s)', $count, 'themes' ); + /* translators: %s: Number of themes. */ + $text = _nx( + 'All (%s)', + 'All (%s)', + $count, + 'themes' + ); break; case 'enabled': - $text = _n( 'Enabled (%s)', 'Enabled (%s)', $count ); + /* translators: %s: Number of themes. */ + $text = _nx( + 'Enabled (%s)', + 'Enabled (%s)', + $count, + 'themes' + ); break; case 'disabled': - $text = _n( 'Disabled (%s)', 'Disabled (%s)', $count ); + /* translators: %s: Number of themes. */ + $text = _nx( + 'Disabled (%s)', + 'Disabled (%s)', + $count, + 'themes' + ); break; case 'upgrade': - $text = _n( 'Update Available (%s)', 'Update Available (%s)', $count ); + /* translators: %s: Number of themes. */ + $text = _nx( + 'Update Available (%s)', + 'Update Available (%s)', + $count, + 'themes' + ); break; case 'broken': - $text = _n( 'Broken (%s)', 'Broken (%s)', $count ); + /* translators: %s: Number of themes. */ + $text = _nx( + 'Broken (%s)', + 'Broken (%s)', + $count, + 'themes' + ); + break; + case 'auto-update-enabled': + /* translators: %s: Number of themes. */ + $text = _n( + 'Auto-updates Enabled (%s)', + 'Auto-updates Enabled (%s)', + $count + ); + break; + case 'auto-update-disabled': + /* translators: %s: Number of themes. */ + $text = _n( + 'Auto-updates Disabled (%s)', + 'Auto-updates Disabled (%s)', + $count + ); break; } @@ -321,7 +443,7 @@ $url = 'themes.php'; } - if ( 'search' != $type ) { + if ( 'search' !== $type ) { $status_links[ $type ] = sprintf( "%s", esc_url( add_query_arg( 'theme_status', $type, $url ) ), @@ -343,10 +465,10 @@ global $status; $actions = array(); - if ( 'enabled' != $status ) { + if ( 'enabled' !== $status ) { $actions['enable-selected'] = $this->is_site_themes ? __( 'Enable' ) : __( 'Network Enable' ); } - if ( 'disabled' != $status ) { + if ( 'disabled' !== $status ) { $actions['disable-selected'] = $this->is_site_themes ? __( 'Disable' ) : __( 'Network Disable' ); } if ( ! $this->is_site_themes ) { @@ -357,6 +479,17 @@ $actions['delete-selected'] = __( 'Delete' ); } } + + if ( $this->show_autoupdates ) { + if ( 'auto-update-enabled' !== $status ) { + $actions['enable-auto-update-selected'] = __( 'Enable Auto-updates' ); + } + + if ( 'auto-update-disabled' !== $status ) { + $actions['disable-auto-update-selected'] = __( 'Disable Auto-updates' ); + } + } + return $actions; } @@ -430,10 +563,10 @@ ); if ( $this->is_site_themes ) { - /* translators: %s: theme name */ + /* translators: %s: Theme name. */ $aria_label = sprintf( __( 'Enable %s' ), $theme->display( 'Name' ) ); } else { - /* translators: %s: theme name */ + /* translators: %s: Theme name. */ $aria_label = sprintf( __( 'Network Enable %s' ), $theme->display( 'Name' ) ); } @@ -456,10 +589,10 @@ ); if ( $this->is_site_themes ) { - /* translators: %s: theme name */ + /* translators: %s: Theme name. */ $aria_label = sprintf( __( 'Disable %s' ), $theme->display( 'Name' ) ); } else { - /* translators: %s: theme name */ + /* translators: %s: Theme name. */ $aria_label = sprintf( __( 'Network Disable %s' ), $theme->display( 'Name' ) ); } @@ -471,7 +604,11 @@ ); } - if ( ! $allowed && current_user_can( 'delete_themes' ) && ! $this->is_site_themes && $stylesheet != get_option( 'stylesheet' ) && $stylesheet != get_option( 'template' ) ) { + if ( ! $allowed && ! $this->is_site_themes + && current_user_can( 'delete_themes' ) + && get_option( 'stylesheet' ) !== $stylesheet + && get_option( 'template' ) !== $stylesheet + ) { $url = add_query_arg( array( 'action' => 'delete-selected', @@ -483,7 +620,7 @@ 'themes.php' ); - /* translators: %s: theme name */ + /* translators: %s: Theme name. */ $aria_label = sprintf( _x( 'Delete %s', 'theme' ), $theme->display( 'Name' ) ); $actions['delete'] = sprintf( @@ -548,8 +685,9 @@ */ public function column_description( $theme ) { global $status, $totals; + if ( $theme->errors() ) { - $pre = $status === 'broken' ? __( 'Broken Theme:' ) . ' ' : ''; + $pre = 'broken' === $status ? __( 'Broken Theme:' ) . ' ' : ''; echo '

' . $pre . $theme->errors()->get_error_message() . '

'; } @@ -571,12 +709,15 @@ $theme_meta = array(); if ( $theme->get( 'Version' ) ) { + /* translators: %s: Theme version. */ $theme_meta[] = sprintf( __( 'Version %s' ), $theme->display( 'Version' ) ); } + + /* translators: %s: Theme author. */ $theme_meta[] = sprintf( __( 'By %s' ), $theme->display( 'Author' ) ); if ( $theme->get( 'ThemeURI' ) ) { - /* translators: %s: theme name */ + /* translators: %s: Theme name. */ $aria_label = sprintf( __( 'Visit %s homepage' ), $theme->display( 'Name' ) ); $theme_meta[] = sprintf( @@ -586,26 +727,123 @@ __( 'Visit Theme Site' ) ); } + /** * Filters the array of row meta for each theme in the Multisite themes * list table. * * @since 3.1.0 * - * @param string[] $theme_meta An array of the theme's metadata, - * including the version, author, and - * theme URI. + * @param string[] $theme_meta An array of the theme's metadata, including + * the version, author, and theme URI. * @param string $stylesheet Directory name of the theme. * @param WP_Theme $theme WP_Theme object. * @param string $status Status of the theme. */ $theme_meta = apply_filters( 'theme_row_meta', $theme_meta, $stylesheet, $theme, $status ); + echo implode( ' | ', $theme_meta ); echo ''; } /** + * Handles the auto-updates column output. + * + * @since 5.5.0 + * + * @global string $status + * @global int $page + * + * @param WP_Theme $theme The current WP_Theme object. + */ + public function column_autoupdates( $theme ) { + global $status, $page; + + static $auto_updates, $available_updates; + + if ( ! $auto_updates ) { + $auto_updates = (array) get_site_option( 'auto_update_themes', array() ); + } + if ( ! $available_updates ) { + $available_updates = get_site_transient( 'update_themes' ); + } + + $stylesheet = $theme->get_stylesheet(); + + if ( isset( $theme->auto_update_forced ) ) { + if ( $theme->auto_update_forced ) { + // Forced on. + $text = __( 'Auto-updates enabled' ); + } else { + $text = __( 'Auto-updates disabled' ); + } + $action = 'unavailable'; + $time_class = ' hidden'; + } elseif ( empty( $theme->update_supported ) ) { + $text = ''; + $action = 'unavailable'; + $time_class = ' hidden'; + } elseif ( in_array( $stylesheet, $auto_updates, true ) ) { + $text = __( 'Disable auto-updates' ); + $action = 'disable'; + $time_class = ''; + } else { + $text = __( 'Enable auto-updates' ); + $action = 'enable'; + $time_class = ' hidden'; + } + + $query_args = array( + 'action' => "{$action}-auto-update", + 'theme' => $stylesheet, + 'paged' => $page, + 'theme_status' => $status, + ); + + $url = add_query_arg( $query_args, 'themes.php' ); + + if ( 'unavailable' === $action ) { + $html[] = '' . $text . ''; + } else { + $html[] = sprintf( + '', + wp_nonce_url( $url, 'updates' ), + $action + ); + + $html[] = ''; + $html[] = '' . $text . ''; + $html[] = ''; + + } + + if ( isset( $available_updates->response[ $stylesheet ] ) ) { + $html[] = sprintf( + '
%s
', + $time_class, + wp_get_auto_update_message() + ); + } + + $html = implode( '', $html ); + + /** + * Filters the HTML of the auto-updates setting for each theme in the Themes list table. + * + * @since 5.5.0 + * + * @param string $html The HTML for theme's auto-update setting, including + * toggle auto-update action link and time to next update. + * @param string $stylesheet Directory name of the theme. + * @param WP_Theme $theme WP_Theme object. + */ + echo apply_filters( 'theme_auto_update_setting_html', $html, $stylesheet, $theme ); + + echo ''; + } + + /** * Handles default column output. * * @since 4.3.0 @@ -640,7 +878,7 @@ foreach ( $columns as $column_name => $column_display_name ) { $extra_classes = ''; - if ( in_array( $column_name, $hidden ) ) { + if ( in_array( $column_name, $hidden, true ) ) { $extra_classes .= ' hidden'; } @@ -687,6 +925,13 @@ echo ''; break; + case 'auto-updates': + echo ""; + + $this->column_autoupdates( $item ); + + echo ''; + break; default: echo "";