Ticket #42049: 42049.2.diff
| File 42049.2.diff, 18.0 KB (added by , 9 years ago) |
|---|
-
src/wp-admin/js/customize-controls.js
1665 1665 section.closeDetails(); 1666 1666 }); 1667 1667 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 }); 1672 1673 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 ) { 1675 1675 1676 // Event listeners for remote queries with user-entered terms. 1676 1677 // Search terms. 1677 1678 debounced = _.debounce( section.checkTerm, 500 ); // Wait until there is no input for 500 milliseconds to initiate a search. 1678 1679 section.contentContainer.on( 'input', '.wp-filter-search', function() { … … 1688 1689 section.filtersChecked(); 1689 1690 section.checkTerm( section ); 1690 1691 }); 1692 } 1691 1693 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(); 1702 1704 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 }); 1714 1715 1715 1716 // Setup section cross-linking. 1716 1717 section.contentContainer.on( 'click', '.no-themes-local .search-dotorg-themes', function() { … … 1754 1755 1755 1756 // Try to load controls if none are loaded yet. 1756 1757 if ( 0 === section.loaded ) { 1757 section.load Controls();1758 section.loadThemes(); 1758 1759 } 1759 1760 1760 1761 // Collapse any sibling sections/panels … … 1769 1770 section.contentContainer.find( '.wp-filter-search' ).val( searchTerm ); 1770 1771 1771 1772 // 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 ) { 1773 1774 section.term = ''; 1774 1775 section.initializeNewQuery( section.term, section.tags ); 1775 1776 } 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 } 1777 1782 } 1778 section.filterSearch( section.contentContainer.find( '.wp-filter-search' ).get( 0 ) );1779 1783 } 1780 1784 otherSection.collapse( { duration: args.duration } ); 1781 1785 } … … 1825 1829 * 1826 1830 * @returns {void} 1827 1831 */ 1828 load Controls: function() {1832 loadThemes: function() { 1829 1833 var section = this, params, page, request; 1830 1834 1831 1835 if ( section.loading ) { … … 1842 1846 'page': page 1843 1847 }; 1844 1848 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 ) { 1847 1851 params.search = section.term; 1848 1852 params.tags = section.tags; 1849 1853 } … … 1854 1858 section.container.find( '.no-themes' ).hide(); 1855 1859 request = wp.ajax.post( 'customize_load_themes', params ); 1856 1860 request.done(function( data ) { 1857 var themes = data.themes , newThemeControls;1861 var themes = data.themes; 1858 1862 1859 1863 // Stop and try again if the term changed while loading. 1860 1864 if ( '' !== section.nextTerm || '' !== section.nextTags ) { … … 1867 1871 section.nextTerm = ''; 1868 1872 section.nextTags = ''; 1869 1873 section.loading = false; 1870 section.load Controls();1874 section.loadThemes(); 1871 1875 return; 1872 1876 } 1873 1877 1874 1878 if ( 0 !== themes.length ) { 1875 newThemeControls = [];1876 1879 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 1891 1882 if ( 1 === page ) { 1892 1883 1893 1884 // Pre-load the first 3 theme screenshots. … … 1898 1889 img.src = src; 1899 1890 } 1900 1891 }); 1901 if ( ' installed' !== section.params.action) {1892 if ( 'local' !== section.params.filter_type ) { 1902 1893 wp.a11y.speak( api.settings.l10n.themeSearchResults.replace( '%d', data.info.results ) ); 1903 1894 } 1904 } else {1905 Array.prototype.push.apply( section.screenshotQueue, newThemeControls ); // Add new themes to the screenshot queue.1906 1895 } 1896 1907 1897 _.delay( section.renderScreenshots, 100 ); // Wait for the controls to become visible. 1908 1898 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. 1910 1900 section.fullyLoaded = true; 1911 1901 } 1912 1902 } else { … … 1917 1907 section.fullyLoaded = true; 1918 1908 } 1919 1909 } 1920 if ( ' installed' === section.params.action) {1910 if ( 'local' === section.params.filter_type ) { 1921 1911 section.updateCount(); // Count of visible theme controls. 1922 1912 } else { 1923 1913 section.updateCount( data.info.results ); // Total number of results including pages not yet loaded. … … 1943 1933 }, 1944 1934 1945 1935 /** 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 /** 1946 1967 * Determines whether more themes should be loaded, and loads them. 1947 1968 * 1948 1969 * @since 4.9.0 … … 1957 1978 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. 1958 1979 1959 1980 if ( bottom > threshold ) { 1960 section.load Controls();1981 section.loadThemes(); 1961 1982 } 1962 1983 } 1963 1984 }, … … 1967 1988 * 1968 1989 * @since 4.9.0 1969 1990 * 1970 * @param { Element} el - The search input element as a raw JS object.1991 * @param {string} term - The raw search input value. 1971 1992 * @returns {void} 1972 1993 */ 1973 filterSearch: function( el) {1994 filterSearch: function( term ) { 1974 1995 var count = 0, 1975 1996 visible = false, 1976 1997 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', 1979 1999 controls = section.controls(); 1980 2000 1981 2001 if ( section.loading ) { … … 1982 2002 return; 1983 2003 } 1984 2004 2005 // Standardize search term format and split into an array of individual words. 2006 term = term.toLowerCase().trim().replace( '-', ' ' ).split( ' ' ); 2007 1985 2008 _.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. 1987 2010 if ( visible ) { 1988 2011 count = count + 1; 1989 2012 } … … 1997 2020 } 1998 2021 1999 2022 section.renderScreenshots(); 2023 api.reflowPaneContents(); 2000 2024 2001 2025 // Update theme count. 2002 2026 section.updateCount( count ); … … 2012 2036 */ 2013 2037 checkTerm: function( section ) { 2014 2038 var newTerm; 2015 if ( ' wporg' === section.params.action) {2039 if ( 'remote' === section.params.filter_type ) { 2016 2040 newTerm = section.contentContainer.find( '.wp-filter-search' ).val(); 2017 2041 if ( section.term !== newTerm ) { 2018 2042 section.initializeNewQuery( newTerm, section.tags ); … … 2052 2076 if ( section.loading ) { 2053 2077 section.nextTags = tags; 2054 2078 } 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 } 2056 2084 } 2057 2085 } 2058 2086 }, … … 2078 2106 section.fullyLoaded = false; 2079 2107 section.screenshotQueue = null; 2080 2108 2081 // Run a new query, with load Controls handling paging, etc.2109 // Run a new query, with loadThemes handling paging, etc. 2082 2110 if ( ! section.loading ) { 2083 2111 section.term = newTerm; 2084 2112 section.tags = newTags; 2085 section.load Controls();2113 section.loadThemes(); 2086 2114 } else { 2087 section.nextTerm = newTerm; // This will reload from load Controls() with the newest term once the current batch is loaded.2088 section.nextTags = newTags; // This will reload from load Controls() 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. 2089 2117 } 2090 2118 if ( ! section.expanded() ) { 2091 2119 section.expand(); // Expand the section if it isn't expanded. … … 4665 4693 * Show or hide the theme based on the presence of the term in the title, description, tags, and author. 4666 4694 * 4667 4695 * @since 4.2.0 4696 * @param {Array} terms - An array of terms to search for. 4668 4697 * @returns {boolean} Whether a theme control was activated or not. 4669 4698 */ 4670 filter: function( term ) {4699 filter: function( terms ) { 4671 4700 var control = this, 4701 matchCount = 0, 4672 4702 haystack = control.params.theme.name + ' ' + 4673 4703 control.params.theme.description + ' ' + 4674 4704 control.params.theme.tags + ' ' + 4675 control.params.theme.author ;4705 control.params.theme.author + ' '; 4676 4706 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 ) { 4678 4734 control.activate(); 4735 control.params.priority = 101 - matchCount; // Sort results by match count. 4679 4736 return true; 4680 4737 } else { 4681 control.deactivate(); 4738 control.deactivate(); // Hide control 4739 control.params.priority = 101; 4682 4740 return false; 4683 4741 } 4684 4742 }, -
src/wp-includes/class-wp-customize-manager.php
4275 4275 $this->add_section( new WP_Customize_Themes_Section( $this, 'wporg_themes', array( 4276 4276 'title' => __( 'WordPress.org themes' ), 4277 4277 'action' => 'wporg', 4278 'filter_type' => 'remote', 4278 4279 'capability' => 'install_themes', 4279 4280 'panel' => 'themes', 4280 4281 'priority' => 5, … … 4806 4807 } 4807 4808 $theme_action = sanitize_key( $_POST['theme_action'] ); 4808 4809 $themes = array(); 4810 $args = array(); 4809 4811 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 4810 4831 require_once ABSPATH . 'wp-admin/includes/theme.php'; 4832 4833 // Load all installed themes from wp_prepare_themes_for_js(). 4811 4834 if ( 'installed' === $theme_action ) { 4812 4835 $themes = array( 'themes' => wp_prepare_themes_for_js() ); 4813 4836 foreach ( $themes['themes'] as &$theme ) { … … 4814 4837 $theme['type'] = 'installed'; 4815 4838 $theme['active'] = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme['id'] ); 4816 4839 } 4840 4841 // Load WordPress.org themes from the .org API and normalize data to match installed theme objects. 4817 4842 } elseif ( 'wporg' === $theme_action ) { 4818 4843 if ( ! current_user_can( 'install_themes' ) ) { 4819 4844 wp_die( -1 ); … … 4820 4845 } 4821 4846 4822 4847 // Arguments for all queries. 4823 $ args = array(4848 $wporg_args = array( 4824 4849 'per_page' => 100, 4825 'page' => isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 1,4826 4850 'fields' => array( 4827 4851 'screenshot_url' => true, 4828 4852 'description' => true, … … 4838 4862 ), 4839 4863 ); 4840 4864 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 ); 4847 4866 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 4854 4867 if ( '' === $args['search'] && '' === $args['tag'] ) { 4855 4868 $args['browse'] = 'new'; // Sort by latest themes by default. 4856 4869 } … … 4920 4933 unset( $theme->author ); 4921 4934 } // End foreach(). 4922 4935 } // 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 4923 4954 wp_send_json_success( $themes ); 4924 4955 } 4925 4956 -
src/wp-includes/customize/class-wp-customize-themes-section.php
37 37 public $action = ''; 38 38 39 39 /** 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 /** 40 51 * Get section parameters for JS. 41 52 * 42 53 * @since 4.9.0 … … 45 56 public function json() { 46 57 $exported = parent::json(); 47 58 $exported['action'] = $this->action; 59 $exported['filter_type'] = $this->filter_type; 48 60 49 61 return $exported; 50 62 }
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)