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