<?php
/**
 * WordPress Bookmark Administration API
 *
 * @package WordPress
 * @subpackage Administration
 */

/**
 * Add a link to using values provided in $_POST.
 *
 * @since 2.0.0
 *
 * @return int|WP_Error Value 0 or WP_Error on failure. The link ID on success.
 */
function add_link() {
	return edit_link();
}

/**
 * Updates or inserts a link using values provided in $_POST.
 *
 * @since 2.0.0
 *
 * @param int $link_id Optional. ID of the link to edit. Default 0.
 * @return int|WP_Error Value 0 or WP_Error on failure. The link ID on success.
 */
function edit_link( $link_id = 0 ) {
	if ( ! current_user_can( 'manage_links' ) ) {
		wp_die(
			'
' . __( 'You need a higher level of permission.' ) . '
' .
			'
' . __( 'Sorry, you are not allowed to edit the links for this site.' ) . '
',
			403
		);
	}

	$_POST['link_url']   = esc_html( $_POST['link_url'] );
	$_POST['link_url']   = esc_url( $_POST['link_url'] );
	$_POST['link_name']  = esc_html( $_POST['link_name'] );
	$_POST['link_image'] = esc_html( $_POST['link_image'] );
	$_POST['link_rss']   = esc_url( $_POST['link_rss'] );
	if ( ! isset( $_POST['link_visible'] ) || 'N' !== $_POST['link_visible'] ) {
		$_POST['link_visible'] = 'Y';
	}

	if ( ! empty( $link_id ) ) {
		$_POST['link_id'] = $link_id;
		return wp_update_link( $_POST );
	} else {
		return wp_insert_link( $_POST );
	}
}

/**
 * Retrieves the default link for editing.
 *
 * @since 2.0.0
 *
 * @return stdClass Default link object.
 */
function get_default_link_to_edit() {
	$link = new stdClass;
	if ( isset( $_GET['linkurl'] ) ) {
		$link->link_url = esc_url( wp_unslash( $_GET['linkurl'] ) );
	} else {
		$link->link_url = '';
	}

	if ( isset( $_GET['name'] ) ) {
		$link->link_name = esc_attr( wp_unslash( $_GET['name'] ) );
	} else {
		$link->link_name = '';
	}

	$link->link_visible = 'Y';

	return $link;
}

/**
 * Deletes a specified link from the database.
 *
 * @since 2.0.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @param int $link_id ID of the link to delete
 * @return true Always true.
 */
function wp_delete_link( $link_id ) {
	global $wpdb;
	/**
	 * Fires before a link is deleted.
	 *
	 * @since 2.0.0
	 *
	 * @param int $link_id ID of the link to delete.
	 */
	do_action( 'delete_link', $link_id );

	wp_delete_object_term_relationships( $link_id, 'link_category' );

	$wpdb->delete( $wpdb->links, array( 'link_id' => $link_id ) );

	/**
	 * Fires after a link has been deleted.
	 *
	 * @since 2.2.0
	 *
	 * @param int $link_id ID of the deleted link.
	 */
	do_action( 'deleted_link', $link_id );

	clean_bookmark_cache( $link_id );

	return true;
}

/**
 * Retrieves the link category IDs associated with the link specified.
 *
 * @since 2.1.0
 *
 * @param int $link_id Link ID to look up.
 * @return int[] The IDs of the requested link's categories.
 */
function wp_get_link_cats( $link_id = 0 ) {
	$cats = wp_get_object_terms( $link_id, 'link_category', array( 'fields' => 'ids' ) );
	return array_unique( $cats );
}

/**
 * Retrieves link data based on its ID.
 *
 * @since 2.0.0
 *
 * @param int|stdClass $link Link ID or object to retrieve.
 * @return object Link object for editing.
 */
function get_link_to_edit( $link ) {
	return get_bookmark( $link, OBJECT, 'edit' );
}

/**
 * Inserts/updates links into/in the database.
 *
 * @since 2.0.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @param array $linkdata Elements that make up the link to insert.
 * @param bool  $wp_error Optional. Whether to return a WP_Error object on failure. Default false.
 * @return int|WP_Error Value 0 or WP_Error on failure. The link ID on success.
 */
function wp_insert_link( $linkdata, $wp_error = false ) {
	global $wpdb;

	$defaults = array(
		'link_id'     => 0,
		'link_name'   => '',
		'link_url'    => '',
		'link_rating' => 0,
	);

	$parsed_args = wp_parse_args( $linkdata, $defaults );
	$parsed_args = wp_unslash( sanitize_bookmark( $parsed_args, 'db' ) );

	$link_id   = $parsed_args['link_id'];
	$link_name = $parsed_args['link_name'];
	$link_url  = $parsed_args['link_url'];

	$update = false;
	if ( ! empty( $link_id ) ) {
		$update = true;
	}

	if ( '' === trim( $link_name ) ) {
		if ( '' !== trim( $link_url ) ) {
			$link_name = $link_url;
		} else {
			return 0;
		}
	}

	if ( '' === trim( $link_url ) ) {
		return 0;
	}

	$link_rating      = ( ! empty( $parsed_args['link_rating'] ) ) ? $parsed_args['link_rating'] : 0;
	$link_image       = ( ! empty( $parsed_args['link_image'] ) ) ? $parsed_args['link_image'] : '';
	$link_target      = ( ! empty( $parsed_args['link_target'] ) ) ? $parsed_args['link_target'] : '';
	$link_visible     = ( ! empty( $parsed_args['link_visible'] ) ) ? $parsed_args['link_visible'] : 'Y';
	$link_owner       = ( ! empty( $parsed_args['link_owner'] ) ) ? $parsed_args['link_owner'] : get_current_user_id();
	$link_notes       = ( ! empty( $parsed_args['link_notes'] ) ) ? $parsed_args['link_notes'] : '';
	$link_description = ( ! empty( $parsed_args['link_description'] ) ) ? $parsed_args['link_description'] : '';
	$link_rss         = ( ! empty( $parsed_args['link_rss'] ) ) ? $parsed_args['link_rss'] : '';
	$link_rel         = ( ! empty( $parsed_args['link_rel'] ) ) ? $parsed_args['link_rel'] : '';
	$link_category    = ( ! empty( $parsed_args['link_category'] ) ) ? $parsed_args['link_category'] : array();

	// Make sure we set a valid category.
	if ( ! is_array( $link_category ) || 0 === count( $link_category ) ) {
		$link_category = array( get_option( 'default_link_category' ) );
	}

	if ( $update ) {
		if ( false === $wpdb->update( $wpdb->links, compact( 'link_url', 'link_name', 'link_image', 'link_target', 'link_description', 'link_visible', 'link_owner', 'link_rating', 'link_rel', 'link_notes', 'link_rss' ), compact( 'link_id' ) ) ) {
			if ( $wp_error ) {
				return new WP_Error( 'db_update_error', __( 'Could not update link in the database.' ), $wpdb->last_error );
			} else {
				return 0;
			}
		}
	} else {
		if ( false === $wpdb->insert( $wpdb->links, compact( 'link_url', 'link_name', 'link_image', 'link_target', 'link_description', 'link_visible', 'link_owner', 'link_rating', 'link_rel', 'link_notes', 'link_rss' ) ) ) {
			if ( $wp_error ) {
				return new WP_Error( 'db_insert_error', __( 'Could not insert link into the database.' ), $wpdb->last_error );
			} else {
				return 0;
			}
		}
		$link_id = (int) $wpdb->insert_id;
	}

	wp_set_link_cats( $link_id, $link_category );

	if ( $update ) {
		/**
		 * Fires after a link was updated in the database.
		 *
		 * @since 2.0.0
		 *
		 * @param int $link_id ID of the link that was updated.
		 */
		do_action( 'edit_link', $link_id );
	} else {
		/**
		 * Fires after a link was added to the database.
		 *
		 * @since 2.0.0
		 *
		 * @param int $link_id ID of the link that was added.
		 */
		do_action( 'add_link', $link_id );
	}
	clean_bookmark_cache( $link_id );

	return $link_id;
}

/**
 * Update link with the specified link categories.
 *
 * @since 2.1.0
 *
 * @param int   $link_id         ID of the link to update.
 * @param int[] $link_categories Array of link category IDs to add the link to.
 */
function wp_set_link_cats( $link_id = 0, $link_categories = array() ) {
	// If $link_categories isn't already an array, make it one:
	if ( ! is_array( $link_categories ) || 0 === count( $link_categories ) ) {
		$link_categories = array( get_option( 'default_link_category' ) );
	}

	$link_categories = array_map( 'intval', $link_categories );
	$link_categories = array_unique( $link_categories );

	wp_set_object_terms( $link_id, $link_categories, 'link_category' );

	clean_bookmark_cache( $link_id );
}

/**
 * Updates a link in the database.
 *
 * @since 2.0.0
 *
 * @param array $linkdata Link data to update.
 * @return int|WP_Error Value 0 or WP_Error on failure. The updated link ID on success.
 */
function wp_update_link( $linkdata ) {
	$link_id = (int) $linkdata['link_id'];

	$link = get_bookmark( $link_id, ARRAY_A );

	// Escape data pulled from DB.
	$link = wp_slash( $link );

	// Passed link category list overwrites existing category list if not empty.
	if ( isset( $linkdata['link_category'] ) && is_array( $linkdata['link_category'] )
		&& count( $linkdata['link_category'] ) > 0
	) {
		$link_cats = $linkdata['link_category'];
	} else {
		$link_cats = $link['link_category'];
	}

	// Merge old and new fields with new fields overwriting old ones.
	$linkdata                  = array_merge( $link, $linkdata );
	$linkdata['link_category'] = $link_cats;

	return wp_insert_link( $linkdata );
}

/**
 * Outputs the 'disabled' message for the WordPress Link Manager.
 *
 * @since 3.5.0
 * @access private
 *
 * @global string $pagenow
 */
function wp_link_manager_disabled_message() {
	global $pagenow;

	if ( ! in_array( $pagenow, array( 'link-manager.php', 'link-add.php', 'link.php' ), true ) ) {
		return;
	}

	add_filter( 'pre_option_link_manager_enabled', '__return_true', 100 );
	$really_can_manage_links = current_user_can( 'manage_links' );
	remove_filter( 'pre_option_link_manager_enabled', '__return_true', 100 );

	if ( $really_can_manage_links && current_user_can( 'install_plugins' ) ) {
		$link = network_admin_url( 'plugin-install.php?tab=search&s=Link+Manager' );
		/* translators: %s: URL to install the Link Manager plugin. */
		wp_die( sprintf( __( 'If you are looking to use the link manager, please install the Link Manager plugin.' ), $link ) );
	}

	wp_die( __( 'Sorry, you are not allowed to edit the links for this site.' ) );
}


<?php
/**
 * Upgrader API: Automatic_Upgrader_Skin class
 *
 * @package WordPress
 * @subpackage Upgrader
 * @since 4.6.0
 */

/**
 * Upgrader Skin for Automatic WordPress Upgrades
 *
 * This skin is designed to be used when no output is intended, all output
 * is captured and stored for the caller to process and log/email/discard.
 *
 * @since 3.7.0
 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader-skins.php.
 *
 * @see Bulk_Upgrader_Skin
 */
class Automatic_Upgrader_Skin extends WP_Upgrader_Skin {
	protected $messages = array();

	/**
	 * Determines whether the upgrader needs FTP/SSH details in order to connect
	 * to the filesystem.
	 *
	 * @since 3.7.0
	 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string.
	 *
	 * @see request_filesystem_credentials()
	 *
	 * @param bool|WP_Error $error                        Optional. Whether the current request has failed to connect,
	 *                                                    or an error object. Default false.
	 * @param string        $context                      Optional. Full path to the directory that is tested
	 *                                                    for being writable. Default empty.
	 * @param bool          $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. Default false.
	 * @return bool True on success, false on failure.
	 */
	public function request_filesystem_credentials( $error = false, $context = '', $allow_relaxed_file_ownership = false ) {
		if ( $context ) {
			$this->options['context'] = $context;
		}
		/*
		 * TODO: Fix up request_filesystem_credentials(), or split it, to allow us to request a no-output version.
		 * This will output a credentials form in event of failure. We don't want that, so just hide with a buffer.
		 */
		ob_start();
		$result = parent::request_filesystem_credentials( $error, $context, $allow_relaxed_file_ownership );
		ob_end_clean();
		return $result;
	}

	/**
	 * @return array
	 */
	public function get_upgrade_messages() {
		return $this->messages;
	}

