diff -r c7c34916027a -r 177826044cd9 wp/wp-admin/includes/theme.php --- a/wp/wp-admin/includes/theme.php Mon Oct 14 18:06:33 2019 +0200 +++ b/wp/wp-admin/includes/theme.php Mon Oct 14 18:28:13 2019 +0200 @@ -11,31 +11,33 @@ * * @since 2.8.0 * - * @global WP_Filesystem_Base $wp_filesystem Subclass + * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * - * @param string $stylesheet Stylesheet of the theme to delete - * @param string $redirect Redirect to page when complete. - * @return void|bool|WP_Error When void, echoes content. + * @param string $stylesheet Stylesheet of the theme to delete. + * @param string $redirect Redirect to page when complete. + * @return bool|null|WP_Error True on success, false if `$stylesheet` is empty, WP_Error on failure. + * Null if filesystem credentials are required to proceed. */ -function delete_theme($stylesheet, $redirect = '') { +function delete_theme( $stylesheet, $redirect = '' ) { global $wp_filesystem; - if ( empty($stylesheet) ) + if ( empty( $stylesheet ) ) { return false; + } if ( empty( $redirect ) ) { - $redirect = wp_nonce_url('themes.php?action=delete&stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet); + $redirect = wp_nonce_url( 'themes.php?action=delete&stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet ); } ob_start(); $credentials = request_filesystem_credentials( $redirect ); - $data = ob_get_clean(); + $data = ob_get_clean(); if ( false === $credentials ) { - if ( ! empty( $data ) ){ - include_once( ABSPATH . 'wp-admin/admin-header.php'); + if ( ! empty( $data ) ) { + include_once( ABSPATH . 'wp-admin/admin-header.php' ); echo $data; - include( ABSPATH . 'wp-admin/admin-footer.php'); + include( ABSPATH . 'wp-admin/admin-footer.php' ); exit; } return; @@ -46,20 +48,22 @@ request_filesystem_credentials( $redirect, '', true ); // Failed to connect, Error and request again. $data = ob_get_clean(); - if ( ! empty($data) ) { - include_once( ABSPATH . 'wp-admin/admin-header.php'); + if ( ! empty( $data ) ) { + include_once( ABSPATH . 'wp-admin/admin-header.php' ); echo $data; - include( ABSPATH . 'wp-admin/admin-footer.php'); + include( ABSPATH . 'wp-admin/admin-footer.php' ); exit; } return; } - if ( ! is_object($wp_filesystem) ) - return new WP_Error('fs_unavailable', __('Could not access filesystem.')); + if ( ! is_object( $wp_filesystem ) ) { + return new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) ); + } - if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() ) - return new WP_Error('fs_error', __('Filesystem error.'), $wp_filesystem->errors); + if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { + return new WP_Error( 'fs_error', __( 'Filesystem error.' ), $wp_filesystem->errors ); + } // Get the base plugin folder. $themes_dir = $wp_filesystem->wp_themes_dir(); @@ -68,8 +72,8 @@ } $themes_dir = trailingslashit( $themes_dir ); - $theme_dir = trailingslashit( $themes_dir . $stylesheet ); - $deleted = $wp_filesystem->delete( $theme_dir, true ); + $theme_dir = trailingslashit( $themes_dir . $stylesheet ); + $deleted = $wp_filesystem->delete( $theme_dir, true ); if ( ! $deleted ) { return new WP_Error( 'could_not_remove_theme', sprintf( __( 'Could not fully remove the theme %s.' ), $stylesheet ) ); @@ -84,6 +88,11 @@ foreach ( $translations as $translation => $data ) { $wp_filesystem->delete( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '.po' ); $wp_filesystem->delete( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '.mo' ); + + $json_translation_files = glob( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '-*.json' ); + if ( $json_translation_files ) { + array_map( array( $wp_filesystem, 'delete' ), $json_translation_files ); + } } } @@ -122,8 +131,8 @@ * @param string $containingfolder Path of the theme parent folder * @return string */ -function _get_template_edit_filename($fullpath, $containingfolder) { - return str_replace(dirname(dirname( $containingfolder )) , '', $fullpath); +function _get_template_edit_filename( $fullpath, $containingfolder ) { + return str_replace( dirname( dirname( $containingfolder ) ), '', $fullpath ); } /** @@ -155,11 +164,13 @@ function get_theme_update_available( $theme ) { static $themes_update = null; - if ( !current_user_can('update_themes' ) ) + if ( ! current_user_can( 'update_themes' ) ) { return false; + } - if ( !isset($themes_update) ) - $themes_update = get_site_transient('update_themes'); + if ( ! isset( $themes_update ) ) { + $themes_update = get_site_transient( 'update_themes' ); + } if ( ! ( $theme instanceof WP_Theme ) ) { return false; @@ -169,19 +180,28 @@ $html = ''; - if ( isset($themes_update->response[ $stylesheet ]) ) { - $update = $themes_update->response[ $stylesheet ]; - $theme_name = $theme->display('Name'); - $details_url = add_query_arg(array('TB_iframe' => 'true', 'width' => 1024, 'height' => 800), $update['url']); //Theme browser inside WP? replace this, Also, theme preview JS will override this on the available list. - $update_url = wp_nonce_url( admin_url( 'update.php?action=upgrade-theme&theme=' . urlencode( $stylesheet ) ), 'upgrade-theme_' . $stylesheet ); + if ( isset( $themes_update->response[ $stylesheet ] ) ) { + $update = $themes_update->response[ $stylesheet ]; + $theme_name = $theme->display( 'Name' ); + $details_url = add_query_arg( + array( + 'TB_iframe' => 'true', + 'width' => 1024, + 'height' => 800, + ), + $update['url'] + ); //Theme browser inside WP? replace this, Also, theme preview JS will override this on the available list. + $update_url = wp_nonce_url( admin_url( 'update.php?action=upgrade-theme&theme=' . urlencode( $stylesheet ) ), 'upgrade-theme_' . $stylesheet ); - if ( !is_multisite() ) { - if ( ! current_user_can('update_themes') ) { + if ( ! is_multisite() ) { + if ( ! current_user_can( 'update_themes' ) ) { /* translators: 1: theme name, 2: theme details URL, 3: additional link attributes, 4: version number */ - $html = sprintf( '
' . __( 'There is a new version of %1$s available. View version %4$s details.' ) . '
', + $html = sprintf( + '' . __( 'There is a new version of %1$s available. View version %4$s details.' ) . '
', $theme_name, esc_url( $details_url ), - sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"', + sprintf( + 'class="thickbox open-plugin-details-modal" aria-label="%s"', /* translators: 1: theme name, 2: version number */ esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) ) ), @@ -189,10 +209,12 @@ ); } elseif ( empty( $update['package'] ) ) { /* translators: 1: theme name, 2: theme details URL, 3: additional link attributes, 4: version number */ - $html = sprintf( '' . __( 'There is a new version of %1$s available. View version %4$s details. Automatic update is unavailable for this theme.' ) . '
', + $html = sprintf( + '' . __( 'There is a new version of %1$s available. View version %4$s details. Automatic update is unavailable for this theme.' ) . '
', $theme_name, esc_url( $details_url ), - sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"', + sprintf( + 'class="thickbox open-plugin-details-modal" aria-label="%s"', /* translators: 1: theme name, 2: version number */ esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) ) ), @@ -200,16 +222,19 @@ ); } else { /* translators: 1: theme name, 2: theme details URL, 3: additional link attributes, 4: version number, 5: update URL, 6: additional link attributes */ - $html = sprintf( '' . __( 'There is a new version of %1$s available. View version %4$s details or update now.' ) . '
', + $html = sprintf( + '' . __( 'There is a new version of %1$s available. View version %4$s details or update now.' ) . '
', $theme_name, esc_url( $details_url ), - sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"', + sprintf( + 'class="thickbox open-plugin-details-modal" aria-label="%s"', /* translators: 1: theme name, 2: version number */ esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) ) ), $update['new_version'], $update_url, - sprintf( 'aria-label="%s" id="update-theme" data-slug="%s"', + sprintf( + 'aria-label="%s" id="update-theme" data-slug="%s"', /* translators: %s: theme name */ esc_attr( sprintf( __( 'Update %s now' ), $theme_name ) ), $stylesheet @@ -223,7 +248,7 @@ } /** - * Retrieve list of WordPress theme features (aka theme tags) + * Retrieve list of WordPress theme features (aka theme tags). * * @since 3.1.0 * @@ -262,7 +287,7 @@ 'theme-options' => __( 'Theme Options' ), ), - __( 'Layout' ) => array( + __( 'Layout' ) => array( 'grid-layout' => __( 'Grid Layout' ), 'one-column' => __( 'One Column' ), 'two-columns' => __( 'Two Columns' ), @@ -270,24 +295,28 @@ 'four-columns' => __( 'Four Columns' ), 'left-sidebar' => __( 'Left Sidebar' ), 'right-sidebar' => __( 'Right Sidebar' ), - ) + ), ); - if ( ! $api || ! current_user_can( 'install_themes' ) ) + if ( ! $api || ! current_user_can( 'install_themes' ) ) { return $features; + } - if ( !$feature_list = get_site_transient( 'wporg_theme_feature_list' ) ) + if ( ! $feature_list = get_site_transient( 'wporg_theme_feature_list' ) ) { set_site_transient( 'wporg_theme_feature_list', array(), 3 * HOUR_IN_SECONDS ); - - if ( !$feature_list ) { - $feature_list = themes_api( 'feature_list', array() ); - if ( is_wp_error( $feature_list ) ) - return $features; } - if ( !$feature_list ) + if ( ! $feature_list ) { + $feature_list = themes_api( 'feature_list', array() ); + if ( is_wp_error( $feature_list ) ) { + return $features; + } + } + + if ( ! $feature_list ) { return $features; + } set_site_transient( 'wporg_theme_feature_list', $feature_list, 3 * HOUR_IN_SECONDS ); @@ -300,15 +329,17 @@ // Loop over the wporg canonical list and apply translations $wporg_features = array(); foreach ( (array) $feature_list as $feature_category => $feature_items ) { - if ( isset($category_translations[$feature_category]) ) - $feature_category = $category_translations[$feature_category]; - $wporg_features[$feature_category] = array(); + if ( isset( $category_translations[ $feature_category ] ) ) { + $feature_category = $category_translations[ $feature_category ]; + } + $wporg_features[ $feature_category ] = array(); foreach ( $feature_items as $feature ) { - if ( isset($features[$feature_category][$feature]) ) - $wporg_features[$feature_category][$feature] = $features[$feature_category][$feature]; - else - $wporg_features[$feature_category][$feature] = $feature; + if ( isset( $features[ $feature_category ][ $feature ] ) ) { + $wporg_features[ $feature_category ][ $feature ] = $features[ $feature_category ][ $feature ]; + } else { + $wporg_features[ $feature_category ][ $feature ] = $feature; + } } } @@ -397,19 +428,27 @@ * for more information on the make-up of possible return objects depending on the value of `$action`. */ function themes_api( $action, $args = array() ) { + // include an unmodified $wp_version + include( ABSPATH . WPINC . '/version.php' ); if ( is_array( $args ) ) { $args = (object) $args; } - if ( ! isset( $args->per_page ) ) { - $args->per_page = 24; + if ( 'query_themes' == $action ) { + if ( ! isset( $args->per_page ) ) { + $args->per_page = 24; + } } if ( ! isset( $args->locale ) ) { $args->locale = get_user_locale(); } + if ( ! isset( $args->wp_version ) ) { + $args->wp_version = substr( $wp_version, 0, 3 ); // X.y + } + /** * Filters arguments used to query for installer pages from the WordPress.org Themes API. * @@ -441,21 +480,24 @@ $res = apply_filters( 'themes_api', false, $action, $args ); if ( ! $res ) { - // include an unmodified $wp_version - include( ABSPATH . WPINC . '/version.php' ); + $url = 'http://api.wordpress.org/themes/info/1.2/'; + $url = add_query_arg( + array( + 'action' => $action, + 'request' => $args, + ), + $url + ); - $url = $http_url = 'http://api.wordpress.org/themes/info/1.0/'; - if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) + $http_url = $url; + if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) { $url = set_url_scheme( $url, 'https' ); + } $http_args = array( 'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ), - 'body' => array( - 'action' => $action, - 'request' => serialize( $args ) - ) ); - $request = wp_remote_post( $url, $http_args ); + $request = wp_remote_get( $url, $http_args ); if ( $ssl && is_wp_error( $request ) ) { if ( ! wp_doing_ajax() ) { @@ -468,11 +510,12 @@ headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE ); } - $request = wp_remote_post( $http_url, $http_args ); + $request = wp_remote_get( $http_url, $http_args ); } - if ( is_wp_error($request) ) { - $res = new WP_Error( 'themes_api_failed', + if ( is_wp_error( $request ) ) { + $res = new WP_Error( + 'themes_api_failed', sprintf( /* translators: %s: support forums URL */ __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ), @@ -481,9 +524,13 @@ $request->get_error_message() ); } else { - $res = maybe_unserialize( wp_remote_retrieve_body( $request ) ); - if ( ! is_object( $res ) && ! is_array( $res ) ) { - $res = new WP_Error( 'themes_api_failed', + $res = json_decode( wp_remote_retrieve_body( $request ), true ); + if ( is_array( $res ) ) { + // Object casting is required in order to match the info/1.0 format. + $res = (object) $res; + } elseif ( null === $res ) { + $res = new WP_Error( + 'themes_api_failed', sprintf( /* translators: %s: support forums URL */ __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ), @@ -492,6 +539,21 @@ wp_remote_retrieve_body( $request ) ); } + + if ( isset( $res->error ) ) { + $res = new WP_Error( 'themes_api_failed', $res->error ); + } + } + + // Back-compat for info/1.2 API, upgrade the theme objects in query_themes to objects. + if ( 'query_themes' == $action ) { + foreach ( $res->themes as $i => $theme ) { + $res->themes[ $i ] = (object) $theme; + } + } + // Back-compat for info/1.2 API, downgrade the feature_list result back to an array. + if ( 'feature_list' == $action ) { + $res = (array) $res; } } @@ -513,8 +575,8 @@ * * @since 3.8.0 * - * @param array $themes Optional. Array of WP_Theme objects to prepare. - * Defaults to all allowed themes. + * @param WP_Theme[] $themes Optional. Array of theme objects to prepare. + * Defaults to all allowed themes. * * @return array An associative array of theme data, sorted by name. */ @@ -529,9 +591,9 @@ * * @since 4.2.0 * - * @param array $prepared_themes An associative array of theme data. Default empty array. - * @param null|array $themes An array of WP_Theme objects to prepare, if any. - * @param string $current_theme The current theme slug. + * @param array $prepared_themes An associative array of theme data. Default empty array. + * @param WP_Theme[]|null $themes An array of theme objects to prepare, if any. + * @param string $current_theme The current theme slug. */ $prepared_themes = (array) apply_filters( 'pre_prepare_themes_for_js', array(), $themes, $current_theme ); @@ -562,24 +624,26 @@ $parents = array(); foreach ( $themes as $theme ) { - $slug = $theme->get_stylesheet(); + $slug = $theme->get_stylesheet(); $encoded_slug = urlencode( $slug ); $parent = false; if ( $theme->parent() ) { - $parent = $theme->parent(); + $parent = $theme->parent(); $parents[ $slug ] = $parent->get_stylesheet(); - $parent = $parent->display( 'Name' ); + $parent = $parent->display( 'Name' ); } $customize_action = null; if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { - $customize_action = esc_url( add_query_arg( - array( - 'return' => urlencode( esc_url_raw( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ), - ), - wp_customize_url( $slug ) - ) ); + $customize_action = esc_url( + add_query_arg( + array( + 'return' => urlencode( esc_url_raw( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ), + ), + wp_customize_url( $slug ) + ) + ); } $prepared_themes[ $slug ] = array( @@ -594,12 +658,12 @@ 'parent' => $parent, 'active' => $slug === $current_theme, 'hasUpdate' => isset( $updates[ $slug ] ), - 'hasPackage' => isset( $updates[ $slug ] ) && ! empty( $updates[ $slug ][ 'package' ] ), + 'hasPackage' => isset( $updates[ $slug ] ) && ! empty( $updates[ $slug ]['package'] ), 'update' => get_theme_update_available( $theme ), 'actions' => array( - 'activate' => current_user_can( 'switch_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=activate&stylesheet=' . $encoded_slug ), 'switch-theme_' . $slug ) : null, + 'activate' => current_user_can( 'switch_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=activate&stylesheet=' . $encoded_slug ), 'switch-theme_' . $slug ) : null, 'customize' => $customize_action, - 'delete' => current_user_can( 'delete_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=delete&stylesheet=' . $encoded_slug ), 'delete-theme_' . $slug ) : null, + 'delete' => current_user_can( 'delete_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=delete&stylesheet=' . $encoded_slug ), 'delete-theme_' . $slug ) : null, ), ); } @@ -657,12 +721,17 @@ <# if ( data.stars && 0 != data.num_ratings ) { #> <# } #> @@ -704,3 +773,140 @@ delete( $extension ); + + if ( ! $result ) { + return new WP_Error( + 'could_not_resume_theme', + __( 'Could not resume the theme.' ) + ); + } + + return true; +} + +/** + * Renders an admin notice in case some themes have been paused due to errors. + * + * @since 5.2.0 + */ +function paused_themes_notice() { + if ( 'themes.php' === $GLOBALS['pagenow'] ) { + return; + } + + if ( ! current_user_can( 'resume_themes' ) ) { + return; + } + + if ( ! isset( $GLOBALS['_paused_themes'] ) || empty( $GLOBALS['_paused_themes'] ) ) { + return; + } + + printf( + '%s
%s