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