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