	/**
	 * @param string|array|WP_Error $data
	 * @param mixed                 ...$args Optional text replacements.
	 */
	public function feedback( $data, ...$args ) {
		if ( is_wp_error( $data ) ) {
			$string = $data->get_error_message();
		} elseif ( is_array( $data ) ) {
			return;
		} else {
			$string = $data;
		}
		if ( ! empty( $this->upgrader->strings[ $string ] ) ) {
			$string = $this->upgrader->strings[ $string ];
		}

		if ( strpos( $string, '%' ) !== false ) {
			if ( ! empty( $args ) ) {
				$string = vsprintf( $string, $args );
			}
		}

		$string = trim( $string );

		// Only allow basic HTML in the messages, as it'll be used in emails/logs rather than direct browser output.
		$string = wp_kses(
			$string,
			array(
				'a'      => array(
					'href' => true,
				),
				'br'     => true,
				'em'     => true,
				'strong' => true,
			)
		);

		if ( empty( $string ) ) {
			return;
		}

		$this->messages[] = $string;
	}

	/**
	 */
	public function header() {
		ob_start();
	}

	/**
	 */
	public function footer() {
		$output = ob_get_clean();
		if ( ! empty( $output ) ) {
			$this->feedback( $output );
		}
	}
}


<?php
/**
 * Upgrader API: Bulk_Plugin_Upgrader_Skin class
 *
 * @package WordPress
 * @subpackage Upgrader
 * @since 4.6.0
 */

/**
 * Bulk Plugin Upgrader Skin for WordPress Plugin Upgrades.
 *
 * @since 3.0.0
 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader-skins.php.
 *
 * @see Bulk_Upgrader_Skin
 */
class Bulk_Plugin_Upgrader_Skin extends Bulk_Upgrader_Skin {
	public $plugin_info = array(); // Plugin_Upgrader::bulk_upgrade() will fill this in.

	public function add_strings() {
		parent::add_strings();
		/* translators: 1: Plugin name, 2: Number of the plugin, 3: Total number of plugins being updated. */
		$this->upgrader->strings['skin_before_update_header'] = __( 'Updating Plugin %1$s (%2$d/%3$d)' );
	}

	/**
	 * @param string $title
	 */
	public function before( $title = '' ) {
		parent::before( $this->plugin_info['Title'] );
	}

	/**
	 * @param string $title
	 */
	public function after( $title = '' ) {
		parent::after( $this->plugin_info['Title'] );
		$this->decrement_update_count( 'plugin' );
	}

	/**
	 */
	public function bulk_footer() {
		parent::bulk_footer();

		$update_actions = array(
			'plugins_page' => sprintf(
				'%s',
				self_admin_url( 'plugins.php' ),
				__( 'Return to Plugins page' )
			),
			'updates_page' => sprintf(
				'%s',
				self_admin_url( 'update-core.php' ),
				__( 'Return to WordPress Updates page' )
			),
		);

		if ( ! current_user_can( 'activate_plugins' ) ) {
			unset( $update_actions['plugins_page'] );
		}

		/**
		 * Filters the list of action links available following bulk plugin updates.
		 *
		 * @since 3.0.0
		 *
		 * @param string[] $update_actions Array of plugin action links.
		 * @param array    $plugin_info    Array of information for the last-updated plugin.
		 */
		$update_actions = apply_filters( 'update_bulk_plugins_complete_actions', $update_actions, $this->plugin_info );

		if ( ! empty( $update_actions ) ) {
			$this->feedback( implode( ' | ', (array) $update_actions ) );
		}
	}
}


<?php
/**
 * Upgrader API: Bulk_Plugin_Upgrader_Skin class
 *
 * @package WordPress
 * @subpackage Upgrader
 * @since 4.6.0
 */

/**
 * Bulk Theme Upgrader Skin for WordPress Theme Upgrades.
 *
 * @since 3.0.0
 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader-skins.php.
 *
 * @see Bulk_Upgrader_Skin
 */
class Bulk_Theme_Upgrader_Skin extends Bulk_Upgrader_Skin {
	public $theme_info = array(); // Theme_Upgrader::bulk_upgrade() will fill this in.

	public function add_strings() {
		parent::add_strings();
		/* translators: 1: Theme name, 2: Number of the theme, 3: Total number of themes being updated. */
		$this->upgrader->strings['skin_before_update_header'] = __( 'Updating Theme %1$s (%2$d/%3$d)' );
	}

	/**
	 * @param string $title
	 */
	public function before( $title = '' ) {
		parent::before( $this->theme_info->display( 'Name' ) );
	}

	/**
	 * @param string $title
	 */
	public function after( $title = '' ) {
		parent::after( $this->theme_info->display( 'Name' ) );
		$this->decrement_update_count( 'theme' );
	}

	/**
	 */
	public function bulk_footer() {
		parent::bulk_footer();

		$update_actions = array(
			'themes_page'  => sprintf(
				'%s',
				self_admin_url( 'themes.php' ),
				__( 'Return to Themes page' )
			),
			'updates_page' => sprintf(
				'%s',
				self_admin_url( 'update-core.php' ),
				__( 'Return to WordPress Updates page' )
			),
		);

		if ( ! current_user_can( 'switch_themes' ) && ! current_user_can( 'edit_theme_options' ) ) {
			unset( $update_actions['themes_page'] );
		}

		/**
		 * Filters the list of action links available following bulk theme updates.
		 *
		 * @since 3.0.0
		 *
		 * @param string[] $update_actions Array of theme action links.
		 * @param WP_Theme $theme_info     Theme object for the last-updated theme.
		 */
		$update_actions = apply_filters( 'update_bulk_theme_complete_actions', $update_actions, $this->theme_info );

		if ( ! empty( $update_actions ) ) {
			$this->feedback( implode( ' | ', (array) $update_actions ) );
		}
	}
}


<?php
/**
 * Upgrader API: Bulk_Upgrader_Skin class
 *
 * @package WordPress
 * @subpackage Upgrader
 * @since 4.6.0
 */

/**
 * Generic Bulk Upgrader Skin for WordPress Upgrades.
 *
 * @since 3.0.0
 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader-skins.php.
 *
 * @see WP_Upgrader_Skin
 */
class Bulk_Upgrader_Skin extends WP_Upgrader_Skin {
	public $in_loop = false;
	/**
	 * @var string|false
	 */
	public $error = false;

	/**
	 * @param array $args
	 */
	public function __construct( $args = array() ) {
		$defaults = array(
			'url'   => '',
			'nonce' => '',
		);
		$args     = wp_parse_args( $args, $defaults );

		parent::__construct( $args );
	}

	/**
	 */
	public function add_strings() {
		$this->upgrader->strings['skin_upgrade_start'] = __( 'The update process is starting. This process may take a while on some hosts, so please be patient.' );
		/* translators: 1: Title of an update, 2: Error message. */
		$this->upgrader->strings['skin_update_failed_error'] = __( 'An error occurred while updating %1$s: %2$s' );
		/* translators: %s: Title of an update. */
		$this->upgrader->strings['skin_update_failed'] = __( 'The update of %s failed.' );
		/* translators: %s: Title of an update. */
		$this->upgrader->strings['skin_update_successful'] = __( '%s updated successfully.' );
		$this->upgrader->strings['skin_upgrade_end']       = __( 'All updates have been completed.' );
	}

	/**
	 * @param string $string
	 * @param mixed  ...$args Optional text replacements.
	 */
	public function feedback( $string, ...$args ) {
		if ( isset( $this->upgrader->strings[ $string ] ) ) {
			$string = $this->upgrader->strings[ $string ];
		}

		if ( strpos( $string, '%' ) !== false ) {
			if ( $args ) {
				$args   = array_map( 'strip_tags', $args );
				$args   = array_map( 'esc_html', $args );
				$string = vsprintf( $string, $args );
			}
		}
		if ( empty( $string ) ) {
			return;
		}
		if ( $this->in_loop ) {
			echo "$string
\n";
		} else {
			echo "
$string
\n";
		}
	}

	/**
	 */
	public function header() {
		// Nothing, This will be displayed within a iframe.
	}

	/**
	 */
	public function footer() {
		// Nothing, This will be displayed within a iframe.
	}

	/**
	 * @param string|WP_Error $error
	 */
	public function error( $error ) {
		if ( is_string( $error ) && isset( $this->upgrader->strings[ $error ] ) ) {
			$this->error = $this->upgrader->strings[ $error ];
		}

		if ( is_wp_error( $error ) ) {
			$messages = array();
			foreach ( $error->get_error_messages() as $emessage ) {
				if ( $error->get_error_data() && is_string( $error->get_error_data() ) ) {
					$messages[] = $emessage . ' ' . esc_html( strip_tags( $error->get_error_data() ) );
				} else {
					$messages[] = $emessage;
				}
			}
			$this->error = implode( ', ', $messages );
		}
		echo '';
	}

	/**
	 */
	public function bulk_header() {
		$this->feedback( 'skin_upgrade_start' );
	}

	/**
	 */
	public function bulk_footer() {
		$this->feedback( 'skin_upgrade_end' );
	}

	/**
	 * @param string $title
	 */
	public function before( $title = '' ) {
		$this->in_loop = true;
		printf( '
' . $this->upgrader->strings['skin_before_update_header'] . ' upgrader->update_current . '">
', $title, $this->upgrader->update_current, $this->upgrader->update_count );
		echo '';
		// This progress messages div gets moved via JavaScript when clicking on "Show details.".
		echo '
upgrader->update_current ) . '">
';
		$this->flush_output();
	}

	/**
	 * @param string $title
	 */
	public function after( $title = '' ) {
		echo '

';
		if ( $this->error || ! $this->result ) {
			if ( $this->error ) {
				echo '

' . sprintf( $this->upgrader->strings['skin_update_failed_error'], $title, '' . $this->error . '' ) . '

';
			} else {
				echo '

' . sprintf( $this->upgrader->strings['skin_update_failed'], $title ) . '

';
			}

			echo '';
		}
		if ( $this->result && ! is_wp_error( $this->result ) ) {
			if ( ! $this->error ) {
				echo '
upgrader->update_current ) . '">' .
					'
' . sprintf( $this->upgrader->strings['skin_update_successful'], $title ) .
					' <button type="button" class="hide-if-no-js button-link js-update-details-toggle" aria-expanded="false">' . __( 'Show details.' ) . '</button>' .
					'

';
			}

			echo '';
		}

		$this->reset();
		$this->flush_output();
	}

	/**
	 */
	public function reset() {
		$this->in_loop = false;
		$this->error   = false;
	}

	/**
	 */
	public function flush_output() {
		wp_ob_end_flush_all();
		flush();
	}
}


<?php
/**
 * Upgrade API: Core_Upgrader class
 *
 * @package WordPress
 * @subpackage Upgrader
 * @since 4.6.0
 */

/**
 * Core class used for updating core.
 *
 * It allows for WordPress to upgrade itself in combination with
 * the wp-admin/includes/update-core.php file.
 *
 * @since 2.8.0
 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader.php.
 *
 * @see WP_Upgrader
 */
class Core_Upgrader extends WP_Upgrader {

	/**
	 * Initialize the upgrade strings.
	 *
	 * @since 2.8.0
	 */
	public function upgrade_strings() {
		$this->strings['up_to_date'] = __( 'WordPress is at the latest version.' );
		$this->strings['locked']     = __( 'Another update is currently in progress.' );
		$this->strings['no_package'] = __( 'Update package not available.' );
		/* translators: %s: Package URL. */
		$this->strings['downloading_package']   = sprintf( __( 'Downloading update from %s…' ), '%s' );
		$this->strings['unpack_package']        = __( 'Unpacking the update…' );
		$this->strings['copy_failed']           = __( 'Could not copy files.' );
		$this->strings['copy_failed_space']     = __( 'Could not copy files. You may have run out of disk space.' );
		$this->strings['start_rollback']        = __( 'Attempting to roll back to previous version.' );
		$this->strings['rollback_was_required'] = __( 'Due to an error during updating, WordPress has rolled back to your previous version.' );
	}

