3 * The custom header image script.
6 * @subpackage Administration
10 * The custom header image class.
14 * @subpackage Administration
16 class Custom_Image_Header {
19 * Callback for administration header.
25 var $admin_header_callback;
28 * Callback for header div.
34 var $admin_image_div_callback;
37 * Holds default headers.
43 var $default_headers = array();
47 * Holds custom headers uploaded by the user
53 var $uploaded_headers = array();
56 * Holds the page menu hook.
65 * Constructor - Register administration header callback.
68 * @param callback $admin_header_callback
69 * @param callback $admin_image_div_callback Optional custom image div output callback.
70 * @return Custom_Image_Header
72 function __construct($admin_header_callback, $admin_image_div_callback = '') {
73 $this->admin_header_callback = $admin_header_callback;
74 $this->admin_image_div_callback = $admin_image_div_callback;
78 * Set up the hooks for the Custom Header admin page.
83 if ( ! current_user_can('edit_theme_options') )
86 $this->page = $page = add_theme_page(__('Header'), __('Header'), 'edit_theme_options', 'custom-header', array(&$this, 'admin_page'));
88 add_action("admin_print_scripts-$page", array(&$this, 'js_includes'));
89 add_action("admin_print_styles-$page", array(&$this, 'css_includes'));
90 add_action("admin_head-$page", array(&$this, 'help') );
91 add_action("admin_head-$page", array(&$this, 'take_action'), 50);
92 add_action("admin_head-$page", array(&$this, 'js'), 50);
93 add_action("admin_head-$page", $this->admin_header_callback, 51);
97 * Adds contextual help.
102 get_current_screen()->add_help_tab( array(
104 'title' => __('Overview'),
106 '<p>' . __( 'You can set a custom image header for your site. Simply upload the image and crop it, and the new header will go live immediately.' ) . '</p>' .
107 '<p>' . __( 'If you want to discard your custom header and go back to the default included in your theme, click on the buttons to remove the custom image and restore the original header image.' ) . '</p>' .
108 '<p>' . __( 'Some themes come with additional header images bundled. If you see multiple images displayed, select the one you’d like and click the Save Changes button.' ) . '</p>'
111 get_current_screen()->set_help_sidebar(
112 '<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
113 '<p>' . __( '<a href="http://codex.wordpress.org/Appearance_Header_Screen" target="_blank">Documentation on Custom Header</a>' ) . '</p>' .
114 '<p>' . __( '<a href="http://wordpress.org/support/" target="_blank">Support Forums</a>' ) . '</p>'
119 * Get the current step.
123 * @return int Current step
126 if ( ! isset( $_GET['step'] ) )
129 $step = (int) $_GET['step'];
130 if ( $step < 1 || 3 < $step )
137 * Set up the enqueue for the JavaScript files.
141 function js_includes() {
142 $step = $this->step();
144 if ( ( 1 == $step || 3 == $step ) && $this->header_text() )
145 wp_enqueue_script('farbtastic');
146 elseif ( 2 == $step )
147 wp_enqueue_script('imgareaselect');
151 * Set up the enqueue for the CSS files
155 function css_includes() {
156 $step = $this->step();
158 if ( ( 1 == $step || 3 == $step ) && $this->header_text() )
159 wp_enqueue_style('farbtastic');
160 elseif ( 2 == $step )
161 wp_enqueue_style('imgareaselect');
165 * Check if header text is allowed
169 function header_text() {
170 if ( defined( 'NO_HEADER_TEXT' ) && NO_HEADER_TEXT )
177 * Execute custom header modification.
181 function take_action() {
182 if ( ! current_user_can('edit_theme_options') )
185 if ( empty( $_POST ) )
188 $this->updated = true;
190 if ( isset( $_POST['resetheader'] ) ) {
191 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
192 remove_theme_mod( 'header_image' );
196 if ( isset( $_POST['resettext'] ) ) {
197 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
198 remove_theme_mod('header_textcolor');
202 if ( isset( $_POST['removeheader'] ) ) {
203 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
204 set_theme_mod( 'header_image', 'remove-header' );
208 if ( isset( $_POST['text-color'] ) ) {
209 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
210 $_POST['text-color'] = str_replace( '#', '', $_POST['text-color'] );
211 if ( 'blank' == $_POST['text-color'] ) {
212 set_theme_mod( 'header_textcolor', 'blank' );
214 $color = preg_replace('/[^0-9a-fA-F]/', '', $_POST['text-color']);
215 if ( strlen($color) == 6 || strlen($color) == 3 )
216 set_theme_mod('header_textcolor', $color);
220 if ( isset( $_POST['default-header'] ) ) {
221 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
222 if ( 'random-default-image' == $_POST['default-header'] ) {
223 set_theme_mod( 'header_image', 'random-default-image' );
224 } elseif ( 'random-uploaded-image' == $_POST['default-header'] ) {
225 set_theme_mod( 'header_image', 'random-uploaded-image' );
227 $this->process_default_headers();
228 $uploaded = get_uploaded_header_images();
229 if ( isset( $uploaded[$_POST['default-header']] ) )
230 set_theme_mod( 'header_image', esc_url( $uploaded[$_POST['default-header']]['url'] ) );
231 elseif ( isset( $this->default_headers[$_POST['default-header']] ) )
232 set_theme_mod( 'header_image', esc_url( $this->default_headers[$_POST['default-header']]['url'] ) );
238 * Process the default headers
242 function process_default_headers() {
243 global $_wp_default_headers;
245 if ( !empty($this->headers) )
248 if ( !isset($_wp_default_headers) )
251 $this->default_headers = $_wp_default_headers;
252 $template_directory_uri = get_template_directory_uri();
253 $stylesheet_directory_uri = get_stylesheet_directory_uri();
254 foreach ( array_keys($this->default_headers) as $header ) {
255 $this->default_headers[$header]['url'] = sprintf( $this->default_headers[$header]['url'], $template_directory_uri, $stylesheet_directory_uri );
256 $this->default_headers[$header]['thumbnail_url'] = sprintf( $this->default_headers[$header]['thumbnail_url'], $template_directory_uri, $stylesheet_directory_uri );
262 * Display UI for selecting one of several default headers.
264 * Show the random image option if this theme has multiple header images.
265 * Random image option is on by default if no header has been set.
269 function show_header_selector( $type = 'default' ) {
270 if ( 'default' == $type ) {
271 $headers = $this->default_headers;
273 $headers = get_uploaded_header_images();
277 if ( 1 < count( $headers ) ) {
278 echo '<div class="random-header">';
279 echo '<label><input name="default-header" type="radio" value="random-' . $type . '-image"' . checked( is_random_header_image( $type ), true, false ) . ' />';
280 echo __( '<strong>Random:</strong> Show a different image on each page.' );
285 echo '<div class="available-headers">';
286 foreach ( $headers as $header_key => $header ) {
287 $header_thumbnail = $header['thumbnail_url'];
288 $header_url = $header['url'];
289 $header_desc = empty( $header['description'] ) ? '' : $header['description'];
290 echo '<div class="default-header">';
291 echo '<label><input name="default-header" type="radio" value="' . esc_attr( $header_key ) . '" ' . checked( $header_url, get_theme_mod( 'header_image' ), false ) . ' />';
293 if ( !empty( $header['uploaded'] ) )
294 $width = ' width="230"';
295 echo '<img src="' . $header_thumbnail . '" alt="' . esc_attr( $header_desc ) .'" title="' . esc_attr( $header_desc ) . '"' . $width . ' /></label>';
298 echo '<div class="clear"></div></div>';
302 * Execute Javascript depending on step.
307 $step = $this->step();
308 if ( ( 1 == $step || 3 == $step ) && $this->header_text() )
310 elseif ( 2 == $step )
315 * Display Javascript based on Step 1 and 3.
320 <script type="text/javascript">
322 var text_objects = ['#name', '#desc', '#text-color-row'];
324 var default_color = '#<?php echo HEADER_TEXTCOLOR; ?>';
325 var old_color = null;
327 function pickColor(color) {
328 jQuery('#name').css('color', color);
329 jQuery('#desc').css('color', color);
330 jQuery('#text-color').val(color);
331 farbtastic.setColor(color);
334 function toggle_text(s) {
335 if (jQuery(s).attr('id') == 'showtext' && jQuery('#text-color').val() != 'blank')
338 if (jQuery(s).attr('id') == 'hidetext' && jQuery('#text-color').val() == 'blank')
341 if (jQuery('#text-color').val() == 'blank') {
343 if (old_color == '#blank')
344 old_color = default_color;
346 jQuery( text_objects.toString() ).show();
347 jQuery('#text-color').val(old_color);
348 jQuery('#name').css('color', old_color);
349 jQuery('#desc').css('color', old_color);
350 pickColor(old_color);
353 jQuery( text_objects.toString() ).hide();
354 old_color = jQuery('#text-color').val();
355 jQuery('#text-color').val('blank');
359 jQuery(document).ready(function() {
360 jQuery('#pickcolor').click(function() {
361 jQuery('#color-picker').show();
364 jQuery('input[name="hidetext"]').click(function() {
368 jQuery('#defaultcolor').click(function() {
369 pickColor(default_color);
370 jQuery('#text-color').val(default_color)
373 jQuery('#text-color').keyup(function() {
374 var _hex = jQuery('#text-color').val();
378 hex = hex.replace(/[^#a-fA-F0-9]+/, '');
380 jQuery('#text-color').val(hex);
381 if ( hex.length == 4 || hex.length == 7 )
385 jQuery(document).mousedown(function(){
386 jQuery('#color-picker').each( function() {
387 var display = jQuery(this).css('display');
388 if (display == 'block')
389 jQuery(this).fadeOut(2);
393 farbtastic = jQuery.farbtastic('#color-picker', function(color) { pickColor(color); });
394 <?php if ( $color = get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) ) { ?>
395 pickColor('#<?php echo $color; ?>');
398 <?php if ( 'blank' == get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) || '' == get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) || ! $this->header_text() ) { ?>
408 * Display Javascript based on Step 2.
413 <script type="text/javascript">
415 function onEndCrop( coords ) {
416 jQuery( '#x1' ).val(coords.x);
417 jQuery( '#y1' ).val(coords.y);
418 jQuery( '#width' ).val(coords.w);
419 jQuery( '#height' ).val(coords.h);
422 jQuery(document).ready(function() {
423 var xinit = <?php echo HEADER_IMAGE_WIDTH; ?>;
424 var yinit = <?php echo HEADER_IMAGE_HEIGHT; ?>;
425 var ratio = xinit / yinit;
426 var ximg = jQuery('img#upload').width();
427 var yimg = jQuery('img#upload').height();
429 if ( yimg < yinit || ximg < xinit ) {
430 if ( ximg / yimg > ratio ) {
432 xinit = yinit * ratio;
435 yinit = xinit / ratio;
439 jQuery('img#upload').imgAreaSelect({
442 aspectRatio: xinit + ':' + yinit,
448 maxHeight: <?php echo HEADER_IMAGE_HEIGHT; ?>,
449 maxWidth: <?php echo HEADER_IMAGE_WIDTH; ?>,
450 onInit: function () {
451 jQuery('#width').val(xinit);
452 jQuery('#height').val(yinit);
454 onSelectChange: function(img, c) {
455 jQuery('#x1').val(c.x1);
456 jQuery('#y1').val(c.y1);
457 jQuery('#width').val(c.width);
458 jQuery('#height').val(c.height);
468 * Display first step of custom header image page.
473 $this->process_default_headers();
477 <?php screen_icon(); ?>
478 <h2><?php _e('Custom Header'); ?></h2>
480 <?php if ( ! empty( $this->updated ) ) { ?>
481 <div id="message" class="updated">
482 <p><?php printf( __( 'Header updated. <a href="%s">Visit your site</a> to see how it looks.' ), home_url( '/' ) ); ?></p>
486 <table class="form-table">
491 <th scope="row"><?php _e( 'Preview' ); ?></th>
493 <?php if ( $this->admin_image_div_callback ) {
494 call_user_func( $this->admin_image_div_callback );
497 <div id="headimg" style="max-width:<?php echo HEADER_IMAGE_WIDTH; ?>px;height:<?php echo HEADER_IMAGE_HEIGHT; ?>px;background-image:url(<?php esc_url ( header_image() ) ?>);">
499 if ( 'blank' == get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) || '' == get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) || ! $this->header_text() )
500 $style = ' style="display:none;"';
502 $style = ' style="color:#' . get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) . ';"';
504 <h1><a id="name"<?php echo $style; ?> onclick="return false;" href="<?php bloginfo('url'); ?>"><?php bloginfo( 'name' ); ?></a></h1>
505 <div id="desc"<?php echo $style; ?>><?php bloginfo( 'description' ); ?></div>
510 <?php if ( current_theme_supports( 'custom-header-uploads' ) ) : ?>
512 <th scope="row"><?php _e( 'Upload Image' ); ?></th>
514 <p><?php _e( 'You can upload a custom header image to be shown at the top of your site instead of the default one. On the next screen you will be able to crop the image.' ); ?><br />
515 <?php printf( __( 'Images of exactly <strong>%1$d × %2$d pixels</strong> will be used as-is.' ), HEADER_IMAGE_WIDTH, HEADER_IMAGE_HEIGHT ); ?></p>
516 <form enctype="multipart/form-data" id="upload-form" method="post" action="<?php echo esc_attr( add_query_arg( 'step', 2 ) ) ?>">
518 <label for="upload"><?php _e( 'Choose an image from your computer:' ); ?></label><br />
519 <input type="file" id="upload" name="import" />
520 <input type="hidden" name="action" value="save" />
521 <?php wp_nonce_field( 'custom-header-upload', '_wpnonce-custom-header-upload' ) ?>
522 <?php submit_button( __( 'Upload' ), 'button', 'submit', false ); ?>
531 <form method="post" action="<?php echo esc_attr( add_query_arg( 'step', 1 ) ) ?>">
532 <table class="form-table">
534 <?php if ( get_uploaded_header_images() ) : ?>
536 <th scope="row"><?php _e( 'Uploaded Images' ); ?></th>
538 <p><?php _e( 'You can choose one of your previously uploaded headers, or show a random one.' ) ?></p>
540 $this->show_header_selector( 'uploaded' );
545 if ( ! empty( $this->default_headers ) ) : ?>
547 <th scope="row"><?php _e( 'Default Images' ); ?></th>
549 <?php if ( current_theme_supports( 'custom-header-uploads' ) ) : ?>
550 <p><?php _e( 'If you don‘t want to upload your own image, you can use one of these cool headers, or show a random one.' ) ?></p>
552 <p><?php _e( 'You can use one of these cool headers or show a random one on each page.' ) ?></p>
555 $this->show_header_selector( 'default' );
560 if ( get_header_image() ) : ?>
562 <th scope="row"><?php _e( 'Remove Image' ); ?></th>
564 <p><?php _e( 'This will remove the header image. You will not be able to restore any customizations.' ) ?></p>
565 <?php submit_button( __( 'Remove Header Image' ), 'button', 'removeheader', false ); ?>
570 if ( defined( 'HEADER_IMAGE' ) && '' != HEADER_IMAGE ) : ?>
572 <th scope="row"><?php _e( 'Reset Image' ); ?></th>
574 <p><?php _e( 'This will restore the original header image. You will not be able to restore any customizations.' ) ?></p>
575 <?php submit_button( __( 'Restore Original Header Image' ), 'button', 'resetheader', false ); ?>
582 <?php if ( $this->header_text() ) : ?>
583 <table class="form-table">
585 <tr valign="top" class="hide-if-no-js">
586 <th scope="row"><?php _e( 'Display Text' ); ?></th>
589 <?php $hidetext = get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ); ?>
590 <label><input type="radio" value="1" name="hidetext" id="hidetext"<?php checked( ( 'blank' == $hidetext || empty( $hidetext ) ) ? true : false ); ?> /> <?php _e( 'No' ); ?></label>
591 <label><input type="radio" value="0" name="hidetext" id="showtext"<?php checked( ( 'blank' == $hidetext || empty( $hidetext ) ) ? false : true ); ?> /> <?php _e( 'Yes' ); ?></label>
596 <tr valign="top" id="text-color-row">
597 <th scope="row"><?php _e( 'Text Color' ); ?></th>
600 <input type="text" name="text-color" id="text-color" value="#<?php echo esc_attr( get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) ); ?>" />
601 <span class="description hide-if-js"><?php _e( 'If you want to hide header text, add <strong>#blank</strong> as text color.' );?></span>
602 <input type="button" class="button hide-if-no-js" value="<?php esc_attr_e( 'Select a Color' ); ?>" id="pickcolor" />
604 <div id="color-picker" style="z-index: 100; background:#eee; border:1px solid #ccc; position:absolute; display:none;"></div>
608 <?php if ( defined('HEADER_TEXTCOLOR') && get_theme_mod('header_textcolor') ) { ?>
610 <th scope="row"><?php _e('Reset Text Color'); ?></th>
612 <p><?php _e( 'This will restore the original header text. You will not be able to restore any customizations.' ) ?></p>
613 <?php submit_button( __( 'Restore Original Header Text' ), 'button', 'resettext', false ); ?>
622 do_action( 'custom_header_options' );
624 wp_nonce_field( 'custom-header-options', '_wpnonce-custom-header-options' ); ?>
626 <?php submit_button( null, 'primary', 'save-header-options' ); ?>
633 * Display second step of custom header image page.
638 check_admin_referer('custom-header-upload', '_wpnonce-custom-header-upload');
639 if ( ! current_theme_supports( 'custom-header-uploads' ) )
640 wp_die( __( 'Cheatin’ uh?' ) );
642 $overrides = array('test_form' => false);
643 $file = wp_handle_upload($_FILES['import'], $overrides);
645 if ( isset($file['error']) )
646 wp_die( $file['error'], __( 'Image Upload Error' ) );
649 $type = $file['type'];
650 $file = $file['file'];
651 $filename = basename($file);
653 // Construct the object array
655 'post_title' => $filename,
656 'post_content' => $url,
657 'post_mime_type' => $type,
659 'context' => 'custom-header'
663 $id = wp_insert_attachment($object, $file);
665 list($width, $height, $type, $attr) = getimagesize( $file );
667 if ( $width == HEADER_IMAGE_WIDTH && $height == HEADER_IMAGE_HEIGHT ) {
669 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
670 update_post_meta( $id, '_wp_attachment_is_custom_header', get_option('stylesheet' ) );
672 set_theme_mod('header_image', esc_url($url));
673 do_action('wp_create_file_in_uploads', $file, $id); // For replication
674 return $this->finished();
675 } elseif ( $width > HEADER_IMAGE_WIDTH ) {
676 $oitar = $width / HEADER_IMAGE_WIDTH;
677 $image = wp_crop_image($file, 0, 0, $width, $height, HEADER_IMAGE_WIDTH, $height / $oitar, false, str_replace(basename($file), 'midsize-'.basename($file), $file));
678 if ( is_wp_error( $image ) )
679 wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
681 $image = apply_filters('wp_create_file_in_uploads', $image, $id); // For replication
683 $url = str_replace(basename($url), basename($image), $url);
684 $width = $width / $oitar;
685 $height = $height / $oitar;
692 <?php screen_icon(); ?>
693 <h2><?php _e( 'Crop Header Image' ); ?></h2>
695 <form method="post" action="<?php echo esc_attr(add_query_arg('step', 3)); ?>">
696 <p class="hide-if-no-js"><?php _e('Choose the part of the image you want to use as your header.'); ?></p>
697 <p class="hide-if-js"><strong><?php _e( 'You need Javascript to choose a part of the image.'); ?></strong></p>
699 <div id="crop_image" style="position: relative">
700 <img src="<?php echo esc_url( $url ); ?>" id="upload" width="<?php echo $width; ?>" height="<?php echo $height; ?>" />
703 <input type="hidden" name="x1" id="x1" value="0"/>
704 <input type="hidden" name="y1" id="y1" value="0"/>
705 <input type="hidden" name="width" id="width" value="<?php echo esc_attr( $width ); ?>"/>
706 <input type="hidden" name="height" id="height" value="<?php echo esc_attr( $height ); ?>"/>
707 <input type="hidden" name="attachment_id" id="attachment_id" value="<?php echo esc_attr( $id ); ?>" />
708 <input type="hidden" name="oitar" id="oitar" value="<?php echo esc_attr( $oitar ); ?>" />
709 <?php wp_nonce_field( 'custom-header-crop-image' ) ?>
711 <?php submit_button( __( 'Crop and Publish' ) ); ?>
719 * Display third step of custom header image page.
724 check_admin_referer('custom-header-crop-image');
725 if ( ! current_theme_supports( 'custom-header-uploads' ) )
726 wp_die( __( 'Cheatin’ uh?' ) );
728 if ( $_POST['oitar'] > 1 ) {
729 $_POST['x1'] = $_POST['x1'] * $_POST['oitar'];
730 $_POST['y1'] = $_POST['y1'] * $_POST['oitar'];
731 $_POST['width'] = $_POST['width'] * $_POST['oitar'];
732 $_POST['height'] = $_POST['height'] * $_POST['oitar'];
735 $attachment_id = absint( $_POST['attachment_id'] );
736 $original = get_attached_file($attachment_id);
738 $cropped = wp_crop_image( $attachment_id, (int) $_POST['x1'], (int) $_POST['y1'], (int) $_POST['width'], (int) $_POST['height'], HEADER_IMAGE_WIDTH, HEADER_IMAGE_HEIGHT );
739 if ( is_wp_error( $cropped ) )
740 wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
742 $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id); // For replication
744 $parent = get_post($attachment_id);
745 $parent_url = $parent->guid;
746 $url = str_replace(basename($parent_url), basename($cropped), $parent_url);
748 // Construct the object array
750 'ID' => $attachment_id,
751 'post_title' => basename($cropped),
752 'post_content' => $url,
753 'post_mime_type' => 'image/jpeg',
755 'context' => 'custom-header'
758 // Update the attachment
759 wp_insert_attachment($object, $cropped);
760 wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $cropped ) );
761 update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', get_option('stylesheet' ) );
763 set_theme_mod('header_image', $url);
766 $medium = str_replace(basename($original), 'midsize-'.basename($original), $original);
767 @unlink( apply_filters( 'wp_delete_file', $medium ) );
768 @unlink( apply_filters( 'wp_delete_file', $original ) );
770 return $this->finished();
774 * Display last step of custom header image page.
778 function finished() {
779 $this->updated = true;
784 * Display the page based on the current step.
788 function admin_page() {
789 if ( ! current_user_can('edit_theme_options') )
790 wp_die(__('You do not have permission to customize headers.'));
791 $step = $this->step();
794 elseif ( 2 == $step )
796 elseif ( 3 == $step )