: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @param string $old_name Old theme name.
* @param WP_Theme $old_theme WP_Theme instance of the old theme.
do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme );
/** This action is documented in wp-includes/theme.php */
do_action( 'after_switch_theme', $stylesheet, $old_theme );
update_option( 'theme_switched', false );
* Includes and instantiates the WP_Customize_Manager class.
* Loads the Customizer at plugins_loaded when accessing the customize.php admin
* page or when any request includes a wp_customize=on param or a customize_changeset
* param (a UUID). 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.
* @global WP_Customize_Manager $wp_customize
function _wp_customize_include() {
$is_customize_admin_page = ( is_admin() && 'customize.php' === basename( $_SERVER['PHP_SELF'] ) );
( isset( $_REQUEST['wp_customize'] ) && 'on' === $_REQUEST['wp_customize'] )
( ! empty( $_GET['customize_changeset_uuid'] ) || ! empty( $_POST['customize_changeset_uuid'] ) )
if ( ! $should_include ) {
* Note that wp_unslash() is not being used on the input vars because it is
* called before wp_magic_quotes() gets called. Besides this fact, none of
* the values should contain any characters needing slashes anyway.
'customize_changeset_uuid',
'customize_messenger_channel',
$input_vars = array_merge(
wp_array_slice_assoc( $_GET, $keys ),
wp_array_slice_assoc( $_POST, $keys )
$messenger_channel = null;
* Value false indicates UUID should be determined after_setup_theme
* to either re-use existing saved changeset or else generate a new UUID if none exists.
* Set initially to false since defaults to true for back-compat;
* can be overridden via the customize_changeset_branching filter.
if ( $is_customize_admin_page && isset( $input_vars['changeset_uuid'] ) ) {
$changeset_uuid = sanitize_key( $input_vars['changeset_uuid'] );
} elseif ( ! empty( $input_vars['customize_changeset_uuid'] ) ) {
$changeset_uuid = sanitize_key( $input_vars['customize_changeset_uuid'] );
// Note that theme will be sanitized via WP_Theme.
if ( $is_customize_admin_page && isset( $input_vars['theme'] ) ) {
$theme = $input_vars['theme'];
} elseif ( isset( $input_vars['customize_theme'] ) ) {
$theme = $input_vars['customize_theme'];
if ( ! empty( $input_vars['customize_autosaved'] ) ) {
if ( isset( $input_vars['customize_messenger_channel'] ) ) {
$messenger_channel = sanitize_key( $input_vars['customize_messenger_channel'] );
* Note that settings must be previewed even outside the customizer preview
* and also in the customizer pane itself. This is to enable loading an existing
* changeset into the customizer. Previewing the settings only has to be prevented
* here in the case of a customize_save action because this will cause WP to think
* there is nothing changed that needs to be saved.
$is_customize_save_action = (
isset( $_REQUEST['action'] )
'customize_save' === wp_unslash( $_REQUEST['action'] )
$settings_previewed = ! $is_customize_save_action;
require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
$GLOBALS['wp_customize'] = new WP_Customize_Manager(
* Publishes a snapshot's changes.
* @global WP_Customize_Manager $wp_customize Customizer instance.
* @param string $new_status New post status.
* @param string $old_status Old post status.
* @param WP_Post $changeset_post Changeset post object.
function _wp_customize_publish_changeset( $new_status, $old_status, $changeset_post ) {
$is_publishing_changeset = (
'customize_changeset' === $changeset_post->post_type
'publish' === $new_status
'publish' !== $old_status
if ( ! $is_publishing_changeset ) {
if ( empty( $wp_customize ) ) {
require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
$wp_customize = new WP_Customize_Manager(
'changeset_uuid' => $changeset_post->post_name,
'settings_previewed' => false,
if ( ! did_action( 'customize_register' ) ) {
* When running from CLI or Cron, the customize_register action will need
* to be triggered in order for core, themes, and plugins to register their
* settings. Normally core will add_action( 'customize_register' ) at
* priority 10 to register the core settings, and if any themes/plugins
* also add_action( 'customize_register' ) at the same priority, they
* will have a $wp_customize with those settings registered since they
* call add_action() afterward, normally. However, when manually doing
* the customize_register action after the setup_theme, then the order
* will be reversed for two actions added at priority 10, resulting in
* the core settings no longer being available as expected to themes/plugins.
* So the following manually calls the method that registers the core
* settings up front before doing the action.
remove_action( 'customize_register', array( $wp_customize, 'register_controls' ) );
$wp_customize->register_controls();
/** This filter is documented in wp-includes/class-wp-customize-manager.php */
do_action( 'customize_register', $wp_customize );
$wp_customize->_publish_changeset_values( $changeset_post->ID );
* Trash the changeset post if revisions are not enabled. Unpublished
* changesets by default get garbage collected due to the auto-draft status.
* When a changeset post is published, however, it would no longer get cleaned
* out. This is a problem when the changeset posts are never displayed anywhere,
* since they would just be endlessly piling up. So here we use the revisions
* feature to indicate whether or not a published changeset should get trashed
* and thus garbage collected.
if ( ! wp_revisions_enabled( $changeset_post ) ) {
$wp_customize->trash_changeset_post( $changeset_post->ID );
* Filters changeset post data upon insert to ensure post_name is intact.
* This is needed to prevent the post_name from being dropped when the post is
* transitioned into pending status by a contributor.
* @param array $post_data An array of slashed post data.
* @param array $supplied_post_data An array of sanitized, but otherwise unmodified post data.
* @return array Filtered data.
function _wp_customize_changeset_filter_insert_post_data( $post_data, $supplied_post_data ) {
if ( isset( $post_data['post_type'] ) && 'customize_changeset' === $post_data['post_type'] ) {
// Prevent post_name from being dropped, such as when contributor saves a changeset post as pending.
if ( empty( $post_data['post_name'] ) && ! empty( $supplied_post_data['post_name'] ) ) {
$post_data['post_name'] = $supplied_post_data['post_name'];
* Adds settings for the customize-loader script.
function _wp_customize_loader_settings() {
$admin_origin = parse_url( admin_url() );
$home_origin = parse_url( home_url() );
$cross_domain = ( strtolower( $admin_origin['host'] ) !== strtolower( $home_origin['host'] ) );
'mobile' => wp_is_mobile(),
'ios' => wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] ),
'url' => esc_url( admin_url( 'customize.php' ) ),
'isCrossDomain' => $cross_domain,
'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.' ),
'mainIframeTitle' => __( 'Customizer' ),
$script = 'var _wpCustomizeLoaderSettings = ' . wp_json_encode( $settings ) . ';';
$wp_scripts = wp_scripts();
$data = $wp_scripts->get_data( 'customize-loader', 'data' );
$script = "$data\n$script";
$wp_scripts->add_data( 'customize-loader', 'data', $script );
* Returns a URL to load the Customizer.
* @param string $stylesheet Optional. Theme to customize. Defaults to active theme.
* The theme's stylesheet will be urlencoded if necessary.
function wp_customize_url( $stylesheet = '' ) {
$url = admin_url( 'customize.php' );
$url .= '?theme=' . urlencode( $stylesheet );
* Prints a script to check whether or not the Customizer is supported,
* and apply either the no-customize-support or customize-support class
* This function MUST be called inside the body tag.
* Ideally, call this function immediately after the body tag is opened.
* This prevents a flash of unstyled content.
* It is also recommended that you add the "no-customize-support" class
* to the body tag by default.
* @since 4.7.0 Support for IE8 and below is explicitly removed via conditional comments.
* @since 5.5.0 IE8 and older are no longer supported.
function wp_customize_support_script() {
$admin_origin = parse_url( admin_url() );
$home_origin = parse_url( home_url() );
$cross_domain = ( strtolower( $admin_origin['host'] ) !== strtolower( $home_origin['host'] ) );
var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
<?php if ( $cross_domain ) : ?>
request = (function(){ var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })();
b[c] = b[c].replace( rcs, ' ' );
// The customizer requires postMessage and CORS (if the site is cross domain).
b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
wp_print_inline_script_tag( wp_remove_surrounding_empty_script_tags( ob_get_clean() ) );
* Whether the site is being previewed in the Customizer.
* @global WP_Customize_Manager $wp_customize Customizer instance.
* @return bool True if the site is being previewed in the Customizer, false otherwise.
function is_customize_preview() {
return ( $wp_customize instanceof WP_Customize_Manager ) && $wp_customize->is_preview();
* Makes sure that auto-draft posts get their post_date bumped or status changed
* to draft to prevent premature garbage-collection.
* When a changeset is updated but remains an auto-draft, ensure the post_date
* for the auto-draft posts remains the same so that it will be
* garbage-collected at the same time by `wp_delete_auto_drafts()`. Otherwise,
* if the changeset is updated to be a draft then update the posts
* to have a far-future post_date so that they will never be garbage collected
* unless the changeset post itself is deleted.
* When a changeset is updated to be a persistent draft or to be scheduled for
* publishing, then transition any dependent auto-drafts to a draft status so
* that they likewise will not be garbage-collected but also so that they can
* be edited in the admin before publishing since there is not yet a post/page
* editing flow in the Customizer. See #39752.
* @link https://core.trac.wordpress.org/ticket/39752
* @see wp_delete_auto_drafts()
* @global wpdb $wpdb WordPress database abstraction object.
* @param string $new_status Transition to this post status.
* @param string $old_status Previous post status.
* @param \WP_Post $post Post data.
function _wp_keep_alive_customize_changeset_dependent_auto_drafts( $new_status, $old_status, $post ) {
// Short-circuit if not a changeset or if the changeset was published.
if ( 'customize_changeset' !== $post->post_type || 'publish' === $new_status ) {
$data = json_decode( $post->post_content, true );
if ( empty( $data['nav_menus_created_posts']['value'] ) ) {
* Actually, in lieu of keeping alive, trash any customization drafts here if the changeset itself is
* getting trashed. This is needed because when a changeset transitions to a draft, then any of the
* dependent auto-draft post/page stubs will also get transitioned to customization drafts which
* are then visible in the WP Admin. We cannot wait for the deletion of the changeset in which
* _wp_delete_customize_changeset_dependent_auto_drafts() will be called, since they need to be
* trashed to remove from visibility immediately.
if ( 'trash' === $new_status ) {
foreach ( $data['nav_menus_created_posts']['value'] as $post_id ) {
if ( ! empty( $post_id ) && 'draft' === get_post_status( $post_id ) ) {
wp_trash_post( $post_id );
if ( 'auto-draft' === $new_status ) {
* Keep the post date for the post matching the changeset
* so that it will not be garbage-collected before the changeset.
$post_args['post_date'] = $post->post_date; // Note wp_delete_auto_drafts() only looks at this date.
* Since the changeset no longer has an auto-draft (and it is not published)
* it is now a persistent changeset, a long-lived draft, and so any
* associated auto-draft posts should likewise transition into having a draft
* status. These drafts will be treated differently than regular drafts in
* that they will be tied to the given changeset. The publish meta box is
* replaced with a notice about how the post is part of a set of customized changes
* which will be published when the changeset is published.
$post_args['post_status'] = 'draft';
foreach ( $data['nav_menus_created_posts']['value'] as $post_id ) {
if ( empty( $post_id ) || 'auto-draft' !== get_post_status( $post_id ) ) {
array( 'ID' => $post_id )
clean_post_cache( $post_id );
* Creates the initial theme features when the 'setup_theme' action is fired.
* See {@see 'setup_theme'}.
* @since 6.0.1 The `block-templates` feature was added.
function create_initial_theme_features() {
'description' => __( 'Whether theme opts in to wide alignment CSS class.' ),
'description' => __( 'Whether posts and comments RSS feed links are added to head.' ),
'description' => __( 'Whether a theme uses block-based templates.' ),
'description' => __( 'Whether a theme uses block-based template parts.' ),
'description' => __( 'Custom background if defined by the theme.' ),
'default-image' => array(
'default-preset' => array(
'default-position-x' => array(
'default-position-y' => array(