	/**
	 * Upgrade WordPress core.
	 *
	 * @since 2.8.0
	 *
	 * @global WP_Filesystem_Base $wp_filesystem                WordPress filesystem subclass.
	 * @global callable           $_wp_filesystem_direct_method
	 *
	 * @param object $current Response object for whether WordPress is current.
	 * @param array  $args {
	 *        Optional. Arguments for upgrading WordPress core. Default empty array.
	 *
	 *        @type bool $pre_check_md5    Whether to check the file checksums before
	 *                                     attempting the upgrade. Default true.
	 *        @type bool $attempt_rollback Whether to attempt to rollback the chances if
	 *                                     there is a problem. Default false.
	 *        @type bool $do_rollback      Whether to perform this "upgrade" as a rollback.
	 *                                     Default false.
	 * }
	 * @return string|false|WP_Error New WordPress version on success, false or WP_Error on failure.
	 */
	public function upgrade( $current, $args = array() ) {
		global $wp_filesystem;

		require ABSPATH . WPINC . '/version.php'; // $wp_version;

		$start_time = time();

		$defaults    = array(
			'pre_check_md5'                => true,
			'attempt_rollback'             => false,
			'do_rollback'                  => false,
			'allow_relaxed_file_ownership' => false,
		);
		$parsed_args = wp_parse_args( $args, $defaults );

		$this->init();
		$this->upgrade_strings();

		// Is an update available?
		if ( ! isset( $current->response ) || 'latest' === $current->response ) {
			return new WP_Error( 'up_to_date', $this->strings['up_to_date'] );
		}

		$res = $this->fs_connect( array( ABSPATH, WP_CONTENT_DIR ), $parsed_args['allow_relaxed_file_ownership'] );
		if ( ! $res || is_wp_error( $res ) ) {
			return $res;
		}

		$wp_dir = trailingslashit( $wp_filesystem->abspath() );

		$partial = true;
		if ( $parsed_args['do_rollback'] ) {
			$partial = false;
		} elseif ( $parsed_args['pre_check_md5'] && ! $this->check_files() ) {
			$partial = false;
		}

		/*
		 * If partial update is returned from the API, use that, unless we're doing
		 * a reinstallation. If we cross the new_bundled version number, then use
		 * the new_bundled zip. Don't though if the constant is set to skip bundled items.
		 * If the API returns a no_content zip, go with it. Finally, default to the full zip.
		 */
		if ( $parsed_args['do_rollback'] && $current->packages->rollback ) {
			$to_download = 'rollback';
		} elseif ( $current->packages->partial && 'reinstall' !== $current->response && $wp_version == $current->partial_version && $partial ) {
			$to_download = 'partial';
		} elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' )
			&& ( ! defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) || ! CORE_UPGRADE_SKIP_NEW_BUNDLED ) ) {
			$to_download = 'new_bundled';
		} elseif ( $current->packages->no_content ) {
			$to_download = 'no_content';
		} else {
			$to_download = 'full';
		}

		// Lock to prevent multiple Core Updates occurring.
		$lock = WP_Upgrader::create_lock( 'core_updater', 15 * MINUTE_IN_SECONDS );
		if ( ! $lock ) {
			return new WP_Error( 'locked', $this->strings['locked'] );
		}

		$download = $this->download_package( $current->packages->$to_download, true );

		// Allow for signature soft-fail.
		// WARNING: This may be removed in the future.
		if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
			// Outout the failure error as a normal feedback, and not as an error:
			/** This filter is documented in wp-admin/includes/update-core.php */
			apply_filters( 'update_feedback', $download->get_error_message() );

			// Report this failure back to WordPress.org for debugging purposes.
			wp_version_check(
				array(
					'signature_failure_code' => $download->get_error_code(),
					'signature_failure_data' => $download->get_error_data(),
				)
			);

			// Pretend this error didn't happen.
			$download = $download->get_error_data( 'softfail-filename' );
		}

		if ( is_wp_error( $download ) ) {
			WP_Upgrader::release_lock( 'core_updater' );
			return $download;
		}

		$working_dir = $this->unpack_package( $download );
		if ( is_wp_error( $working_dir ) ) {
			WP_Upgrader::release_lock( 'core_updater' );
			return $working_dir;
		}

		// Copy update-core.php from the new version into place.
		if ( ! $wp_filesystem->copy( $working_dir . '/wordpress/wp-admin/includes/update-core.php', $wp_dir . 'wp-admin/includes/update-core.php', true ) ) {
			$wp_filesystem->delete( $working_dir, true );
			WP_Upgrader::release_lock( 'core_updater' );
			return new WP_Error( 'copy_failed_for_update_core_file', __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' ), 'wp-admin/includes/update-core.php' );
		}
		$wp_filesystem->chmod( $wp_dir . 'wp-admin/includes/update-core.php', FS_CHMOD_FILE );

		wp_opcache_invalidate( ABSPATH . 'wp-admin/includes/update-core.php' );
		require_once ABSPATH . 'wp-admin/includes/update-core.php';

		if ( ! function_exists( 'update_core' ) ) {
			WP_Upgrader::release_lock( 'core_updater' );
			return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] );
		}

		$result = update_core( $working_dir, $wp_dir );

		// In the event of an issue, we may be able to roll back.
		if ( $parsed_args['attempt_rollback'] && $current->packages->rollback && ! $parsed_args['do_rollback'] ) {
			$try_rollback = false;
			if ( is_wp_error( $result ) ) {
				$error_code = $result->get_error_code();
				/*
				 * Not all errors are equal. These codes are critical: copy_failed__copy_dir,
				 * mkdir_failed__copy_dir, copy_failed__copy_dir_retry, and disk_full.
				 * do_rollback allows for update_core() to trigger a rollback if needed.
				 */
				if ( false !== strpos( $error_code, 'do_rollback' ) ) {
					$try_rollback = true;
				} elseif ( false !== strpos( $error_code, '__copy_dir' ) ) {
					$try_rollback = true;
				} elseif ( 'disk_full' === $error_code ) {
					$try_rollback = true;
				}
			}

			if ( $try_rollback ) {
				/** This filter is documented in wp-admin/includes/update-core.php */
				apply_filters( 'update_feedback', $result );

				/** This filter is documented in wp-admin/includes/update-core.php */
				apply_filters( 'update_feedback', $this->strings['start_rollback'] );

				$rollback_result = $this->upgrade( $current, array_merge( $parsed_args, array( 'do_rollback' => true ) ) );

				$original_result = $result;
				$result          = new WP_Error(
					'rollback_was_required',
					$this->strings['rollback_was_required'],
					(object) array(
						'update'   => $original_result,
						'rollback' => $rollback_result,
					)
				);
			}
		}

		/** This action is documented in wp-admin/includes/class-wp-upgrader.php */
		do_action(
			'upgrader_process_complete',
			$this,
			array(
				'action' => 'update',
				'type'   => 'core',
			)
		);

		// Clear the current updates.
		delete_site_transient( 'update_core' );

		if ( ! $parsed_args['do_rollback'] ) {
			$stats = array(
				'update_type'      => $current->response,
				'success'          => true,
				'fs_method'        => $wp_filesystem->method,
				'fs_method_forced' => defined( 'FS_METHOD' ) || has_filter( 'filesystem_method' ),
				'fs_method_direct' => ! empty( $GLOBALS['_wp_filesystem_direct_method'] ) ? $GLOBALS['_wp_filesystem_direct_method'] : '',
				'time_taken'       => time() - $start_time,
				'reported'         => $wp_version,
				'attempted'        => $current->version,
			);

			if ( is_wp_error( $result ) ) {
				$stats['success'] = false;
				// Did a rollback occur?
				if ( ! empty( $try_rollback ) ) {
					$stats['error_code'] = $original_result->get_error_code();
					$stats['error_data'] = $original_result->get_error_data();
					// Was the rollback successful? If not, collect its error too.
					$stats['rollback'] = ! is_wp_error( $rollback_result );
					if ( is_wp_error( $rollback_result ) ) {
						$stats['rollback_code'] = $rollback_result->get_error_code();
						$stats['rollback_data'] = $rollback_result->get_error_data();
					}
				} else {
					$stats['error_code'] = $result->get_error_code();
					$stats['error_data'] = $result->get_error_data();
				}
			}

			wp_version_check( $stats );
		}

		WP_Upgrader::release_lock( 'core_updater' );

		return $result;
	}

	/**
	 * Determines if this WordPress Core version should update to an offered version or not.
	 *
	 * @since 3.7.0
	 *
	 * @param string $offered_ver The offered version, of the format x.y.z.
	 * @return bool True if we should update to the offered version, otherwise false.
	 */
	public static function should_update_to_version( $offered_ver ) {
		require ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z

		$current_branch = implode( '.', array_slice( preg_split( '/[.-]/', $wp_version ), 0, 2 ) ); // x.y
		$new_branch     = implode( '.', array_slice( preg_split( '/[.-]/', $offered_ver ), 0, 2 ) ); // x.y

		$current_is_development_version = (bool) strpos( $wp_version, '-' );

		// Defaults:
		$upgrade_dev   = true;
		$upgrade_minor = true;
		$upgrade_major = false;

		// WP_AUTO_UPDATE_CORE = true (all), 'minor', false.
		if ( defined( 'WP_AUTO_UPDATE_CORE' ) ) {
			if ( false === WP_AUTO_UPDATE_CORE ) {
				// Defaults to turned off, unless a filter allows it.
				$upgrade_dev   = false;
				$upgrade_minor = false;
				$upgrade_major = false;
			} elseif ( true === WP_AUTO_UPDATE_CORE ) {
				// ALL updates for core.
				$upgrade_dev   = true;
				$upgrade_minor = true;
				$upgrade_major = true;
			} elseif ( 'minor' === WP_AUTO_UPDATE_CORE ) {
				// Only minor updates for core.
				$upgrade_dev   = false;
				$upgrade_minor = true;
				$upgrade_major = false;
			}
		}

		// 1: If we're already on that version, not much point in updating?
		if ( $offered_ver == $wp_version ) {
			return false;
		}

		// 2: If we're running a newer version, that's a nope.
		if ( version_compare( $wp_version, $offered_ver, '>' ) ) {
			return false;
		}

		$failure_data = get_site_option( 'auto_core_update_failed' );
		if ( $failure_data ) {
			// If this was a critical update failure, cannot update.
			if ( ! empty( $failure_data['critical'] ) ) {
				return false;
			}

			// Don't claim we can update on update-core.php if we have a non-critical failure logged.
			if ( $wp_version == $failure_data['current'] && false !== strpos( $offered_ver, '.1.next.minor' ) ) {
				return false;
			}

			/*
			 * Cannot update if we're retrying the same A to B update that caused a non-critical failure.
			 * Some non-critical failures do allow retries, like download_failed.
			 * 3.7.1 => 3.7.2 resulted in files_not_writable, if we are still on 3.7.1 and still trying to update to 3.7.2.
			 */
			if ( empty( $failure_data['retry'] ) && $wp_version == $failure_data['current'] && $offered_ver == $failure_data['attempted'] ) {
				return false;
			}
		}

		// 3: 3.7-alpha-25000 -> 3.7-alpha-25678 -> 3.7-beta1 -> 3.7-beta2.
		if ( $current_is_development_version ) {

			/**
			 * Filters whether to enable automatic core updates for development versions.
			 *
			 * @since 3.7.0
			 *
			 * @param bool $upgrade_dev Whether to enable automatic updates for
			 *                          development versions.
			 */
			if ( ! apply_filters( 'allow_dev_auto_core_updates', $upgrade_dev ) ) {
				return false;
			}
			// Else fall through to minor + major branches below.
		}

		// 4: Minor in-branch updates (3.7.0 -> 3.7.1 -> 3.7.2 -> 3.7.4).
		if ( $current_branch == $new_branch ) {

			/**
			 * Filters whether to enable minor automatic core updates.
			 *
			 * @since 3.7.0
			 *
			 * @param bool $upgrade_minor Whether to enable minor automatic core updates.
			 */
			return apply_filters( 'allow_minor_auto_core_updates', $upgrade_minor );
		}

		// 5: Major version updates (3.7.0 -> 3.8.0 -> 3.9.1).
		if ( version_compare( $new_branch, $current_branch, '>' ) ) {

			/**
			 * Filters whether to enable major automatic core updates.
			 *
			 * @since 3.7.0
			 *
			 * @param bool $upgrade_major Whether to enable major automatic core updates.
			 */
			return apply_filters( 'allow_major_auto_core_updates', $upgrade_major );
		}

		// If we're not sure, we don't want it.
		return false;
	}

	/**
	 * Compare the disk file checksums against the expected checksums.
	 *
	 * @since 3.7.0
	 *
	 * @global string $wp_version       The WordPress version string.
	 * @global string $wp_local_package Locale code of the package.
	 *
	 * @return bool True if the checksums match, otherwise false.
	 */
	public function check_files() {
		global $wp_version, $wp_local_package;

		$checksums = get_core_checksums( $wp_version, isset( $wp_local_package ) ? $wp_local_package : 'en_US' );

		if ( ! is_array( $checksums ) ) {
			return false;
		}

		foreach ( $checksums as $file => $checksum ) {
			// Skip files which get updated.
			if ( 'wp-content' === substr( $file, 0, 10 ) ) {
				continue;
			}
			if ( ! file_exists( ABSPATH . $file ) || md5_file( ABSPATH . $file ) !== $checksum ) {
				return false;
			}
		}

		return true;
	}
}


<?php
/**
 * The custom background script.
 *
 * @package WordPress
 * @subpackage Administration
 */

