Theme Installer: Revert to proxying through PHP for WordPress.org API requests.

This is to ensure we have valid installation nonces, though we've run into this as a problem previously (see #27639, #27581, #27055).

A tad slower, but we gained speed in 3.9 by simplifying the request made to the API.

props ocean90.
fixes #27798.

Built from https://develop.svn.wordpress.org/trunk@28126


git-svn-id: http://core.svn.wordpress.org/trunk@27957 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Andrew Nacin 2014-04-15 01:16:14 +00:00
parent d1e72b8e20
commit 9f81d0526e
7 changed files with 85 additions and 59 deletions

View File

@ -58,7 +58,7 @@ $core_actions_post = array(
'wp-remove-post-lock', 'dismiss-wp-pointer', 'upload-attachment', 'get-attachment', 'wp-remove-post-lock', 'dismiss-wp-pointer', 'upload-attachment', 'get-attachment',
'query-attachments', 'save-attachment', 'save-attachment-compat', 'send-link-to-editor', 'query-attachments', 'save-attachment', 'save-attachment-compat', 'send-link-to-editor',
'send-attachment-to-editor', 'save-attachment-order', 'heartbeat', 'get-revision-diffs', 'send-attachment-to-editor', 'save-attachment-order', 'heartbeat', 'get-revision-diffs',
'save-user-color-scheme', 'update-widget', 'save-user-color-scheme', 'update-widget', 'query-themes',
); );
// Register core Ajax calls. // Register core Ajax calls.

View File

@ -2204,3 +2204,48 @@ function wp_ajax_save_user_color_scheme() {
update_user_meta( get_current_user_id(), 'admin_color', $color_scheme ); update_user_meta( get_current_user_id(), 'admin_color', $color_scheme );
wp_send_json_success(); wp_send_json_success();
} }
/**
* Get themes from themes_api().
*
* @since 3.9.0
*/
function wp_ajax_query_themes() {
global $themes_allowedtags, $theme_field_defaults;
if ( ! current_user_can( 'install_themes' ) ) {
wp_send_json_error();
}
$args = wp_parse_args( wp_unslash( $_REQUEST['request'] ), array(
'per_page' => 20,
'fields' => $theme_field_defaults
) );
$old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search';
/** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */
$args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args );
$api = themes_api( 'query_themes', $args );
if ( is_wp_error( $api ) ) {
wp_send_json_error();
}
$update_php = self_admin_url( 'update.php?action=install-theme' );
foreach ( $api->themes as &$theme ) {
$theme->install_url = add_query_arg( array(
'theme' => $theme->slug,
'_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug )
), $update_php );
$theme->name = wp_kses( $theme->name, $themes_allowedtags );
$theme->author = wp_kses( $theme->author, $themes_allowedtags );
$theme->version = wp_kses( $theme->version, $themes_allowedtags );
$theme->description = wp_kses( $theme->description, $themes_allowedtags );
$theme->num_ratings = sprintf( _n( '(based on %s rating)', '(based on %s ratings)', $theme->num_ratings ), number_format_i18n( $theme->num_ratings ) );
}
wp_send_json_success( $api );
}

View File

@ -346,7 +346,9 @@ function themes_api( $action, $args = null ) {
$request = wp_remote_post( $url, $args ); $request = wp_remote_post( $url, $args );
if ( $ssl && is_wp_error( $request ) ) { if ( $ssl && is_wp_error( $request ) ) {
trigger_error( __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="https://wordpress.org/support/">support forums</a>.' ) . ' ' . '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)', headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE ); if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
trigger_error( __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="https://wordpress.org/support/">support forums</a>.' ) . ' ' . '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)', headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE );
}
$request = wp_remote_post( $http_url, $args ); $request = wp_remote_post( $http_url, $args );
} }
@ -452,4 +454,4 @@ function wp_prepare_themes_for_js( $themes = null ) {
*/ */
$prepared_themes = apply_filters( 'wp_prepare_themes_for_js', $prepared_themes ); $prepared_themes = apply_filters( 'wp_prepare_themes_for_js', $prepared_themes );
return array_values( $prepared_themes ); return array_values( $prepared_themes );
} }

View File

