diff --git a/src/wp-admin/admin-ajax.php b/src/wp-admin/admin-ajax.php index 838877efc8..f7aae0ff52 100644 --- a/src/wp-admin/admin-ajax.php +++ b/src/wp-admin/admin-ajax.php @@ -58,7 +58,7 @@ $core_actions_post = array( 'wp-remove-post-lock', 'dismiss-wp-pointer', 'upload-attachment', 'get-attachment', 'query-attachments', 'save-attachment', 'save-attachment-compat', 'send-link-to-editor', '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. diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 9d0e156630..ba26be0ded 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -2204,3 +2204,48 @@ function wp_ajax_save_user_color_scheme() { update_user_meta( get_current_user_id(), 'admin_color', $color_scheme ); 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 ); +} diff --git a/src/wp-admin/includes/theme.php b/src/wp-admin/includes/theme.php index aa3441c3ed..f411ab144b 100644 --- a/src/wp-admin/includes/theme.php +++ b/src/wp-admin/includes/theme.php @@ -346,7 +346,9 @@ function themes_api( $action, $args = null ) { $request = wp_remote_post( $url, $args ); if ( $ssl && is_wp_error( $request ) ) { - trigger_error( __( '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.' ) . ' ' . '(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’s configuration. If you continue to have problems, please try the support forums.' ) . ' ' . '(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 ); } @@ -452,4 +454,4 @@ function wp_prepare_themes_for_js( $themes = null ) { */ $prepared_themes = apply_filters( 'wp_prepare_themes_for_js', $prepared_themes ); return array_values( $prepared_themes ); -} \ No newline at end of file +} diff --git a/src/wp-admin/js/theme.js b/src/wp-admin/js/theme.js index 5a6a80ab2a..3a96d1ab4b 100644 --- a/src/wp-admin/js/theme.js +++ b/src/wp-admin/js/theme.js @@ -22,18 +22,7 @@ themes.Model = Backbone.Model.extend({ // Adds attributes to the default data coming through the .org themes api // Map `id` to `slug` for shared code initialize: function() { - var install, 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 ); + var description; // If theme is already installed, set an attribute. if ( _.indexOf( themes.data.installedThemes, this.get( 'slug' ) ) !== -1 ) { @@ -42,7 +31,6 @@ themes.Model = Backbone.Model.extend({ // Set the attributes this.set({ - installURI: ( this.get( 'slug' ) ) ? install : false, // slug is for installation, id is for existing. 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() // which triggers events of `query:success` or `query:fail` - query: function( request, action ) { + query: function( request ) { /** * @static * @type Array @@ -254,7 +242,7 @@ themes.Collection = Backbone.Collection.extend({ // Otherwise, send a new API call and add it to the cache. 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. if ( data.themes ) { @@ -262,11 +250,6 @@ themes.Collection = Backbone.Collection.extend({ count = data.info.results; // Store the results and the query request 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 @@ -284,7 +267,7 @@ themes.Collection = Backbone.Collection.extend({ } else { // If it's a paginated request we need to fetch more themes... 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 // @todo update counter self.add( data.themes ); @@ -310,12 +293,13 @@ themes.Collection = Backbone.Collection.extend({ this.count = query.total; } + this.reset( query.themes ); if ( ! query.total ) { this.count = this.length; } - this.reset( query.themes ); 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 - apiCall: function( request, action, paginated ) { - - // 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 + apiCall: function( request, paginated ) { + return wp.ajax.send( 'query-themes', { data: { - action: action, + // Request data request: _.extend({ - per_page: 72, - fields: 'description,tested,requires,rating,downloaded,downloadLink,last_updated,homepage,num_ratings' + per_page: 100, + fields: { + description: true, + tested: true, + requires: true, + rating: true, + downloaded: true, + downloadLink: true, + last_updated: true, + homepage: true, + num_ratings: true + } }, 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' ); }, @@ -1634,8 +1615,8 @@ themes.RunInstaller = { // Handles `theme` route event // Queries the API for the passed theme slug themes.router.on( 'route:preview', function( slug ) { - request.slug = slug; - self.view.collection.query( request, 'theme_information' ); + request.theme = slug; + self.view.collection.query( request ); }); // Handles sorting / browsing routes diff --git a/src/wp-admin/theme-install.php b/src/wp-admin/theme-install.php index aa19ce9648..088feed4a4 100644 --- a/src/wp-admin/theme-install.php +++ b/src/wp-admin/theme-install.php @@ -46,9 +46,7 @@ wp_localize_script( 'theme', '_wpThemeSettings', array( 'isInstall' => true, 'canInstall' => current_user_can( 'install_themes' ), 'installURI' => current_user_can( 'install_themes' ) ? self_admin_url( 'theme-install.php' ) : null, - 'adminUrl' => parse_url( self_admin_url(), PHP_URL_PATH ), - 'updateURI' => self_admin_url( 'update.php' ), - '_nonceInstall' => wp_create_nonce( 'install-theme' ) + 'adminUrl' => parse_url( self_admin_url(), PHP_URL_PATH ) ), 'l10n' => array( 'addNew' => __( 'Add New Theme' ), @@ -56,7 +54,7 @@ wp_localize_script( 'theme', '_wpThemeSettings', array( 'searchPlaceholder' => __( 'Search themes...' ), // placeholder (no ellipsis) 'upload' => __( 'Upload Theme' ), 'back' => __( 'Back' ), - 'error' => sprintf( __( 'An unexpected error occurred and we can᾿t reach WordPress.org. If you continue to have problems, please try the support forums.' ), __( 'https://wordpress.org/support/' ) ) + 'error' => __( '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.' ) ), 'installedThemes' => array_keys( $installed_themes ), 'browse' => array( @@ -199,7 +197,7 @@ if ( $tab ) {

{{ data.name }}

- +
@@ -215,7 +213,7 @@ if ( $tab ) { <# if ( data.installed ) { #> <# } else { #> - + <# } #>
@@ -233,7 +231,7 @@ if ( $tab ) { <# if ( data.num_ratings ) { #> -

({{ data.num_ratings }})

+

{{ data.num_ratings }}

<# } else { #>

<# } #> diff --git a/src/wp-admin/update.php b/src/wp-admin/update.php index 16145b85cf..fc86574469 100644 --- a/src/wp-admin/update.php +++ b/src/wp-admin/update.php @@ -202,7 +202,7 @@ if ( isset($_GET['action']) ) { 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. if ( is_wp_error($api) )