/**
 * The custom background class.
 *
 * @since 3.0.0
 */
class Custom_Background {

	/**
	 * Callback for administration header.
	 *
	 * @var callable
	 * @since 3.0.0
	 */
	public $admin_header_callback;

	/**
	 * Callback for header div.
	 *
	 * @var callable
	 * @since 3.0.0
	 */
	public $admin_image_div_callback;

	/**
	 * Used to trigger a success message when settings updated and set to true.
	 *
	 * @since 3.0.0
	 * @var bool
	 */
	private $updated;

	/**
	 * Constructor - Register administration header callback.
	 *
	 * @since 3.0.0
	 * @param callable $admin_header_callback
	 * @param callable $admin_image_div_callback Optional custom image div output callback.
	 */
	public function __construct( $admin_header_callback = '', $admin_image_div_callback = '' ) {
		$this->admin_header_callback    = $admin_header_callback;
		$this->admin_image_div_callback = $admin_image_div_callback;

		add_action( 'admin_menu', array( $this, 'init' ) );

		add_action( 'wp_ajax_custom-background-add', array( $this, 'ajax_background_add' ) );

		// Unused since 3.5.0.
		add_action( 'wp_ajax_set-background-image', array( $this, 'wp_set_background_image' ) );
	}

	/**
	 * Set up the hooks for the Custom Background admin page.
	 *
	 * @since 3.0.0
	 */
	public function init() {
		$page = add_theme_page( __( 'Background' ), __( 'Background' ), 'edit_theme_options', 'custom-background', array( $this, 'admin_page' ) );
		if ( ! $page ) {
			return;
		}

		add_action( "load-{$page}", array( $this, 'admin_load' ) );
		add_action( "load-{$page}", array( $this, 'take_action' ), 49 );
		add_action( "load-{$page}", array( $this, 'handle_upload' ), 49 );

		if ( $this->admin_header_callback ) {
			add_action( "admin_head-{$page}", $this->admin_header_callback, 51 );
		}
	}

	/**
	 * Set up the enqueue for the CSS & JavaScript files.
	 *
	 * @since 3.0.0
	 */
	public function admin_load() {
		get_current_screen()->add_help_tab(
			array(
				'id'      => 'overview',
				'title'   => __( 'Overview' ),
				'content' =>
					'
' . __( 'You can customize the look of your site without touching any of your theme’s code by using a custom background. Your background can be an image or a color.' ) . '
' .
					'
' . __( 'To use a background image, simply upload it or choose an image that has already been uploaded to your Media Library by clicking the “Choose Image” button. You can display a single instance of your image, or tile it to fill the screen. You can have your background fixed in place, so your site content moves on top of it, or you can have it scroll with your site.' ) . '
' .
					'
' . __( 'You can also choose a background color 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.' ) . '
' .
					'
' . __( 'Don’t forget to click on the Save Changes button when you are finished.' ) . '
',
			)
		);

		get_current_screen()->set_help_sidebar(
			'
' . __( 'For more information:' ) . '
' .
			'
' . __( 'Documentation on Custom Background' ) . '
' .
			'
' . __( 'Support' ) . '
'
		);

		wp_enqueue_media();
		wp_enqueue_script( 'custom-background' );
		wp_enqueue_style( 'wp-color-picker' );
	}

	/**
	 * Execute custom background modification.
	 *
	 * @since 3.0.0
	 */
	public function take_action() {
		if ( empty( $_POST ) ) {
			return;
		}

		if ( isset( $_POST['reset-background'] ) ) {
			check_admin_referer( 'custom-background-reset', '_wpnonce-custom-background-reset' );
			remove_theme_mod( 'background_image' );
			remove_theme_mod( 'background_image_thumb' );
			$this->updated = true;
			return;
		}

		if ( isset( $_POST['remove-background'] ) ) {
			// @todo Uploaded files are not removed here.
			check_admin_referer( 'custom-background-remove', '_wpnonce-custom-background-remove' );
			set_theme_mod( 'background_image', '' );
			set_theme_mod( 'background_image_thumb', '' );
			$this->updated = true;
			wp_safe_redirect( $_POST['_wp_http_referer'] );
			return;
		}

		if ( isset( $_POST['background-preset'] ) ) {
			check_admin_referer( 'custom-background' );

			if ( in_array( $_POST['background-preset'], array( 'default', 'fill', 'fit', 'repeat', 'custom' ), true ) ) {
				$preset = $_POST['background-preset'];
			} else {
				$preset = 'default';
			}

			set_theme_mod( 'background_preset', $preset );
		}

		if ( isset( $_POST['background-position'] ) ) {
			check_admin_referer( 'custom-background' );

			$position = explode( ' ', $_POST['background-position'] );

			if ( in_array( $position[0], array( 'left', 'center', 'right' ), true ) ) {
				$position_x = $position[0];
			} else {
				$position_x = 'left';
			}

			if ( in_array( $position[1], array( 'top', 'center', 'bottom' ), true ) ) {
				$position_y = $position[1];
			} else {
				$position_y = 'top';
			}

			set_theme_mod( 'background_position_x', $position_x );
			set_theme_mod( 'background_position_y', $position_y );
		}

		if ( isset( $_POST['background-size'] ) ) {
			check_admin_referer( 'custom-background' );

			if ( in_array( $_POST['background-size'], array( 'auto', 'contain', 'cover' ), true ) ) {
				$size = $_POST['background-size'];
			} else {
				$size = 'auto';
			}

			set_theme_mod( 'background_size', $size );
		}

		if ( isset( $_POST['background-repeat'] ) ) {
			check_admin_referer( 'custom-background' );

			$repeat = $_POST['background-repeat'];

			if ( 'no-repeat' !== $repeat ) {
				$repeat = 'repeat';
			}

			set_theme_mod( 'background_repeat', $repeat );
		}

		if ( isset( $_POST['background-attachment'] ) ) {
			check_admin_referer( 'custom-background' );

			$attachment = $_POST['background-attachment'];

			if ( 'fixed' !== $attachment ) {
				$attachment = 'scroll';
			}

			set_theme_mod( 'background_attachment', $attachment );
		}

		if ( isset( $_POST['background-color'] ) ) {
			check_admin_referer( 'custom-background' );
			$color = preg_replace( '/[^0-9a-fA-F]/', '', $_POST['background-color'] );
			if ( strlen( $color ) == 6 || strlen( $color ) == 3 ) {
				set_theme_mod( 'background_color', $color );
			} else {
				set_theme_mod( 'background_color', '' );
			}
		}

		$this->updated = true;
	}

	/**
	 * Display the custom background page.
	 *
	 * @since 3.0.0
	 */
	public function admin_page() {
		?>

<?php _e( 'Custom Background' ); ?>

		<?php if ( current_user_can( 'customize' ) ) { ?>

			<?php
			printf(
				/* translators: %s: URL to background image configuration in Customizer. */
				__( 'You can now manage and live-preview Custom Backgrounds in the Customizer.' ),
				admin_url( 'customize.php?autofocus[control]=background_image' )
			);
			?>

		<?php } ?>

		<?php if ( ! empty( $this->updated ) ) { ?>

			<?php
			/* translators: %s: Home URL. */
			printf( __( 'Background updated. Visit your site to see how it looks.' ), home_url( '/' ) );
			?>

		<?php } ?>

<?php _e( 'Background Image' ); ?>

<?php _e( 'Preview' ); ?>

		<?php
		if ( $this->admin_image_div_callback ) {
			call_user_func( $this->admin_image_div_callback );
		} else {
			$background_styles = '';
			$bgcolor           = get_background_color();
			if ( $bgcolor ) {
				$background_styles .= 'background-color: #' . $bgcolor . ';';
			}

			$background_image_thumb = get_background_image();
			if ( $background_image_thumb ) {
				$background_image_thumb = esc_url( set_url_scheme( get_theme_mod( 'background_image_thumb', str_replace( '%', '%%', $background_image_thumb ) ) ) );
				$background_position_x  = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) );
				$background_position_y  = get_theme_mod( 'background_position_y', get_theme_support( 'custom-background', 'default-position-y' ) );
				$background_size        = get_theme_mod( 'background_size', get_theme_support( 'custom-background', 'default-size' ) );
				$background_repeat      = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) );
				$background_attachment  = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) );

				// Background-image URL must be single quote, see below.
				$background_styles .= " background-image: url('$background_image_thumb');"
				. " background-size: $background_size;"
				. " background-position: $background_position_x $background_position_y;"
				. " background-repeat: $background_repeat;"
				. " background-attachment: $background_attachment;";
			}
			?>
	<div id="custom-background-image" style="<?php echo $background_styles; ?>"><?php // Must be double quote, see above. ?>
			<?php if ( $background_image_thumb ) { ?>
		<img class="custom-background-image" src="<?php echo $background_image_thumb; ?>" style="visibility:hidden;" alt="" />

		<img class="custom-background-image" src="<?php echo $background_image_thumb; ?>" style="visibility:hidden;" alt="" />
		<?php } ?>

	<?php } ?>

		<?php if ( get_background_image() ) : ?>

<?php _e( 'Remove Image' ); ?>

<form method="post">
			<?php wp_nonce_field( 'custom-background-remove', '_wpnonce-custom-background-remove' ); ?>
			<?php submit_button( __( 'Remove Background Image' ), '', 'remove-background', false ); ?>

			<?php _e( 'This will remove the background image. You will not be able to restore any customizations.' ); ?>
</form>

		<?php endif; ?>

		<?php $default_image = get_theme_support( 'custom-background', 'default-image' ); ?>
		<?php if ( $default_image && get_background_image() != $default_image ) : ?>

<?php _e( 'Restore Original Image' ); ?>

<form method="post">
			<?php wp_nonce_field( 'custom-background-reset', '_wpnonce-custom-background-reset' ); ?>
			<?php submit_button( __( 'Restore Original Image' ), '', 'reset-background', false ); ?>

			<?php _e( 'This will restore the original background image. You will not be able to restore any customizations.' ); ?>
</form>

		<?php endif; ?>

		<?php if ( current_user_can( 'upload_files' ) ) : ?>

<?php _e( 'Select Image' ); ?>
<form enctype="multipart/form-data" id="upload-form" class="wp-upload-form" method="post">

		<?php _e( 'Choose an image from your computer:' ); ?>

		<input type="file" id="upload" name="import" />
		<input type="hidden" name="action" value="save" />
			<?php wp_nonce_field( 'custom-background-upload', '_wpnonce-custom-background-upload' ); ?>
			<?php submit_button( __( 'Upload' ), '', 'submit', false ); ?>

		<?php _e( 'Or choose an image from your media library:' ); ?>

		<button id="choose-from-library-link" class="button"
			data-choose="<?php esc_attr_e( 'Choose a Background Image' ); ?>"
			data-update="<?php esc_attr_e( 'Set as background' ); ?>"><?php _e( 'Choose Image' ); ?></button>

	</form>

		<?php endif; ?>

<?php _e( 'Display Options' ); ?>

<form method="post">

		<?php if ( get_background_image() ) : ?>
<input name="background-preset" type="hidden" value="custom">

			<?php
			$background_position = sprintf(
				'%s %s',
				get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) ),
				get_theme_mod( 'background_position_y', get_theme_support( 'custom-background', 'default-position-y' ) )
			);

			$background_position_options = array(
				array(
					'left top'   => array(
						'label' => __( 'Top Left' ),
						'icon'  => 'dashicons dashicons-arrow-left-alt',
					),
					'center top' => array(
						'label' => __( 'Top' ),
						'icon'  => 'dashicons dashicons-arrow-up-alt',
					),
					'right top'  => array(
						'label' => __( 'Top Right' ),
						'icon'  => 'dashicons dashicons-arrow-right-alt',
					),
				),
				array(
					'left center'   => array(
						'label' => __( 'Left' ),
						'icon'  => 'dashicons dashicons-arrow-left-alt',
					),
					'center center' => array(
						'label' => __( 'Center' ),
						'icon'  => 'background-position-center-icon',
					),
					'right center'  => array(
						'label' => __( 'Right' ),
						'icon'  => 'dashicons dashicons-arrow-right-alt',
					),
				),
				array(
					'left bottom'   => array(
						'label' => __( 'Bottom Left' ),
						'icon'  => 'dashicons dashicons-arrow-left-alt',
					),
					'center bottom' => array(
						'label' => __( 'Bottom' ),
						'icon'  => 'dashicons dashicons-arrow-down-alt',
					),
					'right bottom'  => array(
						'label' => __( 'Bottom Right' ),
						'icon'  => 'dashicons dashicons-arrow-right-alt',
					),
				),
			);
			?>

