WordPress.org

Make WordPress Core

Ticket #31303: 31303.diff

File 31303.diff, 25.4 KB (added by celloexpressions, 7 years ago)

Direct merge of the Customizer Theme Switcher plugin into core, without addressing other issues that couldn't be handled in the plugin.

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

     
    831831        float: right;
    832832}
    833833
     834/**
     835 * Themes
     836 */
     837@-webkit-keyframes customize-reload {
     838        0%   { opacity: 0; }
     839        100% { opacity: 1; }
     840}
    834841
     842@-moz-keyframes customize-reload {
     843        0%   { opacity: 0; }
     844        100% { opacity: 1; }
     845}
     846
     847@keyframes customize-reload {
     848        0%   { opacity: 0; }
     849        100% { opacity: 1; }
     850}
     851
     852/* #customize-container is reused from customize-loader.js, hence the naming. */
     853.wp-customizer.customize-loading #customize-container {
     854        display: block;
     855        -webkit-animation: customize-reload .75s; /* Can't use `transition` because `display` changes here. */
     856        -moz-animation: customize-reload .75s;
     857        animation: customize-reload .75s;
     858}
     859
     860.customize-themes-panel {
     861        display: none;
     862        padding: 0 8px;
     863        background: #f1f1f1;
     864        box-sizing: border-box;
     865        -webkit-box-sizing: border-box;
     866        -moz-box-sizing: border-box;
     867}
     868
     869.control-section.open .customize-themes-panel {
     870        display: block;
     871}
     872
     873#customize-theme-controls .customize-themes-panel .accordion-section-content {
     874        background: transparent;
     875        display: block;
     876}
     877
     878.customize-control.customize-control-theme {
     879        margin-bottom: 8px;
     880}
     881
     882.wp-customizer .theme-browser .themes {
     883        padding-bottom: 8px;
     884}
     885
     886.wp-customizer .theme-browser .theme {
     887        margin: 0;
     888        width: 100%;
     889}
     890
     891.wp-customizer .theme-browser .theme .theme-actions {
     892        -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
     893        opacity: 1;
     894}
     895
     896#customize-controls h3.theme-name {
     897        font-size: 15px;
     898}
     899
     900.wp-customizer .theme-browser .theme.active .theme-name {
     901        padding-right: 15px;
     902}
     903
     904.wp-customizer #themes-filter {
     905        width: 100%;
     906}
     907
     908/* Panel-like behavior */
     909#accordion-section-themes .accordion-section-title:after {
     910        content: "\f148";
     911}
     912
     913.rtl #accordion-section-themes .accordion-section-title:after {
     914        -webkit-transform: rotate(180deg);
     915        -ms-transform: rotate(180deg);
     916        transform: rotate(180deg);
     917}
     918
     919#customize-theme-controls .control-section.current-panel > h3.accordion-section-title {
     920        left: 0;
     921}
     922
     923.customize-themes-panel.control-panel-content {
     924        position: absolute;
     925        left: -100%;
     926        top: 0;
     927        width: 100%;
     928        border-top: 1px solid #ddd;
     929}
     930
     931.in-themes-panel #customize-info,
     932.in-themes-panel #customize-theme-controls > ul > .accordion-section {
     933        left: 100%;
     934}
     935
     936.themes-panel-back:before {
     937        top: 13px;
     938        left: 14px;
     939}
     940
     941.in-themes-panel .themes-panel-back {
     942        left: 0;
     943}
     944
     945.in-sub-panel .themes-panel-back {
     946        display: none;
     947}
     948
     949.control-panel-back.themes-panel-back:before {
     950        content: "\f345";
     951}
     952
     953.rtl .control-panel-back.themes-panel-back:before {
     954        content: "\f341";
     955}
     956
     957/* Details View */
     958.wp-customizer .theme-overlay {
     959        display: none;
     960}
     961
     962.wp-customizer.modal-open .theme-overlay {
     963        position: fixed;
     964        left: 0;
     965        top: 0;
     966        right: 0;
     967        bottom: 0;
     968        z-index: 109;
     969}
     970
     971.wp-customizer .theme-overlay .theme-backdrop {
     972        background: rgba( 238, 238, 238, 0.75 );
     973        position: fixed;
     974        z-index: 110;
     975}
     976
     977.wp-customizer .theme-overlay .theme-wrap {
     978        left: 90px;
     979        right: 90px;
     980        top: 45px;
     981        bottom: 45px;
     982        z-index: 120;
     983        max-width: 1740px; /* To ensure that theme screenshots are not displayed larger than 880px wide. */
     984}
     985
     986.wp-customizer .theme-overlay .theme-actions {
     987        text-align: right; /* Because there's only one action, match the pattern of media modals and right-align the action. */
     988}
     989
     990.modal-open .in-themes-panel #customize-controls .wp-full-overlay-sidebar-content {
     991        overflow: visible; /* Prevent the top-level Customizer controls from becoming visible when elements on the right of the details modal are focused.
     992}
     993
     994/* Small Screens */
     995@media (max-width:850px), (max-height:472px) {
     996        .wp-customizer .theme-overlay .theme-wrap {
     997                left: 0;
     998                right: 0;
     999                top: 0;
     1000                bottom: 0;
     1001        }       
     1002}
     1003
     1004
    8351005/** Handle cheaters. */
    8361006body.cheatin {
    8371007        font-size: medium;
  • src/wp-admin/includes/theme.php

     
    486486        $prepared_themes = apply_filters( 'wp_prepare_themes_for_js', $prepared_themes );
    487487        return array_values( $prepared_themes );
    488488}
     489
     490/**
     491 * Print JS templates for the theme-browsing UI in the Customizer.
     492 *
     493 * @since 4.2.0
     494 */
     495function customize_themes_print_templates() {
     496        ?>
     497        <script type="text/html" id="tmpl-customize-themes-details-view">
     498                <div class="theme-backdrop"></div>
     499                <div class="theme-wrap">
     500                        <div class="theme-header">
     501                                <button type="button" class="left dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Show previous theme' ); ?></span></button>
     502                                <button type="button" class="right dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Show next theme' ); ?></span></button>
     503                                <button type="button" class="close dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Close details dialog' ); ?></span></button>
     504                        </div>
     505                        <div class="theme-about">
     506                                <div class="theme-screenshots">
     507                                <# if ( data.screenshot[0] ) { #>
     508                                        <div class="screenshot"><img src="{{ data.screenshot[0] }}" alt="" /></div>
     509                                <# } else { #>
     510                                        <div class="screenshot blank"></div>
     511                                <# } #>
     512                                </div>
     513
     514                                <div class="theme-info">
     515                                        <# if ( data.active ) { #>
     516                                                <span class="current-label"><?php _e( 'Current Theme' ); ?></span>
     517                                        <# } #>
     518                                        <h3 class="theme-name">{{{ data.name }}}<span class="theme-version"><?php printf( __( 'Version: %s' ), '{{ data.version }}' ); ?></span></h3>
     519                                        <h4 class="theme-author"><?php printf( __( 'By %s' ), '{{{ data.authorAndUri }}}' ); ?></h4>
     520                                        <p class="theme-description">{{{ data.description }}}</p>
     521
     522                                        <# if ( data.parent ) { #>
     523                                                <p class="parent-theme"><?php printf( __( 'This is a child theme of %s.' ), '<strong>{{{ data.parent }}}</strong>' ); ?></p>
     524                                        <# } #>
     525
     526                                        <# if ( data.tags ) { #>
     527                                                <p class="theme-tags"><span><?php _e( 'Tags:' ); ?></span> {{ data.tags }}</p>
     528                                        <# } #>
     529                                </div>
     530                        </div>
     531
     532                        <div class="theme-actions">
     533                                <# if ( ! data.active ) { #>
     534                                        <div class="inactive-theme">
     535                                                <a href="<?php echo add_query_arg( 'theme', '{{ data.id }}', remove_query_arg( 'theme' ) ); ?>" target="_top" class="button button-primary"><?php _e( 'Live Preview' ); ?></a>
     536                                        </div>
     537                                <# } #>
     538                        </div>
     539                </div>
     540        </script>
     541        <?php
     542}
     543add_action( 'customize_controls_print_footer_scripts', 'customize_themes_print_templates' );
  • src/wp-admin/js/customize-controls.js

     
    521521        });
    522522
    523523        /**
     524         * wp.customize.ThemesSection
     525         *
     526         * Custom section for themes that functions similarly to a backwards panel,
     527         * and also handles the theme-details view rendering and navigation.
     528         *
     529         * @constructor
     530         * @augments wp.customize.Section
     531         * @augments wp.customize.Container
     532         */
     533        api.ThemesSection = api.Section.extend({
     534                currentTheme: '',
     535                overlay: '',
     536                template: '',
     537
     538                /**
     539                 * @since 4.2.0
     540                 */
     541                ready: function () {
     542                        var section = this;
     543                        section.overlay = section.container.find( '.theme-overlay' );
     544                        section.template = wp.template( 'customize-themes-details-view' );
     545
     546                        // Bind global keyboard events.
     547                        $( 'body' ).on( 'keyup', function( event ) {
     548                                if ( ! section.overlay.find( '.theme-wrap' ).is( ':visible' ) ) {
     549                                        return;
     550                                }
     551
     552                                // Pressing the right arrow key fires a theme:next event
     553                                if ( 39 === event.keyCode ) {
     554                                        section.nextTheme();
     555                                }
     556
     557                                // Pressing the left arrow key fires a theme:previous event
     558                                if ( 37 === event.keyCode ) {
     559                                        section.previousTheme();
     560                                }
     561
     562                                // Pressing the escape key fires a theme:collapse event
     563                                if ( 27 === event.keyCode ) {
     564                                        section.closeDetails();
     565                                }
     566                        });
     567                },
     568
     569                /**
     570                 * @since 4.2.0
     571                 */
     572                attachEvents: function () {
     573                        var meta, section = this;
     574
     575                        // Expand/Collapse section/panel.
     576                        section.container.find( '.accordion-section-title' ).on( 'click keydown', function( event ) {
     577                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     578                                        return;
     579                                }
     580                                event.preventDefault(); // Keep this AFTER the key filter above
     581
     582                                if ( section.expanded() ) {
     583                                        section.collapse();
     584                                } else {
     585                                        section.expand();
     586                                }
     587                        });
     588
     589                        section.container.find( '.themes-panel-back' ).on( 'click keydown', function( event ) {
     590                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     591                                        return;
     592                                }
     593
     594                                event.preventDefault(); // Keep this AFTER the key filter above
     595
     596                                section.collapse();
     597                        });
     598
     599                        // Theme navigation in details view.
     600                        section.container.on( 'click keydown', '.left', function( event ) {
     601                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     602                                        return;
     603                                }
     604
     605                                event.preventDefault(); // Keep this AFTER the key filter above
     606
     607                                section.previousTheme();
     608                        });
     609
     610                        section.container.on( 'click keydown', '.right', function( event ) {
     611                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     612                                        return;
     613                                }
     614
     615                                event.preventDefault(); // Keep this AFTER the key filter above
     616
     617                                section.nextTheme();
     618                        });
     619
     620                        section.container.on( 'click keydown', '.theme-backdrop, .close', function( event ) {
     621                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     622                                        return;
     623                                }
     624
     625                                event.preventDefault(); // Keep this AFTER the key filter above
     626
     627                                section.closeDetails();
     628                        });
     629
     630                        section.container.on( 'click keydown', '.theme-actions .button', function( event ) {
     631                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     632                                        return;
     633                                }
     634
     635                                $( 'body' ).addClass( 'customize-loading' ); // @todo if they get a `confirm()`, remove the class if they stay (core patch).
     636                        });
     637
     638                        section.container.on( 'input', '#themes-filter', function( event ) {
     639                                var term = event.currentTarget.value.toLowerCase().trim().replace( '-', ' ' ),
     640                                        controls = section.controls();
     641                                controls.pop(); // Remove the last control (the add-new control).
     642                                _.each( controls, function( control ) {
     643                                        control.filter( term );
     644                                });
     645                                // Update theme count. Note that the add-theme tile is a div.customize-control.
     646                                count = section.container.find( 'li.customize-control:visible' ).length;
     647                                section.container.find( '.theme-count' ).text( count );
     648                        });
     649                },
     650
     651                /**
     652                 * Update UI to reflect expanded state
     653                 *
     654                 * @since 4.2.0
     655                 *
     656                 * @param {Boolean}  expanded
     657                 * @param {Object}   args
     658                 * @param {Boolean}  args.unchanged
     659                 * @param {Callback} args.completeCallback
     660                 */
     661                onChangeExpanded: function ( expanded, args ) {
     662
     663                        // Immediately call the complete callback if there were no changes
     664                        if ( args.unchanged ) {
     665                                if ( args.completeCallback ) {
     666                                        args.completeCallback();
     667                                }
     668                                return;
     669                        }
     670
     671                        // Note: there is a second argument 'args' passed
     672                        var position, scroll,
     673                                panel = this,
     674                                section = panel.container.closest( '.accordion-section' ),
     675                                overlay = section.closest( '.wp-full-overlay' ),
     676                                container = section.closest( '.accordion-container' ),
     677                                siblings = container.find( '.open' ),
     678                                topPanel = overlay.find( '#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ).add( '#customize-info > .accordion-section-title' ),
     679                                backBtn = overlay.find( '.themes-panel-back' ),
     680                                panelTitle = section.find( '.accordion-section-title' ).first(),
     681                                content = section.find( '.control-panel-content' );
     682
     683                        if ( expanded ) {
     684
     685                                // Collapse any sibling sections/panels
     686                                api.section.each( function ( otherSection ) {
     687                                        if ( otherSection !== panel ) {
     688                                                otherSection.collapse( { duration: args.duration } );
     689                                        }
     690                                });
     691                                api.panel.each( function ( otherPanel ) {
     692                                        if ( panel !== otherPanel ) {
     693                                                otherPanel.collapse( { duration: 0 } );
     694                                        }
     695                                });
     696
     697                                content.show( 0, function() {
     698                                        position = content.offset().top;
     699                                        scroll = container.scrollTop();
     700                                        content.css( 'margin-top', ( 45 - position - scroll ) );
     701                                        section.addClass( 'current-panel' );
     702                                        overlay.addClass( 'in-themes-panel' );
     703                                        container.scrollTop( 0 );
     704                                        if ( args.completeCallback ) {
     705                                                args.completeCallback();
     706                                        }
     707                                } );
     708                                topPanel.attr( 'tabindex', '-1' );
     709                                backBtn.attr( 'tabindex', '0' );
     710                                backBtn.focus();
     711                        } else {
     712                                siblings.removeClass( 'open' );
     713                                section.removeClass( 'current-panel' );
     714                                overlay.removeClass( 'in-themes-panel' );
     715                                content.delay( 180 ).hide( 0, function() {
     716                                        content.css( 'margin-top', 'inherit' ); // Reset
     717                                        if ( args.completeCallback ) {
     718                                                args.completeCallback();
     719                                        }
     720                                } );
     721                                topPanel.attr( 'tabindex', '0' );
     722                                backBtn.attr( 'tabindex', '-1' );
     723                                panelTitle.focus();
     724                                container.scrollTop( 0 );
     725                        }
     726                },
     727
     728                /**
     729                 * Advance the modal to the next theme.
     730                 *
     731                 * @since 4.2.0
     732                 */
     733                nextTheme: function () {
     734                        var section = this;
     735                        if ( section.getNextTheme() ) {
     736                                section.showDetails( section.getNextTheme(), function() {
     737                                        section.overlay.find( '.right' ).focus();
     738                                } );
     739                        }
     740                },
     741
     742                /**
     743                 * Get the next theme model.
     744                 *
     745                 * @since 4.2.0
     746                 */
     747                getNextTheme: function () {
     748                        var control, next;
     749                        control = api.control( 'theme_' + this.currentTheme );
     750                        next = control.container.next( 'li.customize-control-theme' );
     751                        if ( ! next.length ) {
     752                                return false;
     753                        }
     754                        next = next[0].id.replace( 'customize-control-', '' );
     755                        control = api.control( next );
     756
     757                        return control.params.theme;
     758                },
     759
     760                /**
     761                 * Advance the modal to the previous theme.
     762                 *
     763                 * @since 4.2.0
     764                 */
     765                previousTheme: function () {
     766                        var section = this;
     767                        if ( section.getPreviousTheme() ) {
     768                                section.showDetails( section.getPreviousTheme(), function() {
     769                                        section.overlay.find( '.left' ).focus();
     770                                } );
     771                        }
     772                },
     773
     774                /**
     775                 * Get the previous theme model.
     776                 *
     777                 * @since 4.2.0
     778                 */
     779                getPreviousTheme: function () {
     780                        var control, previous;
     781                        control = api.control( 'theme_' + this.currentTheme );
     782                        previous = control.container.prev( 'li.customize-control-theme' );
     783                        if ( ! previous.length ) {
     784                                return false;
     785                        }
     786                        previous = previous[0].id.replace( 'customize-control-', '' );
     787                        control = api.control( previous );
     788
     789                        return control.params.theme;
     790                },
     791
     792                /**
     793                 * Disable buttons when we're viewing the first or last theme.
     794                 *
     795                 * @since 4.2.0
     796                 */
     797                updateLimits: function () {
     798                        if ( ! this.getNextTheme() ) {
     799                                this.overlay.find( '.right' ).addClass( 'disabled' );
     800                        }
     801                        if ( ! this.getPreviousTheme() ) {
     802                                this.overlay.find( '.left' ).addClass( 'disabled' );
     803                        }
     804                },
     805
     806                /**
     807                 * Render & show the theme details for a given theme model.
     808                 *
     809                 * @since 4.2.0
     810                 *
     811                 * @param {Object}   theme
     812                 */
     813                showDetails: function ( theme, callback ) {
     814                        var section = this;
     815                        callback = callback || function(){};
     816                        section.currentTheme = theme.id;
     817                        section.overlay.html( section.template( theme ) )
     818                                       .fadeIn( 'fast' )
     819                                       .focus();
     820                        $( 'body' ).addClass( 'modal-open' );
     821                        section.containFocus( section.overlay );
     822                        section.updateLimits();
     823                        callback();
     824                },
     825
     826                /**
     827                 * Close the theme details modal.
     828                 *
     829                 * @since 4.2.0
     830                 */
     831                closeDetails: function ( theme ) {
     832                        $( 'body' ).removeClass( 'modal-open' );
     833                        this.overlay.fadeOut( 'fast' );
     834                        api.control( 'theme_' + this.currentTheme ).focus();
     835                },
     836
     837                /**
     838                 * Keep tab focus within the theme details modal.
     839                 *
     840                 * @since 4.2.0
     841                 */
     842                containFocus: function( el ) {
     843                        var tabbables;
     844
     845                        el.on( 'keydown', function( event ) {
     846
     847                                // Return if it's not the tab key
     848                                // When navigating with prev/next focus is already handled
     849                                if ( 9 !== event.keyCode ) {
     850                                        return;
     851                                }
     852
     853                                // uses jQuery UI to get the tabbable elements
     854                                tabbables = $( ':tabbable', el );
     855
     856                                // Keep focus within the overlay
     857                                if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
     858                                        tabbables.first().focus();
     859                                        return false;
     860                                } else if ( tabbables.first()[0] === event.target && event.shiftKey ) {
     861                                        tabbables.last().focus();
     862                                        return false;
     863                                }
     864                        });
     865                }
     866        });
     867
     868        /**
    524869         * @since 4.1.0
    525870         *
    526871         * @class
     
    14091754
    14101755        });
    14111756
     1757        /**
     1758         * wp.customize.ThemeControl
     1759         *
     1760         * @constructor
     1761         * @augments wp.customize.Control
     1762         * @augments wp.customize.Class
     1763         */
     1764        api.ThemeControl = api.Control.extend({
     1765
     1766                /**
     1767                 * @since 4.2.0
     1768                 */
     1769                ready: function() {
     1770                        var control = this;
     1771
     1772                        // Bind details view trigger.
     1773                        control.container.on( 'click keydown', '.theme', function( event ) {
     1774                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     1775                                        return;
     1776                                }
     1777
     1778                                if ( 'button' === event.target.className ) {
     1779                                        return;
     1780                                }
     1781
     1782                                api.section( control.section() ).showDetails( control.params.theme );
     1783                        });
     1784
     1785                        control.container.on( 'click keydown', '.theme-actions .button', function( event ) {
     1786                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     1787                                        return;
     1788                                }
     1789
     1790                                $( 'body' ).addClass( 'customize-loading' );
     1791                        });
     1792                },
     1793
     1794                /**
     1795                 * Show or hide the theme based on the presence of the term in the title, description, and author.
     1796                 *
     1797                 * @since 4.2.0
     1798                 */
     1799                filter: function( term ) {
     1800                        var control = this,
     1801                            haystack = control.params.theme.name + ' '
     1802                                           + control.params.theme.description + ' '
     1803                                           + control.params.theme.tags + ' '
     1804                                           + control.params.theme.author;
     1805                        haystack = haystack.toLowerCase().replace( '-', ' ' );
     1806                        if ( -1 !== haystack.search( term ) ) {
     1807                                control.activate();
     1808                        } else {
     1809                                control.deactivate();
     1810                        }
     1811                }
     1812        });
     1813
    14121814        // Change objects contained within the main customize object to Settings.
    14131815        api.defaultConstructor = api.Setting;
    14141816
     
    18532255        });
    18542256
    18552257        api.controlConstructor = {
    1856                 color:  api.ColorControl,
    1857                 upload: api.UploadControl,
    1858                 image:  api.ImageControl,
    1859                 header: api.HeaderControl,
    1860                 background: api.BackgroundControl
     2258                color:      api.ColorControl,
     2259                upload:     api.UploadControl,
     2260                image:      api.ImageControl,
     2261                header:     api.HeaderControl,
     2262                background: api.BackgroundControl,
     2263                theme:      api.ThemeControl
    18612264        };
    18622265        api.panelConstructor = {};
    1863         api.sectionConstructor = {};
     2266        api.sectionConstructor = {
     2267                themes: api.ThemesSection
     2268        };
    18642269
    18652270        $( function() {
    18662271                api.settings = window._wpCustomizeSettings;
  • src/wp-includes/class-wp-customize-control.php

     
    11011101}
    11021102
    11031103/**
     1104 * Customize Theme Control Class
     1105 *
     1106 * @package WordPress
     1107 * @subpackage Customize
     1108 * @since 4.2.0
     1109 */
     1110class WP_Customize_Theme_Control extends WP_Customize_Control {
     1111
     1112        public $type = 'theme';
     1113        public $theme;
     1114
     1115        /**
     1116         * Refresh the parameters passed to the JavaScript via JSON.
     1117         *
     1118         * @since 4.2.0
     1119         * @uses WP_Customize_Control::to_json()
     1120         */
     1121        public function to_json() {
     1122                parent::to_json();
     1123                $this->json['theme'] = $this->theme;
     1124        }
     1125
     1126        /**
     1127         * Don't render the control content from PHP, as it's rendered via JS on load.
     1128         *
     1129         * @since 4.2.0
     1130         */
     1131        public function render_content() {}
     1132
     1133        /**
     1134         * Render a JS template for theme display.
     1135         *
     1136         * @since 4.2.0
     1137         */
     1138        public function content_template() {
     1139        ?>
     1140                <div class="theme<# if ( data.theme.active ) { #> active<# } #>" tabindex="0" aria-describedby="{{ data.theme.id }}-action {{ data.theme.id }}-name">
     1141                        <# if ( data.theme.screenshot[0] ) { #>
     1142                                <div class="theme-screenshot">
     1143                                        <img src="{{ data.theme.screenshot[0] }}" alt="" />
     1144                                </div>
     1145                        <# } else { #>
     1146                                <div class="theme-screenshot blank"></div>
     1147                        <# } #>
     1148                        <span class="more-details" id="{{ data.theme.id }}-action"><?php _e( 'Theme Details' ); ?></span>
     1149                        <div class="theme-author"><?php printf( __( 'By %s' ), '{{ data.theme.author }}' ); ?></div>
     1150
     1151                        <# if ( data.theme.active ) { #>
     1152                                <h3 class="theme-name" id="{{ data.theme.id }}-name"><span><?php _ex( 'Previewing:', 'theme' ); ?></span> {{ data.theme.name }}</h3>
     1153                        <# } else { #>
     1154                                <h3 class="theme-name" id="{{ data.theme.id }}-name">{{ data.theme.name }}</h3>
     1155                        <# } #>
     1156
     1157                        <# if ( ! data.theme.active ) { #>
     1158                                <div class="theme-actions">
     1159                                        <a class="button" href="<?php echo add_query_arg( 'theme', '{{ data.theme.id }}', remove_query_arg( 'theme' ) ); ?>" target="_top"><?php _e( 'Live Preview' ); ?></a>
     1160                                </div>
     1161                        <# } #>
     1162                </div>
     1163        <?php
     1164        }
     1165}
     1166
     1167/**
     1168 * Customize New Theme Control Class
     1169 *
     1170 * @package WordPress
     1171 * @subpackage Customize
     1172 * @since 4.2.0
     1173 */
     1174class WP_Customize_New_Theme_Control extends WP_Customize_Control {
     1175
     1176        /**
     1177         * Render the new control.
     1178         *
     1179         * @since 4.2.0
     1180         */
     1181        public function render() {
     1182                if ( is_multisite() || ! current_user_can( 'install_themes') ) {
     1183                        return;
     1184                }
     1185                ?>
     1186                <div class="theme add-new-theme">
     1187                        <a href="<?php echo admin_url( 'theme-install.php' ); ?>" target="_top">
     1188                                <div class="theme-screenshot">
     1189                                        <span></span>
     1190                                </div>
     1191                                <h3 class="theme-name"><?php _e( 'Add New Theme' ); ?></h3>
     1192                        </a>
     1193                </div>
     1194                <?php
     1195        }
     1196}
     1197
     1198/**
    11041199 * Widget Area Customize Control Class
    11051200 *
    11061201 * @since 3.9.0
  • src/wp-includes/class-wp-customize-manager.php

     
    11101110                $this->register_control_type( 'WP_Customize_Upload_Control' );
    11111111                $this->register_control_type( 'WP_Customize_Image_Control' );
    11121112                $this->register_control_type( 'WP_Customize_Background_Image_Control' );
     1113                $this->register_control_type( 'WP_Customize_Theme_Control' );
    11131114
     1115                /* Themes */
     1116
     1117                $this->add_section( new WP_Customize_Themes_Section( $this, 'themes', array(
     1118                        'title' => sprintf( __( 'Theme: %s' ), $this->theme()->display('Name') ),
     1119                        'capability' => 'switch_themes',
     1120                        'priority' => 0,
     1121                ) ) );
     1122
     1123                // Themes Setting (unused - the theme is considerably more fundamental to the Customizer experience).
     1124                $this->add_setting( new WP_Customize_Filter_Setting( $this, 'active_theme', array(
     1125                        'capability' => 'switch_themes',
     1126                ) ) );
     1127
     1128                require_once( ABSPATH . 'wp-admin/includes/theme.php' );
     1129
     1130                // Theme Controls.
     1131                $themes = wp_prepare_themes_for_js();
     1132                foreach ( $themes as $theme ) {
     1133                        $theme_id = 'theme_' . $theme['id'];
     1134                        $this->add_control( new WP_Customize_Theme_Control( $this, $theme_id, array(
     1135                                'theme' => $theme,
     1136                                'section' => 'themes',
     1137                                'settings' => 'active_theme',
     1138                        ) ) );
     1139                }
     1140
     1141                $this->add_control( new WP_Customize_New_Theme_Control( $this, 'add_theme', array(
     1142                        'section' => 'themes',
     1143                        'settings' => 'active_theme',
     1144                ) ) );
     1145
    11141146                /* Site Title & Tagline */
    11151147
    11161148                $this->add_section( 'title_tagline', array(
  • src/wp-includes/class-wp-customize-section.php

     
    312312}
    313313
    314314/**
     315 * Customize Themes Section Class.
     316 *
     317 * A UI container for theme controls, which behaves like a backwards Panel.
     318 *
     319 * @package WordPress
     320 * @subpackage Customize
     321 * @since 4.2.0
     322 */
     323class WP_Customize_Themes_Section extends WP_Customize_Section {
     324
     325        public $type = 'themes';
     326
     327        /**
     328         * Render the themes section, which behaves like a panel.
     329         *
     330         * @since 4.2.0
     331         */
     332        protected function render() {
     333                $classes = 'accordion-section control-section control-section-' . $this->type;
     334                ?>
     335                <li id="accordion-section-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>">
     336                        <h3 class="accordion-section-title" tabindex="0">
     337                                <?php echo esc_html( $this->title ); ?>
     338                                <span class="screen-reader-text"><?php _e( 'Press return or enter to expand' ); ?></span>
     339                        </h3>
     340                        <span class="control-panel-back themes-panel-back" tabindex="-1"><span class="screen-reader-text"><?php _e( 'Back' ); ?></span></span>
     341                        <div class="customize-themes-panel control-panel-content themes-php">
     342                                <h2><?php esc_html_e( 'Themes' ); ?>
     343                                        <span class="title-count theme-count"><?php echo count( $this->controls ) - 1; ?></span>
     344                                <?php if ( ! is_multisite() && current_user_can( 'install_themes' ) ) : ?>
     345                                        <a href="<?php echo admin_url( 'theme-install.php' ); ?>" target="_top" class="add-new-h2"><?php echo esc_html_x( 'Add New', 'Add new theme' ); ?></a>
     346                                <?php endif; ?>
     347                                </h2>
     348                                <div class="theme-overlay" tabindex="0" role="dialog" aria-label="<?php esc_attr_e( 'Theme details' ); ?>"></div>
     349                                <div id="customize-container"></div>
     350                                <?php if ( 6 < count( $this->controls ) ) : ?>
     351                                        <p><label for="themes-filter">
     352                                                <span class="screen-reader-text"><?php _e( 'Search installed themes...' ); ?></span>
     353                                                <input type="search" id="themes-filter" placeholder="<?php esc_attr_e( 'Search installed themes...' ); ?>" />
     354                                        </label></p>
     355                                <?php endif; ?>
     356                                <div class="theme-browser rendered">
     357                                        <ul class="themes accordion-section-content">
     358                                        </ul>
     359                                </div>
     360                        </div>
     361                </li>
     362<?php }
     363}
     364
     365/**
    315366 * Customizer section representing widget area (sidebar).
    316367 *
    317368 * @package WordPress