Ticket #42049: 42049.diff
| File 42049.diff, 18.6 KB (added by , 9 years ago) |
|---|
-
src/wp-admin/js/customize-controls.js
1593 1593 section.closeDetails(); 1594 1594 }); 1595 1595 1596 // Filter-search all theme objects loaded in the section. 1597 section.container.on( 'input', '.wp-filter-search-themes', function( event ) { 1598 section.filterSearch( event.currentTarget ); 1599 }); 1596 if ( 'local' === section.params.filter_type ) { 1597 // Filter-search all theme objects loaded in the section. 1598 section.container.on( 'input', '.wp-filter-search-themes', function( event ) { 1599 section.filterSearch( event.currentTarget.value ); 1600 }); 1600 1601 1601 // Event listeners for remote wporg queries with user-entered terms. 1602 if ( 'wporg' === section.params.action ) { 1602 } else if ( 'remote' === section.params.filter_type ) { 1603 1603 1604 // Event listeners for remote queries with user-entered terms. 1604 1605 // Search terms. 1605 1606 debounced = _.debounce( section.checkTerm, 500 ); // Wait until there is no input for 500 milliseconds to initiate a search. 1606 1607 section.contentContainer.on( 'input', '#wp-filter-search-input', function() { … … 1616 1617 section.filtersChecked(); 1617 1618 section.checkTerm( section ); 1618 1619 }); 1620 } 1619 1621 1620 // Toggle feature filter sections.1621 section.contentContainer.on( 'click', '.feature-filter-toggle', function( e ) {1622 $( e.currentTarget )1623 .toggleClass( 'open' )1624 .attr( 'aria-expanded', function( i, attr ) {1625 return 'true' === attr ? 'false' : 'true';1626 })1627 .next( '.filter-drawer' ).slideToggle( 180, 'linear', function() {1628 if ( 0 === section.filtersHeight ) {1629 section.filtersHeight = $( this ).height();1622 // Toggle feature filters. 1623 section.contentContainer.on( 'click', '.feature-filter-toggle', function( e ) { 1624 $( e.currentTarget ) 1625 .toggleClass( 'open' ) 1626 .attr( 'aria-expanded', function( i, attr ) { 1627 return 'true' === attr ? 'false' : 'true'; 1628 }) 1629 .next( '.filter-drawer' ).slideToggle( 180, 'linear', function() { 1630 if ( 0 === section.filtersHeight ) { 1631 section.filtersHeight = $( this ).height(); 1630 1632 1631 // First time, so it's opened. 1632 section.contentContainer.find( '.themes' ).css( 'margin-top', section.filtersHeight + 76 ); 1633 } 1634 }); 1635 if ( $( e.currentTarget ).hasClass( 'open' ) ) { 1636 section.contentContainer.find( '.themes' ).css( 'margin-top', section.filtersHeight + 76 ); 1637 } else { 1638 section.contentContainer.find( '.themes' ).css( 'margin-top', 0 ); 1639 } 1640 }); 1641 } 1633 // First time, so it's opened. 1634 section.contentContainer.find( '.themes' ).css( 'margin-top', section.filtersHeight + 76 ); 1635 } 1636 }); 1637 if ( $( e.currentTarget ).hasClass( 'open' ) ) { 1638 section.contentContainer.find( '.themes' ).css( 'margin-top', section.filtersHeight + 76 ); 1639 } else { 1640 section.contentContainer.find( '.themes' ).css( 'margin-top', 0 ); 1641 } 1642 }); 1642 1643 1643 1644 // Setup section cross-linking. 1644 1645 section.contentContainer.on( 'click', '.no-themes-local .search-dotorg-themes', function() { … … 1682 1683 1683 1684 // Try to load controls if none are loaded yet. 1684 1685 if ( 0 === section.loaded ) { 1685 section.load Controls();1686 section.loadThemes(); 1686 1687 } 1687 1688 1688 1689 // Collapse any sibling sections/panels … … 1697 1698 section.contentContainer.find( '.wp-filter-search' ).val( searchTerm ); 1698 1699 1699 1700 // Directly initialize an empty remote search to avoid a race condition. 1700 if ( '' === searchTerm && '' !== section.term && ' installed' !== section.params.action) {1701 if ( '' === searchTerm && '' !== section.term && 'local' !== section.params.filter_type ) { 1701 1702 section.term = ''; 1702 1703 section.initializeNewQuery( section.term, section.tags ); 1703 1704 } else { 1704 section.checkTerm( section ); 1705 if ( 'remote' === section.params.filter_type ) { 1706 section.checkTerm( section ); 1707 } else if ( 'local' === section.params.filter_type ) { 1708 section.filterSearch( searchTerm ); 1709 } 1705 1710 } 1706 section.filterSearch( section.contentContainer.find( '.wp-filter-search' ).get( 0 ) );1707 1711 } 1708 1712 otherSection.collapse( { duration: args.duration } ); 1709 1713 } … … 1753 1757 * 1754 1758 * @returns {void} 1755 1759 */ 1756 load Controls: function() {1760 loadThemes: function() { 1757 1761 var section = this, params, page, request; 1758 1762 1759 1763 if ( section.loading ) { … … 1770 1774 'page': page 1771 1775 }; 1772 1776 1773 // Add fields for wporg actions.1774 if ( ' wporg' === section.params.action) {1777 // Add fields for remote filtering. 1778 if ( 'remote' === section.params.filter_type ) { 1775 1779 params.search = section.term; 1776 1780 params.tags = section.tags; 1777 1781 } … … 1782 1786 section.container.find( '.no-themes' ).hide(); 1783 1787 request = wp.ajax.post( 'customize-load-themes', params ); 1784 1788 request.done(function( data ) { 1785 var themes = data.themes, themeControl , newThemeControls;1789 var themes = data.themes, themeControl; 1786 1790 1787 1791 // Stop and try again if the term changed while loading. 1788 1792 if ( '' !== section.nextTerm || '' !== section.nextTags ) { … … 1795 1799 section.nextTerm = ''; 1796 1800 section.nextTags = ''; 1797 1801 section.loading = false; 1798 section.load Controls();1802 section.loadThemes(); 1799 1803 return; 1800 1804 } 1801 1805 1802 1806 if ( 0 !== themes.length ) { 1803 newThemeControls = [];1804 1807 1805 // Add controls for each theme. 1806 _.each( themes, function( theme ) { 1807 var customizeId = section.params.action + '_theme_' + theme.id; 1808 themeControl = new api.controlConstructor.theme( customizeId, { 1809 params: { 1810 type: 'theme', 1811 content: '<li id="customize-control-theme-' + section.params.action + '_' + theme.id + '" class="customize-control customize-control-theme"></li>', 1812 section: section.params.id, 1813 active: true, 1814 theme: theme, 1815 priority: section.loaded + 1 1816 }, 1817 previewer: api.previewer 1818 } ); 1808 section.loadControls( themes, page ); 1819 1809 1820 api.control.add( customizeId, themeControl );1821 newThemeControls.push( themeControl );1822 section.loaded = section.loaded + 1;1823 });1824 1825 1810 if ( 1 === page ) { 1826 1811 1827 1812 // Pre-load the first 3 theme screenshots. … … 1832 1817 img.src = src; 1833 1818 } 1834 1819 }); 1835 if ( ' installed' !== section.params.action) {1820 if ( 'local' !== section.params.filter_type ) { 1836 1821 wp.a11y.speak( api.settings.l10n.themeSearchResults.replace( '%d', data.info.results ) ); 1837 1822 } 1838 } else {1839 Array.prototype.push.apply( section.screenshotQueue, newThemeControls ); // Add new themes to the screenshot queue.1840 1823 } 1824 1841 1825 _.delay( section.renderScreenshots, 100 ); // Wait for the controls to become visible. 1842 1826 1843 if ( ' installed' === section.params.action|| 100 > themes.length ) { // If we have less than the requested 100 themes, it's the end of the list.1827 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. 1844 1828 section.fullyLoaded = true; 1845 1829 } 1846 1830 } else { … … 1851 1835 section.fullyLoaded = true; 1852 1836 } 1853 1837 } 1854 if ( ' installed' === section.params.action) {1838 if ( 'local' === section.params.filter_type ) { 1855 1839 section.updateCount(); // Count of visible theme controls. 1856 1840 } else { 1857 1841 section.updateCount( data.info.results ); // Total number of results including pages not yet loaded. … … 1877 1861 }, 1878 1862 1879 1863 /** 1864 * Loads controls into the section from data received from loadThemes(). 1865 * 1866 * @since 4.9.0 1867 * @param {Array} themes - Array of theme data to create controls with. 1868 * @param {integer} page - Page of results being loaded. 1869 * @returns {void} 1870 */ 1871 loadControls: function( themes, page ) { 1872 var newThemeControls = [], 1873 section = this; 1874 1875 // Add controls for each theme. 1876 _.each( themes, function( theme ) { 1877 var customizeId = section.params.action + '_theme_' + theme.id; 1878 themeControl = new api.controlConstructor.theme( customizeId, { 1879 params: { 1880 type: 'theme', 1881 content: '<li id="customize-control-theme-' + section.params.action + '_' + theme.id + '" class="customize-control customize-control-theme"></li>', 1882 section: section.params.id, 1883 active: true, 1884 theme: theme, 1885 priority: section.loaded + 1 1886 }, 1887 previewer: api.previewer 1888 } ); 1889 1890 api.control.add( customizeId, themeControl ); 1891 newThemeControls.push( themeControl ); 1892 section.loaded = section.loaded + 1; 1893 }); 1894 1895 if ( 1 !== page ) { 1896 Array.prototype.push.apply( section.screenshotQueue, newThemeControls ); // Add new themes to the screenshot queue. 1897 } 1898 }, 1899 1900 /** 1880 1901 * Determines whether more themes should be loaded, and loads them. 1881 1902 * 1882 1903 * @since 4.9.0 … … 1891 1912 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. 1892 1913 1893 1914 if ( bottom > threshold ) { 1894 section.load Controls();1915 section.loadThemes(); 1895 1916 } 1896 1917 } 1897 1918 }, … … 1901 1922 * 1902 1923 * @since 4.9.0 1903 1924 * 1904 * @param { Element} el - The search input element as a raw JS object.1925 * @param {string} term - The raw search input value. 1905 1926 * @returns {void} 1906 1927 */ 1907 filterSearch: function( el) {1928 filterSearch: function( term ) { 1908 1929 var count = 0, 1909 1930 visible = false, 1910 1931 section = this, 1911 noFilter = ( undefined !== api.section( 'wporg_themes' ) && 'wporg' !== section.params.action ) ? '.no-themes-local' : '.no-themes', 1912 term = el.value.toLowerCase().trim().replace( '-', ' ' ), 1932 noFilter = ( undefined !== api.section( 'wporg_themes' ) && 'remote' !== section.params.filter_type ) ? '.no-themes-local' : '.no-themes', 1913 1933 controls = section.controls(); 1914 1934 1915 1935 if ( section.loading ) { … … 1916 1936 return; 1917 1937 } 1918 1938 1939 // Standardize search term format and split into an array of individual words. 1940 term = term.toLowerCase().trim().replace( '-', ' ' ).split( ' ' ); 1941 1919 1942 _.each( controls, function( control ) { 1920 visible = control.filter( term ); 1943 visible = control.filter( term ); // Shows/hides and sorts control based on the applicability of the search term. 1921 1944 if ( visible ) { 1922 1945 count = count + 1; 1923 1946 } … … 1931 1954 } 1932 1955 1933 1956 section.renderScreenshots(); 1957 api.reflowPaneContents(); 1934 1958 1935 1959 // Update theme count. 1936 1960 section.updateCount( count ); … … 1946 1970 */ 1947 1971 checkTerm: function( section ) { 1948 1972 var newTerm; 1949 if ( ' wporg' === section.params.action) {1973 if ( 'remote' === section.params.filter_type ) { 1950 1974 newTerm = $( '#wp-filter-search-input' ).val(); 1951 1975 if ( section.term !== newTerm ) { 1952 1976 section.initializeNewQuery( newTerm, section.tags ); … … 1986 2010 if ( section.loading ) { 1987 2011 section.nextTags = tags; 1988 2012 } else { 1989 section.initializeNewQuery( section.term, tags ); 2013 if ( 'remote' === section.params.filter_type ) { 2014 section.initializeNewQuery( section.term, tags ); 2015 } else if ( 'local' === section.params.filter_type ) { 2016 section.filterSearch( tags.join( ' ' ) ); 2017 } 1990 2018 } 1991 2019 } 1992 2020 }, … … 2012 2040 section.fullyLoaded = false; 2013 2041 section.screenshotQueue = null; 2014 2042 2015 // Run a new query, with load Controls handling paging, etc.2043 // Run a new query, with loadThemes handling paging, etc. 2016 2044 if ( ! section.loading ) { 2017 2045 section.term = newTerm; 2018 2046 section.tags = newTags; 2019 section.load Controls();2047 section.loadThemes(); 2020 2048 } else { 2021 section.nextTerm = newTerm; // This will reload from load Controls() with the newest term once the current batch is loaded.2022 section.nextTags = newTags; // This will reload from load Controls() with the newest tags once the current batch is loaded.2049 section.nextTerm = newTerm; // This will reload from loadThemes() with the newest term once the current batch is loaded. 2050 section.nextTags = newTags; // This will reload from loadThemes() with the newest tags once the current batch is loaded. 2023 2051 } 2024 2052 if ( ! section.expanded() ) { 2025 2053 section.expand(); // Expand the section if it isn't expanded. … … 4607 4635 * Show or hide the theme based on the presence of the term in the title, description, tags, and author. 4608 4636 * 4609 4637 * @since 4.2.0 4638 * @param {Array} terms - An array of terms to search for. 4610 4639 * @returns {boolean} Whether a theme control was activated or not. 4611 4640 */ 4612 filter: function( term ) {4641 filter: function( terms ) { 4613 4642 var control = this, 4643 matchCount = 0, 4614 4644 haystack = control.params.theme.name + ' ' + 4615 4645 control.params.theme.description + ' ' + 4616 4646 control.params.theme.tags + ' ' + 4617 control.params.theme.author ;4647 control.params.theme.author + ' '; 4618 4648 haystack = haystack.toLowerCase().replace( '-', ' ' ); 4619 if ( -1 !== haystack.search( term ) ) { 4649 4650 // Back-compat for behavior in WordPress 4.2.0 to 4.8.X. 4651 if ( ! terms instanceof Array ) { 4652 terms = [terms]; 4653 } 4654 4655 // Always give exact name matches highest ranking. 4656 if ( control.params.theme.name.toLowerCase() === terms.join( ' ' ) ) { 4657 matchCount = 100; 4658 } else { 4659 4660 // Search for and weight (by 10) complete term matches. 4661 matchCount = matchCount + 10 * ( haystack.split( terms.join( ' ' ) ).length - 1 ); 4662 4663 // Search for each term individually (as whole-word and partial match) and sum weighted match counts. 4664 _.each( terms, function( term ) { 4665 matchCount = matchCount + 2 * ( haystack.split( term + ' ' ).length - 1 ); // Whole-word, double-weighted. 4666 matchCount = matchCount + haystack.split( term ).length - 1; // Partial word, to minimize empty intermediate searches while typing. 4667 }); 4668 4669 // Upper limit on match ranking. 4670 if ( matchCount > 99 ) { 4671 matchCount = 99; 4672 } 4673 } 4674 4675 if ( 0 !== matchCount ) { 4620 4676 control.activate(); 4677 control.params.priority = 101 - matchCount; // Sort results by match count. 4621 4678 return true; 4622 4679 } else { 4623 control.deactivate(); 4680 control.deactivate(); // Hide control 4681 control.params.priority = 101; 4624 4682 return false; 4625 4683 } 4626 4684 }, -
src/wp-includes/class-wp-customize-manager.php
4204 4204 $this->add_section( new WP_Customize_Themes_Section( $this, 'wporg_themes', array( 4205 4205 'title' => __( 'WordPress.org themes' ), 4206 4206 'action' => 'wporg', 4207 'filter_type' => 'remote', 4207 4208 'capability' => 'install_themes', 4208 4209 'panel' => 'themes', 4209 4210 'priority' => 5, … … 4735 4736 } 4736 4737 $theme_action = sanitize_key( $_POST['theme_action'] ); 4737 4738 $themes = array(); 4739 $args = array(); 4738 4740 4741 // Define query filters based on user input. 4742 if ( ! array_key_exists( 'search', $_POST ) ) { 4743 $args['search'] = ''; 4744 } else { 4745 $args['search'] = sanitize_text_field( wp_unslash( $_POST['search'] ) ); 4746 } 4747 4748 if ( ! array_key_exists( 'tags', $_POST ) ) { 4749 $args['tag'] = ''; 4750 } else { 4751 $args['tag'] = array_map( 'sanitize_text_field', wp_unslash( (array) $_POST['tags'] ) ); 4752 } 4753 4754 if ( ! array_key_exists( 'page', $_POST ) ) { 4755 $args['page'] = 1; 4756 } else { 4757 $args['page'] = absint( $_POST['page'] ); 4758 } 4759 4739 4760 require_once ABSPATH . 'wp-admin/includes/theme.php'; 4761 4762 // Load all installed themes from wp_prepare_themes_for_js(). 4740 4763 if ( 'installed' === $theme_action ) { 4741 4764 $themes = array( 'themes' => wp_prepare_themes_for_js() ); 4742 4765 foreach ( $themes['themes'] as &$theme ) { … … 4743 4766 $theme['type'] = 'installed'; 4744 4767 $theme['active'] = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme['id'] ); 4745 4768 } 4769 4770 // Load WordPress.org themes from the .org API and normalize data to match installed theme objects. 4746 4771 } elseif ( 'wporg' === $theme_action ) { 4747 4772 if ( ! current_user_can( 'install_themes' ) ) { 4748 4773 wp_die( -1 ); … … 4749 4774 } 4750 4775 4751 4776 // Arguments for all queries. 4752 $ args = array(4777 $wporg_args = array( 4753 4778 'per_page' => 100, 4754 'page' => isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 1,4755 4779 'fields' => array( 4756 4780 'screenshot_url' => true, 4757 4781 'description' => true, … … 4767 4791 ), 4768 4792 ); 4769 4793 4770 // Define query filters based on user input. 4771 if ( ! array_key_exists( 'search', $_POST ) ) { 4772 $args['search'] = ''; 4773 } else { 4774 $args['search'] = sanitize_text_field( wp_unslash( $_POST['search'] ) ); 4775 } 4794 $args = array_merge( $wporg_args, $args ); 4776 4795 4777 if ( ! array_key_exists( 'tags', $_POST ) ) {4778 $args['tag'] = '';4779 } else {4780 $args['tag'] = array_map( 'sanitize_text_field', wp_unslash( (array) $_POST['tags'] ) );4781 }4782 4783 4796 if ( '' === $args['search'] && '' === $args['tag'] ) { 4784 4797 $args['browse'] = 'new'; // Sort by latest themes by default. 4785 4798 } … … 4849 4862 unset( $theme->author ); 4850 4863 } // End foreach(). 4851 4864 } // End if(). 4865 4866 4867 /** 4868 * Filters the theme data loaded in the customizer. 4869 * 4870 * This allows theme data to be loading from an external source, 4871 * or modification of data loaded from wp_prepare_themes_for_JS 4872 * or WordPress.org. 4873 * 4874 * @since 4.9.0 4875 * 4876 * @see WP_Customize_Manager::__construct() 4877 * 4878 * @param array $themes Nested array of theme data. 4879 * @param array $args List of arguments, such as page, search term, and tags to query for. 4880 */ 4881 $themes = apply_filters( 'customize_load_themes_ajax', $themes, $args ); 4882 4852 4883 wp_send_json_success( $themes ); 4853 4884 } 4854 4885 -
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)