<?php _e( 'Image Position' ); ?>
<fieldset><legend class="screen-reader-text"><?php _e( 'Image Position' ); ?></legend>

			<?php foreach ( $background_position_options as $group ) : ?>

				<?php foreach ( $group as $value => $input ) : ?>

			<input class="screen-reader-text" name="background-position" type="radio" value="<?php echo esc_attr( $value ); ?>"<?php checked( $value, $background_position ); ?>>
			<span class="<?php echo esc_attr( $input['icon'] ); ?>" aria-hidden="true">
			<?php echo $input['label']; ?>

	<?php endforeach; ?>

<?php endforeach; ?>

</fieldset>

<?php _e( 'Image Size' ); ?>
<fieldset><legend class="screen-reader-text"><?php _e( 'Image Size' ); ?></legend>
<select id="background-size" name="background-size">
<option value="auto"<?php selected( 'auto', get_theme_mod( 'background_size', get_theme_support( 'custom-background', 'default-size' ) ) ); ?>><?php _ex( 'Original', 'Original Size' ); ?></option>
<option value="contain"<?php selected( 'contain', get_theme_mod( 'background_size', get_theme_support( 'custom-background', 'default-size' ) ) ); ?>><?php _e( 'Fit to Screen' ); ?></option>
<option value="cover"<?php selected( 'cover', get_theme_mod( 'background_size', get_theme_support( 'custom-background', 'default-size' ) ) ); ?>><?php _e( 'Fill Screen' ); ?></option>
</select>
</fieldset>

<?php _ex( 'Repeat', 'Background Repeat' ); ?>
<fieldset><legend class="screen-reader-text"><?php _ex( 'Repeat', 'Background Repeat' ); ?></legend>
<input name="background-repeat" type="hidden" value="no-repeat">
<input type="checkbox" name="background-repeat" value="repeat"<?php checked( 'repeat', get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) ) ); ?>> <?php _e( 'Repeat Background Image' ); ?>
</fieldset>

<?php _ex( 'Scroll', 'Background Scroll' ); ?>
<fieldset><legend class="screen-reader-text"><?php _ex( 'Scroll', 'Background Scroll' ); ?></legend>
<input name="background-attachment" type="hidden" value="fixed">
<input name="background-attachment" type="checkbox" value="scroll" <?php checked( 'scroll', get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) ) ); ?>> <?php _e( 'Scroll with Page' ); ?>
</fieldset>

<?php endif; // get_background_image() ?>

<?php _e( 'Background Color' ); ?>
<fieldset><legend class="screen-reader-text"><?php _e( 'Background Color' ); ?></legend>
		<?php
		$default_color = '';
		if ( current_theme_supports( 'custom-background', 'default-color' ) ) {
			$default_color = ' data-default-color="#' . esc_attr( get_theme_support( 'custom-background', 'default-color' ) ) . '"';
		}
		?>
<input type="text" name="background-color" id="background-color" value="#<?php echo esc_attr( get_background_color() ); ?>"<?php echo $default_color; ?>>
</fieldset>

		<?php wp_nonce_field( 'custom-background' ); ?>
		<?php submit_button( null, 'primary', 'save-background-options' ); ?>
</form>

		<?php
	}

	/**
	 * Handle an Image upload for the background image.
	 *
	 * @since 3.0.0
	 */
	public function handle_upload() {
		if ( empty( $_FILES ) ) {
			return;
		}

		check_admin_referer( 'custom-background-upload', '_wpnonce-custom-background-upload' );
		$overrides = array( 'test_form' => false );

		$uploaded_file = $_FILES['import'];
		$wp_filetype   = wp_check_filetype_and_ext( $uploaded_file['tmp_name'], $uploaded_file['name'] );
		if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) {
			wp_die( __( 'The uploaded file is not a valid image. Please try again.' ) );
		}

		$file = wp_handle_upload( $uploaded_file, $overrides );

		if ( isset( $file['error'] ) ) {
			wp_die( $file['error'] );
		}

		$url      = $file['url'];
		$type     = $file['type'];
		$file     = $file['file'];
		$filename = wp_basename( $file );

		// Construct the object array.
		$object = array(
			'post_title'     => $filename,
			'post_content'   => $url,
			'post_mime_type' => $type,
			'guid'           => $url,
			'context'        => 'custom-background',
		);

		// Save the data.
		$id = wp_insert_attachment( $object, $file );

		// Add the metadata.
		wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
		update_post_meta( $id, '_wp_attachment_is_custom_background', get_option( 'stylesheet' ) );

		set_theme_mod( 'background_image', esc_url_raw( $url ) );

		$thumbnail = wp_get_attachment_image_src( $id, 'thumbnail' );
		set_theme_mod( 'background_image_thumb', esc_url_raw( $thumbnail[0] ) );

		/** This action is documented in wp-admin/includes/class-custom-image-header.php */
		do_action( 'wp_create_file_in_uploads', $file, $id ); // For replication.
		$this->updated = true;
	}

	/**
	 * Ajax handler for adding custom background context to an attachment.
	 *
	 * Triggers when the user adds a new background image from the
	 * Media Manager.
	 *
	 * @since 4.1.0
	 */
	public function ajax_background_add() {
		check_ajax_referer( 'background-add', 'nonce' );

		if ( ! current_user_can( 'edit_theme_options' ) ) {
			wp_send_json_error();
		}

		$attachment_id = absint( $_POST['attachment_id'] );
		if ( $attachment_id < 1 ) {
			wp_send_json_error();
		}

		update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', get_stylesheet() );

		wp_send_json_success();
	}

	/**
	 * @since 3.4.0
	 * @deprecated 3.5.0
	 *
	 * @param array $form_fields
	 * @return array $form_fields
	 */
	public function attachment_fields_to_edit( $form_fields ) {
		return $form_fields;
	}

	/**
	 * @since 3.4.0
	 * @deprecated 3.5.0
	 *
	 * @param array $tabs
	 * @return array $tabs
	 */
	public function filter_upload_tabs( $tabs ) {
		return $tabs;
	}

	/**
	 * @since 3.4.0
	 * @deprecated 3.5.0
	 */
	public function wp_set_background_image() {
		check_ajax_referer( 'custom-background' );

		if ( ! current_user_can( 'edit_theme_options' ) || ! isset( $_POST['attachment_id'] ) ) {
			exit;
		}

		$attachment_id = absint( $_POST['attachment_id'] );

		$sizes = array_keys(
			/** This filter is documented in wp-admin/includes/media.php */
			apply_filters(
				'image_size_names_choose',
				array(
					'thumbnail' => __( 'Thumbnail' ),
					'medium'    => __( 'Medium' ),
					'large'     => __( 'Large' ),
					'full'      => __( 'Full Size' ),
				)
			)
		);

		$size = 'thumbnail';
		if ( in_array( $_POST['size'], $sizes, true ) ) {
			$size = esc_attr( $_POST['size'] );
		}

		update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', get_option( 'stylesheet' ) );

		$url       = wp_get_attachment_image_src( $attachment_id, $size );
		$thumbnail = wp_get_attachment_image_src( $attachment_id, 'thumbnail' );
		set_theme_mod( 'background_image', esc_url_raw( $url[0] ) );
		set_theme_mod( 'background_image_thumb', esc_url_raw( $thumbnail[0] ) );
		exit;
	}
}


<?php
/**
 * The custom header image script.
 *
 * @package WordPress
 * @subpackage Administration
 */

/**
 * The custom header image class.
 *
 * @since 2.1.0
 */
class Custom_Image_Header {

	/**
	 * Callback for administration header.
	 *
	 * @var callable
	 * @since 2.1.0
	 */
	public $admin_header_callback;

	/**
	 * Callback for header div.
	 *
	 * @var callable
	 * @since 3.0.0
	 */
	public $admin_image_div_callback;

	/**
	 * Holds default headers.
	 *
	 * @var array
	 * @since 3.0.0
	 */
	public $default_headers = array();

	/**
	 * Used to trigger a success message when settings updated and set to true.
	 *
	 * @since 3.0.0
	 * @var bool
	 */
	private $updated;

	/**
	 * Constructor - Register administration header callback.
	 *
	 * @since 2.1.0
	 * @param callable $admin_header_callback
	 * @param callable $admin_image_div_callback Optional custom image div output callback.
	 */
	public function __construct( $admin_header_callback, $admin_image_div_callback = '' ) {
		$this->admin_header_callback    = $admin_header_callback;
		$this->admin_image_div_callback = $admin_image_div_callback;

		add_action( 'admin_menu', array( $this, 'init' ) );

		add_action( 'customize_save_after', array( $this, 'customize_set_last_used' ) );
		add_action( 'wp_ajax_custom-header-crop', array( $this, 'ajax_header_crop' ) );
		add_action( 'wp_ajax_custom-header-add', array( $this, 'ajax_header_add' ) );
		add_action( 'wp_ajax_custom-header-remove', array( $this, 'ajax_header_remove' ) );
	}

	/**
	 * Set up the hooks for the Custom Header admin page.
	 *
	 * @since 2.1.0
	 */
	public function init() {
		$page = add_theme_page( __( 'Header' ), __( 'Header' ), 'edit_theme_options', 'custom-header', array( $this, 'admin_page' ) );
		if ( ! $page ) {
			return;
		}

		add_action( "admin_print_scripts-{$page}", array( $this, 'js_includes' ) );
		add_action( "admin_print_styles-{$page}", array( $this, 'css_includes' ) );
		add_action( "admin_head-{$page}", array( $this, 'help' ) );
		add_action( "admin_head-{$page}", array( $this, 'take_action' ), 50 );
		add_action( "admin_head-{$page}", array( $this, 'js' ), 50 );
		if ( $this->admin_header_callback ) {
			add_action( "admin_head-{$page}", $this->admin_header_callback, 51 );
		}
	}

	/**
	 * Adds contextual help.
	 *
	 * @since 3.0.0
	 */
	public function help() {
		get_current_screen()->add_help_tab(
			array(
				'id'      => 'overview',
				'title'   => __( 'Overview' ),
				'content' =>
					'
' . __( 'This screen is used to customize the header section of your theme.' ) . '
' .
					'
' . __( '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.' ) . '
',
			)
		);

		get_current_screen()->add_help_tab(
			array(
				'id'      => 'set-header-image',
				'title'   => __( 'Header Image' ),
				'content' =>
					'
' . __( '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.' ) . '
' .
					'
' . __( 'Some themes come with additional header images bundled. If you see multiple images displayed, select the one you’d like and click the “Save Changes” button.' ) . '
' .
					'
' . __( '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.' ) . '
' .
					'
' . __( 'If you don’t 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”.' ) . '
',
			)
		);

		get_current_screen()->add_help_tab(
			array(
				'id'      => 'set-header-text',
				'title'   => __( 'Header Text' ),
				'content' =>
					'
' . sprintf(
						/* translators: %s: URL to General Settings screen. */
						__( 'For most themes, the header text is your Site Title and Tagline, as defined in the General Settings section.' ),
						admin_url( 'options-general.php' )
					) .
					'
' .
					'
' . __( '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.' ) . '
' .
					'
' . __( 'Don’t forget to click “Save Changes” when you’re done!' ) . '
',
			)
		);

