Make WordPress Core

Ticket #31303: 31303.diff

File 31303.diff, 25.4 KB (added by celloexpressions, 11 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