*
* @since 1.5.0
*
- * @param string $stylesheet_dir Absolute path to the current them.
+ * @param string $stylesheet_dir Absolute path to the current theme.
* @param string $stylesheet Directory name of the current theme.
* @param string $theme_root Absolute path to themes directory.
*/
set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $_sidebars_widgets ) );
}
- $old_theme = wp_get_theme();
- $new_theme = wp_get_theme( $stylesheet );
+ $nav_menu_locations = get_theme_mod( 'nav_menu_locations' );
if ( func_num_args() > 1 ) {
- $template = $stylesheet;
$stylesheet = func_get_arg( 1 );
- } else {
- $template = $new_theme->get_template();
}
+ $old_theme = wp_get_theme();
+ $new_theme = wp_get_theme( $stylesheet );
+ $template = $new_theme->get_template();
+
update_option( 'template', $template );
update_option( 'stylesheet', $stylesheet );
// Migrate from the old mods_{name} option to theme_mods_{slug}.
if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) {
$default_theme_mods = (array) get_option( 'mods_' . $new_name );
+ if ( ! empty( $nav_menu_locations ) && empty( $default_theme_mods['nav_menu_locations'] ) ) {
+ $default_theme_mods['nav_menu_locations'] = $nav_menu_locations;
+ }
add_option( "theme_mods_$stylesheet", $default_theme_mods );
} else {
/*
* Since retrieve_widgets() is called when initializing a theme in the Customizer,
- * we need to to remove the theme mods to avoid overwriting changes made via
+ * we need to remove the theme mods to avoid overwriting changes made via
* the Customizer when accessing wp-admin/widgets.php.
*/
if ( 'wp_ajax_customize_save' === current_action() ) {
remove_theme_mod( 'sidebars_widgets' );
}
+
+ if ( ! empty( $nav_menu_locations ) ) {
+ $nav_mods = get_theme_mod( 'nav_menu_locations' );
+ if ( empty( $nav_mods ) ) {
+ set_theme_mod( 'nav_menu_locations', $nav_menu_locations );
+ }
+ }
}
update_option( 'theme_switched', $old_theme->get_stylesheet() );
+
/**
* Fires after the theme is switched.
*
* @since 1.5.0
+ * @since 4.5.0 Introduced the `$old_theme` parameter.
*
* @param string $new_name Name of the new theme.
* @param WP_Theme $new_theme WP_Theme instance of the new theme.
+ * @param WP_Theme $old_theme WP_Theme instance of the old theme.
*/
- do_action( 'switch_theme', $new_name, $new_theme );
+ do_action( 'switch_theme', $new_name, $new_theme, $old_theme );
}
/**
* Checks that current theme files 'index.php' and 'style.css' exists.
*
- * Does not check the default theme, which is the fallback and should always exist.
+ * Does not initially check the default theme, which is the fallback and should always exist.
+ * But if it doesn't exist, it'll fall back to the latest core default theme that does exist.
* Will switch theme to the fallback theme if current theme does not validate.
+ *
* You can use the 'validate_current_theme' filter to return false to
* disable this functionality.
*
*
* @since 2.7.0
*
- * @param bool true Validation flag to check the current theme.
+ * @param bool $validate Whether to validate the current theme. Default true.
*/
- if ( defined('WP_INSTALLING') || ! apply_filters( 'validate_current_theme', true ) )
+ if ( wp_installing() || ! apply_filters( 'validate_current_theme', true ) )
return true;
- if ( get_template() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/index.php') ) {
- switch_theme( WP_DEFAULT_THEME );
- return false;
+ if ( ! file_exists( get_template_directory() . '/index.php' ) ) {
+ // Invalid.
+ } elseif ( ! file_exists( get_template_directory() . '/style.css' ) ) {
+ // Invalid.
+ } elseif ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
+ // Invalid.
+ } else {
+ // Valid.
+ return true;
}
- if ( get_stylesheet() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/style.css') ) {
+ $default = wp_get_theme( WP_DEFAULT_THEME );
+ if ( $default->exists() ) {
switch_theme( WP_DEFAULT_THEME );
return false;
}
- if ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
- switch_theme( WP_DEFAULT_THEME );
- return false;
+ /**
+ * If we're in an invalid state but WP_DEFAULT_THEME doesn't exist,
+ * switch to the latest core default theme that's installed.
+ * If it turns out that this latest core default theme is our current
+ * theme, then there's nothing we can do about that, so we have to bail,
+ * rather than going into an infinite loop. (This is why there are
+ * checks against WP_DEFAULT_THEME above, also.) We also can't do anything
+ * if it turns out there is no default theme installed. (That's `false`.)
+ */
+ $default = WP_Theme::get_core_default_theme();
+ if ( false === $default || get_stylesheet() == $default->get_stylesheet() ) {
+ return true;
}
- return true;
+ switch_theme( $default->get_stylesheet() );
+ return false;
}
/**
}
/**
- * Retrieve text color for custom header.
+ * Retrieves the custom header text color in HEX format.
*
* @since 2.1.0
*
- * @return string
+ * @return string Header text color in HEX format (minus the hash symbol).
*/
function get_header_textcolor() {
return get_theme_mod('header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
}
/**
- * Display text color for custom header.
+ * Displays the custom header text color in HEX format (minus the hash symbol).
*
* @since 2.1.0
*/
return esc_url_raw( set_url_scheme( $url ) );
}
+/**
+ * Create image tag markup for a custom header image.
+ *
+ * @since 4.4.0
+ *
+ * @param array $attr Optional. Additional attributes for the image tag. Can be used
+ * to override the default attributes. Default empty.
+ * @return string HTML image element markup or empty string on failure.
+ */
+function get_header_image_tag( $attr = array() ) {
+ $header = get_custom_header();
+
+ if ( empty( $header->url ) ) {
+ return '';
+ }
+
+ $width = absint( $header->width );
+ $height = absint( $header->height );
+
+ $attr = wp_parse_args(
+ $attr,
+ array(
+ 'src' => $header->url,
+ 'width' => $width,
+ 'height' => $height,
+ 'alt' => get_bloginfo( 'name' ),
+ )
+ );
+
+ // Generate 'srcset' and 'sizes' if not already present.
+ if ( empty( $attr['srcset'] ) && ! empty( $header->attachment_id ) ) {
+ $image_meta = get_post_meta( $header->attachment_id, '_wp_attachment_metadata', true );
+ $size_array = array( $width, $height );
+
+ if ( is_array( $image_meta ) ) {
+ $srcset = wp_calculate_image_srcset( $size_array, $header->url, $image_meta, $header->attachment_id );
+ $sizes = ! empty( $attr['sizes'] ) ? $attr['sizes'] : wp_calculate_image_sizes( $size_array, $header->url, $image_meta, $header->attachment_id );
+
+ if ( $srcset && $sizes ) {
+ $attr['srcset'] = $srcset;
+ $attr['sizes'] = $sizes;
+ }
+ }
+ }
+
+ $attr = array_map( 'esc_attr', $attr );
+ $html = '<img';
+
+ foreach ( $attr as $name => $value ) {
+ $html .= ' ' . $name . '="' . $value . '"';
+ }
+
+ $html .= ' />';
+
+ /**
+ * Filter the markup of header images.
+ *
+ * @since 4.4.0
+ *
+ * @param string $html The HTML image tag markup being filtered.
+ * @param object $header The custom header object returned by 'get_custom_header()'.
+ * @param array $attr Array of the attributes for the image tag.
+ */
+ return apply_filters( 'get_header_image_tag', $html, $header, $attr );
+}
+
+/**
+ * Display the image markup for a custom header image.
+ *
+ * @since 4.4.0
+ *
+ * @param array $attr Optional. Attributes for the image markup. Default empty.
+ */
+function the_header_image_tag( $attr = array() ) {
+ echo get_header_image_tag( $attr );
+}
+
/**
* Get random header image data from registered images in theme.
*
foreach ( (array) $headers as $header ) {
$url = esc_url_raw( wp_get_attachment_url( $header->ID ) );
$header_data = wp_get_attachment_metadata( $header->ID );
- $header_index = basename($url);
+ $header_index = $header->ID;
$header_images[$header_index] = array();
$header_images[$header_index]['attachment_id'] = $header->ID;
$args[0] = array_merge( $_wp_theme_features['html5'][0], $args[0] );
break;
+ case 'custom-logo':
+ if ( ! is_array( $args ) ) {
+ $args = array( 0 => array() );
+ }
+ $defaults = array(
+ 'width' => null,
+ 'height' => null,
+ 'flex-width' => false,
+ 'flex-height' => false,
+ 'header-text' => '',
+ );
+ $args[0] = wp_parse_args( array_intersect_key( $args[0], $defaults ), $defaults );
+
+ // Allow full flexibility if no size is specified.
+ if ( is_null( $args[0]['width'] ) && is_null( $args[0]['height'] ) ) {
+ $args[0]['flex-width'] = true;
+ $args[0]['flex-height'] = true;
+ }
+ break;
+
case 'custom-header-uploads' :
return add_theme_support( 'custom-header', array( 'uploads' => true ) );
}
}
+/**
+ * Adds CSS to hide header text for custom logo, based on Customizer setting.
+ *
+ * @since 4.5.0
+ * @access private
+ */
+function _custom_logo_header_styles() {
+ if ( ! current_theme_supports( 'custom-header', 'header-text' ) && get_theme_support( 'custom-logo', 'header-text' ) && ! get_theme_mod( 'header_text', true ) ) {
+ $classes = (array) get_theme_support( 'custom-logo', 'header-text' );
+ $classes = array_map( 'sanitize_html_class', $classes );
+ $classes = '.' . implode( ', .', $classes );
+
+ ?>
+ <!-- Custom Logo: hide header text -->
+ <style id="custom-logo-css" type="text/css">
+ <?php echo $classes; ?> {
+ position: absolute;
+ clip: rect(1px, 1px, 1px, 1px);
+ }
+ </style>
+ <?php
+ }
+}
+
/**
* Gets the theme support arguments passed when registering that support
*
$args = array_slice( func_get_args(), 1 );
switch ( $feature ) {
+ case 'custom-logo' :
case 'custom-header' :
case 'custom-background' :
if ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) )
if ( !isset( $_wp_theme_features[$feature] ) )
return false;
- if ( 'title-tag' == $feature ) {
- // Don't confirm support unless called internally.
- $trace = debug_backtrace();
- if ( ! in_array( $trace[1]['function'], array( '_wp_render_title_tag', 'wp_title' ) ) ) {
- return false;
- }
- }
-
// If no args passed then no extra checks need be performed
if ( func_num_args() <= 1 )
return true;
$type = $args[0];
return in_array( $type, $_wp_theme_features[$feature][0] );
+ case 'custom-logo':
case 'custom-header':
- case 'custom-background' :
- // specific custom header and background capabilities can be registered by passing
- // an array to add_theme_support()
- $header_support = $args[0];
- return ( isset( $_wp_theme_features[$feature][0][$header_support] ) && $_wp_theme_features[$feature][0][$header_support] );
+ case 'custom-background':
+ // Specific capabilities can be registered by passing an array to add_theme_support().
+ return ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) && $_wp_theme_features[ $feature ][0][ $args[0] ] );
}
/**
*
* The dynamic portion of the hook name, `$feature`, refers to the specific theme
* feature. Possible values include 'post-formats', 'post-thumbnails', 'custom-background',
- * 'custom-header', 'menus', 'automatic-feed-links', and 'html5'.
+ * 'custom-header', 'menus', 'automatic-feed-links', 'html5', and `customize-selective-refresh-widgets`.
*
* @since 3.4.0
*
* @access private
* @since 3.0.0
* @since 4.3.0 Also removes `header_image_data`.
+ * @since 4.5.0 Also removes custom logo theme mods.
*
* @param int $id The attachment id.
*/
$attachment_image = wp_get_attachment_url( $id );
$header_image = get_header_image();
$background_image = get_background_image();
+ $custom_logo_id = get_theme_mod( 'custom_logo' );
+
+ if ( $custom_logo_id && $custom_logo_id == $id ) {
+ remove_theme_mod( 'custom_logo' );
+ remove_theme_mod( 'header_text' );
+ }
if ( $header_image && $header_image == $attachment_image ) {
remove_theme_mod( 'header_image' );
/** This action is documented in wp-includes/theme.php */
do_action( 'after_switch_theme', $stylesheet );
}
+ flush_rewrite_rules();
update_option( 'theme_switched', false );
}
/**
* Includes and instantiates the WP_Customize_Manager class.
*
- * Fires when ?wp_customize=on or on wp-admin/customize.php.
+ * Loads the Customizer at plugins_loaded when accessing the customize.php admin
+ * page or when any request includes a wp_customize=on param, either as a GET
+ * query var or as POST data. This param is a signal for whether to bootstrap
+ * the Customizer when WordPress is loading, especially in the Customizer preview
+ * or when making Customizer Ajax requests for widgets or menus.
*
* @since 3.4.0
*
function _wp_customize_include() {
if ( ! ( ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] )
|| ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) )
- ) )
+ ) ) {
return;
+ }
require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
$GLOBALS['wp_customize'] = new WP_Customize_Manager();