		get_current_screen()->set_help_sidebar(
			'
' . __( 'For more information:' ) . '
' .
			'
' . __( 'Documentation on Custom Header' ) . '
' .
			'
' . __( 'Support' ) . '
'
		);
	}

	/**
	 * Get the current step.
	 *
	 * @since 2.6.0
	 *
	 * @return int Current step
	 */
	public function step() {
		if ( ! isset( $_GET['step'] ) ) {
			return 1;
		}

		$step = (int) $_GET['step'];
		if ( $step < 1 || 3 < $step ||
			( 2 == $step && ! wp_verify_nonce( $_REQUEST['_wpnonce-custom-header-upload'], 'custom-header-upload' ) ) ||
			( 3 == $step && ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'custom-header-crop-image' ) )
		) {
			return 1;
		}

		return $step;
	}

	/**
	 * Set up the enqueue for the JavaScript files.
	 *
	 * @since 2.1.0
	 */
	public function js_includes() {
		$step = $this->step();

		if ( ( 1 == $step || 3 == $step ) ) {
			wp_enqueue_media();
			wp_enqueue_script( 'custom-header' );
			if ( current_theme_supports( 'custom-header', 'header-text' ) ) {
				wp_enqueue_script( 'wp-color-picker' );
			}
		} elseif ( 2 == $step ) {
			wp_enqueue_script( 'imgareaselect' );
		}
	}

	/**
	 * Set up the enqueue for the CSS files
	 *
	 * @since 2.7.0
	 */
	public function css_includes() {
		$step = $this->step();

		if ( ( 1 == $step || 3 == $step ) && current_theme_supports( 'custom-header', 'header-text' ) ) {
			wp_enqueue_style( 'wp-color-picker' );
		} elseif ( 2 == $step ) {
			wp_enqueue_style( 'imgareaselect' );
		}
	}

	/**
	 * Execute custom header modification.
	 *
	 * @since 2.6.0
	 */
	public function take_action() {
		if ( ! current_user_can( 'edit_theme_options' ) ) {
			return;
		}

		if ( empty( $_POST ) ) {
			return;
		}

		$this->updated = true;

		if ( isset( $_POST['resetheader'] ) ) {
			check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
			$this->reset_header_image();
			return;
		}

		if ( isset( $_POST['removeheader'] ) ) {
			check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
			$this->remove_header_image();
			return;
		}

		if ( isset( $_POST['text-color'] ) && ! isset( $_POST['display-header-text'] ) ) {
			check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
			set_theme_mod( 'header_textcolor', 'blank' );
		} elseif ( isset( $_POST['text-color'] ) ) {
			check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
			$_POST['text-color'] = str_replace( '#', '', $_POST['text-color'] );
			$color               = preg_replace( '/[^0-9a-fA-F]/', '', $_POST['text-color'] );
			if ( strlen( $color ) == 6 || strlen( $color ) == 3 ) {
				set_theme_mod( 'header_textcolor', $color );
			} elseif ( ! $color ) {
				set_theme_mod( 'header_textcolor', 'blank' );
			}
		}

		if ( isset( $_POST['default-header'] ) ) {
			check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
			$this->set_header_image( $_POST['default-header'] );
			return;
		}
	}

	/**
	 * Process the default headers
	 *
	 * @since 3.0.0
	 *
	 * @global array $_wp_default_headers
	 */
	public function process_default_headers() {
		global $_wp_default_headers;

		if ( ! isset( $_wp_default_headers ) ) {
			return;
		}

		if ( ! empty( $this->default_headers ) ) {
			return;
		}

		$this->default_headers    = $_wp_default_headers;
		$template_directory_uri   = get_template_directory_uri();
		$stylesheet_directory_uri = get_stylesheet_directory_uri();
		foreach ( array_keys( $this->default_headers ) as $header ) {
			$this->default_headers[ $header ]['url']           = sprintf( $this->default_headers[ $header ]['url'], $template_directory_uri, $stylesheet_directory_uri );
			$this->default_headers[ $header ]['thumbnail_url'] = sprintf( $this->default_headers[ $header ]['thumbnail_url'], $template_directory_uri, $stylesheet_directory_uri );
		}
	}

	/**
	 * Display UI for selecting one of several default headers.
	 *
	 * Show the random image option if this theme has multiple header images.
	 * Random image option is on by default if no header has been set.
	 *
	 * @since 3.0.0
	 *
	 * @param string $type The header type. One of 'default' (for the Uploaded Images control)
	 *                     or 'uploaded' (for the Uploaded Images control).
	 */
	public function show_header_selector( $type = 'default' ) {
		if ( 'default' === $type ) {
			$headers = $this->default_headers;
		} else {
			$headers = get_uploaded_header_images();
			$type    = 'uploaded';
		}

		if ( 1 < count( $headers ) ) {
			echo '
';
			echo '<input name="default-header" type="radio" value="random-' . $type . '-image"' . checked( is_random_header_image( $type ), true, false ) . ' />';
			_e( 'Random: Show a different image on each page.' );
			echo '';
			echo '
';
		}

		echo '
';
		foreach ( $headers as $header_key => $header ) {
			$header_thumbnail = $header['thumbnail_url'];
			$header_url       = $header['url'];
			$header_alt_text  = empty( $header['alt_text'] ) ? '' : $header['alt_text'];
			echo '
';
			echo '<input name="default-header" type="radio" value="' . esc_attr( $header_key ) . '" ' . checked( $header_url, get_theme_mod( 'header_image' ), false ) . ' />';
			$width = '';
			if ( ! empty( $header['attachment_id'] ) ) {
				$width = ' width="230"';
			}
			echo '';
			echo '
';
		}
		echo '

';
	}

	/**
	 * Execute JavaScript depending on step.
	 *
	 * @since 2.1.0
	 */
	public function js() {
		$step = $this->step();
		if ( ( 1 == $step || 3 == $step ) && current_theme_supports( 'custom-header', 'header-text' ) ) {
			$this->js_1();
		} elseif ( 2 == $step ) {
			$this->js_2();
		}
	}

	/**
	 * Display JavaScript based on Step 1 and 3.
	 *
	 * @since 2.6.0
	 */
	public function js_1() {
		$default_color = '';
		if ( current_theme_supports( 'custom-header', 'default-text-color' ) ) {
			$default_color = get_theme_support( 'custom-header', 'default-text-color' );
			if ( $default_color && false === strpos( $default_color, '#' ) ) {
				$default_color = '#' . $default_color;
			}
		}
		?>

		<?php
	}

	/**
	 * Display JavaScript based on Step 2.
	 *
	 * @since 2.6.0
	 */
	public function js_2() {

		?>

		<?php
	}

	/**
	 * Display first step of custom header image page.
	 *
	 * @since 2.1.0
	 */
	public function step_1() {
		$this->process_default_headers();
		?>

<?php _e( 'Custom Header' ); ?>

		<?php if ( current_user_can( 'customize' ) ) { ?>

			<?php
			printf(
				/* translators: %s: URL to header image configuration in Customizer. */
				__( 'You can now manage and live-preview Custom Header in the Customizer.' ),
				admin_url( 'customize.php?autofocus[control]=header_image' )
			);
			?>

		<?php } ?>

		<?php if ( ! empty( $this->updated ) ) { ?>

			<?php
			/* translators: %s: Home URL. */
			printf( __( 'Header updated. Visit your site to see how it looks.' ), home_url( '/' ) );
			?>

		<?php } ?>

<?php _e( 'Header Image' ); ?>

		<?php if ( get_custom_header() || display_header_text() ) : ?>

<?php _e( 'Preview' ); ?>

			<?php
			if ( $this->admin_image_div_callback ) {
				call_user_func( $this->admin_image_div_callback );
			} else {
				$custom_header = get_custom_header();
				$header_image  = get_header_image();

				if ( $header_image ) {
					$header_image_style = 'background-image:url(' . esc_url( $header_image ) . ');';
				} else {
					$header_image_style = '';
				}

				if ( $custom_header->width ) {
					$header_image_style .= 'max-width:' . $custom_header->width . 'px;';
				}
				if ( $custom_header->height ) {
					$header_image_style .= 'height:' . $custom_header->height . 'px;';
				}
				?>
	<div id="headimg" style="<?php echo $header_image_style; ?>">
				<?php
				if ( display_header_text() ) {
					$style = ' style="color:#' . get_header_textcolor() . ';"';
				} else {
					$style = ' style="display:none;"';
				}
				?>

<a id="name" class="displaying-header-text" <?php echo $style; ?> onclick="return false;" href="<?php bloginfo( 'url' ); ?>" tabindex="-1"><?php bloginfo( 'name' ); ?>

		<div id="desc" class="displaying-header-text" <?php echo $style; ?>><?php bloginfo( 'description' ); ?>

			<?php } ?>

		<?php endif; ?>

		<?php if ( current_user_can( 'upload_files' ) && current_theme_supports( 'custom-header', 'uploads' ) ) : ?>

<?php _e( 'Select Image' ); ?>

<?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.' ); ?>

			<?php
			if ( ! current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' ) ) {
				/* translators: 1: Image width in pixels, 2: Image height in pixels. */
				printf( __( 'Images of exactly %1$d × %2$d pixels will be used as-is.' ) . '
', get_theme_support( 'custom-header', 'width' ), get_theme_support( 'custom-header', 'height' ) );
			} elseif ( current_theme_supports( 'custom-header', 'flex-height' ) ) {
				if ( ! current_theme_supports( 'custom-header', 'flex-width' ) ) {
					printf(
						/* translators: %s: Size in pixels. */
						__( 'Images should be at least %s wide.' ) . ' ',
						sprintf(
							/* translators: %d: Custom header width. */
							'' . __( '%d pixels' ) . '',
							get_theme_support( 'custom-header', 'width' )
						)
					);
				}
			} elseif ( current_theme_supports( 'custom-header', 'flex-width' ) ) {
				if ( ! current_theme_supports( 'custom-header', 'flex-height' ) ) {
					printf(
						/* translators: %s: Size in pixels. */
						__( 'Images should be at least %s tall.' ) . ' ',
						sprintf(
							/* translators: %d: Custom header height. */
							'' . __( '%d pixels' ) . '',
							get_theme_support( 'custom-header', 'height' )
						)
					);
				}
			}
			if ( current_theme_supports( 'custom-header', 'flex-height' ) || current_theme_supports( 'custom-header', 'flex-width' ) ) {
				if ( current_theme_supports( 'custom-header', 'width' ) ) {
					printf(
						/* translators: %s: Size in pixels. */
						__( 'Suggested width is %s.' ) . ' ',
						sprintf(
							/* translators: %d: Custom header width. */
							'' . __( '%d pixels' ) . '',
							get_theme_support( 'custom-header', 'width' )
						)
					);
				}
				if ( current_theme_supports( 'custom-header', 'height' ) ) {
					printf(
						/* translators: %s: Size in pixels. */
						__( 'Suggested height is %s.' ) . ' ',
						sprintf(
							/* translators: %d: Custom header height. */
							'' . __( '%d pixels' ) . '',
							get_theme_support( 'custom-header', 'height' )
						)
					);
				}
			}
			?>

	<form enctype="multipart/form-data" id="upload-form" class="wp-upload-form" method="post" action="<?php echo esc_url( add_query_arg( 'step', 2 ) ); ?>">

		<?php _e( 'Choose an image from your computer:' ); ?>

		<input type="file" id="upload" name="import" />
		<input type="hidden" name="action" value="save" />
			<?php wp_nonce_field( 'custom-header-upload', '_wpnonce-custom-header-upload' ); ?>
			<?php submit_button( __( 'Upload' ), '', 'submit', false ); ?>

			<?php
				$modal_update_href = esc_url(
					add_query_arg(
						array(
							'page' => 'custom-header',
							'step' => 2,
							'_wpnonce-custom-header-upload' => wp_create_nonce( 'custom-header-upload' ),
						),
						admin_url( 'themes.php' )
					)
				);
			?>

		<?php _e( 'Or choose an image from your media library:' ); ?>

		<button id="choose-from-library-link" class="button"
			data-update-link="<?php echo esc_attr( $modal_update_href ); ?>"
			data-choose="<?php esc_attr_e( 'Choose a Custom Header' ); ?>"
			data-update="<?php esc_attr_e( 'Set as header' ); ?>"><?php _e( 'Choose Image' ); ?></button>

	</form>

		<?php endif; ?>

<form method="post" action="<?php echo esc_url( add_query_arg( 'step', 1 ) ); ?>">
		<?php submit_button( null, 'screen-reader-text', 'save-header-options', false ); ?>

		<?php if ( get_uploaded_header_images() ) : ?>

<?php _e( 'Uploaded Images' ); ?>

<?php _e( 'You can choose one of your previously uploaded headers, or show a random one.' ); ?>

			<?php
			$this->show_header_selector( 'uploaded' );
			?>

			<?php
	endif;
		if ( ! empty( $this->default_headers ) ) :
			?>

<?php _e( 'Default Images' ); ?>

			<?php if ( current_theme_supports( 'custom-header', 'uploads' ) ) : ?>

<?php _e( 'If you don‘t want to upload your own image, you can use one of these cool headers, or show a random one.' ); ?>

	<?php else : ?>

<?php _e( 'You can use one of these cool headers or show a random one on each page.' ); ?>

	<?php endif; ?>
			<?php
			$this->show_header_selector( 'default' );
			?>

			<?php
	endif;
		if ( get_header_image() ) :
			?>

<?php _e( 'Remove Image' ); ?>

<?php _e( 'This will remove the header image. You will not be able to restore any customizations.' ); ?>

			<?php submit_button( __( 'Remove Header Image' ), '', 'removeheader', false ); ?>

			<?php
	endif;

		$default_image = sprintf( get_theme_support( 'custom-header', 'default-image' ), get_template_directory_uri(), get_stylesheet_directory_uri() );
		if ( $default_image && get_header_image() != $default_image ) :
			?>

<?php _e( 'Reset Image' ); ?>

<?php _e( 'This will restore the original header image. You will not be able to restore any customizations.' ); ?>

			<?php submit_button( __( 'Restore Original Header Image' ), '', 'resetheader', false ); ?>

	<?php endif; ?>

		<?php if ( current_theme_supports( 'custom-header', 'header-text' ) ) : ?>

<?php _e( 'Header Text' ); ?>

<?php _e( 'Header Text' ); ?>

	<input type="checkbox" name="display-header-text" id="display-header-text"<?php checked( display_header_text() ); ?> /> <?php _e( 'Show header text with your image.' ); ?>

<?php _e( 'Text Color' ); ?>

			<?php
			$default_color = '';
			if ( current_theme_supports( 'custom-header', 'default-text-color' ) ) {
				$default_color = get_theme_support( 'custom-header', 'default-text-color' );
				if ( $default_color && false === strpos( $default_color, '#' ) ) {
					$default_color = '#' . $default_color;
				}
			}

			$default_color_attr = $default_color ? ' data-default-color="' . esc_attr( $default_color ) . '"' : '';

			$header_textcolor = display_header_text() ? get_header_textcolor() : get_theme_support( 'custom-header', 'default-text-color' );
			if ( $header_textcolor && false === strpos( $header_textcolor, '#' ) ) {
				$header_textcolor = '#' . $header_textcolor;
			}

			echo '<input type="text" name="text-color" id="text-color" value="' . esc_attr( $header_textcolor ) . '"' . $default_color_attr . ' />';
			if ( $default_color ) {
				/* translators: %s: Default text color. */
				echo ' ' . sprintf( _x( 'Default: %s', 'color' ), esc_html( $default_color ) ) . '';
			}
			?>

			<?php
endif;

		/**
		 * Fires just before the submit button in the custom header options form.
		 *
		 * @since 3.1.0
		 */
		do_action( 'custom_header_options' );

		wp_nonce_field( 'custom-header-options', '_wpnonce-custom-header-options' );
		?>

		<?php submit_button( null, 'primary', 'save-header-options' ); ?>
</form>

		<?php
	}

	/**
	 * Display second step of custom header image page.
	 *
	 * @since 2.1.0
	 */
	public function step_2() {
		check_admin_referer( 'custom-header-upload', '_wpnonce-custom-header-upload' );
		if ( ! current_theme_supports( 'custom-header', 'uploads' ) ) {
			wp_die(
				'
' . __( 'Something went wrong.' ) . '
' .
				'
' . __( 'The current theme does not support uploading a custom header image.' ) . '
',
				403
			);
		}

		if ( empty( $_POST ) && isset( $_GET['file'] ) ) {
			$attachment_id = absint( $_GET['file'] );
			$file          = get_attached_file( $attachment_id, true );
			$url           = wp_get_attachment_image_src( $attachment_id, 'full' );
			$url           = $url[0];
		} elseif ( isset( $_POST ) ) {
			$data          = $this->step_2_manage_upload();
			$attachment_id = $data['attachment_id'];
			$file          = $data['file'];
			$url           = $data['url'];
		}

		if ( file_exists( $file ) ) {
			list( $width, $height, $type, $attr ) = @getimagesize( $file );
		} else {
			$data   = wp_get_attachment_metadata( $attachment_id );
			$height = isset( $data['height'] ) ? $data['height'] : 0;
			$width  = isset( $data['width'] ) ? $data['width'] : 0;
			unset( $data );
		}

		$max_width = 0;
		// For flex, limit size of image displayed to 1500px unless theme says otherwise.
		if ( current_theme_supports( 'custom-header', 'flex-width' ) ) {
			$max_width = 1500;
		}

		if ( current_theme_supports( 'custom-header', 'max-width' ) ) {
			$max_width = max( $max_width, get_theme_support( 'custom-header', 'max-width' ) );
		}
		$max_width = max( $max_width, get_theme_support( 'custom-header', 'width' ) );

		// If flexible height isn't supported and the image is the exact right size.
		if ( ! current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' )
			&& get_theme_support( 'custom-header', 'width' ) == $width && get_theme_support( 'custom-header', 'height' ) == $height ) {
			// Add the metadata.
			if ( file_exists( $file ) ) {
				wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file ) );
			}

			$this->set_header_image( compact( 'url', 'attachment_id', 'width', 'height' ) );

			/**
			 * Fires after the header image is set or an error is returned.
			 *
			 * @since 2.1.0
			 *
			 * @param string $file          Path to the file.
			 * @param int    $attachment_id Attachment ID.
			 */
			do_action( 'wp_create_file_in_uploads', $file, $attachment_id ); // For replication.

			return $this->finished();
		} elseif ( $width > $max_width ) {
			$oitar = $width / $max_width;
			$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 ) );
			if ( ! $image || is_wp_error( $image ) ) {
				wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
			}

			/** This filter is documented in wp-admin/includes/class-custom-image-header.php */
			$image = apply_filters( 'wp_create_file_in_uploads', $image, $attachment_id ); // For replication.

			$url    = str_replace( wp_basename( $url ), wp_basename( $image ), $url );
			$width  = $width / $oitar;
			$height = $height / $oitar;
		} else {
			$oitar = 1;
		}
		?>

