Index: src/wp-admin/css/customize-controls.css =================================================================== --- src/wp-admin/css/customize-controls.css (revision 30022) +++ src/wp-admin/css/customize-controls.css (working copy) @@ -10,6 +10,10 @@ font-size: 14px; } +#customize-controls img { + max-width: 100%; +} + #customize-controls .submit { text-align: center; } @@ -414,6 +418,21 @@ margin-right: 5px; } +.customize-control .attachment-thumb.type-icon { + float: left; + margin-right: 12px; +} + +.customize-control .attachment-title { + font-weight: bold; + margin: 0 0 12px 0; +} + +.customize-control .remove-button { + margin-left: 8px; + vertical-align: middle; +} + #customize-preview iframe { width: 100%; height: 100%; Index: src/wp-admin/js/customize-controls.js =================================================================== --- src/wp-admin/js/customize-controls.js (revision 30022) +++ src/wp-admin/js/customize-controls.js (working copy) @@ -200,56 +200,106 @@ * @augments wp.customize.Class */ api.UploadControl = api.Control.extend({ + + /** + * Setup control and do event bindings. + */ ready: function() { - var control = this; + + // Cache buttons for re-use. + this.$button = this.container.find( '.upload-button' ); + this.$img = this.container.find( '.thumbnail-image img' ); + this.$removeButton = this.container.find( '.remove-button' ); - this.params.removed = this.params.removed || ''; + // Shortcut so that we don't have to use _.bind every time we add a callback. + _.bindAll( this, 'removeFile', 'reRender', 'upload', 'select' ); - this.success = $.proxy( this.success, this ); + // Ensure clicking "remove" removes the file. + this.$removeButton.on( 'click', this.removeFile ); - this.uploader = $.extend({ - container: this.container, - browser: this.container.find('.upload'), - dropzone: this.container.find('.upload-dropzone'), - success: this.success, - plupload: {}, - params: {} - }, this.uploader || {} ); + // Bind upload button, and image (if it exists). + this.$button.on( 'click keydown', this.upload ); + this.$img.on( 'click keydown', this.upload ); + }, - if ( control.params.extensions ) { - control.uploader.plupload.filters = [{ - title: api.l10n.allowedFiles, - extensions: control.params.extensions - }]; + /** + * Remember that _.bind was used to maintain `this` as the control + * object rather than the usual jQuery way of binding to the DOM element. + */ + upload: function( event ) { + event.preventDefault(); + + if ( ! this.frame ) { + this.initFrame(); } - if ( control.params.context ) - control.uploader.params['post_data[context]'] = this.params.context; + this.frame.open(); + }, - if ( api.settings.theme.stylesheet ) - control.uploader.params['post_data[theme]'] = api.settings.theme.stylesheet; + /** + * Set the media frame so that it can be reused and accessed when needed. + */ + initFrame: function() { + this.frame = wp.media({ + // The title of the media modal + title: this.params.button_labels.frame_title, - this.uploader = new wp.Uploader( this.uploader ); + // Restrict to specified mime type. + // @todo try to map $extensions to this in PHP. + library: { + type: this.params.mime_type + }, + button: { + // Change the submit button label. + text: this.params.button_labels.frame_button + }, + multiple: false + }); - this.remover = this.container.find('.remove'); - this.remover.on( 'click keydown', function( event ) { - if ( event.type === 'keydown' && 13 !== event.which ) // enter - return; + // When a file is selected, run a callback. + this.frame.on( 'select', this.select ); + }, - control.setting.set( control.params.removed ); - event.preventDefault(); - }); + /** + * Fired when an image is selected in the media modal. Gets the selected + * image information, and sets it within the control. + */ + select: function() { + // Get the attachment from the modal frame. + var attachment = this.frame.state().get( 'selection' ).first().toJSON(); - this.removerVisibility = $.proxy( this.removerVisibility, this ); - this.setting.bind( this.removerVisibility ); - this.removerVisibility( this.setting.get() ); + this.params.attachment = attachment; + + // Set the Customizer setting; the callback takes care of rendering. + this.setting( attachment.url ); + this.reRender(); }, - success: function( attachment ) { - this.setting.set( attachment.get('url') ); + + /** + * Called on whenever a setting is changed. + * + */ + reRender: function() { + // @todo: something else is needed to preview audio/video files + var self = this; + self.container.html(''); + self.renderContent( function() { + // Don't call ready() until the content has rendered. + self.ready(); + } ); }, - removerVisibility: function( to ) { - this.remover.toggle( to != this.params.removed ); - } + + + /** + * Called when the "Remove" link is clicked. Empties the setting. + * @param {object} event jQuery Event object from click event + */ + removeFile: function( event ) { + event.preventDefault(); + this.setting( '' ); // @todo maybe set to default instead + this.params.attachment = {}; + this.reRender(); + } }); /** Index: src/wp-includes/class-wp-customize-control.php =================================================================== --- src/wp-includes/class-wp-customize-control.php (revision 30022) +++ src/wp-includes/class-wp-customize-control.php (working copy) @@ -580,10 +580,18 @@ * @since 3.4.0 */ class WP_Customize_Upload_Control extends WP_Customize_Control { - public $type = 'upload'; - public $removed = ''; - public $context; - public $extensions = array(); + public $type = 'upload'; + public $mime_type = ''; + public $button_labels = array( + 'select' => 'Select File', // @todo PHP doesn't like letting us translate these + 'change' => 'Change File', + 'remove' => 'Remove', + 'frame_title' => 'Select File', + 'frame_button' => 'Choose File', + ); + public $removed = ''; // unused + public $context; // unused + public $extensions = array(); // unused /** * Enqueue control related scripts/styles. @@ -591,7 +599,7 @@ * @since 3.4.0 */ public function enqueue() { - wp_enqueue_script( 'wp-plupload' ); + wp_enqueue_media(); } /** @@ -602,34 +610,86 @@ */ public function to_json() { parent::to_json(); + $this->json['mime_type'] = $this->mime_type; + $this->json['button_labels'] = $this->button_labels; +// $this->json['attachment'] = array(); - $this->json['removed'] = $this->removed; + // Get the attachment model for the existing file, or make one. +/* if ( $this->setting ) { + $file_url = $this->setting->default; + $attachment_id = attachment_url_to_postid( $file_url ); + $this->json['post_id'] = $attachment_id; + } +*/ } - if ( $this->context ) - $this->json['context'] = $this->context; - - if ( $this->extensions ) - $this->json['extensions'] = implode( ',', $this->extensions ); - } - /** - * Render the control's content. + * Don't render any content for this control from PHP. * + * @see WP_Customize_Upload_Control::content_template() * @since 3.4.0 */ - public function render_content() { + public function render_content() {} + + /** + * Render a JS template for the content of the upload control. + * + * @since 4.1.0 + */ + public function content_template() { ?> 'Select Image', // @todo PHP doesn't like letting us translate these + 'change' => 'Change Image', + 'remove' => 'Remove', + 'frame_title' => 'Select Image', + 'frame_button' => 'Choose Image', + ); - protected $tabs = array(); - /** - * Constructor. - * - * @since 3.4.0 - * @uses WP_Customize_Upload_Control::__construct() - * - * @param WP_Customize_Manager $manager - * @param string $id - * @param array $args - */ - public function __construct( $manager, $id, $args ) { - $this->statuses = array( '' => __('No Image') ); - - parent::__construct( $manager, $id, $args ); - - $this->add_tab( 'upload-new', __('Upload New'), array( $this, 'tab_upload_new' ) ); - $this->add_tab( 'uploaded', __('Uploaded'), array( $this, 'tab_uploaded' ) ); - - // Early priority to occur before $this->manager->prepare_controls(); - add_action( 'customize_controls_init', array( $this, 'prepare_control' ), 5 ); - } - - /** - * Prepares the control. - * - * If no tabs exist, removes the control from the manager. - * - * @since 3.4.2 - */ - public function prepare_control() { - if ( ! $this->tabs ) - $this->manager->remove_control( $this->id ); - } - - /** - * Refresh the parameters passed to the JavaScript via JSON. - * - * @since 3.4.0 - * @uses WP_Customize_Upload_Control::to_json() - */ - public function to_json() { - parent::to_json(); - $this->json['statuses'] = $this->statuses; - } - - /** - * Render the control's content. - * - * @since 3.4.0 - */ - public function render_content() { - $src = $this->value(); - if ( isset( $this->get_url ) ) - $src = call_user_func( $this->get_url, $src ); - - ?> -
' . sprintf( __('The web browser on your device cannot be used to upload files. You may be able to use the native app for your device instead.'), 'https://apps.wordpress.org/' ) . '
'; - } else { - ?> -