WordPress.org

Make WordPress Core

Ticket #42049: 42049.2.diff

File 42049.2.diff, 18.0 KB (added by celloexpressions, 10 months ago)

Refresh for minor conflicts in trunk.

  • src/wp-admin/js/customize-controls.js

     
    16651665                                section.closeDetails();
    16661666                        });
    16671667
    1668                         // Filter-search all theme objects loaded in the section.
    1669                         section.container.on( 'input', '.wp-filter-search-themes', function( event ) {
    1670                                         section.filterSearch( event.currentTarget );
    1671                         });
     1668                        if ( 'local' === section.params.filter_type ) {
     1669                                // Filter-search all theme objects loaded in the section.
     1670                                section.container.on( 'input', '.wp-filter-search-themes', function( event ) {
     1671                                                section.filterSearch( event.currentTarget.value );
     1672                                });
    16721673
    1673                         // Event listeners for remote wporg queries with user-entered terms.
    1674                         if ( 'wporg' === section.params.action ) {
     1674                        } else if ( 'remote' === section.params.filter_type ) {
    16751675
     1676                                // Event listeners for remote queries with user-entered terms.
    16761677                                // Search terms.
    16771678                                debounced = _.debounce( section.checkTerm, 500 ); // Wait until there is no input for 500 milliseconds to initiate a search.
    16781679                                section.contentContainer.on( 'input', '.wp-filter-search', function() {
     
    16881689                                        section.filtersChecked();
    16891690                                        section.checkTerm( section );
    16901691                                });
     1692                        }
    16911693
    1692                                 // Toggle feature filter sections.
    1693                                 section.contentContainer.on( 'click', '.feature-filter-toggle', function( e ) {
    1694                                         $( e.currentTarget )
    1695                                                 .toggleClass( 'open' )
    1696                                                 .attr( 'aria-expanded', function( i, attr ) {
    1697                                                         return 'true' === attr ? 'false' : 'true';
    1698                                                 })
    1699                                                 .next( '.filter-drawer' ).slideToggle( 180, 'linear', function() {
    1700                                                         if ( 0 === section.filtersHeight ) {
    1701                                                                 section.filtersHeight = $( this ).height();
     1694                        // Toggle feature filters.
     1695                        section.contentContainer.on( 'click', '.feature-filter-toggle', function( e ) {
     1696                                $( e.currentTarget )
     1697                                        .toggleClass( 'open' )
     1698                                        .attr( 'aria-expanded', function( i, attr ) {
     1699                                                return 'true' === attr ? 'false' : 'true';
     1700                                        })
     1701                                        .next( '.filter-drawer' ).slideToggle( 180, 'linear', function() {
     1702                                                if ( 0 === section.filtersHeight ) {
     1703                                                        section.filtersHeight = $( this ).height();
    17021704
    1703                                                                 // First time, so it's opened.
    1704                                                                 section.contentContainer.find( '.themes' ).css( 'margin-top', section.filtersHeight + 76 );
    1705                                                         }
    1706                                                 });
    1707                                         if ( $( e.currentTarget ).hasClass( 'open' ) ) {
    1708                                                 section.contentContainer.find( '.themes' ).css( 'margin-top', section.filtersHeight + 76 );
    1709                                         } else {
    1710                                                 section.contentContainer.find( '.themes' ).css( 'margin-top', 0 );
    1711                                         }
    1712                                 });
    1713                         }
     1705                                                        // First time, so it's opened.
     1706                                                        section.contentContainer.find( '.themes' ).css( 'margin-top', section.filtersHeight + 76 );
     1707                                                }
     1708                                        });
     1709                                if ( $( e.currentTarget ).hasClass( 'open' ) ) {
     1710                                        section.contentContainer.find( '.themes' ).css( 'margin-top', section.filtersHeight + 76 );
     1711                                } else {
     1712                                        section.contentContainer.find( '.themes' ).css( 'margin-top', 0 );
     1713                                }
     1714                        });
    17141715
    17151716                        // Setup section cross-linking.
    17161717                        section.contentContainer.on( 'click', '.no-themes-local .search-dotorg-themes', function() {
     
    17541755
    17551756                                // Try to load controls if none are loaded yet.
    17561757                                if ( 0 === section.loaded ) {
    1757                                         section.loadControls();
     1758                                        section.loadThemes();
    17581759                                }
    17591760
    17601761                                // Collapse any sibling sections/panels
     
    17691770                                                        section.contentContainer.find( '.wp-filter-search' ).val( searchTerm );
    17701771
    17711772                                                        // Directly initialize an empty remote search to avoid a race condition.
    1772                                                         if ( '' === searchTerm && '' !== section.term && 'installed' !== section.params.action ) {
     1773                                                        if ( '' === searchTerm && '' !== section.term && 'local' !== section.params.filter_type ) {
    17731774                                                                section.term = '';
    17741775                                                                section.initializeNewQuery( section.term, section.tags );
    17751776                                                        } else {
    1776                                                                 section.checkTerm( section );
     1777                                                                if ( 'remote' === section.params.filter_type ) {
     1778                                                                        section.checkTerm( section );
     1779                                                                } else if ( 'local' === section.params.filter_type ) {
     1780                                                                        section.filterSearch( searchTerm );
     1781                                                                }
    17771782                                                        }
    1778                                                         section.filterSearch( section.contentContainer.find( '.wp-filter-search' ).get( 0 ) );
    17791783                                                }
    17801784                                                otherSection.collapse( { duration: args.duration } );
    17811785                                        }
     
    18251829                 *
    18261830                 * @returns {void}
    18271831                 */
    1828                 loadControls: function() {
     1832                loadThemes: function() {
    18291833                        var section = this, params, page, request;
    18301834
    18311835                        if ( section.loading ) {
     
    18421846                                'page': page
    18431847                        };
    18441848
    1845                         // Add fields for wporg actions.
    1846                         if ( 'wporg' === section.params.action ) {
     1849                        // Add fields for remote filtering.
     1850                        if ( 'remote' === section.params.filter_type ) {
    18471851                                params.search = section.term;
    18481852                                params.tags = section.tags;
    18491853                        }
     
    18541858                        section.container.find( '.no-themes' ).hide();
    18551859                        request = wp.ajax.post( 'customize_load_themes', params );
    18561860                        request.done(function( data ) {
    1857                                 var themes = data.themes, newThemeControls;
     1861                                var themes = data.themes;
    18581862
    18591863                                // Stop and try again if the term changed while loading.
    18601864                                if ( '' !== section.nextTerm || '' !== section.nextTags ) {
     
    18671871                                        section.nextTerm = '';
    18681872                                        section.nextTags = '';
    18691873                                        section.loading = false;
    1870                                         section.loadControls();
     1874                                        section.loadThemes();
    18711875                                        return;
    18721876                                }
    18731877
    18741878                                if ( 0 !== themes.length ) {
    1875                                         newThemeControls = [];
    18761879
    1877                                         // Add controls for each theme.
    1878                                         _.each( themes, function( theme ) {
    1879                                                 var themeControl = new api.controlConstructor.theme( section.params.action + '_theme_' + theme.id, {
    1880                                                         type: 'theme',
    1881                                                         section: section.params.id,
    1882                                                         theme: theme,
    1883                                                         priority: section.loaded + 1
    1884                                                 } );
    1885 
    1886                                                 api.control.add( themeControl );
    1887                                                 newThemeControls.push( themeControl );
    1888                                                 section.loaded = section.loaded + 1;
    1889                                         });
    1890 
     1880                                        section.loadControls( themes, page );
     1881                                       
    18911882                                        if ( 1 === page ) {
    18921883
    18931884                                                // Pre-load the first 3 theme screenshots.
     
    18981889                                                                img.src = src;
    18991890                                                        }
    19001891                                                });
    1901                                                 if ( 'installed' !== section.params.action ) {
     1892                                                if ( 'local' !== section.params.filter_type ) {
    19021893                                                        wp.a11y.speak( api.settings.l10n.themeSearchResults.replace( '%d', data.info.results ) );
    19031894                                                }
    1904                                         } else {
    1905                                                 Array.prototype.push.apply( section.screenshotQueue, newThemeControls ); // Add new themes to the screenshot queue.
    19061895                                        }
     1896
    19071897                                        _.delay( section.renderScreenshots, 100 ); // Wait for the controls to become visible.
    19081898
    1909                                         if ( 'installed' === section.params.action || 100 > themes.length ) { // If we have less than the requested 100 themes, it's the end of the list.
     1899                                        if ( 'local' === section.params.filter_type || 100 > themes.length ) { // If we have less than the requested 100 themes, it's the end of the list.
    19101900                                                section.fullyLoaded = true;
    19111901                                        }
    19121902                                } else {
     
    19171907                                                section.fullyLoaded = true;
    19181908                                        }
    19191909                                }
    1920                                 if ( 'installed' === section.params.action ) {
     1910                                if ( 'local' === section.params.filter_type ) {
    19211911                                        section.updateCount(); // Count of visible theme controls.
    19221912                                } else {
    19231913                                        section.updateCount( data.info.results ); // Total number of results including pages not yet loaded.
     
    19431933                },
    19441934
    19451935                /**
     1936                 * Loads controls into the section from data received from loadThemes().
     1937                 *
     1938                 * @since 4.9.0
     1939                 * @param {Array} themes - Array of theme data to create controls with.
     1940                 * @param {integer} page - Page of results being loaded.
     1941                 * @returns {void}
     1942                 */
     1943                loadControls: function( themes, page ) {
     1944                        var newThemeControls = [],
     1945                                section = this;
     1946
     1947                        // Add controls for each theme.
     1948                        _.each( themes, function( theme ) {
     1949                                var themeControl = new api.controlConstructor.theme( section.params.action + '_theme_' + theme.id, {
     1950                                        type: 'theme',
     1951                                        section: section.params.id,
     1952                                        theme: theme,
     1953                                        priority: section.loaded + 1
     1954                                } );
     1955
     1956                                api.control.add( themeControl );
     1957                                newThemeControls.push( themeControl );
     1958                                section.loaded = section.loaded + 1;
     1959                        });
     1960
     1961                        if ( 1 !== page ) {
     1962                                Array.prototype.push.apply( section.screenshotQueue, newThemeControls ); // Add new themes to the screenshot queue.
     1963                        }
     1964                },
     1965
     1966                /**
    19461967                 * Determines whether more themes should be loaded, and loads them.
    19471968                 *
    19481969                 * @since 4.9.0
     
    19571978                                threshold = container.prop( 'scrollHeight' ) - 3000; // Use a fixed distance to the bottom of loaded results to avoid unnecessarily loading results sooner when using a percentage of scroll distance.
    19581979
    19591980                                if ( bottom > threshold ) {
    1960                                         section.loadControls();
     1981                                        section.loadThemes();
    19611982                                }
    19621983                        }
    19631984                },
     
    19671988                 *
    19681989                 * @since 4.9.0
    19691990                 *
    1970                  * @param {Element} el - The search input element as a raw JS object.
     1991                 * @param {string} term - The raw search input value.
    19711992                 * @returns {void}
    19721993                 */
    1973                 filterSearch: function( el ) {
     1994                filterSearch: function( term ) {
    19741995                        var count = 0,
    19751996                                visible = false,
    19761997                                section = this,
    1977                                 noFilter = ( undefined !== api.section( 'wporg_themes' ) && 'wporg' !== section.params.action ) ? '.no-themes-local' : '.no-themes',
    1978                                 term = el.value.toLowerCase().trim().replace( '-', ' ' ),
     1998                                noFilter = ( undefined !== api.section( 'wporg_themes' ) && 'remote' !== section.params.filter_type ) ? '.no-themes-local' : '.no-themes',
    19791999                                controls = section.controls();
    19802000
    19812001                        if ( section.loading ) {
     
    19822002                                return;
    19832003                        }
    19842004
     2005                        // Standardize search term format and split into an array of individual words.
     2006                        term = term.toLowerCase().trim().replace( '-', ' ' ).split( ' ' );
     2007
    19852008                        _.each( controls, function( control ) {
    1986                                 visible = control.filter( term );
     2009                                visible = control.filter( term ); // Shows/hides and sorts control based on the applicability of the search term.
    19872010                                if ( visible ) {
    19882011                                        count = count + 1;
    19892012                                }
     
    19972020                        }
    19982021
    19992022                        section.renderScreenshots();
     2023                        api.reflowPaneContents();
    20002024
    20012025                        // Update theme count.
    20022026                        section.updateCount( count );
     
    20122036                 */
    20132037                checkTerm: function( section ) {
    20142038                        var newTerm;
    2015                         if ( 'wporg' === section.params.action ) {
     2039                        if ( 'remote' === section.params.filter_type ) {
    20162040                                newTerm = section.contentContainer.find( '.wp-filter-search' ).val();
    20172041                                if ( section.term !== newTerm ) {
    20182042                                        section.initializeNewQuery( newTerm, section.tags );
     
    20522076                                if ( section.loading ) {
    20532077                                        section.nextTags = tags;
    20542078                                } else {
    2055                                         section.initializeNewQuery( section.term, tags );
     2079                                        if ( 'remote' === section.params.filter_type ) {
     2080                                                section.initializeNewQuery( section.term, tags );
     2081                                        } else if ( 'local' === section.params.filter_type ) {
     2082                                                section.filterSearch( tags.join( ' ' ) );
     2083                                        }
    20562084                                }
    20572085                        }
    20582086                },
     
    20782106                        section.fullyLoaded = false;
    20792107                        section.screenshotQueue = null;
    20802108
    2081                         // Run a new query, with loadControls handling paging, etc.
     2109                        // Run a new query, with loadThemes handling paging, etc.
    20822110                        if ( ! section.loading ) {
    20832111                                section.term = newTerm;
    20842112                                section.tags = newTags;
    2085                                 section.loadControls();
     2113                                section.loadThemes();
    20862114                        } else {
    2087                                 section.nextTerm = newTerm; // This will reload from loadControls() with the newest term once the current batch is loaded.
    2088                                 section.nextTags = newTags; // This will reload from loadControls() with the newest tags once the current batch is loaded.
     2115                                section.nextTerm = newTerm; // This will reload from loadThemes() with the newest term once the current batch is loaded.
     2116                                section.nextTags = newTags; // This will reload from loadThemes() with the newest tags once the current batch is loaded.
    20892117                        }
    20902118                        if ( ! section.expanded() ) {
    20912119                                section.expand(); // Expand the section if it isn't expanded.
     
    46654693                 * Show or hide the theme based on the presence of the term in the title, description, tags, and author.
    46664694                 *
    46674695                 * @since 4.2.0
     4696                 * @param {Array} terms - An array of terms to search for.
    46684697                 * @returns {boolean} Whether a theme control was activated or not.
    46694698                 */
    4670                 filter: function( term ) {
     4699                filter: function( terms ) {
    46714700                        var control = this,
     4701                                matchCount = 0,
    46724702                                haystack = control.params.theme.name + ' ' +
    46734703                                        control.params.theme.description + ' ' +
    46744704                                        control.params.theme.tags + ' ' +
    4675                                         control.params.theme.author;
     4705                                        control.params.theme.author + ' ';
    46764706                        haystack = haystack.toLowerCase().replace( '-', ' ' );
    4677                         if ( -1 !== haystack.search( term ) ) {
     4707
     4708                        // Back-compat for behavior in WordPress 4.2.0 to 4.8.X.
     4709                        if ( ! terms instanceof Array ) {
     4710                                terms = [terms];
     4711                        }
     4712
     4713                        // Always give exact name matches highest ranking.
     4714                        if ( control.params.theme.name.toLowerCase() === terms.join( ' ' ) ) {
     4715                                matchCount = 100;
     4716                        } else {
     4717
     4718                                // Search for and weight (by 10) complete term matches.
     4719                                matchCount = matchCount + 10 * ( haystack.split( terms.join( ' ' ) ).length - 1 );
     4720
     4721                                // Search for each term individually (as whole-word and partial match) and sum weighted match counts.
     4722                                _.each( terms, function( term ) {
     4723                                        matchCount = matchCount + 2 * ( haystack.split( term + ' ' ).length - 1 ); // Whole-word, double-weighted.
     4724                                        matchCount = matchCount + haystack.split( term ).length - 1; // Partial word, to minimize empty intermediate searches while typing.
     4725                                });
     4726
     4727                                // Upper limit on match ranking.
     4728                                if ( matchCount > 99 ) {
     4729                                        matchCount = 99;
     4730                                }
     4731                        }
     4732
     4733                        if ( 0 !== matchCount ) {
    46784734                                control.activate();
     4735                                control.params.priority = 101 - matchCount; // Sort results by match count.
    46794736                                return true;
    46804737                        } else {
    4681                                 control.deactivate();
     4738                                control.deactivate(); // Hide control
     4739                                control.params.priority = 101;
    46824740                                return false;
    46834741                        }
    46844742                },
  • src/wp-includes/class-wp-customize-manager.php

     
    42754275                        $this->add_section( new WP_Customize_Themes_Section( $this, 'wporg_themes', array(
    42764276                                'title'       => __( 'WordPress.org themes' ),
    42774277                                'action'      => 'wporg',
     4278                                'filter_type' => 'remote',
    42784279                                'capability'  => 'install_themes',
    42794280                                'panel'       => 'themes',
    42804281                                'priority'    => 5,
     
    48064807                }
    48074808                $theme_action = sanitize_key( $_POST['theme_action'] );
    48084809                $themes = array();
     4810                $args = array();
    48094811
     4812                // Define query filters based on user input.
     4813                if ( ! array_key_exists( 'search', $_POST ) ) {
     4814                        $args['search'] = '';
     4815                } else {
     4816                        $args['search'] = sanitize_text_field( wp_unslash( $_POST['search'] ) );
     4817                }
     4818
     4819                if ( ! array_key_exists( 'tags', $_POST ) ) {
     4820                        $args['tag'] = '';
     4821                } else {
     4822                        $args['tag'] = array_map( 'sanitize_text_field', wp_unslash( (array) $_POST['tags'] ) );
     4823                }
     4824
     4825                if ( ! array_key_exists( 'page', $_POST ) ) {
     4826                        $args['page'] = 1;
     4827                } else {
     4828                        $args['page'] = absint( $_POST['page'] );
     4829                }
     4830
    48104831                require_once ABSPATH . 'wp-admin/includes/theme.php';
     4832
     4833                // Load all installed themes from wp_prepare_themes_for_js().
    48114834                if ( 'installed' === $theme_action ) {
    48124835                        $themes = array( 'themes' => wp_prepare_themes_for_js() );
    48134836                        foreach ( $themes['themes'] as &$theme ) {
     
    48144837                                $theme['type'] = 'installed';
    48154838                                $theme['active'] = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme['id'] );
    48164839                        }
     4840
     4841                // Load WordPress.org themes from the .org API and normalize data to match installed theme objects.
    48174842                } elseif ( 'wporg' === $theme_action ) {
    48184843                        if ( ! current_user_can( 'install_themes' ) ) {
    48194844                                wp_die( -1 );
     
    48204845                        }
    48214846
    48224847                        // Arguments for all queries.
    4823                         $args = array(
     4848                        $wporg_args = array(
    48244849                                'per_page' => 100,
    4825                                 'page' => isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 1,
    48264850                                'fields' => array(
    48274851                                        'screenshot_url' => true,
    48284852                                        'description' => true,
     
    48384862                                ),
    48394863                        );
    48404864
    4841                         // Define query filters based on user input.
    4842                         if ( ! array_key_exists( 'search', $_POST ) ) {
    4843                                 $args['search'] = '';
    4844                         } else {
    4845                                 $args['search'] = sanitize_text_field( wp_unslash( $_POST['search'] ) );
    4846                         }
     4865                        $args = array_merge( $wporg_args, $args );
    48474866
    4848                         if ( ! array_key_exists( 'tags', $_POST ) ) {
    4849                                 $args['tag'] = '';
    4850                         } else {
    4851                                 $args['tag'] = array_map( 'sanitize_text_field', wp_unslash( (array) $_POST['tags'] ) );
    4852                         }
    4853 
    48544867                        if ( '' === $args['search'] && '' === $args['tag'] ) {
    48554868                                $args['browse'] = 'new'; // Sort by latest themes by default.
    48564869                        }
     
    49204933                                unset( $theme->author );
    49214934                        } // End foreach().
    49224935                } // End if().
     4936
     4937
     4938                /**
     4939                 * Filters the theme data loaded in the customizer.
     4940                 *
     4941                 * This allows theme data to be loading from an external source,
     4942                 * or modification of data loaded from wp_prepare_themes_for_JS
     4943                 * or WordPress.org.
     4944                 *
     4945                 * @since 4.9.0
     4946                 *
     4947                 * @see WP_Customize_Manager::__construct()
     4948                 *
     4949                 * @param array $themes Nested array of theme data.
     4950                 * @param array $args   List of arguments, such as page, search term, and tags to query for.
     4951                 */
     4952                $themes = apply_filters( 'customize_load_themes_ajax', $themes, $args );
     4953
    49234954                wp_send_json_success( $themes );
    49244955        }
    49254956
  • src/wp-includes/customize/class-wp-customize-themes-section.php

     
    3737        public $action = '';
    3838
    3939        /**
     40         * Theme section filter type.
     41         *
     42         * Determines whether filters are applied to loaded (local) themes or by initiating a new remote query (remote).
     43         * When filtering is local, the initial themes query is not paginated by default.
     44         *
     45         * @since 4.9.0
     46         * @var string
     47         */
     48        public $filter_type = 'local';
     49
     50        /**
    4051         * Get section parameters for JS.
    4152         *
    4253         * @since 4.9.0
     
    4556        public function json() {
    4657                $exported = parent::json();
    4758                $exported['action'] = $this->action;
     59                $exported['filter_type'] = $this->filter_type;
    4860
    4961                return $exported;
    5062        }