<?php _e( 'Crop Header Image' ); ?>

<form method="post" action="<?php echo esc_url( add_query_arg( 'step', 3 ) ); ?>">

<?php _e( 'Choose the part of the image you want to use as your header.' ); ?>

<?php _e( 'You need JavaScript to choose a part of the image.' ); ?>

		<img src="<?php echo esc_url( $url ); ?>" id="upload" width="<?php echo $width; ?>" height="<?php echo $height; ?>" alt="" />

	<input type="hidden" name="x1" id="x1" value="0"/>
	<input type="hidden" name="y1" id="y1" value="0"/>
	<input type="hidden" name="width" id="width" value="<?php echo esc_attr( $width ); ?>"/>
	<input type="hidden" name="height" id="height" value="<?php echo esc_attr( $height ); ?>"/>
	<input type="hidden" name="attachment_id" id="attachment_id" value="<?php echo esc_attr( $attachment_id ); ?>" />
	<input type="hidden" name="oitar" id="oitar" value="<?php echo esc_attr( $oitar ); ?>" />
		<?php if ( empty( $_POST ) && isset( $_GET['file'] ) ) { ?>
	<input type="hidden" name="create-new-attachment" value="true" />
	<?php } ?>
		<?php wp_nonce_field( 'custom-header-crop-image' ); ?>

		<?php submit_button( __( 'Crop and Publish' ), 'primary', 'submit', false ); ?>
		<?php
		if ( isset( $oitar ) && 1 == $oitar && ( current_theme_supports( 'custom-header', 'flex-height' ) || current_theme_supports( 'custom-header', 'flex-width' ) ) ) {
			submit_button( __( 'Skip Cropping, Publish Image as Is' ), '', 'skip-cropping', false );
		}
		?>

