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