@ -22,18 +22,7 @@ themes.Model = Backbone.Model.extend({
// Adds attributes to the default data coming through the .org themes api // Adds attributes to the default data coming through the .org themes api
// Map `id` to `slug` for shared code // Map `id` to `slug` for shared code
initialize: function() { initialize: function() {
var install, description; var description;
// Install url for the theme
// using the install nonce
install = {
action: 'install-theme',
theme: this.get( 'slug' ),
_wpnonce: themes.data.settings._nonceInstall
};
// Build the url query
install = themes.data.settings.updateURI + '?' + $.param( install );
// If theme is already installed, set an attribute. // If theme is already installed, set an attribute.
if ( _.indexOf( themes.data.installedThemes, this.get( 'slug' ) ) !== -1 ) { if ( _.indexOf( themes.data.installedThemes, this.get( 'slug' ) ) !== -1 ) {
@ -42,7 +31,6 @@ themes.Model = Backbone.Model.extend({
// Set the attributes // Set the attributes
this.set({ this.set({
installURI: ( this.get( 'slug' ) ) ? install : false,
// slug is for installation, id is for existing. // slug is for installation, id is for existing.
id: this.get( 'slug' ) || this.get( 'id' ) id: this.get( 'slug' ) || this.get( 'id' )
}); });
@ -225,7 +213,7 @@ themes.Collection = Backbone.Collection.extend({
// //
// When we are missing a cache object we fire an apiCall() // When we are missing a cache object we fire an apiCall()
// which triggers events of `query:success` or `query:fail` // which triggers events of `query:success` or `query:fail`
query: function( request, action ) { query: function( request ) {
/** /**
* @static * @static
* @type Array * @type Array
@ -254,7 +242,7 @@ themes.Collection = Backbone.Collection.extend({
// Otherwise, send a new API call and add it to the cache. // Otherwise, send a new API call and add it to the cache.
if ( ! query && ! isPaginated ) { if ( ! query && ! isPaginated ) {
query = this.apiCall( request, action ).done( function( data ) { query = this.apiCall( request ).done( function( data ) {
// Update the collection with the queried data. // Update the collection with the queried data.
if ( data.themes ) { if ( data.themes ) {
@ -262,11 +250,6 @@ themes.Collection = Backbone.Collection.extend({
count = data.info.results; count = data.info.results;
// Store the results and the query request // Store the results and the query request
queries.push( { themes: data.themes, request: request, total: count } ); queries.push( { themes: data.themes, request: request, total: count } );
} else if ( action ) {
self.reset( data );
count = 1;
self.trigger( 'query:theme' );
} }
// Trigger a collection refresh event // Trigger a collection refresh event
@ -284,7 +267,7 @@ themes.Collection = Backbone.Collection.extend({
} else { } else {
// If it's a paginated request we need to fetch more themes... // If it's a paginated request we need to fetch more themes...
if ( isPaginated ) { if ( isPaginated ) {
return this.apiCall( request, action, isPaginated ).done( function( data ) { return this.apiCall( request, isPaginated ).done( function( data ) {
// Add the new themes to the current collection // Add the new themes to the current collection
// @todo update counter // @todo update counter
self.add( data.themes ); self.add( data.themes );
@ -310,12 +293,13 @@ themes.Collection = Backbone.Collection.extend({
this.count = query.total; this.count = query.total;
} }
this.reset( query.themes );
if ( ! query.total ) { if ( ! query.total ) {
this.count = this.length; this.count = this.length;
} }
this.reset( query.themes );
this.trigger( 'update' ); this.trigger( 'update' );
this.trigger( 'query:success', this.count );
} }
}, },
@ -329,30 +313,23 @@ themes.Collection = Backbone.Collection.extend({
}, },
// Send request to api.wordpress.org/themes // Send request to api.wordpress.org/themes
apiCall: function( request, action, paginated ) { apiCall: function( request, paginated ) {
return wp.ajax.send( 'query-themes', {
// Send tags (and fields) as comma-separated to keep the JSONP query string short.
if ( request.tag && _.isArray( request.tag ) ) {
request.tag = request.tag.join( ',' );
}
// Set request action
if ( ! action ) {
action = 'query_themes'
}
// JSONP request to .org API
return $.ajax({
url: 'https://api.wordpress.org/themes/info/1.1/?callback=?',
dataType: 'jsonp',
timeout: 15000, // 15 seconds
// Request data
data: { data: {
action: action, // Request data
request: _.extend({ request: _.extend({
per_page: 72, per_page: 100,
fields: 'description,tested,requires,rating,downloaded,downloadLink,last_updated,homepage,num_ratings' fields: {
description: true,
tested: true,
requires: true,
rating: true,
downloaded: true,
downloadLink: true,
last_updated: true,
homepage: true,
num_ratings: true
}
}, request) }, request)
}, },
@ -1567,7 +1544,11 @@ themes.view.Installer = themes.view.Appearance.extend({
}); });
}, },
backToFilters: function() { backToFilters: function( event ) {
if ( event ) {
event.preventDefault();
}
$( 'body' ).removeClass( 'filters-applied' ); $( 'body' ).removeClass( 'filters-applied' );
}, },
@ -1634,8 +1615,8 @@ themes.RunInstaller = {
// Handles `theme` route event // Handles `theme` route event
// Queries the API for the passed theme slug // Queries the API for the passed theme slug
themes.router.on( 'route:preview', function( slug ) { themes.router.on( 'route:preview', function( slug ) {
request.slug = slug; request.theme = slug;
self.view.collection.query( request, 'theme_information' ); self.view.collection.query( request );
}); });
// Handles sorting / browsing routes // Handles sorting / browsing routes

File diff suppressed because one or more lines are too long

View File

@ -46,9 +46,7 @@ wp_localize_script( 'theme', '_wpThemeSettings', array(
'isInstall' => true, 'isInstall' => true,
'canInstall' => current_user_can( 'install_themes' ), 'canInstall' => current_user_can( 'install_themes' ),
'installURI' => current_user_can( 'install_themes' ) ? self_admin_url( 'theme-install.php' ) : null, 'installURI' => current_user_can( 'install_themes' ) ? self_admin_url( 'theme-install.php' ) : null,
'adminUrl' => parse_url( self_admin_url(), PHP_URL_PATH ), 'adminUrl' => parse_url( self_admin_url(), PHP_URL_PATH )
'updateURI' => self_admin_url( 'update.php' ),
'_nonceInstall' => wp_create_nonce( 'install-theme' )
), ),
'l10n' => array( 'l10n' => array(
'addNew' => __( 'Add New Theme' ), 'addNew' => __( 'Add New Theme' ),
@ -56,7 +54,7 @@ wp_localize_script( 'theme', '_wpThemeSettings', array(
'searchPlaceholder' => __( 'Search themes...' ), // placeholder (no ellipsis) 'searchPlaceholder' => __( 'Search themes...' ), // placeholder (no ellipsis)
'upload' => __( 'Upload Theme' ), 'upload' => __( 'Upload Theme' ),
'back' => __( 'Back' ), 'back' => __( 'Back' ),
'error' => sprintf( __( 'An unexpected error occurred and we can&#8127;t reach WordPress.org. If you continue to have problems, please try the <a href="%s">support forums</a>.' ), __( 'https://wordpress.org/support/' ) ) 'error' => __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="https://wordpress.org/support/">support forums</a>.' )
), ),
'installedThemes' => array_keys( $installed_themes ), 'installedThemes' => array_keys( $installed_themes ),
'browse' => array( 'browse' => array(
@ -199,7 +197,7 @@ if ( $tab ) {
<h3 class="theme-name">{{ data.name }}</h3> <h3 class="theme-name">{{ data.name }}</h3>
<div class="theme-actions"> <div class="theme-actions">
<a class="button button-primary" href="{{ data.installURI }}"><?php esc_html_e( 'Install' ); ?></a> <a class="button button-primary" href="{{ data.install_url }}"><?php esc_html_e( 'Install' ); ?></a>
<a class="button button-secondary preview install-theme-preview" href="#"><?php esc_html_e( 'Preview' ); ?></a> <a class="button button-secondary preview install-theme-preview" href="#"><?php esc_html_e( 'Preview' ); ?></a>
</div> </div>
@ -215,7 +213,7 @@ if ( $tab ) {
<# if ( data.installed ) { #> <# if ( data.installed ) { #>
<a href="#" class="button button-primary theme-install disabled"><?php _e( 'Installed' ); ?></a> <a href="#" class="button button-primary theme-install disabled"><?php _e( 'Installed' ); ?></a>
<# } else { #> <# } else { #>
<a href="{{ data.installURI }}" class="button button-primary theme-install"><?php _e( 'Install' ); ?></a> <a href="{{ data.install_url }}" class="button button-primary theme-install"><?php _e( 'Install' ); ?></a>
<# } #> <# } #>
</div> </div>
<div class="wp-full-overlay-sidebar-content"> <div class="wp-full-overlay-sidebar-content">
@ -233,7 +231,7 @@ if ( $tab ) {
<span class="four"></span> <span class="four"></span>
<span class="five"></span> <span class="five"></span>
<# if ( data.num_ratings ) { #> <# if ( data.num_ratings ) { #>
<p class="ratings">({{ data.num_ratings }})</p> <p class="ratings">{{ data.num_ratings }}</p>
<# } else { #> <# } else { #>
<p class="ratings"><?php _e( 'No ratings.' ); ?></p> <p class="ratings"><?php _e( 'No ratings.' ); ?></p>
<# } #> <# } #>

View File

@ -202,7 +202,7 @@ if ( isset($_GET['action']) ) {
include_once ABSPATH . 'wp-admin/includes/theme-install.php'; //for themes_api.. include_once ABSPATH . 'wp-admin/includes/theme-install.php'; //for themes_api..
check_admin_referer( 'install-theme' ); check_admin_referer( 'install-theme_' . $theme );
$api = themes_api('theme_information', array('slug' => $theme, 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth. $api = themes_api('theme_information', array('slug' => $theme, 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth.
if ( is_wp_error($api) ) if ( is_wp_error($api) )