: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
'customize_changeset_uuid' => $this->changeset_uuid(),
if ( ! $this->is_theme_active() ) {
$query_params['customize_theme'] = $this->get_stylesheet();
if ( $this->messenger_channel ) {
$query_params['customize_messenger_channel'] = $this->messenger_channel;
$url = add_query_arg( $query_params, $url );
* Prevents sending a 404 status when returning the response for the customize
* preview, since it causes the jQuery Ajax to fail. Send 200 instead.
public function customize_preview_override_404_status() {
_deprecated_function( __METHOD__, '4.7.0' );
* Prints base element for preview frame.
public function customize_preview_base() {
_deprecated_function( __METHOD__, '4.7.0' );
* Prints a workaround to handle HTML5 tags in IE < 9.
* @deprecated 4.7.0 Customizer no longer supports IE8, so all supported browsers recognize HTML5.
public function customize_preview_html5() {
_deprecated_function( __FUNCTION__, '4.7.0' );
* Prints CSS for loading indicators for the Customizer preview.
public function customize_preview_loading_style() {
body.wp-customizer-unloading {
cursor: progress !important;
-webkit-transition: opacity 0.5s;
transition: opacity 0.5s;
body.wp-customizer-unloading * {
pointer-events: none !important;
form.customize-unpreviewable,
form.customize-unpreviewable input,
form.customize-unpreviewable select,
form.customize-unpreviewable button,
a.customize-unpreviewable,
area.customize-unpreviewable {
cursor: not-allowed !important;
* Removes customize_messenger_channel query parameter from the preview window when it is not in an iframe.
* This ensures that the admin bar will be shown. It also ensures that link navigation will
* work as expected since the parent frame is not being sent the URL to navigate to.
public function remove_frameless_preview_messenger_channel() {
if ( ! $this->messenger_channel ) {
if ( parent !== window ) {
const url = new URL( location.href );
if ( url.searchParams.has( 'customize_messenger_channel' ) ) {
url.searchParams.delete( 'customize_messenger_channel' );
wp_print_inline_script_tag( wp_remove_surrounding_empty_script_tags( ob_get_clean() ) );
* Prints JavaScript settings for preview frame.
public function customize_preview_settings() {
$post_values = $this->unsanitized_post_values( array( 'exclude_changeset' => true ) );
$setting_validities = $this->validate_setting_values( $post_values );
$exported_setting_validities = array_map( array( $this, 'prepare_setting_validity_for_js' ), $setting_validities );
// Note that the REQUEST_URI is not passed into home_url() since this breaks subdirectory installations.
$self_url = empty( $_SERVER['REQUEST_URI'] ) ? home_url( '/' ) : sanitize_url( wp_unslash( $_SERVER['REQUEST_URI'] ) );
$state_query_params = array(
'customize_changeset_uuid',
'customize_messenger_channel',
$self_url = remove_query_arg( $state_query_params, $self_url );
$allowed_urls = $this->get_allowed_urls();
$allowed_hosts = array();
foreach ( $allowed_urls as $allowed_url ) {
$parsed = wp_parse_url( $allowed_url );
if ( empty( $parsed['host'] ) ) {
if ( ! empty( $parsed['port'] ) ) {
$host .= ':' . $parsed['port'];
$allowed_hosts[] = $host;
$switched_locale = switch_to_user_locale( get_current_user_id() );
'shiftClickToEdit' => __( 'Shift-click to edit this element.' ),
'linkUnpreviewable' => __( 'This link is not live-previewable.' ),
'formUnpreviewable' => __( 'This form is not live-previewable.' ),
if ( $switched_locale ) {
restore_previous_locale();
'uuid' => $this->changeset_uuid(),
'autosaved' => $this->autosaved(),
'selectiveRefresh' => 250,
'stylesheet' => $this->get_stylesheet(),
'active' => $this->is_theme_active(),
'allowed' => array_map( 'sanitize_url', $this->get_allowed_urls() ),
'allowedHosts' => array_unique( $allowed_hosts ),
'isCrossDomain' => $this->is_cross_domain(),
'channel' => $this->messenger_channel,
'activePanels' => array(),
'activeSections' => array(),
'activeControls' => array(),
'settingValidities' => $exported_setting_validities,
'nonce' => current_user_can( 'customize' ) ? $this->get_nonces() : array(),
'_dirty' => array_keys( $post_values ),
foreach ( $this->panels as $panel_id => $panel ) {
if ( $panel->check_capabilities() ) {
$settings['activePanels'][ $panel_id ] = $panel->active();
foreach ( $panel->sections as $section_id => $section ) {
if ( $section->check_capabilities() ) {
$settings['activeSections'][ $section_id ] = $section->active();
foreach ( $this->sections as $id => $section ) {
if ( $section->check_capabilities() ) {
$settings['activeSections'][ $id ] = $section->active();
foreach ( $this->controls as $id => $control ) {
if ( $control->check_capabilities() ) {
$settings['activeControls'][ $id ] = $control->active();
var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>;
_wpCustomizeSettings.values = {};
* Serialize settings separately from the initial _wpCustomizeSettings
* serialization in order to avoid a peak memory usage spike.
* @todo We may not even need to export the values at all since the pane syncs them anyway.
foreach ( $this->settings as $id => $setting ) {
if ( $setting->check_capabilities() ) {
wp_json_encode( $setting->js_value() )
})( _wpCustomizeSettings.values );
wp_print_inline_script_tag( wp_remove_surrounding_empty_script_tags( ob_get_clean() ) );
* Prints a signature so we can ensure the Customizer was properly executed.
public function customize_preview_signature() {
_deprecated_function( __METHOD__, '4.7.0' );
* Removes the signature in case we experience a case where the Customizer was not properly executed.
* @param callable|null $callback Optional. Value passed through for {@see 'wp_die_handler'} filter.
* @return callable|null Value passed through for {@see 'wp_die_handler'} filter.
public function remove_preview_signature( $callback = null ) {
_deprecated_function( __METHOD__, '4.7.0' );
* Determines whether it is a theme preview or not.
* @return bool True if it's a preview, false if not.
public function is_preview() {
return (bool) $this->previewing;
* Retrieves the template name of the previewed theme.
* @return string Template name.
public function get_template() {
return $this->theme()->get_template();
* Retrieves the stylesheet name of the previewed theme.
* @return string Stylesheet name.
public function get_stylesheet() {
return $this->theme()->get_stylesheet();
* Retrieves the template root of the previewed theme.
* @return string Theme root.
public function get_template_root() {
return get_raw_theme_root( $this->get_template(), true );
* Retrieves the stylesheet root of the previewed theme.
* @return string Theme root.
public function get_stylesheet_root() {
return get_raw_theme_root( $this->get_stylesheet(), true );
* Filters the active theme and return the name of the previewed theme.
* @param mixed $current_theme {@internal Parameter is not used}
* @return string Theme name.
public function current_theme( $current_theme ) {
return $this->theme()->display( 'Name' );
* Validates setting values.
* Validation is skipped for unregistered settings or for values that are
* already null since they will be skipped anyway. Sanitization is applied
* to values that pass validation, and values that become null or `WP_Error`
* after sanitizing are marked invalid.
* @see WP_REST_Request::has_valid_params()
* @see WP_Customize_Setting::validate()
* @param array $setting_values Mapping of setting IDs to values to validate and sanitize.
* @param array $options {
* @type bool $validate_existence Whether a setting's existence will be checked.
* @type bool $validate_capability Whether the setting capability will be checked.
* @return array Mapping of setting IDs to return value of validate method calls, either `true` or `WP_Error`.
public function validate_setting_values( $setting_values, $options = array() ) {
$options = wp_parse_args(
'validate_capability' => false,
'validate_existence' => false,
foreach ( $setting_values as $setting_id => $unsanitized_value ) {
$setting = $this->get_setting( $setting_id );
if ( $options['validate_existence'] ) {
$validities[ $setting_id ] = new WP_Error( 'unrecognized', __( 'Setting does not exist or is unrecognized.' ) );
if ( $options['validate_capability'] && ! current_user_can( $setting->capability ) ) {
$validity = new WP_Error( 'unauthorized', __( 'Unauthorized to modify setting due to capability.' ) );
if ( is_null( $unsanitized_value ) ) {
$validity = $setting->validate( $unsanitized_value );
if ( ! is_wp_error( $validity ) ) {
/** This filter is documented in wp-includes/class-wp-customize-setting.php */
$late_validity = apply_filters( "customize_validate_{$setting->id}", new WP_Error(), $unsanitized_value, $setting );
if ( is_wp_error( $late_validity ) && $late_validity->has_errors() ) {
$validity = $late_validity;
if ( ! is_wp_error( $validity ) ) {
$value = $setting->sanitize( $unsanitized_value );
if ( is_null( $value ) ) {
} elseif ( is_wp_error( $value ) ) {
if ( false === $validity ) {
$validity = new WP_Error( 'invalid_value', __( 'Invalid value.' ) );
$validities[ $setting_id ] = $validity;
* Prepares setting validity for exporting to the client (JS).
* Converts `WP_Error` instance into array suitable for passing into the
* `wp.customize.Notification` JS model.
* @param true|WP_Error $validity Setting validity.
* @return true|array If `$validity` was a WP_Error, the error codes will be array-mapped
* to their respective `message` and `data` to pass into the
* `wp.customize.Notification` JS model.
public function prepare_setting_validity_for_js( $validity ) {
if ( is_wp_error( $validity ) ) {
foreach ( $validity->errors as $error_code => $error_messages ) {
$notification[ $error_code ] = array(
'message' => implode( ' ', $error_messages ),
'data' => $validity->get_error_data( $error_code ),
* Handles customize_save WP Ajax request to save/update a changeset.
* @since 4.7.0 The semantics of this method have changed to update a changeset, optionally to also change the status and other attributes.
if ( ! is_user_logged_in() ) {
wp_send_json_error( 'unauthenticated' );
if ( ! $this->is_preview() ) {
wp_send_json_error( 'not_preview' );
$action = 'save-customize_' . $this->get_stylesheet();
if ( ! check_ajax_referer( $action, 'nonce', false ) ) {
wp_send_json_error( 'invalid_nonce' );
$changeset_post_id = $this->changeset_post_id();
$is_new_changeset = empty( $changeset_post_id );
if ( $is_new_changeset ) {
if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->create_posts ) ) {
wp_send_json_error( 'cannot_create_changeset_post' );
if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->edit_post, $changeset_post_id ) ) {
wp_send_json_error( 'cannot_edit_changeset_post' );
if ( ! empty( $_POST['customize_changeset_data'] ) ) {
$input_changeset_data = json_decode( wp_unslash( $_POST['customize_changeset_data'] ), true );
if ( ! is_array( $input_changeset_data ) ) {
wp_send_json_error( 'invalid_customize_changeset_data' );
$input_changeset_data = array();
if ( isset( $_POST['customize_changeset_title'] ) ) {
$changeset_title = sanitize_text_field( wp_unslash( $_POST['customize_changeset_title'] ) );
// Validate changeset status param.
$changeset_status = null;
if ( isset( $_POST['customize_changeset_status'] ) ) {
$changeset_status = wp_unslash( $_POST['customize_changeset_status'] );
if ( ! get_post_status_object( $changeset_status ) || ! in_array( $changeset_status, array( 'draft', 'pending', 'publish', 'future' ), true ) ) {
wp_send_json_error( 'bad_customize_changeset_status', 400 );
$is_publish = ( 'publish' === $changeset_status || 'future' === $changeset_status );
if ( $is_publish && ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) {
wp_send_json_error( 'changeset_publish_unauthorized', 403 );
* Validate changeset date param. Date is assumed to be in local time for
* the WP if in MySQL format (YYYY-MM-DD HH:MM:SS). Otherwise, the date
* is parsed with strtotime() so that ISO date format may be supplied
* or a string like "+10 minutes".
$changeset_date_gmt = null;
if ( isset( $_POST['customize_changeset_date'] ) ) {
$changeset_date = wp_unslash( $_POST['customize_changeset_date'] );
if ( preg_match( '/^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$/', $changeset_date ) ) {
$mm = substr( $changeset_date, 5, 2 );
$jj = substr( $changeset_date, 8, 2 );
$aa = substr( $changeset_date, 0, 4 );
$valid_date = wp_checkdate( $mm, $jj, $aa, $changeset_date );
wp_send_json_error( 'bad_customize_changeset_date', 400 );