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