· 8 years ago · Mar 02, 2018, 02:40 AM
1<?php
2
3class Custom_Image_Header {
4
5 /**
6 * Callback for administration header.
7 *
8 * @var callable
9 * @since 2.1.0
10 */
11 public $admin_header_callback;
12
13 /**
14 * Callback for header div.
15 *
16 * @var callable
17 * @since 3.0.0
18 */
19 public $admin_image_div_callback;
20
21 /**
22 * Holds default headers.
23 *
24 * @var array
25 * @since 3.0.0
26 */
27 public $default_headers = array();
28
29 /**
30 * Used to trigger a success message when settings updated and set to true.
31 *
32 * @since 3.0.0
33 * @var bool
34 */
35 private $updated;
36
37 /**
38 * Constructor - Register administration header callback.
39 *
40 * @since 2.1.0
41 * @param callable $admin_header_callback
42 * @param callable $admin_image_div_callback Optional custom image div output callback.
43 */
44 public function __construct($admin_header_callback, $admin_image_div_callback = '') {
45 $this->admin_header_callback = $admin_header_callback;
46 $this->admin_image_div_callback = $admin_image_div_callback;
47
48 add_action( 'admin_menu', array( $this, 'init' ) );
49
50 add_action( 'customize_save_after', array( $this, 'customize_set_last_used' ) );
51 add_action( 'wp_ajax_custom-header-crop', array( $this, 'ajax_header_crop' ) );
52 add_action( 'wp_ajax_custom-header-add', array( $this, 'ajax_header_add' ) );
53 add_action( 'wp_ajax_custom-header-remove', array( $this, 'ajax_header_remove' ) );
54 }
55
56 /**
57 * Set up the hooks for the Custom Header admin page.
58 *
59 * @since 2.1.0
60 */
61 public function init() {
62 $page = add_theme_page( __( 'Header' ), __( 'Header' ), 'edit_theme_options', 'custom-header', array( $this, 'admin_page' ) );
63 if ( ! $page ) {
64 return;
65 }
66
67 add_action( "admin_print_scripts-$page", array( $this, 'js_includes' ) );
68 add_action( "admin_print_styles-$page", array( $this, 'css_includes' ) );
69 add_action( "admin_head-$page", array( $this, 'help' ) );
70 add_action( "admin_head-$page", array( $this, 'take_action' ), 50 );
71 add_action( "admin_head-$page", array( $this, 'js' ), 50 );
72 if ( $this->admin_header_callback ) {
73 add_action( "admin_head-$page", $this->admin_header_callback, 51 );
74 }
75 }
76
77 /**
78 * Adds contextual help.
79 *
80 * @since 3.0.0
81 */
82 public function help() {
83 get_current_screen()->add_help_tab( array(
84 'id' => 'overview',
85 'title' => __('Overview'),
86 'content' =>
87 '<p>' . __( 'This screen is used to customize the header section of your theme.') . '</p>' .
88 '<p>' . __( 'You can choose from the theme’s default header images, or use one of your own. You can also customize how your Site Title and Tagline are displayed.') . '<p>'
89 ) );
90
91 get_current_screen()->add_help_tab( array(
92 'id' => 'set-header-image',
93 'title' => __('Header Image'),
94 'content' =>
95 '<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. Alternatively, you can use an image that has already been uploaded to your Media Library by clicking the “Choose Image” button.' ) . '</p>' .
96 '<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>' .
97 '<p>' . __( 'If your theme has more than one default header image, or you have uploaded more than one custom header image, you have the option of having WordPress display a randomly different image on each page of your site. Click the “Random” radio button next to the Uploaded Images or Default Images section to enable this feature.') . '</p>' .
98 '<p>' . __( 'If you don’t want a header image to be displayed on your site at all, click the “Remove Header Image” button at the bottom of the Header Image section of this page. If you want to re-enable the header image later, you just have to select one of the other image options and click “Save Changes”.') . '</p>'
99 ) );
100
101 get_current_screen()->add_help_tab( array(
102 'id' => 'set-header-text',
103 'title' => __('Header Text'),
104 'content' =>
105 '<p>' . sprintf( __( 'For most themes, the header text is your Site Title and Tagline, as defined in the <a href="%1$s">General Settings</a> section.' ), admin_url( 'options-general.php' ) ) . '<p>' .
106 '<p>' . __( 'In the Header Text section of this page, you can choose whether to display this text or hide it. You can also choose a color for the text by clicking the Select Color button and either typing in a legitimate HTML hex value, e.g. “#ff0000” for red, or by choosing a color using the color picker.' ) . '</p>' .
107 '<p>' . __( 'Don’t forget to click “Save Changes” when you’re done!') . '</p>'
108 ) );
109
110 get_current_screen()->set_help_sidebar(
111 '<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
112 '<p>' . __( '<a href="https://codex.wordpress.org/Appearance_Header_Screen">Documentation on Custom Header</a>' ) . '</p>' .
113 '<p>' . __( '<a href="https://wordpress.org/support/">Support Forums</a>' ) . '</p>'
114 );
115 }
116
117 /**
118 * Get the current step.
119 *
120 * @since 2.6.0
121 *
122 * @return int Current step
123 */
124 public function step() {
125 if ( ! isset( $_GET['step'] ) )
126 return 1;
127
128 $step = (int) $_GET['step'];
129 if ( $step < 1 || 3 < $step ||
130 ( 2 == $step && ! wp_verify_nonce( $_REQUEST['_wpnonce-custom-header-upload'], 'custom-header-upload' ) ) ||
131 ( 3 == $step && ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'custom-header-crop-image' ) )
132 )
133 return 1;
134
135 return $step;
136 }
137
138 /**
139 * Set up the enqueue for the JavaScript files.
140 *
141 * @since 2.1.0
142 */
143 public function js_includes() {
144 $step = $this->step();
145
146 if ( ( 1 == $step || 3 == $step ) ) {
147 wp_enqueue_media();
148 wp_enqueue_script( 'custom-header' );
149 if ( current_theme_supports( 'custom-header', 'header-text' ) )
150 wp_enqueue_script( 'wp-color-picker' );
151 } elseif ( 2 == $step ) {
152 wp_enqueue_script('imgareaselect');
153 }
154 }
155
156 /**
157 * Set up the enqueue for the CSS files
158 *
159 * @since 2.7.0
160 */
161 public function css_includes() {
162 $step = $this->step();
163
164 if ( ( 1 == $step || 3 == $step ) && current_theme_supports( 'custom-header', 'header-text' ) )
165 wp_enqueue_style( 'wp-color-picker' );
166 elseif ( 2 == $step )
167 wp_enqueue_style('imgareaselect');
168 }
169
170 /**
171 * Execute custom header modification.
172 *
173 * @since 2.6.0
174 */
175 public function take_action() {
176 if ( ! current_user_can('edit_theme_options') )
177 return;
178
179 if ( empty( $_POST ) )
180 return;
181
182 $this->updated = true;
183
184 if ( isset( $_POST['resetheader'] ) ) {
185 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
186 $this->reset_header_image();
187 return;
188 }
189
190 if ( isset( $_POST['removeheader'] ) ) {
191 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
192 $this->remove_header_image();
193 return;
194 }
195
196 if ( isset( $_POST['text-color'] ) && ! isset( $_POST['display-header-text'] ) ) {
197 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
198 set_theme_mod( 'header_textcolor', 'blank' );
199 } elseif ( isset( $_POST['text-color'] ) ) {
200 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
201 $_POST['text-color'] = str_replace( '#', '', $_POST['text-color'] );
202 $color = preg_replace('/[^0-9a-fA-F]/', '', $_POST['text-color']);
203 if ( strlen($color) == 6 || strlen($color) == 3 )
204 set_theme_mod('header_textcolor', $color);
205 elseif ( ! $color )
206 set_theme_mod( 'header_textcolor', 'blank' );
207 }
208
209 if ( isset( $_POST['default-header'] ) ) {
210 check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
211 $this->set_header_image( $_POST['default-header'] );
212 return;
213 }
214 }
215
216 /**
217 * Process the default headers
218 *
219 * @since 3.0.0
220 *
221 * @global array $_wp_default_headers
222 */
223 public function process_default_headers() {
224 global $_wp_default_headers;
225
226 if ( !isset($_wp_default_headers) )
227 return;
228
229 if ( ! empty( $this->default_headers ) ) {
230 return;
231 }
232
233 $this->default_headers = $_wp_default_headers;
234 $template_directory_uri = get_template_directory_uri();
235 $stylesheet_directory_uri = get_stylesheet_directory_uri();
236 foreach ( array_keys($this->default_headers) as $header ) {
237 $this->default_headers[$header]['url'] = sprintf( $this->default_headers[$header]['url'], $template_directory_uri, $stylesheet_directory_uri );
238 $this->default_headers[$header]['thumbnail_url'] = sprintf( $this->default_headers[$header]['thumbnail_url'], $template_directory_uri, $stylesheet_directory_uri );
239 }
240 }
241
242 /**
243 * Display UI for selecting one of several default headers.
244 *
245 * Show the random image option if this theme has multiple header images.
246 * Random image option is on by default if no header has been set.
247 *
248 * @since 3.0.0
249 *
250 * @param string $type The header type. One of 'default' (for the Uploaded Images control)
251 * or 'uploaded' (for the Uploaded Images control).
252 */
253 public function show_header_selector( $type = 'default' ) {
254 if ( 'default' == $type ) {
255 $headers = $this->default_headers;
256 } else {
257 $headers = get_uploaded_header_images();
258 $type = 'uploaded';
259 }
260
261 if ( 1 < count( $headers ) ) {
262 echo '<div class="random-header">';
263 echo '<label><input name="default-header" type="radio" value="random-' . $type . '-image"' . checked( is_random_header_image( $type ), true, false ) . ' />';
264 _e( '<strong>Random:</strong> Show a different image on each page.' );
265 echo '</label>';
266 echo '</div>';
267 }
268
269 echo '<div class="available-headers">';
270 foreach ( $headers as $header_key => $header ) {
271 $header_thumbnail = $header['thumbnail_url'];
272 $header_url = $header['url'];
273 $header_alt_text = empty( $header['alt_text'] ) ? '' : $header['alt_text'];
274 echo '<div class="default-header">';
275 echo '<label><input name="default-header" type="radio" value="' . esc_attr( $header_key ) . '" ' . checked( $header_url, get_theme_mod( 'header_image' ), false ) . ' />';
276 $width = '';
277 if ( !empty( $header['attachment_id'] ) )
278 $width = ' width="230"';
279 echo '<img src="' . set_url_scheme( $header_thumbnail ) . '" alt="' . esc_attr( $header_alt_text ) .'"' . $width . ' /></label>';
280 echo '</div>';
281 }
282 echo '<div class="clear"></div></div>';
283 }
284
285 /**
286 * Execute JavaScript depending on step.
287 *
288 * @since 2.1.0
289 */
290 public function js() {
291 $step = $this->step();
292 if ( ( 1 == $step || 3 == $step ) && current_theme_supports( 'custom-header', 'header-text' ) )
293 $this->js_1();
294 elseif ( 2 == $step )
295 $this->js_2();
296 }
297
298 /**
299 * Display JavaScript based on Step 1 and 3.
300 *
301 * @since 2.6.0
302 */
303 public function js_1() {
304 $default_color = '';
305 if ( current_theme_supports( 'custom-header', 'default-text-color' ) ) {
306 $default_color = get_theme_support( 'custom-header', 'default-text-color' );
307 if ( $default_color && false === strpos( $default_color, '#' ) ) {
308 $default_color = '#' . $default_color;
309 }
310 }
311 ?>
312<script type="text/javascript">
313(function($){
314 var default_color = '<?php echo $default_color; ?>',
315 header_text_fields;
316
317 function pickColor(color) {
318 $('#name').css('color', color);
319 $('#desc').css('color', color);
320 $('#text-color').val(color);
321 }
322
323 function toggle_text() {
324 var checked = $('#display-header-text').prop('checked'),
325 text_color;
326 header_text_fields.toggle( checked );
327 if ( ! checked )
328 return;
329 text_color = $('#text-color');
330 if ( '' == text_color.val().replace('#', '') ) {
331 text_color.val( default_color );
332 pickColor( default_color );
333 } else {
334 pickColor( text_color.val() );
335 }
336 }
337
338 $(document).ready(function() {
339 var text_color = $('#text-color');
340 header_text_fields = $('.displaying-header-text');
341 text_color.wpColorPicker({
342 change: function( event, ui ) {
343 pickColor( text_color.wpColorPicker('color') );
344 },
345 clear: function() {
346 pickColor( '' );
347 }
348 });
349 $('#display-header-text').click( toggle_text );
350 <?php if ( ! display_header_text() ) : ?>
351 toggle_text();
352 <?php endif; ?>
353 });
354})(jQuery);
355</script>
356<?php
357 }
358
359 /**
360 * Display JavaScript based on Step 2.
361 *
362 * @since 2.6.0
363 */
364 public function js_2() { ?>
365<script type="text/javascript">
366 function onEndCrop( coords ) {
367 jQuery( '#x1' ).val(coords.x);
368 jQuery( '#y1' ).val(coords.y);
369 jQuery( '#width' ).val(coords.w);
370 jQuery( '#height' ).val(coords.h);
371 }
372
373 jQuery(document).ready(function() {
374 var xinit = <?php echo absint( get_theme_support( 'custom-header', 'width' ) ); ?>;
375 var yinit = <?php echo absint( get_theme_support( 'custom-header', 'height' ) ); ?>;
376 var ratio = xinit / yinit;
377 var ximg = jQuery('img#upload').width();
378 var yimg = jQuery('img#upload').height();
379
380 if ( yimg < yinit || ximg < xinit ) {
381 if ( ximg / yimg > ratio ) {
382 yinit = yimg;
383 xinit = yinit * ratio;
384 } else {
385 xinit = ximg;
386 yinit = xinit / ratio;
387 }
388 }
389
390 jQuery('img#upload').imgAreaSelect({
391 handles: true,
392 keys: true,
393 show: true,
394 x1: 0,
395 y1: 0,
396 x2: xinit,
397 y2: yinit,
398 <?php
399 if ( ! current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' ) ) {
400 ?>
401 aspectRatio: xinit + ':' + yinit,
402 <?php
403 }
404 if ( ! current_theme_supports( 'custom-header', 'flex-height' ) ) {
405 ?>
406 maxHeight: <?php echo get_theme_support( 'custom-header', 'height' ); ?>,
407 <?php
408 }
409 if ( ! current_theme_supports( 'custom-header', 'flex-width' ) ) {
410 ?>
411 maxWidth: <?php echo get_theme_support( 'custom-header', 'width' ); ?>,
412 <?php
413 }
414 ?>
415 onInit: function () {
416 jQuery('#width').val(xinit);
417 jQuery('#height').val(yinit);
418 },
419 onSelectChange: function(img, c) {
420 jQuery('#x1').val(c.x1);
421 jQuery('#y1').val(c.y1);
422 jQuery('#width').val(c.width);
423 jQuery('#height').val(c.height);
424 }
425 });
426 });
427</script>
428<?php
429 }
430
431 /**
432 * Display first step of custom header image page.
433 *
434 * @since 2.1.0
435 */
436 public function step_1() {
437 $this->process_default_headers();
438?>
439
440<div class="wrap">
441<h1><?php _e( 'Custom Header' ); ?></h1>
442
443<?php if ( current_user_can( 'customize' ) ) { ?>
444<div class="notice notice-info hide-if-no-customize">
445 <p>
446 <?php
447 printf(
448 __( 'You can now manage and live-preview Custom Header in the <a href="%1$s">Customizer</a>.' ),
449 admin_url( 'customize.php?autofocus[control]=header_image' )
450 );
451 ?>
452 </p>
453</div>
454<?php } ?>
455
456<?php if ( ! empty( $this->updated ) ) { ?>
457<div id="message" class="updated">
458<p><?php printf( __( 'Header updated. <a href="%s">Visit your site</a> to see how it looks.' ), home_url( '/' ) ); ?></p>
459</div>
460<?php } ?>
461
462<h3><?php _e( 'Header Image' ); ?></h3>
463
464<table class="form-table">
465<tbody>
466
467<?php if ( get_custom_header() || display_header_text() ) : ?>
468<tr>
469<th scope="row"><?php _e( 'Preview' ); ?></th>
470<td>
471 <?php
472 if ( $this->admin_image_div_callback ) {
473 call_user_func( $this->admin_image_div_callback );
474 } else {
475 $custom_header = get_custom_header();
476 $header_image = get_header_image();
477
478 if ( $header_image ) {
479 $header_image_style = 'background-image:url(' . esc_url( $header_image ) . ');';
480 } else {
481 $header_image_style = '';
482 }
483
484 if ( $custom_header->width )
485 $header_image_style .= 'max-width:' . $custom_header->width . 'px;';
486 if ( $custom_header->height )
487 $header_image_style .= 'height:' . $custom_header->height . 'px;';
488 ?>
489 <div id="headimg" style="<?php echo $header_image_style; ?>">
490 <?php
491 if ( display_header_text() )
492 $style = ' style="color:#' . get_header_textcolor() . ';"';
493 else
494 $style = ' style="display:none;"';
495 ?>
496 <h1><a id="name" class="displaying-header-text" <?php echo $style; ?> onclick="return false;" href="<?php bloginfo('url'); ?>" tabindex="-1"><?php bloginfo( 'name' ); ?></a></h1>
497 <div id="desc" class="displaying-header-text" <?php echo $style; ?>><?php bloginfo( 'description' ); ?></div>
498 </div>
499 <?php } ?>
500</td>
501</tr>
502<?php endif; ?>
503
504<?php if ( current_user_can( 'upload_files' ) && current_theme_supports( 'custom-header', 'uploads' ) ) : ?>
505<tr>
506<th scope="row"><?php _e( 'Select Image' ); ?></th>
507<td>
508 <p><?php _e( 'You can select an image to be shown at the top of your site by uploading from your computer or choosing from your media library. After selecting an image you will be able to crop it.' ); ?><br />
509 <?php
510 if ( ! current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' ) ) {
511 printf( __( 'Images of exactly <strong>%1$d × %2$d pixels</strong> will be used as-is.' ) . '<br />', get_theme_support( 'custom-header', 'width' ), get_theme_support( 'custom-header', 'height' ) );
512 } elseif ( current_theme_supports( 'custom-header', 'flex-height' ) ) {
513 if ( ! current_theme_supports( 'custom-header', 'flex-width' ) )
514 printf(
515 /* translators: %s: size in pixels */
516 __( 'Images should be at least %s wide.' ) . ' ',
517 sprintf(
518 /* translators: %d: custom header width */
519 '<strong>' . __( '%d pixels' ) . '</strong>',
520 get_theme_support( 'custom-header', 'width' )
521 )
522 );
523 } elseif ( current_theme_supports( 'custom-header', 'flex-width' ) ) {
524 if ( ! current_theme_supports( 'custom-header', 'flex-height' ) )
525 printf(
526 /* translators: %s: size in pixels */
527 __( 'Images should be at least %s tall.' ) . ' ',
528 sprintf(
529 /* translators: %d: custom header height */
530 '<strong>' . __( '%d pixels' ) . '</strong>',
531 get_theme_support( 'custom-header', 'height' )
532 )
533 );
534 }
535 if ( current_theme_supports( 'custom-header', 'flex-height' ) || current_theme_supports( 'custom-header', 'flex-width' ) ) {
536 if ( current_theme_supports( 'custom-header', 'width' ) )
537 printf(
538 /* translators: %s: size in pixels */
539 __( 'Suggested width is %s.' ) . ' ',
540 sprintf(
541 /* translators: %d: custom header width */
542 '<strong>' . __( '%d pixels' ) . '</strong>',
543 get_theme_support( 'custom-header', 'width' )
544 )
545 );
546 if ( current_theme_supports( 'custom-header', 'height' ) )
547 printf(
548 /* translators: %s: size in pixels */
549 __( 'Suggested height is %s.' ) . ' ',
550 sprintf(
551 /* translators: %d: custom header height */
552 '<strong>' . __( '%d pixels' ) . '</strong>',
553 get_theme_support( 'custom-header', 'height' )
554 )
555 );
556 }
557 ?></p>
558 <form enctype="multipart/form-data" id="upload-form" class="wp-upload-form" method="post" action="<?php echo esc_url( add_query_arg( 'step', 2 ) ) ?>">
559 <p>
560 <label for="upload"><?php _e( 'Choose an image from your computer:' ); ?></label><br />
561 <input type="file" id="upload" name="import" />
562 <input type="hidden" name="action" value="save" />
563 <?php wp_nonce_field( 'custom-header-upload', '_wpnonce-custom-header-upload' ); ?>
564 <?php submit_button( __( 'Upload' ), '', 'submit', false ); ?>
565 </p>
566 <?php
567 $modal_update_href = esc_url( add_query_arg( array(
568 'page' => 'custom-header',
569 'step' => 2,
570 '_wpnonce-custom-header-upload' => wp_create_nonce('custom-header-upload'),
571 ), admin_url('themes.php') ) );
572 ?>
573 <p>
574 <label for="choose-from-library-link"><?php _e( 'Or choose an image from your media library:' ); ?></label><br />
575 <button id="choose-from-library-link" class="button"
576 data-update-link="<?php echo esc_attr( $modal_update_href ); ?>"
577 data-choose="<?php esc_attr_e( 'Choose a Custom Header' ); ?>"
578 data-update="<?php esc_attr_e( 'Set as header' ); ?>"><?php _e( 'Choose Image' ); ?></button>
579 </p>
580 </form>
581</td>
582</tr>
583<?php endif; ?>
584</tbody>
585</table>
586
587<form method="post" action="<?php echo esc_url( add_query_arg( 'step', 1 ) ) ?>">
588<?php submit_button( null, 'screen-reader-text', 'save-header-options', false ); ?>
589<table class="form-table">
590<tbody>
591 <?php if ( get_uploaded_header_images() ) : ?>
592<tr>
593<th scope="row"><?php _e( 'Uploaded Images' ); ?></th>
594<td>
595 <p><?php _e( 'You can choose one of your previously uploaded headers, or show a random one.' ) ?></p>
596 <?php
597 $this->show_header_selector( 'uploaded' );
598 ?>
599</td>
600</tr>
601 <?php endif;
602 if ( ! empty( $this->default_headers ) ) : ?>
603<tr>
604<th scope="row"><?php _e( 'Default Images' ); ?></th>
605<td>
606<?php if ( current_theme_supports( 'custom-header', 'uploads' ) ) : ?>
607 <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>
608<?php else: ?>
609 <p><?php _e( 'You can use one of these cool headers or show a random one on each page.' ) ?></p>
610<?php endif; ?>
611 <?php
612 $this->show_header_selector( 'default' );
613 ?>
614</td>
615</tr>
616 <?php endif;
617 if ( get_header_image() ) : ?>
618<tr>
619<th scope="row"><?php _e( 'Remove Image' ); ?></th>
620<td>
621 <p><?php _e( 'This will remove the header image. You will not be able to restore any customizations.' ) ?></p>
622 <?php submit_button( __( 'Remove Header Image' ), '', 'removeheader', false ); ?>
623</td>
624</tr>
625 <?php endif;
626
627 $default_image = sprintf( get_theme_support( 'custom-header', 'default-image' ), get_template_directory_uri(), get_stylesheet_directory_uri() );
628 if ( $default_image && get_header_image() != $default_image ) : ?>
629<tr>
630<th scope="row"><?php _e( 'Reset Image' ); ?></th>
631<td>
632 <p><?php _e( 'This will restore the original header image. You will not be able to restore any customizations.' ) ?></p>
633 <?php submit_button( __( 'Restore Original Header Image' ), '', 'resetheader', false ); ?>
634</td>
635</tr>
636 <?php endif; ?>
637</tbody>
638</table>
639
640<?php if ( current_theme_supports( 'custom-header', 'header-text' ) ) : ?>
641
642<h3><?php _e( 'Header Text' ); ?></h3>
643
644<table class="form-table">
645<tbody>
646<tr>
647<th scope="row"><?php _e( 'Header Text' ); ?></th>
648<td>
649 <p>
650 <label><input type="checkbox" name="display-header-text" id="display-header-text"<?php checked( display_header_text() ); ?> /> <?php _e( 'Show header text with your image.' ); ?></label>
651 </p>
652</td>
653</tr>
654
655<tr class="displaying-header-text">
656<th scope="row"><?php _e( 'Text Color' ); ?></th>
657<td>
658 <p>
659 <?php
660 $default_color = '';
661 if ( current_theme_supports( 'custom-header', 'default-text-color' ) ) {
662 $default_color = get_theme_support( 'custom-header', 'default-text-color' );
663 if ( $default_color && false === strpos( $default_color, '#' ) ) {
664 $default_color = '#' . $default_color;
665 }
666 }
667
668 $default_color_attr = $default_color ? ' data-default-color="' . esc_attr( $default_color ) . '"' : '';
669
670 $header_textcolor = display_header_text() ? get_header_textcolor() : get_theme_support( 'custom-header', 'default-text-color' );
671 if ( $header_textcolor && false === strpos( $header_textcolor, '#' ) ) {
672 $header_textcolor = '#' . $header_textcolor;
673 }
674
675 echo '<input type="text" name="text-color" id="text-color" value="' . esc_attr( $header_textcolor ) . '"' . $default_color_attr . ' />';
676 if ( $default_color ) {
677 echo ' <span class="description hide-if-js">' . sprintf( _x( 'Default: %s', 'color' ), esc_html( $default_color ) ) . '</span>';
678 }
679 ?>
680 </p>
681</td>
682</tr>
683</tbody>
684</table>
685<?php endif;
686
687/**
688 * Fires just before the submit button in the custom header options form.
689 *
690 * @since 3.1.0
691 */
692do_action( 'custom_header_options' );
693
694wp_nonce_field( 'custom-header-options', '_wpnonce-custom-header-options' ); ?>
695
696<?php submit_button( null, 'primary', 'save-header-options' ); ?>
697</form>
698</div>
699
700<?php }
701
702 /**
703 * Display second step of custom header image page.
704 *
705 * @since 2.1.0
706 */
707 public function step_2() {
708 check_admin_referer('custom-header-upload', '_wpnonce-custom-header-upload');
709 if ( ! current_theme_supports( 'custom-header', 'uploads' ) ) {
710 wp_die(
711 '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' .
712 '<p>' . __( 'The current theme does not support uploading a custom header image.' ) . '</p>',
713 403
714 );
715 }
716
717 if ( empty( $_POST ) && isset( $_GET['file'] ) ) {
718 $attachment_id = absint( $_GET['file'] );
719 $file = get_attached_file( $attachment_id, true );
720 $url = wp_get_attachment_image_src( $attachment_id, 'full' );
721 $url = $url[0];
722 } elseif ( isset( $_POST ) ) {
723 $data = $this->step_2_manage_upload();
724 $attachment_id = $data['attachment_id'];
725 $file = $data['file'];
726 $url = $data['url'];
727 }
728
729 if ( file_exists( $file ) ) {
730 list( $width, $height, $type, $attr ) = getimagesize( $file );
731 } else {
732 $data = wp_get_attachment_metadata( $attachment_id );
733 $height = isset( $data[ 'height' ] ) ? $data[ 'height' ] : 0;
734 $width = isset( $data[ 'width' ] ) ? $data[ 'width' ] : 0;
735 unset( $data );
736 }
737
738 $max_width = 0;
739 // For flex, limit size of image displayed to 1500px unless theme says otherwise
740 if ( current_theme_supports( 'custom-header', 'flex-width' ) )
741 $max_width = 1500;
742
743 if ( current_theme_supports( 'custom-header', 'max-width' ) )
744 $max_width = max( $max_width, get_theme_support( 'custom-header', 'max-width' ) );
745 $max_width = max( $max_width, get_theme_support( 'custom-header', 'width' ) );
746
747 // If flexible height isn't supported and the image is the exact right size
748 if ( ! current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' )
749 && $width == get_theme_support( 'custom-header', 'width' ) && $height == get_theme_support( 'custom-header', 'height' ) )
750 {
751 // Add the meta-data
752 if ( file_exists( $file ) )
753 wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file ) );
754
755 $this->set_header_image( compact( 'url', 'attachment_id', 'width', 'height' ) );
756
757 /**
758 * Fires after the header image is set or an error is returned.
759 *
760 * @since 2.1.0
761 *
762 * @param string $file Path to the file.
763 * @param int $attachment_id Attachment ID.
764 */
765 do_action( 'wp_create_file_in_uploads', $file, $attachment_id ); // For replication
766
767 return $this->finished();
768 } elseif ( $width > $max_width ) {
769 $oitar = $width / $max_width;
770 $image = wp_crop_image($attachment_id, 0, 0, $width, $height, $max_width, $height / $oitar, false, str_replace(basename($file), 'midsize-'.basename($file), $file));
771 if ( ! $image || is_wp_error( $image ) )
772 wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
773
774 /** This filter is documented in wp-admin/custom-header.php */
775 $image = apply_filters( 'wp_create_file_in_uploads', $image, $attachment_id ); // For replication
776
777 $url = str_replace(basename($url), basename($image), $url);
778 $width = $width / $oitar;
779 $height = $height / $oitar;
780 } else {
781 $oitar = 1;
782 }
783 ?>
784
785<div class="wrap">
786<h1><?php _e( 'Crop Header Image' ); ?></h1>
787
788<form method="post" action="<?php echo esc_url(add_query_arg('step', 3)); ?>">
789 <p class="hide-if-no-js"><?php _e('Choose the part of the image you want to use as your header.'); ?></p>
790 <p class="hide-if-js"><strong><?php _e( 'You need JavaScript to choose a part of the image.'); ?></strong></p>
791
792 <div id="crop_image" style="position: relative">
793 <img src="<?php echo esc_url( $url ); ?>" id="upload" width="<?php echo $width; ?>" height="<?php echo $height; ?>" alt="" />
794 </div>
795
796 <input type="hidden" name="x1" id="x1" value="0"/>
797 <input type="hidden" name="y1" id="y1" value="0"/>
798 <input type="hidden" name="width" id="width" value="<?php echo esc_attr( $width ); ?>"/>
799 <input type="hidden" name="height" id="height" value="<?php echo esc_attr( $height ); ?>"/>
800 <input type="hidden" name="attachment_id" id="attachment_id" value="<?php echo esc_attr( $attachment_id ); ?>" />
801 <input type="hidden" name="oitar" id="oitar" value="<?php echo esc_attr( $oitar ); ?>" />
802 <?php if ( empty( $_POST ) && isset( $_GET['file'] ) ) { ?>
803 <input type="hidden" name="create-new-attachment" value="true" />
804 <?php } ?>
805 <?php wp_nonce_field( 'custom-header-crop-image' ) ?>
806
807 <p class="submit">
808 <?php submit_button( __( 'Crop and Publish' ), 'primary', 'submit', false ); ?>
809 <?php
810 if ( isset( $oitar ) && 1 == $oitar && ( current_theme_supports( 'custom-header', 'flex-height' ) || current_theme_supports( 'custom-header', 'flex-width' ) ) )
811 submit_button( __( 'Skip Cropping, Publish Image as Is' ), '', 'skip-cropping', false );
812 ?>
813 </p>
814</form>
815</div>
816 <?php
817 }
818
819
820 /**
821 * Upload the file to be cropped in the second step.
822 *
823 * @since 3.4.0
824 */
825 public function step_2_manage_upload() {
826 $overrides = array('test_form' => false);
827
828 $uploaded_file = $_FILES['import'];
829 $wp_filetype = wp_check_filetype_and_ext( $uploaded_file['tmp_name'], $uploaded_file['name'] );
830 if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) )
831 wp_die( __( 'The uploaded file is not a valid image. Please try again.' ) );
832
833 $file = wp_handle_upload($uploaded_file, $overrides);
834
835 if ( isset($file['error']) )
836 wp_die( $file['error'], __( 'Image Upload Error' ) );
837
838 $url = $file['url'];
839 $type = $file['type'];
840 $file = $file['file'];
841 $filename = basename($file);
842
843 // Construct the object array
844 $object = array(
845 'post_title' => $filename,
846 'post_content' => $url,
847 'post_mime_type' => $type,
848 'guid' => $url,
849 'context' => 'custom-header'
850 );
851
852 // Save the data
853 $attachment_id = wp_insert_attachment( $object, $file );
854 return compact( 'attachment_id', 'file', 'filename', 'url', 'type' );
855 }
856
857 /**
858 * Display third step of custom header image page.
859 *
860 * @since 2.1.0
861 * @since 4.4.0 Switched to using wp_get_attachment_url() instead of the guid
862 * for retrieving the header image URL.
863 */
864 public function step_3() {
865 check_admin_referer( 'custom-header-crop-image' );
866
867 if ( ! current_theme_supports( 'custom-header', 'uploads' ) ) {
868 wp_die(
869 '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' .
870 '<p>' . __( 'The current theme does not support uploading a custom header image.' ) . '</p>',
871 403
872 );
873 }
874
875 if ( ! empty( $_POST['skip-cropping'] ) && ! ( current_theme_supports( 'custom-header', 'flex-height' ) || current_theme_supports( 'custom-header', 'flex-width' ) ) ) {
876 wp_die(
877 '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' .
878 '<p>' . __( 'The current theme does not support a flexible sized header image.' ) . '</p>',
879 403
880 );
881 }
882
883 if ( $_POST['oitar'] > 1 ) {
884 $_POST['x1'] = $_POST['x1'] * $_POST['oitar'];
885 $_POST['y1'] = $_POST['y1'] * $_POST['oitar'];
886 $_POST['width'] = $_POST['width'] * $_POST['oitar'];
887 $_POST['height'] = $_POST['height'] * $_POST['oitar'];
888 }
889
890 $attachment_id = absint( $_POST['attachment_id'] );
891 $original = get_attached_file($attachment_id);
892
893 $dimensions = $this->get_header_dimensions( array(
894 'height' => $_POST['height'],
895 'width' => $_POST['width'],
896 ) );
897 $height = $dimensions['dst_height'];
898 $width = $dimensions['dst_width'];
899
900 if ( empty( $_POST['skip-cropping'] ) )
901 $cropped = wp_crop_image( $attachment_id, (int) $_POST['x1'], (int) $_POST['y1'], (int) $_POST['width'], (int) $_POST['height'], $width, $height );
902 elseif ( ! empty( $_POST['create-new-attachment'] ) )
903 $cropped = _copy_image_file( $attachment_id );
904 else
905 $cropped = get_attached_file( $attachment_id );
906
907 if ( ! $cropped || is_wp_error( $cropped ) )
908 wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
909
910 /** This filter is documented in wp-admin/custom-header.php */
911 $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication
912
913 $object = $this->create_attachment_object( $cropped, $attachment_id );
914
915 if ( ! empty( $_POST['create-new-attachment'] ) )
916 unset( $object['ID'] );
917
918 // Update the attachment
919 $attachment_id = $this->insert_attachment( $object, $cropped );
920
921 $url = wp_get_attachment_url( $attachment_id );
922 $this->set_header_image( compact( 'url', 'attachment_id', 'width', 'height' ) );
923
924 // Cleanup.
925 $medium = str_replace( basename( $original ), 'midsize-' . basename( $original ), $original );
926 if ( file_exists( $medium ) ) {
927 wp_delete_file( $medium );
928 }
929
930 if ( empty( $_POST['create-new-attachment'] ) && empty( $_POST['skip-cropping'] ) ) {
931 wp_delete_file( $original );
932 }
933
934 return $this->finished();
935 }
936
937 /**
938 * Display last step of custom header image page.
939 *
940 * @since 2.1.0
941 */
942 public function finished() {
943 $this->updated = true;
944 $this->step_1();
945 }
946
947 /**
948 * Display the page based on the current step.
949 *
950 * @since 2.1.0
951 */
952 public function admin_page() {
953 if ( ! current_user_can('edit_theme_options') )
954 wp_die(__('Sorry, you are not allowed to customize headers.'));
955 $step = $this->step();
956 if ( 2 == $step )
957 $this->step_2();
958 elseif ( 3 == $step )
959 $this->step_3();
960 else
961 $this->step_1();
962 }
963
964 /**
965 * Unused since 3.5.0.
966 *
967 * @since 3.4.0
968 *
969 * @param array $form_fields
970 * @return array $form_fields
971 */
972 public function attachment_fields_to_edit( $form_fields ) {
973 return $form_fields;
974 }
975
976 /**
977 * Unused since 3.5.0.
978 *
979 * @since 3.4.0
980 *
981 * @param array $tabs
982 * @return array $tabs
983 */
984 public function filter_upload_tabs( $tabs ) {
985 return $tabs;
986 }
987
988 /**
989 * Choose a header image, selected from existing uploaded and default headers,
990 * or provide an array of uploaded header data (either new, or from media library).
991 *
992 * @since 3.4.0
993 *
994 * @param mixed $choice Which header image to select. Allows for values of 'random-default-image',
995 * for randomly cycling among the default images; 'random-uploaded-image', for randomly cycling
996 * among the uploaded images; the key of a default image registered for that theme; and
997 * the key of an image uploaded for that theme (the attachment ID of the image).
998 * Or an array of arguments: attachment_id, url, width, height. All are required.
999 */
1000 final public function set_header_image( $choice ) {
1001 if ( is_array( $choice ) || is_object( $choice ) ) {
1002 $choice = (array) $choice;
1003 if ( ! isset( $choice['attachment_id'] ) || ! isset( $choice['url'] ) )
1004 return;
1005
1006 $choice['url'] = esc_url_raw( $choice['url'] );
1007
1008 $header_image_data = (object) array(
1009 'attachment_id' => $choice['attachment_id'],
1010 'url' => $choice['url'],
1011 'thumbnail_url' => $choice['url'],
1012 'height' => $choice['height'],
1013 'width' => $choice['width'],
1014 );
1015
1016 update_post_meta( $choice['attachment_id'], '_wp_attachment_is_custom_header', get_stylesheet() );
1017 set_theme_mod( 'header_image', $choice['url'] );
1018 set_theme_mod( 'header_image_data', $header_image_data );
1019 return;
1020 }
1021
1022 if ( in_array( $choice, array( 'remove-header', 'random-default-image', 'random-uploaded-image' ) ) ) {
1023 set_theme_mod( 'header_image', $choice );
1024 remove_theme_mod( 'header_image_data' );
1025 return;
1026 }
1027
1028 $uploaded = get_uploaded_header_images();
1029 if ( $uploaded && isset( $uploaded[ $choice ] ) ) {
1030 $header_image_data = $uploaded[ $choice ];
1031
1032 } else {
1033 $this->process_default_headers();
1034 if ( isset( $this->default_headers[ $choice ] ) )
1035 $header_image_data = $this->default_headers[ $choice ];
1036 else
1037 return;
1038 }
1039
1040 set_theme_mod( 'header_image', esc_url_raw( $header_image_data['url'] ) );
1041 set_theme_mod( 'header_image_data', $header_image_data );
1042 }
1043
1044 /**
1045 * Remove a header image.
1046 *
1047 * @since 3.4.0
1048 */
1049 final public function remove_header_image() {
1050 $this->set_header_image( 'remove-header' );
1051 }
1052
1053 /**
1054 * Reset a header image to the default image for the theme.
1055 *
1056 * This method does not do anything if the theme does not have a default header image.
1057 *
1058 * @since 3.4.0
1059 */
1060 final public function reset_header_image() {
1061 $this->process_default_headers();
1062 $default = get_theme_support( 'custom-header', 'default-image' );
1063
1064 if ( ! $default ) {
1065 $this->remove_header_image();
1066 return;
1067 }
1068 $default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
1069
1070 $default_data = array();
1071 foreach ( $this->default_headers as $header => $details ) {
1072 if ( $details['url'] == $default ) {
1073 $default_data = $details;
1074 break;
1075 }
1076 }
1077
1078 set_theme_mod( 'header_image', $default );
1079 set_theme_mod( 'header_image_data', (object) $default_data );
1080 }
1081
1082 /**
1083 * Calculate width and height based on what the currently selected theme supports.
1084 *
1085 * @since 3.9.0
1086 *
1087 * @param array $dimensions
1088 * @return array dst_height and dst_width of header image.
1089 */
1090 final public function get_header_dimensions( $dimensions ) {
1091 $max_width = 0;
1092 $width = absint( $dimensions['width'] );
1093 $height = absint( $dimensions['height'] );
1094 $theme_height = get_theme_support( 'custom-header', 'height' );
1095 $theme_width = get_theme_support( 'custom-header', 'width' );
1096 $has_flex_width = current_theme_supports( 'custom-header', 'flex-width' );
1097 $has_flex_height = current_theme_supports( 'custom-header', 'flex-height' );
1098 $has_max_width = current_theme_supports( 'custom-header', 'max-width' ) ;
1099 $dst = array( 'dst_height' => null, 'dst_width' => null );
1100
1101 // For flex, limit size of image displayed to 1500px unless theme says otherwise
1102 if ( $has_flex_width ) {
1103 $max_width = 1500;
1104 }
1105
1106 if ( $has_max_width ) {
1107 $max_width = max( $max_width, get_theme_support( 'custom-header', 'max-width' ) );
1108 }
1109 $max_width = max( $max_width, $theme_width );
1110
1111 if ( $has_flex_height && ( ! $has_flex_width || $width > $max_width ) ) {
1112 $dst['dst_height'] = absint( $height * ( $max_width / $width ) );
1113 }
1114 elseif ( $has_flex_height && $has_flex_width ) {
1115 $dst['dst_height'] = $height;
1116 }
1117 else {
1118 $dst['dst_height'] = $theme_height;
1119 }
1120
1121 if ( $has_flex_width && ( ! $has_flex_height || $width > $max_width ) ) {
1122 $dst['dst_width'] = absint( $width * ( $max_width / $width ) );
1123 }
1124 elseif ( $has_flex_width && $has_flex_height ) {
1125 $dst['dst_width'] = $width;
1126 }
1127 else {
1128 $dst['dst_width'] = $theme_width;
1129 }
1130
1131 return $dst;
1132 }
1133
1134 /**
1135 * Create an attachment 'object'.
1136 *
1137 * @since 3.9.0
1138 *
1139 * @param string $cropped Cropped image URL.
1140 * @param int $parent_attachment_id Attachment ID of parent image.
1141 * @return array Attachment object.
1142 */
1143 final public function create_attachment_object( $cropped, $parent_attachment_id ) {
1144 $parent = get_post( $parent_attachment_id );
1145 $parent_url = wp_get_attachment_url( $parent->ID );
1146 $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
1147
1148 $size = @getimagesize( $cropped );
1149 $image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
1150
1151 $object = array(
1152 'ID' => $parent_attachment_id,
1153 'post_title' => basename($cropped),
1154 'post_mime_type' => $image_type,
1155 'guid' => $url,
1156 'context' => 'custom-header',
1157 'post_parent' => $parent_attachment_id,
1158 );
1159
1160 return $object;
1161 }
1162
1163 /**
1164 * Insert an attachment and its metadata.
1165 *
1166 * @since 3.9.0
1167 *
1168 * @param array $object Attachment object.
1169 * @param string $cropped Cropped image URL.
1170 * @return int Attachment ID.
1171 */
1172 final public function insert_attachment( $object, $cropped ) {
1173 $parent_id = isset( $object['post_parent'] ) ? $object['post_parent'] : null;
1174 unset( $object['post_parent'] );
1175
1176 $attachment_id = wp_insert_attachment( $object, $cropped );
1177 $metadata = wp_generate_attachment_metadata( $attachment_id, $cropped );
1178
1179 // If this is a crop, save the original attachment ID as metadata.
1180 if ( $parent_id ) {
1181 $metadata['attachment_parent'] = $parent_id;
1182 }
1183
1184 /**
1185 * Filters the header image attachment metadata.
1186 *
1187 * @since 3.9.0
1188 *
1189 * @see wp_generate_attachment_metadata()
1190 *
1191 * @param array $metadata Attachment metadata.
1192 */
1193 $metadata = apply_filters( 'wp_header_image_attachment_metadata', $metadata );
1194
1195 wp_update_attachment_metadata( $attachment_id, $metadata );
1196
1197 return $attachment_id;
1198 }
1199
1200 /**
1201 * Gets attachment uploaded by Media Manager, crops it, then saves it as a
1202 * new object. Returns JSON-encoded object details.
1203 *
1204 * @since 3.9.0
1205 */
1206 public function ajax_header_crop() {
1207 check_ajax_referer( 'image_editor-' . $_POST['id'], 'nonce' );
1208
1209 if ( ! current_user_can( 'edit_theme_options' ) ) {
1210 wp_send_json_error();
1211 }
1212
1213 if ( ! current_theme_supports( 'custom-header', 'uploads' ) ) {
1214 wp_send_json_error();
1215 }
1216
1217 $crop_details = $_POST['cropDetails'];
1218
1219 $dimensions = $this->get_header_dimensions( array(
1220 'height' => $crop_details['height'],
1221 'width' => $crop_details['width'],
1222 ) );
1223
1224 $attachment_id = absint( $_POST['id'] );
1225
1226 $cropped = wp_crop_image(
1227 $attachment_id,
1228 (int) $crop_details['x1'],
1229 (int) $crop_details['y1'],
1230 (int) $crop_details['width'],
1231 (int) $crop_details['height'],
1232 (int) $dimensions['dst_width'],
1233 (int) $dimensions['dst_height']
1234 );
1235
1236 if ( ! $cropped || is_wp_error( $cropped ) ) {
1237 wp_send_json_error( array( 'message' => __( 'Image could not be processed. Please go back and try again.' ) ) );
1238 }
1239
1240 /** This filter is documented in wp-admin/custom-header.php */
1241 $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication
1242
1243 $object = $this->create_attachment_object( $cropped, $attachment_id );
1244
1245 $previous = $this->get_previous_crop( $object );
1246
1247 if ( $previous ) {
1248 $object['ID'] = $previous;
1249 } else {
1250 unset( $object['ID'] );
1251 }
1252
1253 $new_attachment_id = $this->insert_attachment( $object, $cropped );
1254
1255 $object['attachment_id'] = $new_attachment_id;
1256 $object['url'] = wp_get_attachment_url( $new_attachment_id );;
1257 $object['width'] = $dimensions['dst_width'];
1258 $object['height'] = $dimensions['dst_height'];
1259
1260 wp_send_json_success( $object );
1261 }
1262
1263 /**
1264 * Given an attachment ID for a header image, updates its "last used"
1265 * timestamp to now.
1266 *
1267 * Triggered when the user tries adds a new header image from the
1268 * Media Manager, even if s/he doesn't save that change.
1269 *
1270 * @since 3.9.0
1271 */
1272 public function ajax_header_add() {
1273 check_ajax_referer( 'header-add', 'nonce' );
1274
1275 if ( ! current_user_can( 'edit_theme_options' ) ) {
1276 wp_send_json_error();
1277 }
1278
1279 $attachment_id = absint( $_POST['attachment_id'] );
1280 if ( $attachment_id < 1 ) {
1281 wp_send_json_error();
1282 }
1283
1284 $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
1285 update_post_meta( $attachment_id, $key, time() );
1286 update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', get_stylesheet() );
1287
1288 wp_send_json_success();
1289 }
1290
1291 /**
1292 * Given an attachment ID for a header image, unsets it as a user-uploaded
1293 * header image for the current theme.
1294 *
1295 * Triggered when the user clicks the overlay "X" button next to each image
1296 * choice in the Customizer's Header tool.
1297 *
1298 * @since 3.9.0
1299 */
1300 public function ajax_header_remove() {
1301 check_ajax_referer( 'header-remove', 'nonce' );
1302
1303 if ( ! current_user_can( 'edit_theme_options' ) ) {
1304 wp_send_json_error();
1305 }
1306
1307 $attachment_id = absint( $_POST['attachment_id'] );
1308 if ( $attachment_id < 1 ) {
1309 wp_send_json_error();
1310 }
1311
1312 $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
1313 delete_post_meta( $attachment_id, $key );
1314 delete_post_meta( $attachment_id, '_wp_attachment_is_custom_header', get_stylesheet() );
1315
1316 wp_send_json_success();
1317 }
1318
1319 /**
1320 * Updates the last-used postmeta on a header image attachment after saving a new header image via the Customizer.
1321 *
1322 * @since 3.9.0
1323 *
1324 * @param WP_Customize_Manager $wp_customize Customize manager.
1325 */
1326 public function customize_set_last_used( $wp_customize ) {
1327
1328 $header_image_data_setting = $wp_customize->get_setting( 'header_image_data' );
1329 if ( ! $header_image_data_setting ) {
1330 return;
1331 }
1332 $data = $header_image_data_setting->post_value();
1333
1334 if ( ! isset( $data['attachment_id'] ) ) {
1335 return;
1336 }
1337
1338 $attachment_id = $data['attachment_id'];
1339 $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
1340 update_post_meta( $attachment_id, $key, time() );
1341 }
1342
1343 /**
1344 * Gets the details of default header images if defined.
1345 *
1346 * @since 3.9.0
1347 *
1348 * @return array Default header images.
1349 */
1350 public function get_default_header_images() {
1351 $this->process_default_headers();
1352
1353 // Get the default image if there is one.
1354 $default = get_theme_support( 'custom-header', 'default-image' );
1355
1356 if ( ! $default ) { // If not,
1357 return $this->default_headers; // easy peasy.
1358 }
1359
1360 $default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
1361 $already_has_default = false;
1362
1363 foreach ( $this->default_headers as $k => $h ) {
1364 if ( $h['url'] === $default ) {
1365 $already_has_default = true
1366 break;
1367 }
1368 }
1369
1370 if ( $already_has_default ) {
1371 return $this->default_headers;
1372 }
1373
1374 // If the one true image isn't included in the default set, prepend it.
1375 $header_images = array();
1376 $header_images['default'] = array(
1377 'url' => $default,
1378 'thumbnail_url' => $default,
1379 'description' => 'Default'
1380 );
1381
1382 // The rest of the set comes after.
1383 return array_merge( $header_images, $this->default_headers );
1384 }
1385
1386 /**
1387 * Gets the previously uploaded header images.
1388 *
1389 * @since 3.9.0
1390 *
1391 * @return array Uploaded header images.
1392 */
1393 public function get_uploaded_header_images() {
1394 $header_images = get_uploaded_header_images();
1395 $timestamp_key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
1396 $alt_text_key = '_wp_attachment_image_alt';
1397
1398 foreach ( $header_images as &$header_image ) {
1399 $header_meta = get_post_meta( $header_image['attachment_id'] );
1400 $header_image['timestamp'] = isset( $header_meta[ $timestamp_key ] ) ? $header_meta[ $timestamp_key ] : '';
1401 $header_image['alt_text'] = isset( $header_meta[ $alt_text_key ] ) ? $header_meta[ $alt_text_key ] : '';
1402 }
1403
1404 return $header_images;
1405 }
1406
1407 /**
1408 * Get the ID of a previous crop from the same base image.
1409 *
1410 * @since 4.9.0
1411 *
1412 * @param array $object A crop attachment object.
1413 * @return int|false An attachment ID if one exists. False if none.
1414 */
1415 public function get_previous_crop( $object ) {
1416 $header_images = $this->get_uploaded_header_images();
1417
1418 // Bail early if there are no header images.
1419 if ( empty( $header_images ) ) {
1420 return false;
1421 }
1422
1423 $previous = false;
1424
1425 foreach ( $header_images as $image ) {
1426 if ( $image['attachment_parent'] === $object['post_parent'] ) {
1427 $previous = $image['attachment_id'];
1428 break;
1429 }
1430 }
1431
1432 return $previous;
1433 }
1434}