diff --git a/wp-admin/includes/class-wp-list-table.php b/wp-admin/includes/class-wp-list-table.php index aa56a3e9cd..ee5f9f9b34 100644 --- a/wp-admin/includes/class-wp-list-table.php +++ b/wp-admin/includes/class-wp-list-table.php @@ -484,7 +484,7 @@ class WP_List_Table { if ( empty( $this->_pagination_args ) ) return; - extract( $this->_pagination_args ); + extract( $this->_pagination_args, EXTR_SKIP ); $output = '' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . ''; @@ -543,7 +543,10 @@ class WP_List_Table { '»' ); - $output .= "\n" . join( "\n", $page_links ) . ''; + $pagination_links_class = 'pagination-links'; + if ( ! empty( $infinite_scroll ) ) + $pagination_links_class = ' hide-if-js'; + $output .= "\n" . join( "\n", $page_links ) . ''; if ( $total_pages ) $page_class = $total_pages < 2 ? ' one-page' : ''; @@ -872,7 +875,7 @@ class WP_List_Table { $this->prepare_items(); extract( $this->_args ); - extract( $this->_pagination_args ); + extract( $this->_pagination_args, EXTR_SKIP ); ob_start(); if ( ! empty( $_REQUEST['no_placeholder'] ) ) diff --git a/wp-admin/includes/class-wp-theme-install-list-table.php b/wp-admin/includes/class-wp-theme-install-list-table.php index eab12598a2..41fbb64506 100644 --- a/wp-admin/includes/class-wp-theme-install-list-table.php +++ b/wp-admin/includes/class-wp-theme-install-list-table.php @@ -7,25 +7,30 @@ * @since 3.1.0 * @access private */ -class WP_Theme_Install_List_Table extends WP_List_Table { +class WP_Theme_Install_List_Table extends WP_Themes_List_Table { - function __construct() { - parent::__construct( array( - 'ajax' => true, - ) ); - } + var $features = array(); function ajax_user_can() { - return current_user_can('install_themes'); + return current_user_can( 'install_themes' ); } function prepare_items() { include( ABSPATH . 'wp-admin/includes/theme-install.php' ); - global $tabs, $tab, $paged, $type, $term, $theme_field_defaults; - + global $tabs, $tab, $paged, $type, $theme_field_defaults; wp_reset_vars( array( 'tab' ) ); + $search_terms = array(); + $search_string = ''; + if ( ! empty( $_REQUEST['s'] ) ){ + $search_string = strtolower( stripslashes( $_REQUEST['s'] ) ); + $search_terms = array_unique( array_filter( array_map( 'trim', explode( ',', $search_string ) ) ) ); + } + + if ( ! empty( $_REQUEST['features'] ) ) + $this->features = $_REQUEST['features']; + $paged = $this->get_pagenum(); $per_page = 36; @@ -55,29 +60,21 @@ class WP_Theme_Install_List_Table extends WP_List_Table { switch ( $tab ) { case 'search': $type = isset( $_REQUEST['type'] ) ? stripslashes( $_REQUEST['type'] ) : ''; - $term = isset( $_REQUEST['s'] ) ? stripslashes( $_REQUEST['s'] ) : ''; - switch ( $type ) { case 'tag': - $terms = explode( ',', $term ); - $terms = array_map( 'trim', $terms ); - $terms = array_map( 'sanitize_title_with_dashes', $terms ); - $args['tag'] = $terms; + $args['tag'] = array_map( 'sanitize_title_with_dashes', $search_terms ); break; case 'term': - $args['search'] = $term; + $args['search'] = $search_string; break; case 'author': - $args['author'] = $term; + $args['author'] = $search_string; break; } - if ( !empty( $_REQUEST['features'] ) ) { - $terms = $_REQUEST['features']; - $terms = array_map( 'trim', $terms ); - $terms = array_map( 'sanitize_title_with_dashes', $terms ); - $args['tag'] = $terms; - $_REQUEST['s'] = implode( ',', $terms ); + if ( ! empty( $this->features ) ) { + $args['tag'] = $this->features; + $_REQUEST['s'] = implode( ',', $this->features ); $_REQUEST['type'] = 'tag'; } @@ -95,7 +92,7 @@ class WP_Theme_Install_List_Table extends WP_List_Table { $args = false; } - if ( !$args ) + if ( ! $args ) return; $api = themes_api( 'query_themes', $args ); @@ -108,6 +105,7 @@ class WP_Theme_Install_List_Table extends WP_List_Table { $this->set_pagination_args( array( 'total_items' => $api->info['results'], 'per_page' => $per_page, + 'infinite_scroll' => true, ) ); } @@ -128,12 +126,7 @@ class WP_Theme_Install_List_Table extends WP_List_Table { return $display_tabs; } - function get_columns() { - return array(); - } - function display() { - wp_nonce_field( "fetch-list-" . get_class( $this ), '_ajax_fetch_list_nonce' ); ?>
@@ -149,12 +142,8 @@ class WP_Theme_Install_List_Table extends WP_List_Table { display_rows_or_placeholder(); ?>
-
- pagination( 'bottom' ); ?> - -
-
- Install screen + * @uses $type Global; type of search. + */ + function _js_vars() { + global $tab, $type; + parent::_js_vars( compact( $tab, $type ) ); + } } diff --git a/wp-admin/includes/class-wp-themes-list-table.php b/wp-admin/includes/class-wp-themes-list-table.php index 257ce1f6c9..e1ce61caa4 100644 --- a/wp-admin/includes/class-wp-themes-list-table.php +++ b/wp-admin/includes/class-wp-themes-list-table.php @@ -20,7 +20,7 @@ class WP_Themes_List_Table extends WP_List_Table { function ajax_user_can() { // Do not check edit_theme_options here. AJAX calls for available themes require switch_themes. - return current_user_can('switch_themes'); + return current_user_can( 'switch_themes' ); } function prepare_items() { @@ -52,6 +52,7 @@ class WP_Themes_List_Table extends WP_List_Table { $this->set_pagination_args( array( 'total_items' => count( $themes ), 'per_page' => $per_page, + 'infinite_scroll' => true, ) ); } @@ -202,4 +203,35 @@ class WP_Themes_List_Table extends WP_List_Table { return true; } + + /** + * Send required variables to JavaScript land + * + * @since 3.4 + * @access private + * + * @uses $this->features Array of all feature search terms. + * @uses get_pagenum() + * @uses _pagination_args['total_pages'] + */ + function _js_vars( $extra_args = array() ) { + $search_string = isset( $_REQUEST['s'] ) ? esc_attr( stripslashes( $_REQUEST['s'] ) ) : ''; + + $total_pages = 1; + if ( ! empty( $this->_pagination_args['total_pages'] ) ) + $total_pages = $this->_pagination_args['total_pages']; + + $args = array( + 'search' => $search_string, + 'features' => $this->features, + 'paged' => $this->get_pagenum(), + 'total_pages' => $total_pages, + ); + + if ( is_array( $extra_args ) ) + $args = array_merge( $args, $extra_args ); + + printf( "\n", json_encode( $args ) ); + parent::_js_vars(); + } } diff --git a/wp-admin/includes/list-table.php b/wp-admin/includes/list-table.php index f9785d7ac6..2dff578e0d 100644 --- a/wp-admin/includes/list-table.php +++ b/wp-admin/includes/list-table.php @@ -28,7 +28,7 @@ function _get_list_table( $class ) { 'WP_Links_List_Table' => 'links', 'WP_Plugin_Install_List_Table' => 'plugin-install', 'WP_Themes_List_Table' => 'themes', - 'WP_Theme_Install_List_Table' => 'theme-install', + 'WP_Theme_Install_List_Table' => array( 'themes', 'theme-install' ), 'WP_Plugins_List_Table' => 'plugins', // Network Admin 'WP_MS_Sites_List_Table' => 'ms-sites', @@ -37,7 +37,8 @@ function _get_list_table( $class ) { ); if ( isset( $core_classes[ $class ] ) ) { - require_once( ABSPATH . 'wp-admin/includes/class-wp-' . $core_classes[ $class ] . '-list-table.php' ); + foreach ( (array) $core_classes[ $class ] as $required ) + require_once( ABSPATH . 'wp-admin/includes/class-wp-' . $required . '-list-table.php' ); return new $class; } diff --git a/wp-admin/js/theme.dev.js b/wp-admin/js/theme.dev.js index 98b44583a7..54788433cb 100644 --- a/wp-admin/js/theme.dev.js +++ b/wp-admin/js/theme.dev.js @@ -54,48 +54,50 @@ jQuery( document ).ready( function($) { theme_viewer.init(); }); -var ThemeScroller; +/** + * Class that provides infinite scroll for Themes admin screens + * + * @since 3.4 + * + * @uses ajaxurl + * @uses list_args + * @uses theme_list_args + * @uses $('#_ajax_fetch_list_nonce').val() +* */ +var ThemeScroller; (function($){ ThemeScroller = { - // Inputs nonce: '', - search: '', - tab: '', - type: '', - nextPage: 2, - features: {}, - - // Preferences + nextPage: 2, // By default, assume we're on the first page. + querying: false, scrollPollingDelay: 500, failedRetryDelay: 4000, outListBottomThreshold: 300, - // Flags - scrolling: false, - querying: false, - + /** + * Initializer + * + * @since 3.4 + * @access private + */ init: function() { var self = this, - startPage, - queryArray = {}, - queryString = window.location.search; + startPage; - // We're using infinite scrolling, so hide all pagination. - $('.pagination-links').hide(); - - // Parse GET query string - queryArray = this.parseQuery( queryString.substring( 1 ) ); + // Get out early if we don't have the required arguments. + if ( typeof ajaxurl === 'undefined' || + typeof list_args === 'undefined' || + typeof theme_list_args === 'undefined' ) { + $('.pagination-links').show(); + return; + } // Handle inputs this.nonce = $('#_ajax_fetch_list_nonce').val(); - this.search = queryArray['s']; - this.features = queryArray['features']; - this.tab = queryArray['tab']; - this.type = queryArray['type']; - startPage = parseInt( queryArray['paged'], 10 ); - if ( ! isNaN( startPage ) ) + startPage = theme_list_args.paged; + if ( startPage !== undefined ) this.nextPage = ( startPage + 1 ); // Cache jQuery selectors @@ -104,12 +106,25 @@ var ThemeScroller; this.$window = $(window); this.$document = $(document); - if ( $('.tablenav-pages').length ) + /** + * If there are more pages to query, then start polling to track + * when user hits the bottom of the current page + */ + if ( theme_list_args.total_pages !== undefined && + theme_list_args.total_pages >= this.nextPage ) this.pollInterval = setInterval( function() { return self.poll(); }, this.scrollPollingDelay ); }, + + /** + * Checks to see if user has scrolled to bottom of page. + * If so, requests another page of content from self.ajax(). + * + * @since 3.4 + * @access private + */ poll: function() { var bottom = this.$document.scrollTop() + this.$window.innerHeight(); @@ -119,32 +134,49 @@ var ThemeScroller; this.ajax(); }, + + /** + * Applies results passed from this.ajax() to $outList + * + * @since 3.4 + * @access private + * + * @param results Array with results from this.ajax() query. + */ process: function( results ) { if ( ( results === undefined ) || - ( results.rows.indexOf( 'no-items' ) != -1 ) ) { + ( results.rows === undefined ) || + ( results.rows.indexOf( 'no-items' ) != -1 ) ) { clearInterval( this.pollInterval ); return; } - var totalPages = parseInt( results.total_pages, 10 ); - if ( this.nextPage > totalPages ) + if ( this.nextPage > theme_list_args.total_pages ) clearInterval( this.pollInterval ); - if ( this.nextPage <= ( totalPages + 1 ) ) + if ( this.nextPage <= ( theme_list_args.total_pages + 1 ) ) this.$outList.append( results.rows ); }, + + /** + * Queries next page of themes + * + * @since 3.4 + * @access private + */ ajax: function() { var self = this; + this.querying = true; var query = { action: 'fetch-list', - tab: this.tab, paged: this.nextPage, - s: this.search, - type: this.type, + s: theme_list_args.search, + tab: theme_list_args.tab, + type: theme_list_args.type, _ajax_fetch_list_nonce: this.nonce, - 'features[]': this.features, + 'features[]': theme_list_args.features, 'list_args': list_args }; @@ -159,40 +191,13 @@ var ThemeScroller; .fail( function() { self.$spinner.css( 'visibility', 'hidden' ); self.querying = false; - setTimeout( function() { self.ajax(); }, self.failedRetryDelay ) + setTimeout( function() { self.ajax(); }, self.failedRetryDelay ); }); - }, - parseQuery: function( query ) { - var params = {}; - if ( ! query ) - return params; - - var pairs = query.split( /[;&]/ ); - for ( var i = 0; i < pairs.length; i++ ) { - var keyVal = pairs[i].split( '=' ); - - if ( ! keyVal || keyVal.length != 2 ) - continue; - - var key = unescape( keyVal[0] ); - var val = unescape( keyVal[1] ); - val = val.replace( /\+/g, ' ' ); - key = key.replace( /\[.*\]$/g, '' ); - - if ( params[key] === undefined ) { - params[key] = val; - } else { - var oldVal = params[key]; - if ( ! $.isArray( params[key] ) ) - params[key] = new Array( oldVal, val ); - else - params[key].push( val ); - } - } - return params; } } - $(document).ready( function( $ ) { ThemeScroller.init(); }); + $(document).ready( function($) { + ThemeScroller.init(); + }); })(jQuery);