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();
46 * Holds the page menu hook.
55 * PHP4 Constructor - Register administration header callback.
58 * @param callback $admin_header_callback
59 * @param callback $admin_image_div_callback Optional custom image div output callback.
60 * @return Custom_Image_Header
62 function Custom_Image_Header($admin_header_callback, $admin_image_div_callback = '') {
63 $this->admin_header_callback = $admin_header_callback;
64 $this->admin_image_div_callback = $admin_image_div_callback;
68 * Set up the hooks for the Custom Header admin page.
73 if ( ! current_user_can('edit_theme_options') )
76 $this->page = $page = add_theme_page(__('Header'), __('Header'), 'edit_theme_options', 'custom-header', array(&$this, 'admin_page'));
78 add_action("admin_print_scripts-$page", array(&$this, 'js_includes'));
79 add_action("admin_print_styles-$page", array(&$this, 'css_includes'));
80 add_action("admin_head-$page", array(&$this, 'help') );
81 add_action("admin_head-$page", array(&$this, 'take_action'), 50);
82 add_action("admin_head-$page", array(&$this, 'js'), 50);
83 add_action("admin_head-$page", $this->admin_header_callback, 51);
87 * Adds contextual help.
92 add_contextual_help( $this->page, '<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>' .
93 '<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>' .
94 '<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>' .
95 '<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
96 '<p>' . __( '<a href="http://codex.wordpress.org/Appearance_Header_SubPanel" target="_blank">Documentation on Custom Header</a>' ) . '</p>' .
97 '<p>' . __( '<a href="http://wordpress.org/support/" target="_blank">Support Forums</a>' ) . '</p>' );
101 * Get the current step.
105 * @return int Current step
108 if ( ! isset( $_GET['step'] ) )
111 $step = (int) $_GET['step'];
112 if ( $step < 1 || 3 < $step )
119 * Set up the enqueue for the JavaScript files.
123 function js_includes() {
124 $step = $this->step();
126 if ( ( 1 == $step || 3 == $step ) && $this->header_text() )
127 wp_enqueue_script('farbtastic');
128 elseif ( 2 == $step )
129 wp_enqueue_script('imgareaselect');
133 * Set up the enqueue for the CSS files
137 function css_includes() {
138 $step = $this->step();
140 if ( ( 1 == $step || 3 == $step ) && $this->header_text() )
141 wp_enqueue_style('farbtastic');
142 elseif ( 2 == $step )
143 wp_enqueue_style('imgareaselect');
147 * Check if header text is allowed
151 function header_text() {
152 if ( defined( 'NO_HEADER_TEXT' ) && NO_HEADER_TEXT )
159 * Execute custom header modification.
163 function take_action() {
164 if ( ! current_user_can('edit_theme_options') )
167 if ( empty( $_POST ) )
170 $this->updated = true;
172 if ( isset( $_POST['resetheader'] ) ) {
173 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
174 remove_theme_mod( 'header_image' );
178 if ( isset( $_POST['resettext'] ) ) {
179 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
180 remove_theme_mod('header_textcolor');
184 if ( isset( $_POST['removeheader'] ) ) {
185 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
186 set_theme_mod( 'header_image', '' );
190 if ( isset( $_POST['text-color'] ) ) {
191 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
192 $_POST['text-color'] = str_replace( '#', '', $_POST['text-color'] );
193 if ( 'blank' == $_POST['text-color'] ) {
194 set_theme_mod( 'header_textcolor', 'blank' );
196 $color = preg_replace('/[^0-9a-fA-F]/', '', $_POST['text-color']);
197 if ( strlen($color) == 6 || strlen($color) == 3 )
198 set_theme_mod('header_textcolor', $color);
202 if ( isset($_POST['default-header']) ) {
203 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
204 $this->process_default_headers();
205 if ( isset($this->default_headers[$_POST['default-header']]) )
206 set_theme_mod('header_image', esc_url($this->default_headers[$_POST['default-header']]['url']));
211 * Process the default headers
215 function process_default_headers() {
216 global $_wp_default_headers;
218 if ( !empty($this->headers) )
221 if ( !isset($_wp_default_headers) )
224 $this->default_headers = $_wp_default_headers;
225 foreach ( array_keys($this->default_headers) as $header ) {
226 $this->default_headers[$header]['url'] = sprintf( $this->default_headers[$header]['url'], get_template_directory_uri(), get_stylesheet_directory_uri() );
227 $this->default_headers[$header]['thumbnail_url'] = sprintf( $this->default_headers[$header]['thumbnail_url'], get_template_directory_uri(), get_stylesheet_directory_uri() );
232 * Display UI for selecting one of several default headers.
236 function show_default_header_selector() {
237 echo '<div id="available-headers">';
238 foreach ( $this->default_headers as $header_key => $header ) {
239 $header_thumbnail = $header['thumbnail_url'];
240 $header_url = $header['url'];
241 $header_desc = $header['description'];
242 echo '<div class="default-header">';
243 echo '<label><input name="default-header" type="radio" value="' . esc_attr($header_key) . '" ' . checked($header_url, get_theme_mod( 'header_image' ), false) . ' />';
244 echo '<img src="' . $header_thumbnail . '" alt="' . esc_attr($header_desc) .'" title="' . esc_attr($header_desc) .'" /></label>';
247 echo '<div class="clear"></div></div>';
251 * Execute Javascript depending on step.
256 $step = $this->step();
257 if ( ( 1 == $step || 3 == $step ) && $this->header_text() )
259 elseif ( 2 == $step )
264 * Display Javascript based on Step 1 and 3.
269 <script type="text/javascript">
271 var text_objects = ['#name', '#desc', '#text-color-row'];
273 var default_color = '#<?php echo HEADER_TEXTCOLOR; ?>';
274 var old_color = null;
276 function pickColor(color) {
277 jQuery('#name').css('color', color);
278 jQuery('#desc').css('color', color);
279 jQuery('#text-color').val(color);
280 farbtastic.setColor(color);
283 function toggle_text(s) {
284 if (jQuery(s).attr('id') == 'showtext' && jQuery('#text-color').val() != 'blank')
287 if (jQuery(s).attr('id') == 'hidetext' && jQuery('#text-color').val() == 'blank')
290 if (jQuery('#text-color').val() == 'blank') {
292 if (old_color == '#blank')
293 old_color = default_color;
295 jQuery( text_objects.toString() ).show();
296 jQuery('#text-color').val(old_color);
297 jQuery('#name').css('color', old_color);
298 jQuery('#desc').css('color', old_color);
299 pickColor(old_color);
302 jQuery( text_objects.toString() ).hide();
303 old_color = jQuery('#text-color').val();
304 jQuery('#text-color').val('blank');
308 jQuery(document).ready(function() {
309 jQuery('#pickcolor').click(function() {
310 jQuery('#color-picker').show();
313 jQuery('input[name="hidetext"]').click(function() {
317 jQuery('#defaultcolor').click(function() {
318 pickColor(default_color);
319 jQuery('#text-color').val(default_color)
322 jQuery('#text-color').keyup(function() {
323 var _hex = jQuery('#text-color').val();
327 hex = hex.replace(/[^#a-fA-F0-9]+/, '');
329 jQuery('#text-color').val(hex);
330 if ( hex.length == 4 || hex.length == 7 )
334 jQuery(document).mousedown(function(){
335 jQuery('#color-picker').each( function() {
336 var display = jQuery(this).css('display');
337 if (display == 'block')
338 jQuery(this).fadeOut(2);
342 farbtastic = jQuery.farbtastic('#color-picker', function(color) { pickColor(color); });
343 <?php if ( $color = get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) ) { ?>
344 pickColor('#<?php echo $color; ?>');
347 <?php if ( 'blank' == get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) || '' == get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) || ! $this->header_text() ) { ?>
356 * Display Javascript based on Step 2.
361 <script type="text/javascript">
363 function onEndCrop( coords ) {
364 jQuery( '#x1' ).val(coords.x);
365 jQuery( '#y1' ).val(coords.y);
366 jQuery( '#width' ).val(coords.w);
367 jQuery( '#height' ).val(coords.h);
370 jQuery(document).ready(function() {
371 var xinit = <?php echo HEADER_IMAGE_WIDTH; ?>;
372 var yinit = <?php echo HEADER_IMAGE_HEIGHT; ?>;
373 var ratio = xinit / yinit;
374 var ximg = jQuery('img#upload').width();
375 var yimg = jQuery('img#upload').height();
377 if ( yimg < yinit || ximg < xinit ) {
378 if ( ximg / yimg > ratio ) {
380 xinit = yinit * ratio;
383 yinit = xinit / ratio;
387 jQuery('img#upload').imgAreaSelect({
390 aspectRatio: xinit + ':' + yinit,
396 maxHeight: <?php echo HEADER_IMAGE_HEIGHT; ?>,
397 maxWidth: <?php echo HEADER_IMAGE_WIDTH; ?>,
398 onInit: function () {
399 jQuery('#width').val(xinit);
400 jQuery('#height').val(yinit);
402 onSelectChange: function(img, c) {
403 jQuery('#x1').val(c.x1);
404 jQuery('#y1').val(c.y1);
405 jQuery('#width').val(c.width);
406 jQuery('#height').val(c.height);
416 * Display first step of custom header image page.
421 $this->process_default_headers();
425 <?php screen_icon(); ?>
426 <h2><?php _e('Custom Header'); ?></h2>
428 <?php if ( ! empty( $this->updated ) ) { ?>
429 <div id="message" class="updated">
430 <p><?php printf( __( 'Header updated. <a href="%s">Visit your site</a> to see how it looks.' ), home_url( '/' ) ); ?></p>
434 <h3><?php _e( 'Header Image' ) ?></h3>
435 <table class="form-table">
440 <th scope="row"><?php _e( 'Preview' ); ?></th>
442 <?php if ( $this->admin_image_div_callback ) {
443 call_user_func( $this->admin_image_div_callback );
446 <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() ) ?>);">
448 if ( 'blank' == get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) || '' == get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) || ! $this->header_text() )
449 $style = ' style="display:none;"';
451 $style = ' style="color:#' . get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) . ';"';
453 <h1><a id="name"<?php echo $style; ?> onclick="return false;" href="<?php bloginfo('url'); ?>"><?php bloginfo( 'name' ); ?></a></h1>
454 <div id="desc"<?php echo $style; ?>><?php bloginfo( 'description' ); ?></div>
459 <?php if ( current_theme_supports( 'custom-header-uploads' ) ) : ?>
461 <th scope="row"><?php _e( 'Upload Image' ); ?></th>
463 <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 />
464 <?php printf( __( 'Images of exactly <strong>%1$d × %2$d pixels</strong> will be used as-is.' ), HEADER_IMAGE_WIDTH, HEADER_IMAGE_HEIGHT ); ?></p>
465 <form enctype="multipart/form-data" id="upload-form" method="post" action="<?php echo esc_attr( add_query_arg( 'step', 2 ) ) ?>">
467 <label for="upload"><?php _e( 'Choose an image from your computer:' ); ?></label><br />
468 <input type="file" id="upload" name="import" />
469 <input type="hidden" name="action" value="save" />
470 <?php wp_nonce_field( 'custom-header-upload', '_wpnonce-custom-header-upload' ) ?>
471 <?php submit_button( __( 'Upload' ), 'button', 'submit', false ); ?>
480 <form method="post" action="<?php echo esc_attr( add_query_arg( 'step', 1 ) ) ?>">
481 <table class="form-table">
483 <?php if ( ! empty( $this->default_headers ) ) : ?>
485 <th scope="row"><?php _e( 'Default Images' ); ?></th>
487 <?php if ( current_theme_supports( 'custom-header-uploads' ) ) : ?>
488 <p><?php _e( 'If you don‘t want to upload your own image, you can use one of these cool headers.' ) ?></p>
490 <p><?php _e( 'You can use one of these cool headers.' ) ?>
493 $this->show_default_header_selector();
499 if ( get_header_image() ) : ?>
501 <th scope="row"><?php _e( 'Remove Image' ); ?></th>
503 <p><?php _e( 'This will remove the header image. You will not be able to restore any customizations.' ) ?></p>
504 <?php submit_button( __( 'Remove Header Image' ), 'button', 'removeheader', false ); ?>
509 if ( defined( 'HEADER_IMAGE' ) ) : ?>
511 <th scope="row"><?php _e( 'Reset Image' ); ?></th>
513 <p><?php _e( 'This will restore the original header image. You will not be able to restore any customizations.' ) ?></p>
514 <?php submit_button( __( 'Restore Original Header Image' ), 'button', 'resetheader', false ); ?>
521 <?php if ( $this->header_text() ) : ?>
522 <h3><?php _e( 'Header Text' ) ?></h3>
523 <table class="form-table">
525 <tr valign="top" class="hide-if-no-js">
526 <th scope="row"><?php _e( 'Display Text' ); ?></th>
529 <?php $hidetext = get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ); ?>
530 <label><input type="radio" value="1" name="hidetext" id="hidetext"<?php checked( ( 'blank' == $hidetext || empty( $hidetext ) ) ? true : false ); ?> /> <?php _e( 'No' ); ?></label>
531 <label><input type="radio" value="0" name="hidetext" id="showtext"<?php checked( ( 'blank' == $hidetext || empty( $hidetext ) ) ? false : true ); ?> /> <?php _e( 'Yes' ); ?></label>
536 <tr valign="top" id="text-color-row">
537 <th scope="row"><?php _e( 'Text Color' ); ?></th>
540 <input type="text" name="text-color" id="text-color" value="#<?php echo esc_attr( get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) ); ?>" />
541 <span class="description hide-if-js"><?php _e( 'If you want to hide header text, add <strong>#blank</strong> as text color.' );?></span>
542 <input type="button" class="button hide-if-no-js" value="<?php esc_attr_e( 'Select a Color' ); ?>" id="pickcolor" />
544 <div id="color-picker" style="z-index: 100; background:#eee; border:1px solid #ccc; position:absolute; display:none;"></div>
548 <?php if ( defined('HEADER_TEXTCOLOR') && get_theme_mod('header_textcolor') ) { ?>
550 <th scope="row"><?php _e('Reset Text Color'); ?></th>
552 <p><?php _e( 'This will restore the original header text. You will not be able to restore any customizations.' ) ?></p>
553 <?php submit_button( __( 'Restore Original Header Text' ), 'button', 'resettext', false ); ?>
562 do_action( 'custom_header_options' );
564 wp_nonce_field( 'custom-header-options', '_wpnonce-custom-header-options' ); ?>
566 <?php submit_button( null, 'primary', 'save-header-options' ); ?>
573 * Display second step of custom header image page.
578 check_admin_referer('custom-header-upload', '_wpnonce-custom-header-upload');
579 if ( ! current_theme_supports( 'custom-header-uploads' ) )
580 wp_die( 'Cheatin’ uh?' );
582 $overrides = array('test_form' => false);
583 $file = wp_handle_upload($_FILES['import'], $overrides);
585 if ( isset($file['error']) )
586 wp_die( $file['error'], __( 'Image Upload Error' ) );
589 $type = $file['type'];
590 $file = $file['file'];
591 $filename = basename($file);
593 // Construct the object array
595 'post_title' => $filename,
596 'post_content' => $url,
597 'post_mime_type' => $type,
599 'context' => 'custom-header');
602 $id = wp_insert_attachment($object, $file);
604 list($width, $height, $type, $attr) = getimagesize( $file );
606 if ( $width == HEADER_IMAGE_WIDTH && $height == HEADER_IMAGE_HEIGHT ) {
608 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
610 set_theme_mod('header_image', esc_url($url));
611 do_action('wp_create_file_in_uploads', $file, $id); // For replication
612 return $this->finished();
613 } elseif ( $width > HEADER_IMAGE_WIDTH ) {
614 $oitar = $width / HEADER_IMAGE_WIDTH;
615 $image = wp_crop_image($file, 0, 0, $width, $height, HEADER_IMAGE_WIDTH, $height / $oitar, false, str_replace(basename($file), 'midsize-'.basename($file), $file));
616 if ( is_wp_error( $image ) )
617 wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
619 $image = apply_filters('wp_create_file_in_uploads', $image, $id); // For replication
621 $url = str_replace(basename($url), basename($image), $url);
622 $width = $width / $oitar;
623 $height = $height / $oitar;
630 <?php screen_icon(); ?>
631 <h2><?php _e( 'Crop Header Image' ); ?></h2>
633 <form method="post" action="<?php echo esc_attr(add_query_arg('step', 3)); ?>">
634 <p class="hide-if-no-js"><?php _e('Choose the part of the image you want to use as your header.'); ?></p>
635 <p class="hide-if-js"><strong><?php _e( 'You need Javascript to choose a part of the image.'); ?></strong></p>
637 <div id="crop_image" style="position: relative">
638 <img src="<?php echo esc_url( $url ); ?>" id="upload" width="<?php echo $width; ?>" height="<?php echo $height; ?>" />
641 <input type="hidden" name="x1" id="x1" value="0"/>
642 <input type="hidden" name="y1" id="y1" value="0"/>
643 <input type="hidden" name="width" id="width" value="<?php echo esc_attr( $width ); ?>"/>
644 <input type="hidden" name="height" id="height" value="<?php echo esc_attr( $height ); ?>"/>
645 <input type="hidden" name="attachment_id" id="attachment_id" value="<?php echo esc_attr( $id ); ?>" />
646 <input type="hidden" name="oitar" id="oitar" value="<?php echo esc_attr( $oitar ); ?>" />
647 <?php wp_nonce_field( 'custom-header-crop-image' ) ?>
649 <?php submit_button( __( 'Crop and Publish' ) ); ?>
657 * Display third step of custom header image page.
662 check_admin_referer('custom-header-crop-image');
663 if ( ! current_theme_supports( 'custom-header-uploads' ) )
664 wp_die( 'Cheatin’ uh?' );
666 if ( $_POST['oitar'] > 1 ) {
667 $_POST['x1'] = $_POST['x1'] * $_POST['oitar'];
668 $_POST['y1'] = $_POST['y1'] * $_POST['oitar'];
669 $_POST['width'] = $_POST['width'] * $_POST['oitar'];
670 $_POST['height'] = $_POST['height'] * $_POST['oitar'];
673 $original = get_attached_file( $_POST['attachment_id'] );
675 $cropped = wp_crop_image($_POST['attachment_id'], $_POST['x1'], $_POST['y1'], $_POST['width'], $_POST['height'], HEADER_IMAGE_WIDTH, HEADER_IMAGE_HEIGHT);
676 if ( is_wp_error( $cropped ) )
677 wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
679 $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $_POST['attachment_id']); // For replication
681 $parent = get_post($_POST['attachment_id']);
682 $parent_url = $parent->guid;
683 $url = str_replace(basename($parent_url), basename($cropped), $parent_url);
685 // Construct the object array
687 'ID' => $_POST['attachment_id'],
688 'post_title' => basename($cropped),
689 'post_content' => $url,
690 'post_mime_type' => 'image/jpeg',
692 'context' => 'custom-header'
695 // Update the attachment
696 wp_insert_attachment($object, $cropped);
697 wp_update_attachment_metadata( $_POST['attachment_id'], wp_generate_attachment_metadata( $_POST['attachment_id'], $cropped ) );
699 set_theme_mod('header_image', $url);
702 $medium = str_replace(basename($original), 'midsize-'.basename($original), $original);
703 @unlink( apply_filters( 'wp_delete_file', $medium ) );
704 @unlink( apply_filters( 'wp_delete_file', $original ) );
706 return $this->finished();
710 * Display last step of custom header image page.
714 function finished() {
715 $this->updated = true;
720 * Display the page based on the current step.
724 function admin_page() {
725 if ( ! current_user_can('edit_theme_options') )
726 wp_die(__('You do not have permission to customize headers.'));
727 $step = $this->step();
730 elseif ( 2 == $step )
732 elseif ( 3 == $step )