Make WordPress Core

Ticket #21483: 21483.6.diff

File 21483.6.diff, 28.6 KB (added by ericlewis, 10 years ago)
  • src/wp-admin/css/customize-controls.css

    diff --git src/wp-admin/css/customize-controls.css src/wp-admin/css/customize-controls.css
    index 5503cfd..88cc6c4 100644
    body { 
    1010        font-size: 14px;
    1111}
    1212
     13#customize-controls img {
     14        max-width: 100%;
     15}
     16
    1317#customize-controls .submit {
    1418        text-align: center;
    1519}
    p.customize-section-description { 
    414418        margin-right: 5px;
    415419}
    416420
     421.customize-control .attachment-thumb.type-icon {
     422        float: left;
     423        margin-right: 12px;
     424}
     425
     426.customize-control .attachment-title {
     427        font-weight: bold;
     428        margin: 0 0 12px 0;
     429}
     430
     431.customize-control .remove-button {
     432        margin-left: 8px;
     433        vertical-align: middle;
     434}
     435
     436.customize-control .thumbnail-audio .attachment-title,
     437.customize-control .thumbnail.thumbnail-audio,
     438.customize-control .thumbnail.thumbnail-video,
     439.customize-control .thumbnail.thumbnail-image {
     440        margin-bottom: 8px;
     441}
     442
     443.customize-control .placeholder-text {
     444        font-size: 18px;
     445        font-weight: 300;
     446        text-align: center;
     447        color: #aaa;
     448        cursor: default;
     449}
     450
     451.customize-control .thumbnail-image img {
     452        cursor: pointer;
     453}
     454
    417455#customize-preview iframe {
    418456        width: 100%;
    419457        height: 100%;
  • src/wp-admin/js/customize-controls.js

    diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js
    index 66d63bd..5bfabe2 100644
     
    55        // @todo Move private helper functions to wp.customize.utils so they can be unit tested
    66
    77        /**
    8          * @constructor
     8         * @class
    99         * @augments wp.customize.Value
    1010         * @augments wp.customize.Class
    1111         *
     
    127127        /**
    128128         * Base class for Panel and Section
    129129         *
    130          * @constructor
     130         * @class
    131131         * @augments wp.customize.Class
    132132         */
    133133        Container = api.Class.extend({
     
    312312        });
    313313
    314314        /**
    315          * @constructor
     315         * @class
    316316         * @augments wp.customize.Class
    317317         */
    318318        api.Section = Container.extend({
     
    467467        });
    468468
    469469        /**
    470          * @constructor
     470         * @class
    471471         * @augments wp.customize.Class
    472472         */
    473473        api.Panel = Container.extend({
     
    636636        });
    637637
    638638        /**
    639          * @constructor
     639         * A Customizer Control.
     640         *
     641         * A control provides a UI element that allows a user to modify a Customizer Setting.
     642         *
     643         * @see PHP class WP_Customize_Control.
     644         *
     645         * @class
    640646         * @augments wp.customize.Class
     647         *
     648         * @param {string} id                            Unique identifier for the control instance.
     649         * @param {object} options                       Options hash for the control instance.
     650         * @param {object} options.params
     651         * @param {object} options.params.type           Type of control (e.g. text, radio, dropdown-pages, etc.)
     652         * @param {string} options.params.content        The HTML content for the control.
     653         * @param {string} options.params.priority       Order of priority to show the control within the section.
     654         * @param {string} options.params.active
     655         * @param {string} options.params.section
     656         * @param {string} options.params.label
     657         * @param {string} options.params.description
     658         * @param {string} options.params.instanceNumber Order in which this instance was created in relation to other instances.
    641659         */
    642660        api.Control = api.Class.extend({
    643661                defaultActiveArguments: { duration: 'fast' },
     
    648666
    649667                        control.params = {};
    650668                        $.extend( control, options || {} );
    651 
    652669                        control.id = id;
    653670                        control.selector = '#customize-control-' + id.replace( /\]/g, '' ).replace( /\[/g, '-' );
    654671                        control.templateSelector = 'customize-control-' + control.params.type + '-content';
     
    724741                },
    725742
    726743                /**
    727                  *
     744                 * Embed the control into the page.
    728745                 */
    729746                embed: function () {
    730747                        var control = this,
     
    754771                },
    755772
    756773                /**
     774                 * Triggered when the control's markup has been injected into the DOM.
     775                 *
    757776                 * @abstract
    758777                 */
    759778                ready: function() {},
     
    867886        });
    868887
    869888        /**
    870          * @constructor
     889         * A colorpicker control.
     890         *
     891         * @class
    871892         * @augments wp.customize.Control
    872893         * @augments wp.customize.Class
    873894         */
     
    893914        });
    894915
    895916        /**
    896          * @constructor
     917         * An upload control, which utilizes the media modal.
     918         *
     919         * @class
    897920         * @augments wp.customize.Control
    898921         * @augments wp.customize.Class
    899922         */
    900923        api.UploadControl = api.Control.extend({
    901                 ready: function() {
    902                         var control = this;
    903 
    904                         this.params.removed = this.params.removed || '';
    905924
    906                         this.success = $.proxy( this.success, this );
    907 
    908                         this.uploader = $.extend({
    909                                 container: this.container,
    910                                 browser:   this.container.find('.upload'),
    911                                 dropzone:  this.container.find('.upload-dropzone'),
    912                                 success:   this.success,
    913                                 plupload:  {},
    914                                 params:    {}
    915                         }, this.uploader || {} );
    916 
    917                         if ( control.params.extensions ) {
    918                                 control.uploader.plupload.filters = [{
    919                                         title:      api.l10n.allowedFiles,
    920                                         extensions: control.params.extensions
    921                                 }];
    922                         }
    923 
    924                         if ( control.params.context )
    925                                 control.uploader.params['post_data[context]'] = this.params.context;
    926 
    927                         if ( api.settings.theme.stylesheet )
    928                                 control.uploader.params['post_data[theme]'] = api.settings.theme.stylesheet;
    929 
    930                         this.uploader = new wp.Uploader( this.uploader );
    931 
    932                         this.remover = this.container.find('.remove');
    933                         this.remover.on( 'click keydown', function( event ) {
    934                                 if ( isKeydownButNotEnterEvent( event ) ) {
    935                                         return;
    936                                 }
    937 
    938                                 control.setting.set( control.params.removed );
    939                                 event.preventDefault();
    940                         });
     925                /**
     926                 * When the control's DOM structure is ready,
     927                 * set up internal event bindings.
     928                 */
     929                ready: function() {
     930                        var control = this;
    941931
    942                         this.removerVisibility = $.proxy( this.removerVisibility, this );
    943                         this.setting.bind( this.removerVisibility );
    944                         this.removerVisibility( this.setting.get() );
    945                 },
    946                 success: function( attachment ) {
    947                         this.setting.set( attachment.get('url') );
     932                        control.setupEvents();
     933                        control.setting.bind( function () { control.reRender(); } );
    948934                },
    949                 removerVisibility: function( to ) {
    950                         this.remover.toggle( to != this.params.removed );
    951                 }
    952         });
    953 
    954         /**
    955          * @constructor
    956          * @augments wp.customize.UploadControl
    957          * @augments wp.customize.Control
    958          * @augments wp.customize.Class
    959          */
    960         api.ImageControl = api.UploadControl.extend({
    961                 ready: function() {
    962                         var control = this,
    963                                 panels;
    964 
    965                         this.uploader = {
    966                                 init: function() {
    967                                         var fallback, button;
    968 
    969                                         if ( this.supports.dragdrop )
    970                                                 return;
    971 
    972                                         // Maintain references while wrapping the fallback button.
    973                                         fallback = control.container.find( '.upload-fallback' );
    974                                         button   = fallback.children().detach();
    975935
    976                                         this.browser.detach().empty().append( button );
    977                                         fallback.append( this.browser ).show();
    978                                 }
    979                         };
     936                /**
     937                 * Set up event bindings.
     938                 */
     939                setupEvents: function() {
     940                        // Cache buttons for re-use.
     941                        this.$button = this.container.find( '.upload-button' );
     942                        this.$img = this.container.find( '.thumbnail-image img' );
     943                        this.$defaultButton = this.container.find( '.default-button' );
     944                        this.$removeButton = this.container.find( '.remove-button' );
     945
     946                        // Shortcut so that we don't have to use _.bind every time we add a callback.
     947                        _.bindAll( this, 'removeFile', 'reRender', 'openFrame', 'select' );
     948
     949                        this.$defaultButton.on( 'click keydown', this.restoreDefault );
     950                        this.$removeButton.on( 'click keydown', this.removeFile );
     951                        this.$button.on( 'click keydown', this.openFrame );
     952                        this.$img.on( 'click keydown', this.openFrame );
     953                },
    980954
    981                         api.UploadControl.prototype.ready.call( this );
     955                /**
     956                 * Open the media modal.
     957                 */
     958                openFrame: function( event ) {
     959                        if ( event.type === 'keydown' &&  13 !== event.which ) { // enter
     960                                return;
     961                        }
    982962
    983                         this.thumbnail    = this.container.find('.preview-thumbnail img');
    984                         this.thumbnailSrc = $.proxy( this.thumbnailSrc, this );
    985                         this.setting.bind( this.thumbnailSrc );
     963                        event.preventDefault();
    986964
    987                         this.library = this.container.find('.library');
     965                        if ( ! this.frame ) {
     966                                this.initFrame();
     967                        }
    988968
    989                         // Generate tab objects
    990                         this.tabs = {};
    991                         panels    = this.library.find('.library-content');
     969                        this.frame.open();
     970                },
    992971
    993                         this.library.children('ul').children('li').each( function() {
    994                                 var link  = $(this),
    995                                         id    = link.data('customizeTab'),
    996                                         panel = panels.filter('[data-customize-tab="' + id + '"]');
     972                /**
     973                 * Create a media modal select frame, and store it so the instance can be reused when needed.
     974                 */
     975                initFrame: function() {
     976                        this.frame = wp.media({
     977                                // The title of the media modal.
     978                                title: this.params.button_labels.frame_title,
    997979
    998                                 control.tabs[ id ] = {
    999                                         both:  link.add( panel ),
    1000                                         link:  link,
    1001                                         panel: panel
    1002                                 };
    1003                         });
     980                                // Restrict the library to specified mime type.
     981                                library: {
     982                                        type: this.params.mime_type
     983                                },
     984                                button: {
     985                                        // Change the submit button label.
     986                                        text: this.params.button_labels.frame_button
     987                                },
     988                                multiple: false
     989                        });
    1004990
    1005                         // Bind tab switch events
    1006                         this.library.children('ul').on( 'click keydown', 'li', function( event ) {
    1007                                 if ( isKeydownButNotEnterEvent( event ) ) {
    1008                                         return;
    1009                                 }
     991                        // When a file is selected, run a callback.
     992                        this.frame.on( 'select', this.select );
     993                },
    1010994
    1011                                 var id  = $(this).data('customizeTab'),
    1012                                         tab = control.tabs[ id ];
     995                /**
     996                 * Callback handler for when an attachment is selected in the media modal.
     997                 * Gets the selected image information, and sets it within the control.
     998                 */
     999                select: function() {
     1000                        // Get the attachment from the modal frame.
     1001                        var attachment = this.frame.state().get( 'selection' ).first().toJSON();
    10131002
    1014                                 event.preventDefault();
     1003                        this.params.attachment = attachment;
    10151004
    1016                                 if ( tab.link.hasClass('library-selected') )
    1017                                         return;
     1005                        // Set the Customizer setting; the callback takes care of rendering.
     1006                        this.setting( attachment.url );
     1007                },
    10181008
    1019                                 control.selected.both.removeClass('library-selected');
    1020                                 control.selected = tab;
    1021                                 control.selected.both.addClass('library-selected');
    1022                         });
     1009                /**
     1010                 * Called on whenever a setting is changed.
     1011                 */
     1012                reRender: function() {
     1013                        this.container.html('');
     1014                        this.renderContent();
     1015                        this.setupEvents();
     1016                        // @todo: something else is needed to preview audio/video files
     1017                },
    10231018
    1024                         // Bind events to switch image urls.
    1025                         this.library.on( 'click keydown', 'a', function( event ) {
    1026                                 if ( isKeydownButNotEnterEvent( event ) ) {
    1027                                         return;
    1028                                 }
     1019                /**
     1020                 * Reset the setting to the default value.
     1021                 *
     1022                 * @todo default button instead of remove that calls this.
     1023                 */
     1024                restoreDefault: function( event ) {
     1025                        if ( event.type === 'keydown' &&  13 !== event.which ) { // enter
     1026                                return;
     1027                        }
    10291028
    1030                                 var value = $(this).data('customizeImageValue');
     1029                        event.preventDefault();
     1030                        this.setting( this.params.defaultAttachment.url );
     1031                        this.params.attachment = this.params.defaultAttachment;
     1032                },
    10311033
    1032                                 if ( value ) {
    1033                                         control.setting.set( value );
    1034                                         event.preventDefault();
    1035                                 }
    1036                         });
     1034                /**
     1035                 * Called when the "Remove" link is clicked. Empties the setting.
     1036                 *
     1037                 * @param {object} event jQuery Event object
     1038                 */
     1039                removeFile: function( event ) {
     1040                        if ( event.type === 'keydown' &&  13 !== event.which ) { // enter
     1041                                return;
     1042                        }
    10371043
    1038                         if ( this.tabs.uploaded ) {
    1039                                 this.tabs.uploaded.target = this.library.find('.uploaded-target');
    1040                                 if ( ! this.tabs.uploaded.panel.find('.thumbnail').length )
    1041                                         this.tabs.uploaded.both.addClass('hidden');
    1042                         }
     1044                        event.preventDefault();
     1045                        this.params.attachment = {};
     1046                        this.setting( '' );
     1047                },
    10431048
    1044                         // Select a tab
    1045                         panels.each( function() {
    1046                                 var tab = control.tabs[ $(this).data('customizeTab') ];
     1049                // @deprecated
     1050                success: function( attachment ) {},
    10471051
    1048                                 // Select the first visible tab.
    1049                                 if ( ! tab.link.hasClass('hidden') ) {
    1050                                         control.selected = tab;
    1051                                         tab.both.addClass('library-selected');
    1052                                         return false;
    1053                                 }
    1054                         });
     1052                // @deprecated
     1053                removerVisibility: function( to ) {}
     1054        });
    10551055
    1056                         this.dropdownInit();
    1057                 },
    1058                 success: function( attachment ) {
    1059                         api.UploadControl.prototype.success.call( this, attachment );
    1060 
    1061                         // Add the uploaded image to the uploaded tab.
    1062                         if ( this.tabs.uploaded && this.tabs.uploaded.target.length ) {
    1063                                 this.tabs.uploaded.both.removeClass('hidden');
    1064 
    1065                                 // @todo: Do NOT store this on the attachment model. That is bad.
    1066                                 attachment.element = $( '<a href="#" class="thumbnail"></a>' )
    1067                                         .data( 'customizeImageValue', attachment.get('url') )
    1068                                         .append( '<img src="' +  attachment.get('url')+ '" />' )
    1069                                         .appendTo( this.tabs.uploaded.target );
    1070                         }
    1071                 },
    1072                 thumbnailSrc: function( to ) {
    1073                         if ( /^(https?:)?\/\//.test( to ) )
    1074                                 this.thumbnail.prop( 'src', to ).show();
    1075                         else
    1076                                 this.thumbnail.hide();
    1077                 }
     1056        /**
     1057         * A control for uploading images.
     1058         *
     1059         * @class
     1060         * @augments wp.customize.UploadControl
     1061         * @augments wp.customize.Control
     1062         * @augments wp.customize.Class
     1063         */
     1064        api.ImageControl = api.UploadControl.extend({
     1065                // @deprecated
     1066                thumbnailSrc: function( to ) {}
    10781067        });
    10791068
    10801069        /**
    1081          * @constructor
     1070         * @class
    10821071         * @augments wp.customize.Control
    10831072         * @augments wp.customize.Class
    10841073         */
     
    12251214                        this.frame.open();
    12261215                },
    12271216
     1217                /**
     1218                 * After an image is selected in the media modal,
     1219                 * switch to the cropper state.
     1220                 */
    12281221                onSelect: function() {
    12291222                        this.frame.setState('cropper');
    12301223                },
     1224
     1225                /**
     1226                 * After the image has been cropped, apply the cropped image data to the setting.
     1227                 *
     1228                 * @param {object} croppedImage Cropped attachment data.
     1229                 */
    12311230                onCropped: function(croppedImage) {
    12321231                        var url = croppedImage.post_content,
    12331232                                attachmentId = croppedImage.attachment_id,
     
    12351234                                h = croppedImage.height;
    12361235                        this.setImageFromURL(url, attachmentId, w, h);
    12371236                },
     1237
     1238                /**
     1239                 * If cropping was skipped, apply the image data directly to the setting.
     1240                 *
     1241                 * @param {object} selection
     1242                 */
    12381243                onSkippedCrop: function(selection) {
    12391244                        var url = selection.get('url'),
    12401245                                w = selection.get('width'),
     
    13011306        api.panel = new api.Values({ defaultConstructor: api.Panel });
    13021307
    13031308        /**
    1304          * @constructor
     1309         * @class
    13051310         * @augments wp.customize.Messenger
    13061311         * @augments wp.customize.Class
    13071312         * @mixes wp.customize.Events
     
    15001505        }());
    15011506
    15021507        /**
    1503          * @constructor
     1508         * @class
    15041509         * @augments wp.customize.Messenger
    15051510         * @augments wp.customize.Class
    15061511         * @mixes wp.customize.Events
  • src/wp-includes/class-wp-customize-control.php

    diff --git src/wp-includes/class-wp-customize-control.php src/wp-includes/class-wp-customize-control.php
    index 7fe3c0f..1286c3a 100644
    class WP_Customize_Color_Control extends WP_Customize_Control { 
    637637 * @since 3.4.0
    638638 */
    639639class WP_Customize_Upload_Control extends WP_Customize_Control {
    640         public $type    = 'upload';
    641         public $removed = '';
    642         public $context;
    643         public $extensions = array();
     640        public $type     = 'upload';
     641        public $mime_type = '';
     642        public $button_labels = array();
     643        public $removed = ''; // unused
     644        public $context; // unused
     645        public $extensions = array(); // unused
     646
     647        /**
     648         * Constructor.
     649         *
     650         * @since 4.1.0
     651         * @uses WP_Customize_Control::__construct()
     652         *
     653         * @param WP_Customize_Manager $manager
     654         */
     655        public function __construct( $manager, $id, $args = array() ) {
     656                parent::__construct( $manager, $id, $args );
     657
     658                $this->button_labels = array(
     659                        'select'       => __( 'Select File' ),
     660                        'change'       => __( 'Change File' ),
     661                        'default'      => __( 'Default' ),
     662                        'remove'       => __( 'Remove' ),
     663                        'placeholder'  => __( 'No file selected' ),
     664                        'frame_title'  => __( 'Select File' ),
     665                        'frame_button' => __( 'Choose File' ),
     666                );
     667        }
    644668
    645669        /**
    646670         * Enqueue control related scripts/styles.
    class WP_Customize_Upload_Control extends WP_Customize_Control { 
    648672         * @since 3.4.0
    649673         */
    650674        public function enqueue() {
    651                 wp_enqueue_script( 'wp-plupload' );
     675                wp_enqueue_media();
    652676        }
    653677
    654678        /**
    class WP_Customize_Upload_Control extends WP_Customize_Control { 
    659683         */
    660684        public function to_json() {
    661685                parent::to_json();
     686                $this->json['mime_type'] = $this->mime_type;
     687                $this->json['button_labels'] = $this->button_labels;
     688
     689                if ( is_object( $this->setting ) ) {
     690                        if ( $this->setting->default ) {
     691                                // @todo: fake an attachment model - needs all fields used by template
     692                                $default_attachment = array(
     693                                        'id' => -1,
     694                                        'url' => $this->setting->default,
     695                                        'type' => ( in_array( substr( $this->setting->default, -3), array( 'jpg', 'png', 'gif', 'bmp' ) ) ) ? 'image' : 'document',
     696                                        'sizes' => array(
     697                                                'full' => $this->setting->default,
     698                                        ),
     699                                        'icon' => 'http://localhost/develop/src/wp-includes/images/media/default.png',//@todo
     700                                        'title' => '', //@todo filename
    662701
    663                 $this->json['removed'] = $this->removed;
     702                                );
     703                                $this->json['defaultAttachment'] = $default_attachment;
     704                        }
    664705
    665                 if ( $this->context )
    666                         $this->json['context'] = $this->context;
     706                        // Get the attachment model for the existing file, or make one.
     707                        if ( $this->value() ) {
     708                                $attachment_id = attachment_url_to_postid( $this->value() );
     709                                if ( $attachment_id ) {
     710                                        $this->json['attachment'] = wp_prepare_attachment_for_js( $attachment_id);
     711                                }
     712                        } elseif ( $this->setting->default ) {
    667713
    668                 if ( $this->extensions )
    669                         $this->json['extensions'] = implode( ',', $this->extensions );
     714                        }
     715                }
    670716        }
    671717
    672718        /**
    673          * Render the control's content.
     719         * Don't render any content for this control from PHP.
    674720         *
     721         * @see WP_Customize_Upload_Control::content_template()
    675722         * @since 3.4.0
    676723         */
    677         public function render_content() {
     724        public function render_content() {}
     725
     726        /**
     727         * Render a JS template for the content of the upload control.
     728         *
     729         * @since 4.1.0
     730         */
     731        public function content_template() {
    678732                ?>
    679                 <label>
    680                         <?php if ( ! empty( $this->label ) ) : ?>
    681                                 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
    682                         <?php endif;
    683                         if ( ! empty( $this->description ) ) : ?>
    684                                 <span class="description customize-control-description"><?php echo $this->description; ?></span>
    685                         <?php endif; ?>
    686                         <div>
    687                                 <a href="#" class="button-secondary upload"><?php _e( 'Upload' ); ?></a>
    688                                 <a href="#" class="remove"><?php _e( 'Remove' ); ?></a>
    689                         </div>
     733                <label for="{{ data.settings.default }}-button">
     734                        <# if ( data.label ) { #>
     735                                <span class="customize-control-title">{{ data.label }}</span>
     736                        <# } #>
     737                        <# if ( data.description ) { #>
     738                                <span class="description customize-control-description">{{ data.description }}</span>
     739                        <# } #>
    690740                </label>
     741
     742                <# if ( data.attachment && data.attachment.id ) { #>
     743                        <div class="attachment-media-view {{ data.attachment.orientation }}">
     744                                <div class="thumbnail thumbnail-{{ data.attachment.type }}">
     745                                        <# if ( 'image' === data.attachment.type && data.attachment.sizes && data.attachment.sizes.medium ) { #>
     746                                                <img class="attachment-thumb" src="{{ data.attachment.sizes.medium.url }}" draggable="false" />
     747                                        <# } else if ( 'image' === data.attachment.type && data.attachment.sizes && data.attachment.sizes.full ) { #>
     748                                                <img class="attachment-thumb" src="{{ data.attachment.sizes.full.url }}" draggable="false" />
     749                                        <# } else if ( -1 === jQuery.inArray( data.attachment.type, [ 'audio', 'video' ] ) ) { #>
     750                                                <img class="attachment-thumb type-icon" src="{{ data.attachment.icon }}" class="icon" draggable="false" />
     751                                                <p class="attachment-title">{{ data.attachment.title }}</p>
     752                                        <# } #>
     753
     754                                        <# if ( 'audio' === data.attachment.type ) { #>
     755                                        <div class="wp-media-wrapper">
     756                                                <p class="attachment-title">{{ data.attachment.title }}</p>
     757                                                <audio style="visibility: hidden" controls class="wp-audio-shortcode" width="100%" preload="none">
     758                                                        <source type="{{ data.attachment.mime }}" src="{{ data.attachment.url }}"/>
     759                                                </audio>
     760                                        </div>
     761                                        <# } else if ( 'video' === data.attachment.type ) {
     762                                                var w_rule = h_rule = '';
     763                                                if ( data.attachment.width ) {
     764                                                        w_rule = 'width: ' + data.attachment.width + 'px;';
     765                                                } else if ( wp.media.view.settings.contentWidth ) {
     766                                                        w_rule = 'width: ' + wp.media.view.settings.contentWidth + 'px;';
     767                                                }
     768                                                if ( data.attachment.height ) {
     769                                                        h_rule = 'height: ' + data.attachment.height + 'px;';
     770                                                }
     771                                                #>
     772                                                <div style="{{ w_rule }}{{ h_rule }}" class="wp-media-wrapper wp-video">
     773                                                        <video controls="controls" class="wp-video-shortcode" preload="metadata"
     774                                                                <# if ( data.attachment.width ) { #>width="{{ data.attachment.width }}"<# } #>
     775                                                                <# if ( data.attachment.height ) { #>height="{{ data.attachment.height }}"<# } #>
     776                                                                <# if ( data.attachment.image && data.attachment.image.src !== data.attachment.icon ) { #>poster="{{ data.attachment.image.src }}"<# } #>>
     777                                                                <source type="{{ data.attachment.mime }}" src="{{ data.attachment.url }}"/>
     778                                                        </video>
     779                                                </div>
     780                                        <# } #>
     781                                </div>
     782                        </div>
     783                        <a class="button upload-button" id="{{ data.settings.default }}-button" href="#"><?php echo $this->button_labels['change']; ?></a>
     784                        <# if ( data.defaultAttachmet && data.defaultAttachment.id !== data.attachment.id ) { #>
     785                                <a class="default-button remove-button" href="#"><?php echo $this->button_labels['default']; ?></a>
     786                                <p>{{ data.defaultAttachment.src }}</p>
     787                        <# } else { #>
     788                                <a class="remove-button" href="#"><?php echo $this->button_labels['remove']; ?></a>
     789                        <# } #>
     790                <# } else { #>
     791                        <p class="placeholder-text"><?php echo $this->button_labels['placeholder']; ?></p>
     792                        <a class="button upload-button" id="{{ data.settings.default }}-button" href="#"><?php echo $this->button_labels['select']; ?></a>
     793                <# } #>
    691794                <?php
    692795        }
    693796}
    class WP_Customize_Upload_Control extends WP_Customize_Control { 
    701804 */
    702805class WP_Customize_Image_Control extends WP_Customize_Upload_Control {
    703806        public $type = 'image';
    704         public $get_url;
    705         public $statuses;
    706         public $extensions = array( 'jpg', 'jpeg', 'gif', 'png' );
    707 
    708         protected $tabs = array();
     807        public $mime_type = 'image';
    709808
    710809        /**
    711810         * Constructor.
    712811         *
    713          * @since 3.4.0
     812         * @since 4.1.0
    714813         * @uses WP_Customize_Upload_Control::__construct()
    715814         *
    716815         * @param WP_Customize_Manager $manager
    717          * @param string $id
    718          * @param array $args
    719816         */
    720         public function __construct( $manager, $id, $args ) {
    721                 $this->statuses = array( '' => __('No Image') );
    722 
     817        public function __construct( $manager, $id, $args = array() ) {
    723818                parent::__construct( $manager, $id, $args );
    724819
    725                 $this->add_tab( 'upload-new', __('Upload New'), array( $this, 'tab_upload_new' ) );
    726                 $this->add_tab( 'uploaded',   __('Uploaded'),   array( $this, 'tab_uploaded' ) );
    727 
    728                 // Early priority to occur before $this->manager->prepare_controls();
    729                 add_action( 'customize_controls_init', array( $this, 'prepare_control' ), 5 );
     820                $this->button_labels = array(
     821                        'select'       => __( 'Select Image' ),
     822                        'change'       => __( 'Change Image' ),
     823                        'remove'       => __( 'Remove' ),
     824                        'default'      => __( 'Default' ),
     825                        'placeholder'  => __( 'No image selected' ),
     826                        'frame_title'  => __( 'Select Image' ),
     827                        'frame_button' => __( 'Choose Image' ),
     828                );
    730829        }
    731830
    732831        /**
    733          * Prepares the control.
    734          *
    735          * If no tabs exist, removes the control from the manager.
    736          *
    737832         * @since 3.4.2
     833         * @deprecated 4.1.0
    738834         */
    739         public function prepare_control() {
    740                 if ( ! $this->tabs )
    741                         $this->manager->remove_control( $this->id );
    742         }
     835        public function prepare_control() {}
    743836
    744837        /**
    745          * Refresh the parameters passed to the JavaScript via JSON.
    746          *
    747          * @since 3.4.0
    748          * @uses WP_Customize_Upload_Control::to_json()
    749          */
    750         public function to_json() {
    751                 parent::to_json();
    752                 $this->json['statuses'] = $this->statuses;
    753         }
    754 
    755         /**
    756          * Render the control's content.
    757          *
    758          * @since 3.4.0
    759          */
    760         public function render_content() {
    761                 $src = $this->value();
    762                 if ( isset( $this->get_url ) )
    763                         $src = call_user_func( $this->get_url, $src );
    764 
    765                 ?>
    766                 <div class="customize-image-picker">
    767                         <?php if ( ! empty( $this->label ) ) : ?>
    768                                 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
    769                         <?php endif;
    770                         if ( ! empty( $this->description ) ) : ?>
    771                                 <span class="description customize-control-description"><?php echo $this->description; ?></span>
    772                         <?php endif; ?>
    773 
    774                         <div class="customize-control-content">
    775                                 <div class="dropdown preview-thumbnail" tabindex="0">
    776                                         <div class="dropdown-content">
    777                                                 <?php if ( empty( $src ) ): ?>
    778                                                         <img style="display:none;" />
    779                                                 <?php else: ?>
    780                                                         <img src="<?php echo esc_url( set_url_scheme( $src ) ); ?>" />
    781                                                 <?php endif; ?>
    782                                                 <div class="dropdown-status"></div>
    783                                         </div>
    784                                         <div class="dropdown-arrow"></div>
    785                                 </div>
    786                         </div>
    787 
    788                         <div class="library">
    789                                 <ul>
    790                                         <?php foreach ( $this->tabs as $id => $tab ): ?>
    791                                                 <li data-customize-tab='<?php echo esc_attr( $id ); ?>' tabindex='0'>
    792                                                         <?php echo esc_html( $tab['label'] ); ?>
    793                                                 </li>
    794                                         <?php endforeach; ?>
    795                                 </ul>
    796                                 <?php foreach ( $this->tabs as $id => $tab ): ?>
    797                                         <div class="library-content" data-customize-tab='<?php echo esc_attr( $id ); ?>'>
    798                                                 <?php call_user_func( $tab['callback'] ); ?>
    799                                         </div>
    800                                 <?php endforeach; ?>
    801                         </div>
    802 
    803                         <div class="actions">
    804                                 <a href="#" class="remove"><?php _e( 'Remove Image' ); ?></a>
    805                         </div>
    806                 </div>
    807                 <?php
    808         }
    809 
    810         /**
    811          * Add a tab to the control.
    812          *
    813838         * @since 3.4.0
     839         * @deprecated 4.1.0
    814840         *
    815841         * @param string $id
    816842         * @param string $label
    817843         * @param mixed $callback
    818844         */
    819         public function add_tab( $id, $label, $callback ) {
    820                 $this->tabs[ $id ] = array(
    821                         'label'    => $label,
    822                         'callback' => $callback,
    823                 );
    824         }
     845        public function add_tab( $id, $label, $callback ) {}
    825846
    826847        /**
    827          * Remove a tab from the control.
    828          *
    829848         * @since 3.4.0
     849         * @deprecated 4.1.0
    830850         *
    831851         * @param string $id
    832852         */
    833         public function remove_tab( $id ) {
    834                 unset( $this->tabs[ $id ] );
    835         }
    836 
    837         /**
    838          * @since 3.4.0
    839          */
    840         public function tab_upload_new() {
    841                 if ( ! _device_can_upload() ) {
    842                         echo '<p>' . sprintf( __('The web browser on your device cannot be used to upload files. You may be able to use the <a href="%s">native app for your device</a> instead.'), 'https://apps.wordpress.org/' ) . '</p>';
    843                 } else {
    844                         ?>
    845                         <div class="upload-dropzone">
    846                                 <?php _e('Drop a file here or <a href="#" class="upload">select a file</a>.'); ?>
    847                         </div>
    848                         <div class="upload-fallback">
    849                                 <span class="button-secondary"><?php _e('Select File'); ?></span>
    850                         </div>
    851                         <?php
    852                 }
    853         }
    854 
    855         /**
    856          * @since 3.4.0
    857          */
    858         public function tab_uploaded() {
    859                 ?>
    860                 <div class="uploaded-target"></div>
    861                 <?php
    862         }
     853        public function remove_tab( $id ) {}
    863854
    864855        /**
    865856         * @since 3.4.0
     857         * @deprecated 4.1.0
    866858         *
    867859         * @param string $url
    868860         * @param string $thumbnail_url
    869861         */
    870         public function print_tab_image( $url, $thumbnail_url = null ) {
    871                 $url = set_url_scheme( $url );
    872                 $thumbnail_url = ( $thumbnail_url ) ? set_url_scheme( $thumbnail_url ) : $url;
    873                 ?>
    874                 <a href="#" class="thumbnail" data-customize-image-value="<?php echo esc_url( $url ); ?>">
    875                         <img src="<?php echo esc_url( $thumbnail_url ); ?>" />
    876                 </a>
    877                 <?php
    878         }
     862        public function print_tab_image( $url, $thumbnail_url = null ) {}
    879863}
    880864
    881865/**
    class WP_Customize_Background_Image_Control extends WP_Customize_Image_Control { 
    899883                parent::__construct( $manager, 'background_image', array(
    900884                        'label'    => __( 'Background Image' ),
    901885                        'section'  => 'background_image',
    902                         'context'  => 'custom-background',
    903                         'get_url'  => 'get_background_image',
    904886                ) );
    905 
    906                 if ( $this->setting->default )
    907                         $this->add_tab( 'default',  __('Default'),  array( $this, 'tab_default_background' ) );
    908         }
    909 
    910         /**
    911          * @since 3.4.0
    912          */
    913         public function tab_uploaded() {
    914                 $backgrounds = get_posts( array(
    915                         'post_type'  => 'attachment',
    916                         'meta_key'   => '_wp_attachment_is_custom_background',
    917                         'meta_value' => $this->manager->get_stylesheet(),
    918                         'orderby'    => 'none',
    919                         'nopaging'   => true,
    920                 ) );
    921 
    922                 ?><div class="uploaded-target"></div><?php
    923 
    924                 if ( empty( $backgrounds ) )
    925                         return;
    926 
    927                 foreach ( (array) $backgrounds as $background )
    928                         $this->print_tab_image( esc_url_raw( $background->guid ) );
    929         }
    930 
    931         /**
    932          * @since 3.4.0
    933          * @uses WP_Customize_Image_Control::print_tab_image()
    934          */
    935         public function tab_default_background() {
    936                 $this->print_tab_image( $this->setting->default );
    937887        }
    938888}
    939889
  • src/wp-includes/class-wp-customize-manager.php

    diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php
    index 987edc6..45ad34f 100644
    final class WP_Customize_Manager { 
    965965
    966966                /* Control Types (custom control classes) */
    967967                $this->register_control_type( 'WP_Customize_Color_Control' );
     968                $this->register_control_type( 'WP_Customize_Upload_Control' );
     969                $this->register_control_type( 'WP_Customize_Image_Control' );
    968970
    969971                /* Site Title & Tagline */
    970972