</form>

		<?php
	}

	/**
	 * Upload the file to be cropped in the second step.
	 *
	 * @since 3.4.0
	 */
	public function step_2_manage_upload() {
		$overrides = array( 'test_form' => false );

		$uploaded_file = $_FILES['import'];
		$wp_filetype   = wp_check_filetype_and_ext( $uploaded_file['tmp_name'], $uploaded_file['name'] );
		if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) {
			wp_die( __( 'The uploaded file is not a valid image. Please try again.' ) );
		}

		$file = wp_handle_upload( $uploaded_file, $overrides );

		if ( isset( $file['error'] ) ) {
			wp_die( $file['error'], __( 'Image Upload Error' ) );
		}

		$url      = $file['url'];
		$type     = $file['type'];
		$file     = $file['file'];
		$filename = wp_basename( $file );

		// Construct the object array.
		$object = array(
			'post_title'     => $filename,
			'post_content'   => $url,
			'post_mime_type' => $type,
			'guid'           => $url,
			'context'        => 'custom-header',
		);

		// Save the data.
		$attachment_id = wp_insert_attachment( $object, $file );
		return compact( 'attachment_id', 'file', 'filename', 'url', 'type' );
	}

	/**
	 * Display third step of custom header image page.
	 *
	 * @since 2.1.0
	 * @since 4.4.0 Switched to using wp_get_attachment_url() instead of the guid
	 *              for retrieving the header image URL.
	 */
	public function step_3() {
		check_admin_referer( 'custom-header-crop-image' );

		if ( ! current_theme_supports( 'custom-header', 'uploads' ) ) {
			wp_die(
				'
' . __( 'Something went wrong.' ) . '
' .
				'
' . __( 'The current theme does not support uploading a custom header image.' ) . '
',
				403
			);
		}

		if ( ! empty( $_POST['skip-cropping'] ) && ! ( current_theme_supports( 'custom-header', 'flex-height' ) || current_theme_supports( 'custom-header', 'flex-width' ) ) ) {
			wp_die(
				'
' . __( 'Something went wrong.' ) . '
' .
				'
' . __( 'The current theme does not support a flexible sized header image.' ) . '
',
				403
			);
		}

		if ( $_POST['oitar'] > 1 ) {
			$_POST['x1']     = $_POST['x1'] * $_POST['oitar'];
			$_POST['y1']     = $_POST['y1'] * $_POST['oitar'];
			$_POST['width']  = $_POST['width'] * $_POST['oitar'];
			$_POST['height'] = $_POST['height'] * $_POST['oitar'];
		}

		$attachment_id = absint( $_POST['attachment_id'] );
		$original      = get_attached_file( $attachment_id );

		$dimensions = $this->get_header_dimensions(
			array(
				'height' => $_POST['height'],
				'width'  => $_POST['width'],
			)
		);
		$height     = $dimensions['dst_height'];
		$width      = $dimensions['dst_width'];

		if ( empty( $_POST['skip-cropping'] ) ) {
			$cropped = wp_crop_image( $attachment_id, (int) $_POST['x1'], (int) $_POST['y1'], (int) $_POST['width'], (int) $_POST['height'], $width, $height );
		} elseif ( ! empty( $_POST['create-new-attachment'] ) ) {
			$cropped = _copy_image_file( $attachment_id );
		} else {
			$cropped = get_attached_file( $attachment_id );
		}

		if ( ! $cropped || is_wp_error( $cropped ) ) {
			wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
		}

		/** This filter is documented in wp-admin/includes/class-custom-image-header.php */
		$cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.

		$object = $this->create_attachment_object( $cropped, $attachment_id );

		if ( ! empty( $_POST['create-new-attachment'] ) ) {
			unset( $object['ID'] );
		}

		// Update the attachment.
		$attachment_id = $this->insert_attachment( $object, $cropped );

		$url = wp_get_attachment_url( $attachment_id );
		$this->set_header_image( compact( 'url', 'attachment_id', 'width', 'height' ) );

		// Cleanup.
		$medium = str_replace( wp_basename( $original ), 'midsize-' . wp_basename( $original ), $original );
		if ( file_exists( $medium ) ) {
			wp_delete_file( $medium );
		}

		if ( empty( $_POST['create-new-attachment'] ) && empty( $_POST['skip-cropping'] ) ) {
			wp_delete_file( $original );
		}

		return $this->finished();
	}

	/**
	 * Display last step of custom header image page.
	 *
	 * @since 2.1.0
	 */
	public function finished() {
		$this->updated = true;
		$this->step_1();
	}

	/**
	 * Display the page based on the current step.
	 *
	 * @since 2.1.0
	 */
	public function admin_page() {
		if ( ! current_user_can( 'edit_theme_options' ) ) {
			wp_die( __( 'Sorry, you are not allowed to customize headers.' ) );
		}
		$step = $this->step();
		if ( 2 == $step ) {
			$this->step_2();
		} elseif ( 3 == $step ) {
			$this->step_3();
		} else {
			$this->step_1();
		}
	}

	/**
	 * Unused since 3.5.0.
	 *
	 * @since 3.4.0
	 *
	 * @param array $form_fields
	 * @return array $form_fields
	 */
	public function attachment_fields_to_edit( $form_fields ) {
		return $form_fields;
	}

	/**
	 * Unused since 3.5.0.
	 *
	 * @since 3.4.0
	 *
	 * @param array $tabs
	 * @return array $tabs
	 */
	public function filter_upload_tabs( $tabs ) {
		return $tabs;
	}

	/**
	 * Choose a header image, selected from existing uploaded and default headers,
	 * or provide an array of uploaded header data (either new, or from media library).
	 *
	 * @since 3.4.0
	 *
	 * @param mixed $choice Which header image to select. Allows for values of 'random-default-image',
	 *  for randomly cycling among the default images; 'random-uploaded-image', for randomly cycling
	 *  among the uploaded images; the key of a default image registered for that theme; and
	 *  the key of an image uploaded for that theme (the attachment ID of the image).
	 *  Or an array of arguments: attachment_id, url, width, height. All are required.
	 */
	final public function set_header_image( $choice ) {
		if ( is_array( $choice ) || is_object( $choice ) ) {
			$choice = (array) $choice;
			if ( ! isset( $choice['attachment_id'] ) || ! isset( $choice['url'] ) ) {
				return;
			}

			$choice['url'] = esc_url_raw( $choice['url'] );

			$header_image_data = (object) array(
				'attachment_id' => $choice['attachment_id'],
				'url'           => $choice['url'],
				'thumbnail_url' => $choice['url'],
				'height'        => $choice['height'],
				'width'         => $choice['width'],
			);

			update_post_meta( $choice['attachment_id'], '_wp_attachment_is_custom_header', get_stylesheet() );
			set_theme_mod( 'header_image', $choice['url'] );
			set_theme_mod( 'header_image_data', $header_image_data );
			return;
		}

		if ( in_array( $choice, array( 'remove-header', 'random-default-image', 'random-uploaded-image' ), true ) ) {
			set_theme_mod( 'header_image', $choice );
			remove_theme_mod( 'header_image_data' );
			return;
		}

		$uploaded = get_uploaded_header_images();
		if ( $uploaded && isset( $uploaded[ $choice ] ) ) {
			$header_image_data = $uploaded[ $choice ];

		} else {
			$this->process_default_headers();
			if ( isset( $this->default_headers[ $choice ] ) ) {
				$header_image_data = $this->default_headers[ $choice ];
			} else {
				return;
			}
		}

		set_theme_mod( 'header_image', esc_url_raw( $header_image_data['url'] ) );
		set_theme_mod( 'header_image_data', $header_image_data );
	}

	/**
	 * Remove a header image.
	 *
	 * @since 3.4.0
	 */
	final public function remove_header_image() {
		$this->set_header_image( 'remove-header' );
	}

	/**
	 * Reset a header image to the default image for the theme.
	 *
	 * This method does not do anything if the theme does not have a default header image.
	 *
	 * @since 3.4.0
	 */
	final public function reset_header_image() {
		$this->process_default_headers();
		$default = get_theme_support( 'custom-header', 'default-image' );

		if ( ! $default ) {
			$this->remove_header_image();
			return;
		}
		$default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );

		$default_data = array();
		foreach ( $this->default_headers as $header => $details ) {
			if ( $details['url'] == $default ) {
				$default_data = $details;
				break;
			}
		}

		set_theme_mod( 'header_image', $default );
		set_theme_mod( 'header_image_data', (object) $default_data );
	}

	/**
	 * Calculate width and height based on what the currently selected theme supports.
	 *
	 * @since 3.9.0
	 *
	 * @param array $dimensions
	 * @return array dst_height and dst_width of header image.
	 */
	final public function get_header_dimensions( $dimensions ) {
		$max_width       = 0;
		$width           = absint( $dimensions['width'] );
		$height          = absint( $dimensions['height'] );
		$theme_height    = get_theme_support( 'custom-header', 'height' );
		$theme_width     = get_theme_support( 'custom-header', 'width' );
		$has_flex_width  = current_theme_supports( 'custom-header', 'flex-width' );
		$has_flex_height = current_theme_supports( 'custom-header', 'flex-height' );
		$has_max_width   = current_theme_supports( 'custom-header', 'max-width' );
		$dst             = array(
			'dst_height' => null,
			'dst_width'  => null,
		);

		// For flex, limit size of image displayed to 1500px unless theme says otherwise.
		if ( $has_flex_width ) {
			$max_width = 1500;
		}

		if ( $has_max_width ) {
			$max_width = max( $max_width, get_theme_support( 'custom-header', 'max-width' ) );
		}
		$max_width = max( $max_width, $theme_width );

		if ( $has_flex_height && ( ! $has_flex_width || $width > $max_width ) ) {
			$dst['dst_height'] = absint( $height * ( $max_width / $width ) );
		} elseif ( $has_flex_height && $has_flex_width ) {
			$dst['dst_height'] = $height;
		} else {
			$dst['dst_height'] = $theme_height;
		}

		if ( $has_flex_width && ( ! $has_flex_height || $width > $max_width ) ) {
			$dst['dst_width'] = absint( $width * ( $max_width / $width ) );
		} elseif ( $has_flex_width && $has_flex_height ) {
			$dst['dst_width'] = $width;
		} else {
			$dst['dst_width'] = $theme_width;
		}

		return $dst;
	}

	/**
	 * Create an attachment 'object'.
	 *
	 * @since 3.9.0
	 *
	 * @param string $cropped              Cropped image URL.
	 * @param int    $parent_attachment_id Attachment ID of parent image.
	 * @return array Attachment object.
	 */
	final public function create_attachment_object( $cropped, $parent_attachment_id ) {
		$parent     = get_post( $parent_attachment_id );
		$parent_url = wp_get_attachment_url( $parent->ID );
		$url        = str_replace( wp_basename( $parent_url ), wp_basename( $cropped ), $parent_url );

		$size       = @getimagesize( $cropped );
		$image_type = ( $size ) ? $size['mime'] : 'image/jpeg';

		$object = array(
			'ID'             => $parent_attachment_id,
			'post_title'     => wp_basename( $cropped ),
			'post_mime_type' => $image_type,
			'guid'           => $url,
			'context'        => 'custom-header',
			'post_parent'    => $parent_attachment_id,
		);

		return $object;
	}

	/**
	 * Insert an attachment and its metadata.
	 *
	 * @since 3.9.0
	 *
	 * @param array  $object  Attachment object.
	 * @param string $cropped File path to cropped image.
	 * @return int Attachment ID.
	 */
	final public function insert_attachment( $object, $cropped ) {
		$parent_id = isset( $object['post_parent'] ) ? $object['post_parent'] : null;
		unset( $object['post_parent'] );

		$attachment_id = wp_insert_attachment( $object, $cropped );
		$metadata      = wp_generate_attachment_metadata( $attachment_id, $cropped );

		// If this is a crop, save the original attachment ID as metadata.
		if ( $parent_id ) {
			$metadata['attachment_parent'] = $parent_id;
		}

		/**
		 * Filters the header image attachment metadata.
		 *
		 * @since 3.9.0
		 *
		 * @see wp_generate_attachment_metadata()
		 *
		 * @param array $metadata Attachment metadata.
		 */
		$metadata = apply_filters( 'wp_header_image_attachment_metadata', $metadata );

		wp_update_attachment_metadata( $attachment_id, $metadata );

		return $attachment_id;
	}

	/**
	 * Gets attachment uploaded by Media Manager, crops it, then saves it as a
	 * new object. Returns JSON-encoded object details.
	 *
	 * @since 3.9.0
	 */
	public function ajax_header_crop() {
		check_ajax_referer( 'image_editor-' . $_POST['id'], 'nonce' );

		if ( ! current_user_can( 'edit_theme_options' ) ) {
			wp_send_json_error();
		}

		if ( ! current_theme_supports( 'custom-header', 'uploads' ) ) {
			wp_send_json_error();
		}

		$crop_details = $_POST['cropDetails'];

		$dimensions = $this->get_header_dimensions(
			array(
				'height' => $crop_details['height'],
				'width'  => $crop_details['width'],
			)
		);

		$attachment_id = absint( $_POST['id'] );

		$cropped = wp_crop_image(
			$attachment_id,
			(int) $crop_details['x1'],
			(int) $crop_details['y1'],
			(int) $crop_details['width'],
			(int) $crop_details['height'],
			(int) $dimensions['dst_width'],
			(int) $dimensions['dst_height']
		);

		if ( ! $cropped || is_wp_error( $cropped ) ) {
			wp_send_json_error( array( 'message' => __( 'Image could not be processed. Please go back and try again.' ) ) );
		}

		/** This filter is documented in wp-admin/includes/class-custom-image-header.php */
		$cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.

		$object = $this->create_attachment_object( $cropped, $attachment_id );

		$previous = $this->get_previous_crop( $object );

		if ( $previous ) {
			$object['ID'] = $previous;
		} else {
			unset( $object['ID'] );
		}

		$new_attachment_id = $this->insert_attachment( $object, $cropped );

		$object['attachment_id'] = $new_attachment_id;
		$object['url']           = wp_get_attachment_url( $new_attachment_id );

		$object['width']  = $dimensions['dst_width'];
		$object['height'] = $dimensions['dst_height'];

		wp_send_json_success( $object );
	}

	/**
	 * Given an attachment ID for a header image, updates its "last used"
	 * timestamp to now.
	 *
	 * Triggered when the user tries adds a new header image from the
	 * Media Manager, even if s/he doesn't save that change.
	 *
	 * @since 3.9.0
	 */
	public function ajax_header_add() {
		check_ajax_referer( 'header-add', 'nonce' );

		if ( ! current_user_can( 'edit_theme_options' ) ) {
			wp_send_json_error();
		}

		$attachment_id = absint( $_POST['attachment_id'] );
		if ( $attachment_id < 1 ) {
			wp_send_json_error();
		}

		$key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
		update_post_meta( $attachment_id, $key, time() );
		update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', get_stylesheet() );

		wp_send_json_success();
	}

	/**
	 * Given an attachment ID for a header image, unsets it as a user-uploaded
	 * header image for the current theme.
	 *
	 * Triggered when the user clicks the overlay "X" button next to each image
	 * choice in the Customizer's Header tool.
	 *
	 * @since 3.9.0
	 */
	public function ajax_header_remove() {
		check_ajax_referer( 'header-remove', 'nonce' );

		if ( ! current_user_can( 'edit_theme_options' ) ) {
			wp_send_json_error();
		}

		$attachment_id = absint( $_POST['attachment_id'] );
		if ( $attachment_id < 1 ) {
			wp_send_json_error();
		}

		$key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
		delete_post_meta( $attachment_id, $key );
		delete_post_meta( $attachment_id, '_wp_attachment_is_custom_header', get_stylesheet() );

		wp_send_json_success();
	}

	/**
	 * Updates the last-used postmeta on a header image attachment after saving a new header image via the Customizer.
	 *
	 * @since 3.9.0
	 *
	 * @param WP_Customize_Manager $wp_customize Customize manager.
	 */
	public function customize_set_last_used( $wp_customize ) {

		$header_image_data_setting = $wp_customize->get_setting( 'header_image_data' );
		if ( ! $header_image_data_setting ) {
			return;
		}
		$data = $header_image_data_setting->post_value();

		if ( ! isset( $data['attachment_id'] ) ) {
			return;
		}

		$attachment_id = $data['attachment_id'];
		$key           = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
		update_post_meta( $attachment_id, $key, time() );
	}

	/**
	 * Gets the details of default header images if defined.
	 *
	 * @since 3.9.0
	 *
	 * @return array Default header images.
	 */
	public function get_default_header_images() {
		$this->process_default_headers();

		// Get the default image if there is one.
		$default = get_theme_support( 'custom-header', 'default-image' );

		if ( ! $default ) { // If not, easy peasy.
			return $this->default_headers;
		}

		$default             = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
		$already_has_default = false;

		foreach ( $this->default_headers as $k => $h ) {
			if ( $h['url'] === $default ) {
				$already_has_default = true;
				break;
			}
		}

		if ( $already_has_default ) {
			return $this->default_headers;
		}

		// If the one true image isn't included in the default set, prepend it.
		$header_images            = array();
		$header_images['default'] = array(
			'url'           => $default,
			'thumbnail_url' => $default,
			'description'   => 'Default',
		);

		// The rest of the set comes after.
		return array_merge( $header_images, $this->default_headers );
	}

	/**
	 * Gets the previously uploaded header images.
	 *
	 * @since 3.9.0
	 *
	 * @return array Uploaded header images.
	 */
	public function get_uploaded_header_images() {
		$header_images = get_uploaded_header_images();
		$timestamp_key = '_wp_attachment_custom_header_last_used_' . get_stylesheet();
		$alt_text_key  = '_wp_attachment_image_alt';

		foreach ( $header_images as &$header_image ) {
			$header_meta               = get_post_meta( $header_image['attachment_id'] );
			$header_image['timestamp'] = isset( $header_meta[ $timestamp_key ] ) ? $header_meta[ $timestamp_key ] : '';
			$header_image['alt_text']  = isset( $header_meta[ $alt_text_key ] ) ? $header_meta[ $alt_text_key ] : '';
		}

		return $header_images;
	}

	/**
	 * Get the ID of a previous crop from the same base image.
	 *
	 * @since 4.9.0
	 *
	 * @param array $object A crop attachment object.
	 * @return int|false An attachment ID if one exists. False if none.
	 */
	public function get_previous_crop( $object ) {
		$header_images = $this->get_uploaded_header_images();

		// Bail early if there are no header images.
		if ( empty( $header_images ) ) {
			return false;
		}

		$previous = false;

		foreach ( $header_images as $image ) {
			if ( $image['attachment_parent'] === $object['post_parent'] ) {
				$previous = $image['attachment_id'];
				break;
			}
		}

		return $previous;
	}
}