diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css
index c76a6e5fa3..448f1b5593 100644
--- a/src/wp-admin/css/customize-controls.css
+++ b/src/wp-admin/css/customize-controls.css
@@ -455,6 +455,167 @@ body {
-webkit-overflow-scrolling: touch;
}
+/** Header control **/
+
+#customize-control-header_image .current {
+ margin-bottom: 8px;
+}
+
+#customize-control-header_image .uploaded {
+ margin-bottom: 18px;
+}
+
+/* Header control: current image container */
+
+#customize-control-header_image .current .container {
+ overflow: hidden;
+ border-radius: 2px;
+}
+
+#customize-control-header_image .placeholder {
+ width: 100%;
+ position: relative;
+ background: #262626;
+ text-align: center;
+ cursor: default;
+}
+
+#customize-control-header_image .inner {
+ display: none;
+ position: absolute;
+ width: 100%;
+ height: 18px;
+ margin-top: -9px;
+ top: 50%;
+ color: #eee;
+}
+
+/* Header control: overlay "close" button */
+
+#customize-control-header_image .header-view {
+ position: relative;
+}
+
+#customize-control-header_image .uploaded .header-view .close {
+ font-size: 2em;
+ color: grey;
+ position: absolute;
+ visibility: hidden;
+ top: 10px;
+ right: 10px;
+ z-index: 1;
+ width: 20px;
+ height: 20px;
+ cursor: pointer;
+}
+
+#customize-control-header_image .uploaded .header-view .close:hover {
+ color: black;
+ text-shadow:
+ -1px -1px 0 #fff,
+ 1px -1px 0 #fff,
+ -1px 1px 0 #fff,
+ 1px 1px 0 #fff;
+}
+
+#customize-control-header_image .header-view:hover .close {
+ visibility: visible;
+}
+
+/* Header control: randomiz(s)er */
+
+#customize-control-header_image .random.placeholder {
+ cursor: pointer;
+ border-radius: 2px;
+ height: 40px;
+}
+
+#customize-control-header_image .random .inner {
+ display: block;
+}
+
+#customize-control-header_image .dice {
+ font-size: 16px;
+ vertical-align: -1px;
+}
+
+#customize-control-header_image .placeholder:hover .dice {
+ -webkit-animation: dice-color-change 3s infinite;
+ -moz-animation: dice-color-change 3s infinite;
+ -ms-animation: dice-color-change 3s infinite;
+ animation: dice-color-change 3s infinite;
+}
+
+@-webkit-keyframes dice-color-change {
+ 0% { color: #d4b146; }
+ 50% { color: #ef54b0; }
+ 75% { color: #7190d3; }
+ 100% { color: #d4b146; }
+}
+
+@-moz-keyframes dice-color-change {
+ 0% { color: #d4b146; }
+ 50% { color: #ef54b0; }
+ 75% { color: #7190d3; }
+ 100% { color: #d4b146; }
+}
+
+@-ms-keyframes dice-color-change {
+ 0% { color: #d4b146; }
+ 50% { color: #ef54b0; }
+ 75% { color: #7190d3; }
+ 100% { color: #d4b146; }
+}
+
+@keyframes dice-color-change {
+ 0% { color: #d4b146; }
+ 50% { color: #ef54b0; }
+ 75% { color: #7190d3; }
+ 100% { color: #d4b146; }
+}
+
+/* Header control: actions and choices */
+
+#customize-control-header_image .actions {
+ margin-bottom: 32px;
+}
+
+#customize-control-header_image .choice {
+ position: relative;
+ display: block;
+ margin-bottom: 9px;
+}
+
+#customize-control-header_image .choice.random:before {
+ position: absolute;
+ content: attr(data-label);
+ left: 0;
+ top: 0;
+}
+
+#customize-control-header_image .uploaded div:last-child > .choice {
+ margin-bottom: 0;
+}
+
+#customize-control-header_image .choices hr {
+ visibility: hidden;
+}
+
+#customize-control-header_image img {
+ width: 100%;
+ border-radius: 2px;
+}
+
+#customize-control-header_image .remove {
+ float: left;
+ margin-right: 3px;
+}
+
+#customize-control-header_image .new {
+ float: right;
+}
+
+
/** Handle cheaters. */
body.cheatin {
min-width: 0;
diff --git a/src/wp-admin/custom-header.php b/src/wp-admin/custom-header.php
index 1a3db1a4a5..07c5dd8101 100644
--- a/src/wp-admin/custom-header.php
+++ b/src/wp-admin/custom-header.php
@@ -43,7 +43,7 @@ class Custom_Image_Header {
var $default_headers = array();
/**
- * Holds custom headers uploaded by the user
+ * Holds custom headers uploaded by the user.
*
* @var array
* @since 3.2.0
@@ -73,6 +73,11 @@ class Custom_Image_Header {
$this->admin_image_div_callback = $admin_image_div_callback;
add_action( 'admin_menu', array( $this, 'init' ) );
+
+ add_action( 'customize_save_after', array( $this, 'customize_set_last_used' ) );
+ add_action( 'wp_ajax_custom-header-crop', array( $this, 'ajax_header_crop' ) );
+ add_action( 'wp_ajax_custom-header-add', array( $this, 'ajax_header_add' ) );
+ add_action( 'wp_ajax_custom-header-remove', array( $this, 'ajax_header_remove' ) );
}
/**
@@ -93,6 +98,7 @@ class Custom_Image_Header {
add_action("admin_head-$page", array($this, 'js'), 50);
if ( $this->admin_header_callback )
add_action("admin_head-$page", $this->admin_header_callback, 51);
+
}
/**
@@ -819,32 +825,15 @@ wp_nonce_field( 'custom-header-options', '_wpnonce-custom-header-options' ); ?>
$attachment_id = absint( $_POST['attachment_id'] );
$original = get_attached_file($attachment_id);
-
- $max_width = 0;
- // For flex, limit size of image displayed to 1500px unless theme says otherwise
- if ( current_theme_supports( 'custom-header', 'flex-width' ) )
- $max_width = 1500;
-
- if ( current_theme_supports( 'custom-header', 'max-width' ) )
- $max_width = max( $max_width, get_theme_support( 'custom-header', 'max-width' ) );
- $max_width = max( $max_width, get_theme_support( 'custom-header', 'width' ) );
-
- if ( ( current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' ) ) || $_POST['width'] > $max_width )
- $dst_height = absint( $_POST['height'] * ( $max_width / $_POST['width'] ) );
- elseif ( current_theme_supports( 'custom-header', 'flex-height' ) && current_theme_supports( 'custom-header', 'flex-width' ) )
- $dst_height = absint( $_POST['height'] );
- else
- $dst_height = get_theme_support( 'custom-header', 'height' );
-
- if ( ( current_theme_supports( 'custom-header', 'flex-width' ) && ! current_theme_supports( 'custom-header', 'flex-height' ) ) || $_POST['width'] > $max_width )
- $dst_width = absint( $_POST['width'] * ( $max_width / $_POST['width'] ) );
- elseif ( current_theme_supports( 'custom-header', 'flex-width' ) && current_theme_supports( 'custom-header', 'flex-height' ) )
- $dst_width = absint( $_POST['width'] );
- else
- $dst_width = get_theme_support( 'custom-header', 'width' );
+ $dimensions = $this->get_header_dimensions( array(
+ 'height' => $_POST['height'],
+ 'width' => $_POST['width'],
+ ) );
+ $height = $dimensions['dst_height'];
+ $width = $dimensions['dst_width'];
if ( empty( $_POST['skip-cropping'] ) )
- $cropped = wp_crop_image( $attachment_id, (int) $_POST['x1'], (int) $_POST['y1'], (int) $_POST['width'], (int) $_POST['height'], $dst_width, $dst_height );
+ $cropped = wp_crop_image( $attachment_id, (int) $_POST['x1'], (int) $_POST['y1'], (int) $_POST['width'], (int) $_POST['height'], $width, $height );
elseif ( ! empty( $_POST['create-new-attachment'] ) )
$cropped = _copy_image_file( $attachment_id );
else
@@ -856,31 +845,15 @@ wp_nonce_field( 'custom-header-options', '_wpnonce-custom-header-options' ); ?>
/** This filter is documented in wp-admin/custom-header.php */
$cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication
- $parent = get_post($attachment_id);
- $parent_url = $parent->guid;
- $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
+ $object = $this->create_attachment_object( $cropped, $attachment_id );
- $size = @getimagesize( $cropped );
- $image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
-
- // Construct the object array
- $object = array(
- 'ID' => $attachment_id,
- 'post_title' => basename($cropped),
- 'post_content' => $url,
- 'post_mime_type' => $image_type,
- 'guid' => $url,
- 'context' => 'custom-header'
- );
if ( ! empty( $_POST['create-new-attachment'] ) )
unset( $object['ID'] );
// Update the attachment
- $attachment_id = wp_insert_attachment( $object, $cropped );
- wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $cropped ) );
+ $attachment_id = $this->insert_attachment( $object, $cropped );
- $width = $dst_width;
- $height = $dst_height;
+ $url = $object['guid'];
$this->set_header_image( compact( 'url', 'attachment_id', 'width', 'height' ) );
// cleanup
@@ -1041,4 +1014,218 @@ wp_nonce_field( 'custom-header-options', '_wpnonce-custom-header-options' ); ?>
set_theme_mod( 'header_image', $default );
set_theme_mod( 'header_image_data', (object) $default_data );
}
+
+ /**
+ * Calculate width and height based on what the currently selected theme supports.
+ *
+ * @return array dst_height and dst_width of header image.
+ */
+ final public function get_header_dimensions( $dimensions ) {
+ $max_width = 0;
+ $width = absint( $dimensions['width'] );
+ $height = absint( $dimensions['height'] );
+ $theme_height = get_theme_support( 'custom-header', 'height' );
+ $theme_width = get_theme_support( 'custom-header', 'width' );
+ $has_flex_width = current_theme_supports( 'custom-header', 'flex-width' );
+ $has_flex_height = current_theme_supports( 'custom-header', 'flex-height' );
+ $has_max_width = current_theme_supports( 'custom-header', 'max-width' ) ;
+ $dst = array( 'dst_height' => null, 'dst_height' => null );
+
+ // For flex, limit size of image displayed to 1500px unless theme says otherwise
+ if ( $has_flex_width ) {
+ $max_width = 1500;
+ }
+
+ if ( $has_max_width ) {
+ $max_width = max( $max_width, get_theme_support( 'custom-header', 'max-width' ) );
+ }
+ $max_width = max( $max_width, $theme_width );
+
+ if ( $has_flex_height && ( ! $has_flex_width || $width > $max_width ) ) {
+ $dst['dst_height'] = absint( $height * ( $max_width / $width ) );
+ }
+ elseif ( $has_flex_height && $has_flex_width ) {
+ $dst['dst_height'] = $height;
+ }
+ else {
+ $dst['dst_height'] = $theme_height;
+ }
+
+ if ( $has_flex_width && ( ! $has_flex_height || $width > $max_width ) ) {
+ $dst['dst_width'] = absint( $width * ( $max_width / $width ) );
+ }
+ elseif ( $has_flex_width && $has_flex_height ) {
+ $dst['dst_width'] = $width;
+ }
+ else {
+ $dst['dst_width'] = $theme_width;
+ }
+
+ return $dst;
+ }
+
+ /**
+ * Create an attachment 'object'.
+ *
+ * @param string $cropped Cropped image URL.
+ * @param int $parent_attachment_id Attachment ID of parent image.
+ *
+ * @return array Attachment object.
+ */
+ final public function create_attachment_object( $cropped, $parent_attachment_id ) {
+ $parent = get_post( $parent_attachment_id );
+ $parent_url = $parent->guid;
+ $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
+
+ $size = @getimagesize( $cropped );
+ $image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
+
+ $object = array(
+ 'ID' => $parent_attachment_id,
+ 'post_title' => basename($cropped),
+ 'post_content' => $url,
+ 'post_mime_type' => $image_type,
+ 'guid' => $url,
+ 'context' => 'custom-header'
+ );
+
+ return $object;
+ }
+
+ /**
+ * Insert an attachment & its metadata.
+ *
+ * @param array $object Attachment object.
+ * @param string $cropped Cropped image URL.
+ *
+ * @return int Attachment ID.
+ */
+ final public function insert_attachment( $object, $cropped ) {
+ $attachment_id = wp_insert_attachment( $object, $cropped );
+ $metadata = wp_generate_attachment_metadata( $attachment_id, $cropped );
+ /**
+ * Allows us to insert custom meta data for an attachment.
+ *
+ */
+ $metadata = apply_filters( 'wp_header_image_attachment_metadata', $metadata );
+ wp_update_attachment_metadata( $attachment_id, $metadata );
+ return $attachment_id;
+ }
+
+ /**
+ * Gets attachment uploaded by Media Manager, crops it, then saves it as a
+ * new object. Returns JSON-encoded object details.
+ */
+ function ajax_header_crop() {
+ check_ajax_referer( 'image_editor-' . $_POST['id'], 'nonce' );
+
+ if ( ! current_user_can( 'edit_theme_options' ) ) {
+ wp_send_json_error();
+ }
+
+ if ( ! current_theme_supports( 'custom-header', 'uploads' ) ) {
+ wp_send_json_error();
+ }
+
+ $crop_details = $_POST['cropDetails'];
+
+ $dimensions = $this->get_header_dimensions( array(
+ 'height' => $crop_details['height'],
+ 'width' => $crop_details['width'],
+ ) );
+
+ $attachment_id = absint( $_POST['id'] );
+
+ $cropped = wp_crop_image(
+ $attachment_id,
+ (int) $crop_details['x1'],
+ (int) $crop_details['y1'],
+ (int) $crop_details['width'],
+ (int) $crop_details['height'],
+ (int) $dimensions['dst_width'],
+ (int) $dimensions['dst_height']
+ );
+
+ if ( ! $cropped || is_wp_error( $cropped ) ) {
+ wp_send_json_error( array( 'message' => __( 'Image could not be processed. Please go back and try again.' ) ) );
+ }
+
+ $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication
+
+ $object = $this->create_attachment_object( $cropped, $attachment_id );
+
+ unset( $object['ID'] );
+
+ $new_attachment_id = $this->insert_attachment( $object, $cropped );
+
+ $object['attachment_id'] = $new_attachment_id;
+ $object['width'] = $dimensions['dst_width'];
+ $object['height'] = $dimensions['dst_height'];
+
+ wp_send_json_success( $object );
+ }
+
+ /**
+ * Given an attachment ID for a header image, updates its "last used"
+ * timestamp to now.
+ *
+ * Triggered when the user tries adds a new header image from the
+ * Media Manager, even if s/he doesn't save that change.
+ */
+ function ajax_header_add() {
+ check_ajax_referer( 'header-add', 'nonce' );
+
+ if ( ! current_user_can( 'edit_theme_options' ) ) {
+ wp_send_json_error();
+ }
+
+ $attachment_id = absint( $_POST['attachment_id'] );
+ if ( $attachment_id < 1 ) {
+ wp_send_json_error();
+ }
+
+ $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
+ update_post_meta( $attachment_id, $key, time() );
+ update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', get_stylesheet() );
+
+ wp_send_json_success();
+ }
+
+ /**
+ * Given an attachment ID for a header image, unsets it as a user-uploaded
+ * header image for the current theme.
+ *
+ * Triggered when the user clicks the overlay "X" button next to each image
+ * choice in the Customizer's Header tool.
+ */
+ function ajax_header_remove() {
+ check_ajax_referer( 'header-remove', 'nonce' );
+
+ if ( ! current_user_can( 'edit_theme_options' ) ) {
+ wp_send_json_error();
+ }
+
+ $attachment_id = absint( $_POST['attachment_id'] );
+ if ( $attachment_id < 1 ) {
+ wp_send_json_error();
+ }
+
+ $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
+ delete_post_meta( $attachment_id, $key );
+ delete_post_meta( $attachment_id, '_wp_attachment_is_custom_header', get_stylesheet() );
+
+ wp_send_json_success();
+ }
+
+ function customize_set_last_used( $wp_customize ) {
+ $data = $wp_customize->get_setting( 'header_image_data' )->post_value();
+
+ if ( ! isset( $data['attachment_id'] ) ) {
+ return;
+ }
+
+ $attachment_id = $data['attachment_id'];
+ $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
+ update_post_meta( $attachment_id, $key, time() );
+ }
}
diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js
index 3a05ad437f..3bd7b414a1 100644
--- a/src/wp-admin/js/customize-controls.js
+++ b/src/wp-admin/js/customize-controls.js
@@ -1,3 +1,4 @@
+/* globals _wpCustomizeHeader, _wpMediaViewsL10n */
(function( exports, $ ){
var api = wp.customize;
@@ -306,6 +307,217 @@
}
});
+ api.HeaderControl = api.Control.extend({
+ ready: function() {
+ this.btnRemove = $('.actions .remove');
+ this.btnNew = $('.actions .new');
+
+ _.bindAll(this, 'openMedia', 'removeImage');
+
+ this.btnNew.on( 'click', this.openMedia );
+ this.btnRemove.on( 'click', this.removeImage );
+
+ api.HeaderTool.currentHeader = new api.HeaderTool.ImageModel();
+
+ new api.HeaderTool.CurrentView({
+ model: api.HeaderTool.currentHeader,
+ el: '.current .container'
+ });
+
+ new api.HeaderTool.ChoiceListView({
+ collection: api.HeaderTool.UploadsList = new api.HeaderTool.ChoiceList(),
+ el: '.choices .uploaded .list'
+ });
+
+ new api.HeaderTool.ChoiceListView({
+ collection: api.HeaderTool.DefaultsList = new api.HeaderTool.DefaultsList(),
+ el: '.choices .default .list'
+ });
+
+ api.HeaderTool.combinedList = api.HeaderTool.CombinedList = new api.HeaderTool.CombinedList([
+ api.HeaderTool.UploadsList,
+ api.HeaderTool.DefaultsList
+ ]);
+ },
+
+ /**
+ * Returns a set of options, computed from the attached image data and
+ * theme-specific data, to be fed to the imgAreaSelect plugin in
+ * wp.media.view.Cropper.
+ *
+ * @param {wp.media.model.Attachment} attachment
+ * @param {wp.media.controller.Cropper} controller
+ * @returns {Object} Options
+ */
+ calculateImageSelectOptions: function(attachment, controller) {
+ var xInit = parseInt(_wpCustomizeHeader.data.width, 10),
+ yInit = parseInt(_wpCustomizeHeader.data.height, 10),
+ flexWidth = !! parseInt(_wpCustomizeHeader.data['flex-width'], 10),
+ flexHeight = !! parseInt(_wpCustomizeHeader.data['flex-height'], 10),
+ ratio, xImg, yImg, realHeight, realWidth,
+ imgSelectOptions;
+
+ realWidth = attachment.get('width');
+ realHeight = attachment.get('height');
+
+ this.headerImage = new api.HeaderTool.ImageModel();
+ this.headerImage.set({
+ themeWidth: xInit,
+ themeHeight: yInit,
+ themeFlexWidth: flexWidth,
+ themeFlexHeight: flexHeight,
+ imageWidth: realWidth,
+ imageHeight: realHeight
+ });
+
+ controller.set( 'canSkipCrop', ! this.headerImage.shouldBeCropped() );
+
+ ratio = xInit / yInit;
+ xImg = realWidth;
+ yImg = realHeight;
+
+ if ( xImg / yImg > ratio ) {
+ yInit = yImg;
+ xInit = yInit * ratio;
+ } else {
+ xInit = xImg;
+ yInit = xInit / ratio;
+ }
+
+ imgSelectOptions = {
+ handles: true,
+ keys: true,
+ instance: true,
+ persistent: true,
+ parent: this.$el,
+ imageWidth: realWidth,
+ imageHeight: realHeight,
+ x1: 0,
+ y1: 0,
+ x2: xInit,
+ y2: yInit
+ };
+
+ if (flexHeight === false && flexWidth === false) {
+ imgSelectOptions.aspectRatio = xInit + ':' + yInit;
+ }
+ if (flexHeight === false ) {
+ imgSelectOptions.maxHeight = yInit;
+ }
+ if (flexWidth === false ) {
+ imgSelectOptions.maxWidth = xInit;
+ }
+
+ return imgSelectOptions;
+ },
+
+ /**
+ * Sets up and opens the Media Manager in order to select an image.
+ * Depending on both the size of the image and the properties of the
+ * current theme, a cropping step after selection may be required or
+ * skippable.
+ *
+ * @param {event} event
+ */
+ openMedia: function(event) {
+ var title, suggestedWidth, suggestedHeight,
+ l10n = _wpMediaViewsL10n;
+
+ event.preventDefault();
+
+ suggestedWidth = l10n.suggestedWidth.replace('%d', _wpCustomizeHeader.data.width);
+ suggestedHeight = l10n.suggestedHeight.replace('%d', _wpCustomizeHeader.data.height);
+
+ /* '' + suggestedWidth + ' ' + suggestedHeight + '' */
+
+ this.frame = wp.media({
+ title: l10n.chooseImage,
+ library: {
+ type: 'image'
+ },
+ button: {
+ text: l10n.selectAndCrop,
+ close: false
+ },
+ multiple: false,
+ imgSelectOptions: this.calculateImageSelectOptions
+ });
+
+ this.frame.states.add([new wp.media.controller.Cropper()]);
+
+ this.frame.on('select', this.onSelect, this);
+ this.frame.on('cropped', this.onCropped, this);
+ this.frame.on('skippedcrop', this.onSkippedCrop, this);
+
+ this.frame.open();
+ },
+
+ onSelect: function() {
+ this.frame.setState('cropper');
+ },
+ onCropped: function(croppedImage) {
+ var url = croppedImage.post_content,
+ attachmentId = croppedImage.attachment_id,
+ w = croppedImage.width,
+ h = croppedImage.height;
+ this.setImageFromURL(url, attachmentId, w, h);
+ },
+ onSkippedCrop: function(selection) {
+ var url = selection.get('url'),
+ w = selection.get('width'),
+ h = selection.get('height');
+ this.setImageFromURL(url, selection.id, w, h);
+ },
+
+ /**
+ * Creates a new wp.customize.HeaderTool.ImageModel from provided
+ * header image data and inserts it into the user-uploaded headers
+ * collection.
+ *
+ * @param {String} url
+ * @param {Number} attachmentId
+ * @param {Number} width
+ * @param {Number} height
+ */
+ setImageFromURL: function(url, attachmentId, width, height) {
+ var choice, data = {};
+
+ data.url = url;
+ data.thumbnail_url = url;
+
+ if (attachmentId) {
+ data.attachment_id = attachmentId;
+ }
+
+ if (width) {
+ data.width = width;
+ }
+
+ if (height) {
+ data.height = height;
+ }
+
+ choice = new api.HeaderTool.ImageModel({
+ header: data,
+ choice: url.split('/').pop()
+ });
+ api.HeaderTool.UploadsList.add(choice);
+ api.HeaderTool.currentHeader.set(choice.toJSON());
+ choice.save();
+ choice.importImage();
+ },
+
+ /**
+ * Triggers the necessary events to deselect an image which was set as
+ * the currently selected one.
+ */
+ removeImage: function() {
+ api.HeaderTool.currentHeader.trigger('hide');
+ api.HeaderTool.CombinedList.trigger('control:removeImage');
+ }
+
+ });
+
// Change objects contained within the main customize object to Settings.
api.defaultConstructor = api.Setting;
@@ -686,7 +898,8 @@
api.controlConstructor = {
color: api.ColorControl,
upload: api.UploadControl,
- image: api.ImageControl
+ image: api.ImageControl,
+ header: api.HeaderControl
};
$( function() {
@@ -961,35 +1174,6 @@
});
});
- // Handle header image data
- api.control( 'header_image', function( control ) {
- control.setting.bind( function( to ) {
- if ( to === control.params.removed )
- control.settings.data.set( false );
- });
-
- control.library.on( 'click', 'a', function() {
- control.settings.data.set( $(this).data('customizeHeaderImageData') );
- });
-
- control.uploader.success = function( attachment ) {
- var data;
-
- api.ImageControl.prototype.success.call( control, attachment );
-
- data = {
- attachment_id: attachment.get('id'),
- url: attachment.get('url'),
- thumbnail_url: attachment.get('url'),
- height: attachment.get('height'),
- width: attachment.get('width')
- };
-
- attachment.element.data( 'customizeHeaderImageData', data );
- control.settings.data.set( data );
- };
- });
-
api.trigger( 'ready' );
// Make sure left column gets focus
diff --git a/src/wp-includes/class-wp-customize-control.php b/src/wp-includes/class-wp-customize-control.php
index 8dedf10fde..896bdd9390 100644
--- a/src/wp-includes/class-wp-customize-control.php
+++ b/src/wp-includes/class-wp-customize-control.php
@@ -708,37 +708,9 @@ class WP_Customize_Background_Image_Control extends WP_Customize_Image_Control {
}
}
-/**
- * Customize Header Image Control Class
- *
- * @package WordPress
- * @subpackage Customize
- * @since 3.4.0
- */
-class WP_Customize_Header_Image_Control extends WP_Customize_Image_Control {
- /**
- * The processed default headers.
- * @since 3.4.2
- * @var array
- */
- protected $default_headers;
+final class WP_Customize_Header_Image_Control extends WP_Customize_Image_Control {
+ public $type = 'header';
- /**
- * The uploaded headers.
- * @since 3.4.2
- * @var array
- */
- protected $uploaded_headers;
-
- /**
- * Constructor.
- *
- * @since 3.4.0
- * @uses WP_Customize_Image_Control::__construct()
- * @uses WP_Customize_Image_Control::add_tab()
- *
- * @param WP_Customize_Manager $manager
- */
public function __construct( $manager ) {
parent::__construct( $manager, 'header_image', array(
'label' => __( 'Header Image' ),
@@ -750,86 +722,305 @@ class WP_Customize_Header_Image_Control extends WP_Customize_Image_Control {
'context' => 'custom-header',
'removed' => 'remove-header',
'get_url' => 'get_header_image',
- 'statuses' => array(
- '' => __('Default'),
- 'remove-header' => __('No Image'),
- 'random-default-image' => __('Random Default Image'),
- 'random-uploaded-image' => __('Random Uploaded Image'),
+ ) );
+
+ }
+
+ public function to_json() {
+ parent::to_json();
+ }
+
+ public function enqueue() {
+ wp_enqueue_media();
+ wp_enqueue_script( 'customize-views' );
+
+ $this->prepare_control();
+
+ wp_localize_script( 'customize-views', '_wpCustomizeHeader', array(
+ 'data' => array(
+ 'width' => absint( get_theme_support( 'custom-header', 'width' ) ),
+ 'height' => absint( get_theme_support( 'custom-header', 'height' ) ),
+ 'flex-width' => absint( get_theme_support( 'custom-header', 'flex-width' ) ),
+ 'flex-height' => absint( get_theme_support( 'custom-header', 'flex-height' ) ),
+ 'currentImgSrc' => $this->get_current_image_src(),
+ ),
+ 'nonces' => array(
+ 'add' => wp_create_nonce( 'header-add' ),
+ 'remove' => wp_create_nonce( 'header-remove' ),
+ ),
+ 'l10n' => array(
+ /* translators: header images uploaded by user */
+ 'uploaded' => __( 'uploaded' ),
+ /* translators: header images suggested by the current theme */
+ 'default' => __( 'suggested' )
+ ),
+ 'uploads' => $this->uploaded_headers,
+ 'defaults' => $this->default_headers
+ ) );
+
+ parent::enqueue();
+ }
+
+ public function get_default_header_images() {
+ global $custom_image_header;
+
+ // Get *the* default image if there is one
+ $default = get_theme_support( 'custom-header', 'default-image' );
+
+ if ( ! $default ) { // If not,
+ return $custom_image_header->default_headers; // easy peasy.
+ }
+
+ $default = sprintf( $default,
+ get_template_directory_uri(),
+ get_stylesheet_directory_uri() );
+
+ $header_images = array();
+ $already_has_default = false;
+
+ // Get the whole set of default images
+ $default_header_images = $custom_image_header->default_headers;
+ foreach ( $default_header_images as $k => $h ) {
+ if ( $h['url'] == $default ) {
+ $already_has_default = true;
+ break;
+ }
+ }
+
+ // If *the one true image* isn't included in the default set, add it in
+ // first position
+ if ( ! $already_has_default ) {
+ $header_images['default'] = array(
+ 'url' => $default,
+ 'thumbnail_url' => $default,
+ 'description' => 'Default'
+ );
+ }
+
+ // The rest of the set comes after
+ $header_images = array_merge( $header_images, $default_header_images );
+
+ return $header_images;
+ }
+
+ public function get_uploaded_header_images() {
+ $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
+ $header_images = array();
+
+ $headers_not_dated = get_posts( array(
+ 'post_type' => 'attachment',
+ 'meta_key' => '_wp_attachment_is_custom_header',
+ 'meta_value' => get_option('stylesheet'),
+ 'orderby' => 'none',
+ 'nopaging' => true,
+ 'meta_query' => array(
+ array(
+ 'key' => '_wp_attachment_is_custom_header',
+ 'value' => get_option( 'stylesheet' ),
+ 'compare' => 'LIKE'
+ ),
+ array(
+ 'key' => $key,
+ 'value' => 'this string must not be empty',
+ 'compare' => 'NOT EXISTS'
+ ),
)
) );
- // Remove the upload tab.
- $this->remove_tab( 'upload-new' );
+ $headers_dated = get_posts( array(
+ 'post_type' => 'attachment',
+ 'meta_key' => $key,
+ 'orderby' => 'meta_value_num',
+ 'order' => 'DESC',
+ 'nopaging' => true,
+ 'meta_query' => array(
+ array(
+ 'key' => '_wp_attachment_is_custom_header',
+ 'value' => get_option( 'stylesheet' ),
+ 'compare' => 'LIKE'
+ ),
+ ),
+ ) );
+
+ $limit = apply_filters( 'custom_header_uploaded_limit', 15 );
+ $headers = array_merge( $headers_dated, $headers_not_dated );
+ $headers = array_slice( $headers, 0, $limit );
+
+ foreach ( (array) $headers as $header ) {
+ $url = esc_url_raw( $header->guid );
+ $header_data = wp_get_attachment_metadata( $header->ID );
+ $timestamp = get_post_meta( $header->ID,
+ '_wp_attachment_custom_header_last_used_' . get_stylesheet(),
+ true );
+
+ $h = array(
+ 'attachment_id' => $header->ID,
+ 'url' => $url,
+ 'thumbnail_url' => $url,
+ 'timestamp' => $timestamp ? $timestamp : 0,
+ );
+
+ if ( isset( $header_data['width'] ) ) {
+ $h['width'] = $header_data['width'];
+ }
+ if ( isset( $header_data['height'] ) ) {
+ $h['height'] = $header_data['height'];
+ }
+
+ $header_images[] = $h;
+ }
+
+ return $header_images;
}
- /**
- * Prepares the control.
- *
- * If no tabs exist, removes the control from the manager.
- *
- * @since 3.4.2
- */
public function prepare_control() {
global $custom_image_header;
- if ( empty( $custom_image_header ) )
- return parent::prepare_control();
+ if ( empty( $custom_image_header ) ) {
+ return;
+ }
// Process default headers and uploaded headers.
$custom_image_header->process_default_headers();
- $this->default_headers = $custom_image_header->default_headers;
- $this->uploaded_headers = get_uploaded_header_images();
-
- if ( $this->default_headers )
- $this->add_tab( 'default', __('Default'), array( $this, 'tab_default_headers' ) );
-
- if ( ! $this->uploaded_headers )
- $this->remove_tab( 'uploaded' );
-
- return parent::prepare_control();
+ $this->default_headers = $this->get_default_header_images();
+ $this->uploaded_headers = $this->get_uploaded_header_images();
}
- /**
- * @since 3.4.0
- *
- * @param mixed $choice Which header image to select. (@see Custom_Image_Header::get_header_image() )
- * @param array $header
- */
- public function print_header_image( $choice, $header ) {
- $header['url'] = set_url_scheme( $header['url'] );
- $header['thumbnail_url'] = set_url_scheme( $header['thumbnail_url'] );
-
- $header_image_data = array( 'choice' => $choice );
- foreach ( array( 'attachment_id', 'width', 'height', 'url', 'thumbnail_url' ) as $key ) {
- if ( isset( $header[ $key ] ) )
- $header_image_data[ $key ] = $header[ $key ];
- }
-
-
+ function print_header_image_template() {
?>
-
-
-
+
+
+
+ + %s, your theme recommends a header size of %dx%d pixels.' ), + _x( 'Add new', 'header image' ), $width, $height ); + } else { + if ( $width ) { + printf( __( 'While you can crop images to your liking after clicking %s, your theme recommends a header width of %d pixels.' ), + _x( 'Add new', 'header image' ), $width ); + } + if ( $height ) { + printf( __( 'While you can crop images to your liking after clicking %s, your theme recommends a header height of %d pixels.' ), + _x( 'Add new', 'header image' ), $height ); + } + } + ?> +
+