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