wp/wp-includes/class-wp-customize-manager.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
   230 
   230 
   231 	/**
   231 	/**
   232 	 * Changeset data loaded from a customize_changeset post.
   232 	 * Changeset data loaded from a customize_changeset post.
   233 	 *
   233 	 *
   234 	 * @since 4.7.0
   234 	 * @since 4.7.0
   235 	 * @var array
   235 	 * @var array|null
   236 	 */
   236 	 */
   237 	private $_changeset_data;
   237 	private $_changeset_data;
   238 
   238 
   239 	/**
   239 	/**
   240 	 * Constructor.
   240 	 * Constructor.
   268 		// Note that the UUID format will be validated in the setup_theme() method.
   268 		// Note that the UUID format will be validated in the setup_theme() method.
   269 		if ( ! isset( $args['changeset_uuid'] ) ) {
   269 		if ( ! isset( $args['changeset_uuid'] ) ) {
   270 			$args['changeset_uuid'] = wp_generate_uuid4();
   270 			$args['changeset_uuid'] = wp_generate_uuid4();
   271 		}
   271 		}
   272 
   272 
   273 		// The theme and messenger_channel should be supplied via $args, but they are also looked at in the $_REQUEST global here for back-compat.
   273 		// The theme and messenger_channel should be supplied via $args,
       
   274 		// but they are also looked at in the $_REQUEST global here for back-compat.
   274 		if ( ! isset( $args['theme'] ) ) {
   275 		if ( ! isset( $args['theme'] ) ) {
   275 			if ( isset( $_REQUEST['customize_theme'] ) ) {
   276 			if ( isset( $_REQUEST['customize_theme'] ) ) {
   276 				$args['theme'] = wp_unslash( $_REQUEST['customize_theme'] );
   277 				$args['theme'] = wp_unslash( $_REQUEST['customize_theme'] );
   277 			} elseif ( isset( $_REQUEST['theme'] ) ) { // Deprecated.
   278 			} elseif ( isset( $_REQUEST['theme'] ) ) { // Deprecated.
   278 				$args['theme'] = wp_unslash( $_REQUEST['theme'] );
   279 				$args['theme'] = wp_unslash( $_REQUEST['theme'] );
   291 			if ( isset( $args[ $key ] ) ) {
   292 			if ( isset( $args[ $key ] ) ) {
   292 				$this->$key = (bool) $args[ $key ];
   293 				$this->$key = (bool) $args[ $key ];
   293 			}
   294 			}
   294 		}
   295 		}
   295 
   296 
   296 		require_once( ABSPATH . WPINC . '/class-wp-customize-setting.php' );
   297 		require_once ABSPATH . WPINC . '/class-wp-customize-setting.php';
   297 		require_once( ABSPATH . WPINC . '/class-wp-customize-panel.php' );
   298 		require_once ABSPATH . WPINC . '/class-wp-customize-panel.php';
   298 		require_once( ABSPATH . WPINC . '/class-wp-customize-section.php' );
   299 		require_once ABSPATH . WPINC . '/class-wp-customize-section.php';
   299 		require_once( ABSPATH . WPINC . '/class-wp-customize-control.php' );
   300 		require_once ABSPATH . WPINC . '/class-wp-customize-control.php';
   300 
   301 
   301 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-color-control.php' );
   302 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-color-control.php';
   302 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-media-control.php' );
   303 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-media-control.php';
   303 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-upload-control.php' );
   304 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-upload-control.php';
   304 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-image-control.php' );
   305 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-image-control.php';
   305 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-control.php' );
   306 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-background-image-control.php';
   306 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-position-control.php' );
   307 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-background-position-control.php';
   307 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-cropped-image-control.php' );
   308 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-cropped-image-control.php';
   308 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php' );
   309 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php';
   309 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-control.php' );
   310 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-header-image-control.php';
   310 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-theme-control.php' );
   311 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-theme-control.php';
   311 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-code-editor-control.php' );
   312 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-code-editor-control.php';
   312 		require_once( ABSPATH . WPINC . '/customize/class-wp-widget-area-customize-control.php' );
   313 		require_once ABSPATH . WPINC . '/customize/class-wp-widget-area-customize-control.php';
   313 		require_once( ABSPATH . WPINC . '/customize/class-wp-widget-form-customize-control.php' );
   314 		require_once ABSPATH . WPINC . '/customize/class-wp-widget-form-customize-control.php';
   314 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-control.php' );
   315 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-control.php';
   315 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-control.php' );
   316 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-control.php';
   316 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-location-control.php' );
   317 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-location-control.php';
   317 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php' );
   318 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php';
   318 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-locations-control.php' );
   319 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-locations-control.php';
   319 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php' );
   320 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php';
   320 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-control.php' ); // @todo Remove in a future release. See #42364.
   321 
   321 
   322 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php';
   322 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' );
   323 
   323 
   324 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-themes-panel.php';
   324 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-panel.php' );
   325 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php';
   325 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php' );
   326 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php';
   326 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php' );
   327 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php';
   327 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php' );
   328 
   328 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-section.php' ); // @todo Remove in a future release. See #42364.
   329 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-custom-css-setting.php';
   329 
   330 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php';
   330 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-custom-css-setting.php' );
   331 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php';
   331 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php' );
   332 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php';
   332 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php' );
   333 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php';
   333 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php' );
   334 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php';
   334 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php' );
       
   335 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php' );
       
   336 
   335 
   337 		/**
   336 		/**
   338 		 * Filters the core Customizer components to load.
   337 		 * Filters the core Customizer components to load.
   339 		 *
   338 		 *
   340 		 * This allows Core components to be excluded from being instantiated by
   339 		 * This allows Core components to be excluded from being instantiated by
   349 		 * @param string[]             $components Array of core components to load.
   348 		 * @param string[]             $components Array of core components to load.
   350 		 * @param WP_Customize_Manager $this       WP_Customize_Manager instance.
   349 		 * @param WP_Customize_Manager $this       WP_Customize_Manager instance.
   351 		 */
   350 		 */
   352 		$components = apply_filters( 'customize_loaded_components', $this->components, $this );
   351 		$components = apply_filters( 'customize_loaded_components', $this->components, $this );
   353 
   352 
   354 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-selective-refresh.php' );
   353 		require_once ABSPATH . WPINC . '/customize/class-wp-customize-selective-refresh.php';
   355 		$this->selective_refresh = new WP_Customize_Selective_Refresh( $this );
   354 		$this->selective_refresh = new WP_Customize_Selective_Refresh( $this );
   356 
   355 
   357 		if ( in_array( 'widgets', $components, true ) ) {
   356 		if ( in_array( 'widgets', $components, true ) ) {
   358 			require_once( ABSPATH . WPINC . '/class-wp-customize-widgets.php' );
   357 			require_once ABSPATH . WPINC . '/class-wp-customize-widgets.php';
   359 			$this->widgets = new WP_Customize_Widgets( $this );
   358 			$this->widgets = new WP_Customize_Widgets( $this );
   360 		}
   359 		}
   361 
   360 
   362 		if ( in_array( 'nav_menus', $components, true ) ) {
   361 		if ( in_array( 'nav_menus', $components, true ) ) {
   363 			require_once( ABSPATH . WPINC . '/class-wp-customize-nav-menus.php' );
   362 			require_once ABSPATH . WPINC . '/class-wp-customize-nav-menus.php';
   364 			$this->nav_menus = new WP_Customize_Nav_Menus( $this );
   363 			$this->nav_menus = new WP_Customize_Nav_Menus( $this );
   365 		}
   364 		}
   366 
   365 
   367 		add_action( 'setup_theme', array( $this, 'setup_theme' ) );
   366 		add_action( 'setup_theme', array( $this, 'setup_theme' ) );
   368 		add_action( 'wp_loaded', array( $this, 'wp_loaded' ) );
   367 		add_action( 'wp_loaded', array( $this, 'wp_loaded' ) );
   383 		add_filter( 'heartbeat_received', array( $this, 'check_changeset_lock_with_heartbeat' ), 10, 3 );
   382 		add_filter( 'heartbeat_received', array( $this, 'check_changeset_lock_with_heartbeat' ), 10, 3 );
   384 		add_action( 'wp_ajax_customize_override_changeset_lock', array( $this, 'handle_override_changeset_lock_request' ) );
   383 		add_action( 'wp_ajax_customize_override_changeset_lock', array( $this, 'handle_override_changeset_lock_request' ) );
   385 		add_action( 'wp_ajax_customize_dismiss_autosave_or_lock', array( $this, 'handle_dismiss_autosave_or_lock_request' ) );
   384 		add_action( 'wp_ajax_customize_dismiss_autosave_or_lock', array( $this, 'handle_dismiss_autosave_or_lock_request' ) );
   386 
   385 
   387 		add_action( 'customize_register', array( $this, 'register_controls' ) );
   386 		add_action( 'customize_register', array( $this, 'register_controls' ) );
   388 		add_action( 'customize_register', array( $this, 'register_dynamic_settings' ), 11 ); // allow code to create settings first
   387 		add_action( 'customize_register', array( $this, 'register_dynamic_settings' ), 11 ); // Allow code to create settings first.
   389 		add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) );
   388 		add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) );
   390 		add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) );
   389 		add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) );
   391 
   390 
   392 		// Render Common, Panel, Section, and Control templates.
   391 		// Render Common, Panel, Section, and Control templates.
   393 		add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_panel_templates' ), 1 );
   392 		add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_panel_templates' ), 1 );
   436 	 * Custom wp_die wrapper. Returns either the standard message for UI
   435 	 * Custom wp_die wrapper. Returns either the standard message for UI
   437 	 * or the Ajax message.
   436 	 * or the Ajax message.
   438 	 *
   437 	 *
   439 	 * @since 3.4.0
   438 	 * @since 3.4.0
   440 	 *
   439 	 *
   441 	 * @param mixed $ajax_message Ajax return
   440 	 * @param string|WP_Error $ajax_message Ajax return.
   442 	 * @param mixed $message UI message
   441 	 * @param string          $message      Optional. UI message.
   443 	 */
   442 	 */
   444 	protected function wp_die( $ajax_message, $message = null ) {
   443 	protected function wp_die( $ajax_message, $message = null ) {
   445 		if ( $this->doing_ajax() ) {
   444 		if ( $this->doing_ajax() ) {
   446 			wp_die( $ajax_message );
   445 			wp_die( $ajax_message );
   447 		}
   446 		}
   504 	 * @global string $pagenow
   503 	 * @global string $pagenow
   505 	 */
   504 	 */
   506 	public function setup_theme() {
   505 	public function setup_theme() {
   507 		global $pagenow;
   506 		global $pagenow;
   508 
   507 
   509 		// Check permissions for customize.php access since this method is called before customize.php can run any code,
   508 		// Check permissions for customize.php access since this method is called before customize.php can run any code.
   510 		if ( 'customize.php' === $pagenow && ! current_user_can( 'customize' ) ) {
   509 		if ( 'customize.php' === $pagenow && ! current_user_can( 'customize' ) ) {
   511 			if ( ! is_user_logged_in() ) {
   510 			if ( ! is_user_logged_in() ) {
   512 				auth_redirect();
   511 				auth_redirect();
   513 			} else {
   512 			} else {
   514 				wp_die(
   513 				wp_die(
   563 
   562 
   564 		if ( $this->is_theme_active() ) {
   563 		if ( $this->is_theme_active() ) {
   565 			// Once the theme is loaded, we'll validate it.
   564 			// Once the theme is loaded, we'll validate it.
   566 			add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) );
   565 			add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) );
   567 		} else {
   566 		} else {
   568 			// If the requested theme is not the active theme and the user doesn't have the
   567 			// If the requested theme is not the active theme and the user doesn't have
   569 			// switch_themes cap, bail.
   568 			// the switch_themes cap, bail.
   570 			if ( ! current_user_can( 'switch_themes' ) ) {
   569 			if ( ! current_user_can( 'switch_themes' ) ) {
   571 				$this->wp_die( -1, __( 'Sorry, you are not allowed to edit theme options on this site.' ) );
   570 				$this->wp_die( -1, __( 'Sorry, you are not allowed to edit theme options on this site.' ) );
   572 			}
   571 			}
   573 
   572 
   574 			// If the theme has errors while loading, bail.
   573 			// If the theme has errors while loading, bail.
   605 	 * this method will determine which UUID should be used. If changeset branching is disabled, then the most saved
   604 	 * this method will determine which UUID should be used. If changeset branching is disabled, then the most saved
   606 	 * changeset will be loaded by default. Otherwise, if there are no existing saved changesets or if changeset branching is
   605 	 * changeset will be loaded by default. Otherwise, if there are no existing saved changesets or if changeset branching is
   607 	 * enabled, then a new UUID will be generated.
   606 	 * enabled, then a new UUID will be generated.
   608 	 *
   607 	 *
   609 	 * @since 4.9.0
   608 	 * @since 4.9.0
       
   609 	 *
   610 	 * @global string $pagenow
   610 	 * @global string $pagenow
   611 	 */
   611 	 */
   612 	public function establish_loaded_changeset() {
   612 	public function establish_loaded_changeset() {
   613 		global $pagenow;
   613 		global $pagenow;
   614 
   614 
   736 
   736 
   737 	/**
   737 	/**
   738 	 * Gets whether settings are or will be previewed.
   738 	 * Gets whether settings are or will be previewed.
   739 	 *
   739 	 *
   740 	 * @since 4.9.0
   740 	 * @since 4.9.0
       
   741 	 *
   741 	 * @see WP_Customize_Setting::preview()
   742 	 * @see WP_Customize_Setting::preview()
   742 	 *
   743 	 *
   743 	 * @return bool
   744 	 * @return bool
   744 	 */
   745 	 */
   745 	public function settings_previewed() {
   746 	public function settings_previewed() {
   748 
   749 
   749 	/**
   750 	/**
   750 	 * Gets whether data from a changeset's autosaved revision should be loaded if it exists.
   751 	 * Gets whether data from a changeset's autosaved revision should be loaded if it exists.
   751 	 *
   752 	 *
   752 	 * @since 4.9.0
   753 	 * @since 4.9.0
       
   754 	 *
   753 	 * @see WP_Customize_Manager::changeset_data()
   755 	 * @see WP_Customize_Manager::changeset_data()
   754 	 *
   756 	 *
   755 	 * @return bool Is using autosaved changeset revision.
   757 	 * @return bool Is using autosaved changeset revision.
   756 	 */
   758 	 */
   757 	public function autosaved() {
   759 	public function autosaved() {
   760 
   762 
   761 	/**
   763 	/**
   762 	 * Whether the changeset branching is allowed.
   764 	 * Whether the changeset branching is allowed.
   763 	 *
   765 	 *
   764 	 * @since 4.9.0
   766 	 * @since 4.9.0
       
   767 	 *
   765 	 * @see WP_Customize_Manager::establish_loaded_changeset()
   768 	 * @see WP_Customize_Manager::establish_loaded_changeset()
   766 	 *
   769 	 *
   767 	 * @return bool Is changeset branching.
   770 	 * @return bool Is changeset branching.
   768 	 */
   771 	 */
   769 	public function branching() {
   772 	public function branching() {
   800 
   803 
   801 	/**
   804 	/**
   802 	 * Get the changeset UUID.
   805 	 * Get the changeset UUID.
   803 	 *
   806 	 *
   804 	 * @since 4.7.0
   807 	 * @since 4.7.0
       
   808 	 *
   805 	 * @see WP_Customize_Manager::establish_loaded_changeset()
   809 	 * @see WP_Customize_Manager::establish_loaded_changeset()
   806 	 *
   810 	 *
   807 	 * @return string UUID.
   811 	 * @return string UUID.
   808 	 */
   812 	 */
   809 	public function changeset_uuid() {
   813 	public function changeset_uuid() {
   888 	 * @since 3.4.0
   892 	 * @since 3.4.0
   889 	 *
   893 	 *
   890 	 * @return bool
   894 	 * @return bool
   891 	 */
   895 	 */
   892 	public function is_theme_active() {
   896 	public function is_theme_active() {
   893 		return $this->get_stylesheet() == $this->original_stylesheet;
   897 		return $this->get_stylesheet() === $this->original_stylesheet;
   894 	}
   898 	}
   895 
   899 
   896 	/**
   900 	/**
   897 	 * Register styles/scripts and initialize the preview of each setting
   901 	 * Register styles/scripts and initialize the preview of each setting
   898 	 *
   902 	 *
   899 	 * @since 3.4.0
   903 	 * @since 3.4.0
   900 	 */
   904 	 */
   901 	public function wp_loaded() {
   905 	public function wp_loaded() {
   902 
   906 
   903 		// Unconditionally register core types for panels, sections, and controls in case plugin unhooks all customize_register actions.
   907 		// Unconditionally register core types for panels, sections, and controls
       
   908 		// in case plugin unhooks all customize_register actions.
   904 		$this->register_panel_type( 'WP_Customize_Panel' );
   909 		$this->register_panel_type( 'WP_Customize_Panel' );
   905 		$this->register_panel_type( 'WP_Customize_Themes_Panel' );
   910 		$this->register_panel_type( 'WP_Customize_Themes_Panel' );
   906 		$this->register_section_type( 'WP_Customize_Section' );
   911 		$this->register_section_type( 'WP_Customize_Section' );
   907 		$this->register_section_type( 'WP_Customize_Sidebar_Section' );
   912 		$this->register_section_type( 'WP_Customize_Sidebar_Section' );
   908 		$this->register_section_type( 'WP_Customize_Themes_Section' );
   913 		$this->register_section_type( 'WP_Customize_Themes_Section' );
  1070 		}
  1075 		}
  1071 		return $dismissed;
  1076 		return $dismissed;
  1072 	}
  1077 	}
  1073 
  1078 
  1074 	/**
  1079 	/**
  1075 	 * Get the changeset post id for the loaded changeset.
  1080 	 * Get the changeset post ID for the loaded changeset.
  1076 	 *
  1081 	 *
  1077 	 * @since 4.7.0
  1082 	 * @since 4.7.0
  1078 	 *
  1083 	 *
  1079 	 * @return int|null Post ID on success or null if there is no post yet saved.
  1084 	 * @return int|null Post ID on success or null if there is no post yet saved.
  1080 	 */
  1085 	 */
  1114 			}
  1119 			}
  1115 		} elseif ( 'customize_changeset' !== $changeset_post->post_type ) {
  1120 		} elseif ( 'customize_changeset' !== $changeset_post->post_type ) {
  1116 			return new WP_Error( 'wrong_post_type' );
  1121 			return new WP_Error( 'wrong_post_type' );
  1117 		}
  1122 		}
  1118 		$changeset_data = json_decode( $changeset_post->post_content, true );
  1123 		$changeset_data = json_decode( $changeset_post->post_content, true );
  1119 		if ( function_exists( 'json_last_error' ) && json_last_error() ) {
  1124 		$last_error     = json_last_error();
  1120 			return new WP_Error( 'json_parse_error', '', json_last_error() );
  1125 		if ( $last_error ) {
       
  1126 			return new WP_Error( 'json_parse_error', '', $last_error );
  1121 		}
  1127 		}
  1122 		if ( ! is_array( $changeset_data ) ) {
  1128 		if ( ! is_array( $changeset_data ) ) {
  1123 			return new WP_Error( 'expected_array' );
  1129 			return new WP_Error( 'expected_array' );
  1124 		}
  1130 		}
  1125 		return $changeset_data;
  1131 		return $changeset_data;
  1227 
  1233 
  1228 					// Find the max widget number for this type.
  1234 					// Find the max widget number for this type.
  1229 					$widget_numbers = array_keys( $settings );
  1235 					$widget_numbers = array_keys( $settings );
  1230 					if ( count( $widget_numbers ) > 0 ) {
  1236 					if ( count( $widget_numbers ) > 0 ) {
  1231 						$widget_numbers[]               = 1;
  1237 						$widget_numbers[]               = 1;
  1232 						$max_widget_numbers[ $id_base ] = call_user_func_array( 'max', $widget_numbers );
  1238 						$max_widget_numbers[ $id_base ] = max( ...$widget_numbers );
  1233 					} else {
  1239 					} else {
  1234 						$max_widget_numbers[ $id_base ] = 1;
  1240 						$max_widget_numbers[ $id_base ] = 1;
  1235 					}
  1241 					}
  1236 				}
  1242 				}
  1237 				$max_widget_numbers[ $id_base ] += 1;
  1243 				$max_widget_numbers[ $id_base ] += 1;
  1373 						array(
  1379 						array(
  1374 							'post_status' => 'auto-draft', // So attachment will be garbage collected in a week if changeset is never published.
  1380 							'post_status' => 'auto-draft', // So attachment will be garbage collected in a week if changeset is never published.
  1375 						)
  1381 						)
  1376 					);
  1382 					);
  1377 
  1383 
  1378 					// In PHP < 5.6 filesize() returns 0 for the temp files unless we clear the file status cache.
       
  1379 					// Technically, PHP < 5.6.0 || < 5.5.13 || < 5.4.29 but no need to be so targeted.
       
  1380 					// See https://bugs.php.net/bug.php?id=65701
       
  1381 					if ( version_compare( PHP_VERSION, '5.6', '<' ) ) {
       
  1382 						clearstatcache();
       
  1383 					}
       
  1384 
       
  1385 					$attachment_id = media_handle_sideload( $file_array, 0, null, $attachment_post_data );
  1384 					$attachment_id = media_handle_sideload( $file_array, 0, null, $attachment_post_data );
  1386 					if ( is_wp_error( $attachment_id ) ) {
  1385 					if ( is_wp_error( $attachment_id ) ) {
  1387 						continue;
  1386 						continue;
  1388 					}
  1387 					}
  1389 					update_post_meta( $attachment_id, '_starter_content_theme', $this->get_stylesheet() );
  1388 					update_post_meta( $attachment_id, '_starter_content_theme', $this->get_stylesheet() );
  1521 			}
  1520 			}
  1522 		}
  1521 		}
  1523 
  1522 
  1524 		// Options.
  1523 		// Options.
  1525 		foreach ( $options as $name => $value ) {
  1524 		foreach ( $options as $name => $value ) {
  1526 			if ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) {
  1525 
       
  1526 			// Serialize the value to check for post symbols.
       
  1527 			$value = maybe_serialize( $value );
       
  1528 
       
  1529 			if ( is_serialized( $value ) ) {
       
  1530 				if ( preg_match( '/s:\d+:"{{(?P<symbol>.+)}}"/', $value, $matches ) ) {
       
  1531 					if ( isset( $posts[ $matches['symbol'] ] ) ) {
       
  1532 						$symbol_match = $posts[ $matches['symbol'] ]['ID'];
       
  1533 					} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
       
  1534 						$symbol_match = $attachment_ids[ $matches['symbol'] ];
       
  1535 					}
       
  1536 
       
  1537 					// If we have any symbol matches, update the values.
       
  1538 					if ( isset( $symbol_match ) ) {
       
  1539 						// Replace found string matches with post IDs.
       
  1540 						$value = str_replace( $matches[0], "i:{$symbol_match}", $value );
       
  1541 					} else {
       
  1542 						continue;
       
  1543 					}
       
  1544 				}
       
  1545 			} elseif ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) {
  1527 				if ( isset( $posts[ $matches['symbol'] ] ) ) {
  1546 				if ( isset( $posts[ $matches['symbol'] ] ) ) {
  1528 					$value = $posts[ $matches['symbol'] ]['ID'];
  1547 					$value = $posts[ $matches['symbol'] ]['ID'];
  1529 				} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
  1548 				} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
  1530 					$value = $attachment_ids[ $matches['symbol'] ];
  1549 					$value = $attachment_ids[ $matches['symbol'] ];
  1531 				} else {
  1550 				} else {
  1532 					continue;
  1551 					continue;
  1533 				}
  1552 				}
  1534 			}
  1553 			}
  1535 
  1554 
       
  1555 			// Unserialize values after checking for post symbols, so they can be properly referenced.
       
  1556 			$value = maybe_unserialize( $value );
       
  1557 
  1536 			if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) {
  1558 			if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) {
  1537 				$this->set_post_value( $name, $value );
  1559 				$this->set_post_value( $name, $value );
  1538 				$this->pending_starter_content_settings_ids[] = $name;
  1560 				$this->pending_starter_content_settings_ids[] = $name;
  1539 			}
  1561 			}
  1540 		}
  1562 		}
  1541 
  1563 
  1542 		// Theme mods.
  1564 		// Theme mods.
  1543 		foreach ( $theme_mods as $name => $value ) {
  1565 		foreach ( $theme_mods as $name => $value ) {
  1544 			if ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) {
  1566 
       
  1567 			// Serialize the value to check for post symbols.
       
  1568 			$value = maybe_serialize( $value );
       
  1569 
       
  1570 			// Check if value was serialized.
       
  1571 			if ( is_serialized( $value ) ) {
       
  1572 				if ( preg_match( '/s:\d+:"{{(?P<symbol>.+)}}"/', $value, $matches ) ) {
       
  1573 					if ( isset( $posts[ $matches['symbol'] ] ) ) {
       
  1574 						$symbol_match = $posts[ $matches['symbol'] ]['ID'];
       
  1575 					} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
       
  1576 						$symbol_match = $attachment_ids[ $matches['symbol'] ];
       
  1577 					}
       
  1578 
       
  1579 					// If we have any symbol matches, update the values.
       
  1580 					if ( isset( $symbol_match ) ) {
       
  1581 						// Replace found string matches with post IDs.
       
  1582 						$value = str_replace( $matches[0], "i:{$symbol_match}", $value );
       
  1583 					} else {
       
  1584 						continue;
       
  1585 					}
       
  1586 				}
       
  1587 			} elseif ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) {
  1545 				if ( isset( $posts[ $matches['symbol'] ] ) ) {
  1588 				if ( isset( $posts[ $matches['symbol'] ] ) ) {
  1546 					$value = $posts[ $matches['symbol'] ]['ID'];
  1589 					$value = $posts[ $matches['symbol'] ]['ID'];
  1547 				} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
  1590 				} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
  1548 					$value = $attachment_ids[ $matches['symbol'] ];
  1591 					$value = $attachment_ids[ $matches['symbol'] ];
  1549 				} else {
  1592 				} else {
  1550 					continue;
  1593 					continue;
  1551 				}
  1594 				}
  1552 			}
  1595 			}
       
  1596 
       
  1597 			// Unserialize values after checking for post symbols, so they can be properly referenced.
       
  1598 			$value = maybe_unserialize( $value );
  1553 
  1599 
  1554 			// Handle header image as special case since setting has a legacy format.
  1600 			// Handle header image as special case since setting has a legacy format.
  1555 			if ( 'header_image' === $name ) {
  1601 			if ( 'header_image' === $name ) {
  1556 				$name     = 'header_image_data';
  1602 				$name     = 'header_image_data';
  1557 				$metadata = wp_get_attachment_metadata( $value );
  1603 				$metadata = wp_get_attachment_metadata( $value );
  1598 		if ( empty( $attachments ) ) {
  1644 		if ( empty( $attachments ) ) {
  1599 			return $prepared_attachments;
  1645 			return $prepared_attachments;
  1600 		}
  1646 		}
  1601 
  1647 
  1602 		// Such is The WordPress Way.
  1648 		// Such is The WordPress Way.
  1603 		require_once( ABSPATH . 'wp-admin/includes/file.php' );
  1649 		require_once ABSPATH . 'wp-admin/includes/file.php';
  1604 		require_once( ABSPATH . 'wp-admin/includes/media.php' );
  1650 		require_once ABSPATH . 'wp-admin/includes/media.php';
  1605 		require_once( ABSPATH . 'wp-admin/includes/image.php' );
  1651 		require_once ABSPATH . 'wp-admin/includes/image.php';
  1606 
  1652 
  1607 		foreach ( $attachments as $symbol => $attachment ) {
  1653 		foreach ( $attachments as $symbol => $attachment ) {
  1608 
  1654 
  1609 			// A file is required and URLs to files are not currently allowed.
  1655 			// A file is required and URLs to files are not currently allowed.
  1610 			if ( empty( $attachment['file'] ) || preg_match( '#^https?://$#', $attachment['file'] ) ) {
  1656 			if ( empty( $attachment['file'] ) || preg_match( '#^https?://$#', $attachment['file'] ) ) {
  1766 	 * @see WP_REST_Request::has_valid_params()
  1812 	 * @see WP_REST_Request::has_valid_params()
  1767 	 *
  1813 	 *
  1768 	 * @param WP_Customize_Setting $setting A WP_Customize_Setting derived object.
  1814 	 * @param WP_Customize_Setting $setting A WP_Customize_Setting derived object.
  1769 	 * @param mixed                $default Value returned $setting has no post value (added in 4.2.0)
  1815 	 * @param mixed                $default Value returned $setting has no post value (added in 4.2.0)
  1770 	 *                                      or the post value is invalid (added in 4.6.0).
  1816 	 *                                      or the post value is invalid (added in 4.6.0).
  1771 	 * @return string|mixed $post_value Sanitized value or the $default provided.
  1817 	 * @return string|mixed Sanitized value or the $default provided.
  1772 	 */
  1818 	 */
  1773 	public function post_value( $setting, $default = null ) {
  1819 	public function post_value( $setting, $default = null ) {
  1774 		$post_values = $this->unsanitized_post_values();
  1820 		$post_values = $this->unsanitized_post_values();
  1775 		if ( ! array_key_exists( $setting->id, $post_values ) ) {
  1821 		if ( ! array_key_exists( $setting->id, $post_values ) ) {
  1776 			return $default;
  1822 			return $default;
  1859 		 * If preview is being served inside the customizer preview iframe, and
  1905 		 * If preview is being served inside the customizer preview iframe, and
  1860 		 * if the user doesn't have customize capability, then it is assumed
  1906 		 * if the user doesn't have customize capability, then it is assumed
  1861 		 * that the user's session has expired and they need to re-authenticate.
  1907 		 * that the user's session has expired and they need to re-authenticate.
  1862 		 */
  1908 		 */
  1863 		if ( $this->messenger_channel && ! current_user_can( 'customize' ) ) {
  1909 		if ( $this->messenger_channel && ! current_user_can( 'customize' ) ) {
  1864 			$this->wp_die( -1, __( 'Unauthorized. You may remove the customize_messenger_channel param to preview as frontend.' ) );
  1910 			$this->wp_die(
       
  1911 				-1,
       
  1912 				sprintf(
       
  1913 					/* translators: %s: customize_messenger_channel */
       
  1914 					__( 'Unauthorized. You may remove the %s param to preview as frontend.' ),
       
  1915 					'<code>customize_messenger_channel<code>'
       
  1916 				)
       
  1917 			);
  1865 			return;
  1918 			return;
  1866 		}
  1919 		}
  1867 
  1920 
  1868 		$this->prepare_controls();
  1921 		$this->prepare_controls();
  1869 
  1922 
  1903 
  1956 
  1904 	/**
  1957 	/**
  1905 	 * Add customize state query params to a given URL if preview is allowed.
  1958 	 * Add customize state query params to a given URL if preview is allowed.
  1906 	 *
  1959 	 *
  1907 	 * @since 4.7.0
  1960 	 * @since 4.7.0
       
  1961 	 *
  1908 	 * @see wp_redirect()
  1962 	 * @see wp_redirect()
  1909 	 * @see WP_Customize_Manager::get_allowed_url()
  1963 	 * @see WP_Customize_Manager::get_allowed_url()
  1910 	 *
  1964 	 *
  1911 	 * @param string $url URL.
  1965 	 * @param string $url URL.
  1912 	 * @return string URL.
  1966 	 * @return string URL.
  2243 	/**
  2297 	/**
  2244 	 * Filters the current theme and return the name of the previewed theme.
  2298 	 * Filters the current theme and return the name of the previewed theme.
  2245 	 *
  2299 	 *
  2246 	 * @since 3.4.0
  2300 	 * @since 3.4.0
  2247 	 *
  2301 	 *
  2248 	 * @param $current_theme {@internal Parameter is not used}
  2302 	 * @param mixed $current_theme {@internal Parameter is not used}
  2249 	 * @return string Theme name.
  2303 	 * @return string Theme name.
  2250 	 */
  2304 	 */
  2251 	public function current_theme( $current_theme ) {
  2305 	public function current_theme( $current_theme ) {
  2252 		return $this->theme()->display( 'Name' );
  2306 		return $this->theme()->display( 'Name' );
  2253 	}
  2307 	}
  2498 			// Dismiss all other auto-draft changeset posts for this user (they serve like autosave revisions), as there should only be one.
  2552 			// Dismiss all other auto-draft changeset posts for this user (they serve like autosave revisions), as there should only be one.
  2499 			if ( $is_new_changeset ) {
  2553 			if ( $is_new_changeset ) {
  2500 				$this->dismiss_user_auto_draft_changesets();
  2554 				$this->dismiss_user_auto_draft_changesets();
  2501 			}
  2555 			}
  2502 
  2556 
  2503 			// Note that if the changeset status was publish, then it will get set to trash if revisions are not supported.
  2557 			// Note that if the changeset status was publish, then it will get set to Trash if revisions are not supported.
  2504 			$response['changeset_status'] = $changeset_post->post_status;
  2558 			$response['changeset_status'] = $changeset_post->post_status;
  2505 			if ( $is_publish && 'trash' === $response['changeset_status'] ) {
  2559 			if ( $is_publish && 'trash' === $response['changeset_status'] ) {
  2506 				$response['changeset_status'] = 'publish';
  2560 				$response['changeset_status'] = 'publish';
  2507 			}
  2561 			}
  2508 
  2562 
  2727 		 * A changeset update is transactional when a status is supplied in the request.
  2781 		 * A changeset update is transactional when a status is supplied in the request.
  2728 		 */
  2782 		 */
  2729 		if ( $update_transactionally && $invalid_setting_count > 0 ) {
  2783 		if ( $update_transactionally && $invalid_setting_count > 0 ) {
  2730 			$response = array(
  2784 			$response = array(
  2731 				'setting_validities' => $setting_validities,
  2785 				'setting_validities' => $setting_validities,
  2732 				/* translators: %s: number of invalid settings */
  2786 				/* translators: %s: Number of invalid settings. */
  2733 				'message'            => sprintf( _n( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.', $invalid_setting_count ), number_format_i18n( $invalid_setting_count ) ),
  2787 				'message'            => sprintf( _n( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.', $invalid_setting_count ), number_format_i18n( $invalid_setting_count ) ),
  2734 			);
  2788 			);
  2735 			return new WP_Error( 'transaction_fail', '', $response );
  2789 			return new WP_Error( 'transaction_fail', '', $response );
  2736 		}
  2790 		}
  2737 
  2791 
  2841 			update_option( 'theme_switched_via_customizer', true );
  2895 			update_option( 'theme_switched_via_customizer', true );
  2842 			$this->start_previewing_theme();
  2896 			$this->start_previewing_theme();
  2843 		}
  2897 		}
  2844 
  2898 
  2845 		// Gather the data for wp_insert_post()/wp_update_post().
  2899 		// Gather the data for wp_insert_post()/wp_update_post().
  2846 		$json_options = 0;
  2900 		$post_array = array(
  2847 		if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) {
  2901 			// JSON_UNESCAPED_SLASHES is only to improve readability as slashes needn't be escaped in storage.
  2848 			$json_options |= JSON_UNESCAPED_SLASHES; // Introduced in PHP 5.4. This is only to improve readability as slashes needn't be escaped in storage.
  2902 			'post_content' => wp_json_encode( $data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT ),
  2849 		}
       
  2850 		$json_options |= JSON_PRETTY_PRINT; // Also introduced in PHP 5.4, but WP defines constant for back compat. See WP Trac #30139.
       
  2851 		$post_array    = array(
       
  2852 			'post_content' => wp_json_encode( $data, $json_options ),
       
  2853 		);
  2903 		);
  2854 		if ( $args['title'] ) {
  2904 		if ( $args['title'] ) {
  2855 			$post_array['post_title'] = $args['title'];
  2905 			$post_array['post_title'] = $args['title'];
  2856 		}
  2906 		}
  2857 		if ( $changeset_post_id ) {
  2907 		if ( $changeset_post_id ) {
  2884 
  2934 
  2885 		$this->store_changeset_revision = $allow_revision;
  2935 		$this->store_changeset_revision = $allow_revision;
  2886 		add_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ), 5, 3 );
  2936 		add_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ), 5, 3 );
  2887 
  2937 
  2888 		/*
  2938 		/*
  2889 		 * Update the changeset post. The publish_customize_changeset action
  2939 		 * Update the changeset post. The publish_customize_changeset action will cause the settings in the
  2890 		 * will cause the settings in the changeset to be saved via
  2940 		 * changeset to be saved via WP_Customize_Setting::save(). Updating a post with publish status will
  2891 		 * WP_Customize_Setting::save().
  2941 		 * trigger WP_Customize_Manager::publish_changeset_values().
  2892 		 */
  2942 		 */
  2893 
  2943 		add_filter( 'wp_insert_post_data', array( $this, 'preserve_insert_changeset_post_content' ), 5, 3 );
  2894 		// Prevent content filters from corrupting JSON in post_content.
       
  2895 		$has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) );
       
  2896 		if ( $has_kses ) {
       
  2897 			kses_remove_filters();
       
  2898 		}
       
  2899 		$has_targeted_link_rel_filters = ( false !== has_filter( 'content_save_pre', 'wp_targeted_link_rel' ) );
       
  2900 		if ( $has_targeted_link_rel_filters ) {
       
  2901 			wp_remove_targeted_link_rel_filters();
       
  2902 		}
       
  2903 
       
  2904 		// Note that updating a post with publish status will trigger WP_Customize_Manager::publish_changeset_values().
       
  2905 		if ( $changeset_post_id ) {
  2944 		if ( $changeset_post_id ) {
  2906 			if ( $args['autosave'] && 'auto-draft' !== get_post_status( $changeset_post_id ) ) {
  2945 			if ( $args['autosave'] && 'auto-draft' !== get_post_status( $changeset_post_id ) ) {
  2907 				// See _wp_translate_postdata() for why this is required as it will use the edit_post meta capability.
  2946 				// See _wp_translate_postdata() for why this is required as it will use the edit_post meta capability.
  2908 				add_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10, 4 );
  2947 				add_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10, 4 );
       
  2948 
  2909 				$post_array['post_ID']   = $post_array['ID'];
  2949 				$post_array['post_ID']   = $post_array['ID'];
  2910 				$post_array['post_type'] = 'customize_changeset';
  2950 				$post_array['post_type'] = 'customize_changeset';
  2911 				$r                       = wp_create_post_autosave( wp_slash( $post_array ) );
  2951 
       
  2952 				$r = wp_create_post_autosave( wp_slash( $post_array ) );
       
  2953 
  2912 				remove_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10 );
  2954 				remove_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10 );
  2913 			} else {
  2955 			} else {
  2914 				$post_array['edit_date'] = true; // Prevent date clearing.
  2956 				$post_array['edit_date'] = true; // Prevent date clearing.
  2915 				$r                       = wp_update_post( wp_slash( $post_array ), true );
  2957 
       
  2958 				$r = wp_update_post( wp_slash( $post_array ), true );
  2916 
  2959 
  2917 				// Delete autosave revision for user when the changeset is updated.
  2960 				// Delete autosave revision for user when the changeset is updated.
  2918 				if ( ! empty( $args['user_id'] ) ) {
  2961 				if ( ! empty( $args['user_id'] ) ) {
  2919 					$autosave_draft = wp_get_post_autosave( $changeset_post_id, $args['user_id'] );
  2962 					$autosave_draft = wp_get_post_autosave( $changeset_post_id, $args['user_id'] );
  2920 					if ( $autosave_draft ) {
  2963 					if ( $autosave_draft ) {
  2926 			$r = wp_insert_post( wp_slash( $post_array ), true );
  2969 			$r = wp_insert_post( wp_slash( $post_array ), true );
  2927 			if ( ! is_wp_error( $r ) ) {
  2970 			if ( ! is_wp_error( $r ) ) {
  2928 				$this->_changeset_post_id = $r; // Update cached post ID for the loaded changeset.
  2971 				$this->_changeset_post_id = $r; // Update cached post ID for the loaded changeset.
  2929 			}
  2972 			}
  2930 		}
  2973 		}
  2931 
  2974 		remove_filter( 'wp_insert_post_data', array( $this, 'preserve_insert_changeset_post_content' ), 5 );
  2932 		// Restore removed content filters.
       
  2933 		if ( $has_kses ) {
       
  2934 			kses_init_filters();
       
  2935 		}
       
  2936 		if ( $has_targeted_link_rel_filters ) {
       
  2937 			wp_init_targeted_link_rel_filters();
       
  2938 		}
       
  2939 
  2975 
  2940 		$this->_changeset_data = null; // Reset so WP_Customize_Manager::changeset_data() will re-populate with updated contents.
  2976 		$this->_changeset_data = null; // Reset so WP_Customize_Manager::changeset_data() will re-populate with updated contents.
  2941 
  2977 
  2942 		remove_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ) );
  2978 		remove_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ) );
  2943 
  2979 
  2949 			$response['changeset_post_save_failure'] = $r->get_error_code();
  2985 			$response['changeset_post_save_failure'] = $r->get_error_code();
  2950 			return new WP_Error( 'changeset_post_save_failure', '', $response );
  2986 			return new WP_Error( 'changeset_post_save_failure', '', $response );
  2951 		}
  2987 		}
  2952 
  2988 
  2953 		return $response;
  2989 		return $response;
       
  2990 	}
       
  2991 
       
  2992 	/**
       
  2993 	 * Preserve the initial JSON post_content passed to save into the post.
       
  2994 	 *
       
  2995 	 * This is needed to prevent KSES and other {@see 'content_save_pre'} filters
       
  2996 	 * from corrupting JSON data.
       
  2997 	 *
       
  2998 	 * Note that WP_Customize_Manager::validate_setting_values() have already
       
  2999 	 * run on the setting values being serialized as JSON into the post content
       
  3000 	 * so it is pre-sanitized.
       
  3001 	 *
       
  3002 	 * Also, the sanitization logic is re-run through the respective
       
  3003 	 * WP_Customize_Setting::sanitize() method when being read out of the
       
  3004 	 * changeset, via WP_Customize_Manager::post_value(), and this sanitized
       
  3005 	 * value will also be sent into WP_Customize_Setting::update() for
       
  3006 	 * persisting to the DB.
       
  3007 	 *
       
  3008 	 * Multiple users can collaborate on a single changeset, where one user may
       
  3009 	 * have the unfiltered_html capability but another may not. A user with
       
  3010 	 * unfiltered_html may add a script tag to some field which needs to be kept
       
  3011 	 * intact even when another user updates the changeset to modify another field
       
  3012 	 * when they do not have unfiltered_html.
       
  3013 	 *
       
  3014 	 * @since 5.4.1
       
  3015 	 *
       
  3016 	 * @param array $data                An array of slashed and processed post data.
       
  3017 	 * @param array $postarr             An array of sanitized (and slashed) but otherwise unmodified post data.
       
  3018 	 * @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as originally passed to wp_insert_post().
       
  3019 	 * @return array Filtered post data.
       
  3020 	 */
       
  3021 	public function preserve_insert_changeset_post_content( $data, $postarr, $unsanitized_postarr ) {
       
  3022 		if (
       
  3023 			isset( $data['post_type'] ) &&
       
  3024 			isset( $unsanitized_postarr['post_content'] ) &&
       
  3025 			'customize_changeset' === $data['post_type'] ||
       
  3026 			(
       
  3027 				'revision' === $data['post_type'] &&
       
  3028 				! empty( $data['post_parent'] ) &&
       
  3029 				'customize_changeset' === get_post_type( $data['post_parent'] )
       
  3030 			)
       
  3031 		) {
       
  3032 			$data['post_content'] = $unsanitized_postarr['post_content'];
       
  3033 		}
       
  3034 		return $data;
  2954 	}
  3035 	}
  2955 
  3036 
  2956 	/**
  3037 	/**
  2957 	 * Trash or delete a changeset post.
  3038 	 * Trash or delete a changeset post.
  2958 	 *
  3039 	 *
  2960 	 * `wp_publish_post()`. The reason for bypassing `wp_trash_post()` is that it
  3041 	 * `wp_publish_post()`. The reason for bypassing `wp_trash_post()` is that it
  2961 	 * will mutate the the `post_content` and the `post_name` when they should be
  3042 	 * will mutate the the `post_content` and the `post_name` when they should be
  2962 	 * untouched.
  3043 	 * untouched.
  2963 	 *
  3044 	 *
  2964 	 * @since 4.9.0
  3045 	 * @since 4.9.0
       
  3046 	 *
       
  3047 	 * @see wp_trash_post()
  2965 	 * @global wpdb $wpdb WordPress database abstraction object.
  3048 	 * @global wpdb $wpdb WordPress database abstraction object.
  2966 	 * @see wp_trash_post()
       
  2967 	 *
  3049 	 *
  2968 	 * @param int|WP_Post $post The changeset post.
  3050 	 * @param int|WP_Post $post The changeset post.
  2969 	 * @return mixed A WP_Post object for the trashed post or an empty value on failure.
  3051 	 * @return mixed A WP_Post object for the trashed post or an empty value on failure.
  2970 	 */
  3052 	 */
  2971 	public function trash_changeset_post( $post ) {
  3053 	public function trash_changeset_post( $post ) {
  3062 				)
  3144 				)
  3063 			);
  3145 			);
  3064 			return;
  3146 			return;
  3065 		}
  3147 		}
  3066 
  3148 
  3067 		if ( $changeset_post_id && ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->delete_post, $changeset_post_id ) ) {
  3149 		if ( $changeset_post_id ) {
  3068 			wp_send_json_error(
  3150 			if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->delete_post, $changeset_post_id ) ) {
  3069 				array(
  3151 				wp_send_json_error(
  3070 					'code'    => 'changeset_trash_unauthorized',
  3152 					array(
  3071 					'message' => __( 'Unable to trash changes.' ),
  3153 						'code'    => 'changeset_trash_unauthorized',
  3072 				)
  3154 						'message' => __( 'Unable to trash changes.' ),
  3073 			);
  3155 					)
       
  3156 				);
       
  3157 			}
       
  3158 
       
  3159 			$lock_user = (int) wp_check_post_lock( $changeset_post_id );
       
  3160 
       
  3161 			if ( $lock_user && get_current_user_id() !== $lock_user ) {
       
  3162 				wp_send_json_error(
       
  3163 					array(
       
  3164 						'code'     => 'changeset_locked',
       
  3165 						'message'  => __( 'Changeset is being edited by other user.' ),
       
  3166 						'lockUser' => $this->get_lock_user_data( $lock_user ),
       
  3167 					)
       
  3168 				);
       
  3169 			}
  3074 		}
  3170 		}
  3075 
  3171 
  3076 		if ( 'trash' === get_post_status( $changeset_post_id ) ) {
  3172 		if ( 'trash' === get_post_status( $changeset_post_id ) ) {
  3077 			wp_send_json_error(
  3173 			wp_send_json_error(
  3078 				array(
  3174 				array(
  3109 	 * `_wp_translate_postdata()` which in turn will check if a user can 'edit_post', but the
  3205 	 * `_wp_translate_postdata()` which in turn will check if a user can 'edit_post', but the
  3110 	 * the caps for the customize_changeset post type are all mapping to the meta capability.
  3206 	 * the caps for the customize_changeset post type are all mapping to the meta capability.
  3111 	 * This should be able to be removed once #40922 is addressed in core.
  3207 	 * This should be able to be removed once #40922 is addressed in core.
  3112 	 *
  3208 	 *
  3113 	 * @since 4.9.0
  3209 	 * @since 4.9.0
       
  3210 	 *
  3114 	 * @link https://core.trac.wordpress.org/ticket/40922
  3211 	 * @link https://core.trac.wordpress.org/ticket/40922
  3115 	 * @see WP_Customize_Manager::save_changeset_post()
  3212 	 * @see WP_Customize_Manager::save_changeset_post()
  3116 	 * @see _wp_translate_postdata()
  3213 	 * @see _wp_translate_postdata()
  3117 	 *
  3214 	 *
  3118 	 * @param string[] $caps    Array of the user's capabilities.
  3215 	 * @param string[] $caps    Array of the user's capabilities.
  3119 	 * @param string   $cap     Capability name.
  3216 	 * @param string   $cap     Capability name.
  3120 	 * @param int      $user_id The user ID.
  3217 	 * @param int      $user_id The user ID.
  3121 	 * @param array    $args    Adds the context to the cap. Typically the object ID.
  3218 	 * @param array    $args    Adds the context to the cap. Typically the object ID.
  3122 	 * @return array   Capabilities.
  3219 	 * @return array Capabilities.
  3123 	 */
  3220 	 */
  3124 	public function grant_edit_post_capability_for_changeset( $caps, $cap, $user_id, $args ) {
  3221 	public function grant_edit_post_capability_for_changeset( $caps, $cap, $user_id, $args ) {
  3125 		if ( 'edit_post' === $cap && ! empty( $args[0] ) && 'customize_changeset' === get_post_type( $args[0] ) ) {
  3222 		if ( 'edit_post' === $cap && ! empty( $args[0] ) && 'customize_changeset' === get_post_type( $args[0] ) ) {
  3126 			$post_type_obj = get_post_type_object( 'customize_changeset' );
  3223 			$post_type_obj = get_post_type_object( 'customize_changeset' );
  3127 			$caps          = map_meta_cap( $post_type_obj->cap->$cap, $user_id );
  3224 			$caps          = map_meta_cap( $post_type_obj->cap->$cap, $user_id );
  3132 	/**
  3229 	/**
  3133 	 * Marks the changeset post as being currently edited by the current user.
  3230 	 * Marks the changeset post as being currently edited by the current user.
  3134 	 *
  3231 	 *
  3135 	 * @since 4.9.0
  3232 	 * @since 4.9.0
  3136 	 *
  3233 	 *
  3137 	 * @param int  $changeset_post_id Changeset post id.
  3234 	 * @param int  $changeset_post_id Changeset post ID.
  3138 	 * @param bool $take_over Take over the changeset, default is false.
  3235 	 * @param bool $take_over Whether to take over the changeset. Default false.
  3139 	 */
  3236 	 */
  3140 	public function set_changeset_lock( $changeset_post_id, $take_over = false ) {
  3237 	public function set_changeset_lock( $changeset_post_id, $take_over = false ) {
  3141 		if ( $changeset_post_id ) {
  3238 		if ( $changeset_post_id ) {
  3142 			$can_override = ! (bool) get_post_meta( $changeset_post_id, '_edit_lock', true );
  3239 			$can_override = ! (bool) get_post_meta( $changeset_post_id, '_edit_lock', true );
  3143 
  3240 
  3157 	/**
  3254 	/**
  3158 	 * Refreshes changeset lock with the current time if current user edited the changeset before.
  3255 	 * Refreshes changeset lock with the current time if current user edited the changeset before.
  3159 	 *
  3256 	 *
  3160 	 * @since 4.9.0
  3257 	 * @since 4.9.0
  3161 	 *
  3258 	 *
  3162 	 * @param int $changeset_post_id Changeset post id.
  3259 	 * @param int $changeset_post_id Changeset post ID.
  3163 	 */
  3260 	 */
  3164 	public function refresh_changeset_lock( $changeset_post_id ) {
  3261 	public function refresh_changeset_lock( $changeset_post_id ) {
  3165 		if ( ! $changeset_post_id ) {
  3262 		if ( ! $changeset_post_id ) {
  3166 			return;
  3263 			return;
  3167 		}
  3264 		}
  3313 	 * @since 4.7.0
  3410 	 * @since 4.7.0
  3314 	 *
  3411 	 *
  3315 	 * @param bool    $post_has_changed Whether the post has changed.
  3412 	 * @param bool    $post_has_changed Whether the post has changed.
  3316 	 * @param WP_Post $last_revision    The last revision post object.
  3413 	 * @param WP_Post $last_revision    The last revision post object.
  3317 	 * @param WP_Post $post             The post object.
  3414 	 * @param WP_Post $post             The post object.
  3318 	 *
       
  3319 	 * @return bool Whether a revision should be made.
  3415 	 * @return bool Whether a revision should be made.
  3320 	 */
  3416 	 */
  3321 	public function _filter_revision_post_has_changed( $post_has_changed, $last_revision, $post ) {
  3417 	public function _filter_revision_post_has_changed( $post_has_changed, $last_revision, $post ) {
  3322 		unset( $last_revision );
  3418 		unset( $last_revision );
  3323 		if ( 'customize_changeset' === $post->post_type ) {
  3419 		if ( 'customize_changeset' === $post->post_type ) {
  3338 	 * Please note that if the settings in the changeset are for a non-activated
  3434 	 * Please note that if the settings in the changeset are for a non-activated
  3339 	 * theme, the theme must first be switched to (via `switch_theme()`) before
  3435 	 * theme, the theme must first be switched to (via `switch_theme()`) before
  3340 	 * invoking this method.
  3436 	 * invoking this method.
  3341 	 *
  3437 	 *
  3342 	 * @since 4.7.0
  3438 	 * @since 4.7.0
       
  3439 	 *
  3343 	 * @see _wp_customize_publish_changeset()
  3440 	 * @see _wp_customize_publish_changeset()
  3344 	 * @global wpdb $wpdb
  3441 	 * @global wpdb $wpdb WordPress database abstraction object.
  3345 	 *
  3442 	 *
  3346 	 * @param int $changeset_post_id ID for customize_changeset post. Defaults to the changeset for the current manager instance.
  3443 	 * @param int $changeset_post_id ID for customize_changeset post. Defaults to the changeset for the current manager instance.
  3347 	 * @return true|WP_Error True or error info.
  3444 	 * @return true|WP_Error True or error info.
  3348 	 */
  3445 	 */
  3349 	public function _publish_changeset_values( $changeset_post_id ) {
  3446 	public function _publish_changeset_values( $changeset_post_id ) {
  3637 	 * Add a customize setting.
  3734 	 * Add a customize setting.
  3638 	 *
  3735 	 *
  3639 	 * @since 3.4.0
  3736 	 * @since 3.4.0
  3640 	 * @since 4.5.0 Return added WP_Customize_Setting instance.
  3737 	 * @since 4.5.0 Return added WP_Customize_Setting instance.
  3641 	 *
  3738 	 *
       
  3739 	 * @see WP_Customize_Setting::__construct()
       
  3740 	 * @link https://developer.wordpress.org/themes/customize-api
       
  3741 	 *
  3642 	 * @param WP_Customize_Setting|string $id   Customize Setting object, or ID.
  3742 	 * @param WP_Customize_Setting|string $id   Customize Setting object, or ID.
  3643 	 * @param array                       $args {
  3743 	 * @param array                       $args Optional. Array of properties for the new Setting object.
  3644 	 *  Optional. Array of properties for the new WP_Customize_Setting. Default empty array.
  3744 	 *                                          See WP_Customize_Setting::__construct() for information
  3645 	 *
  3745 	 *                                          on accepted arguments. Default empty array.
  3646 	 *  @type string       $type                  Type of the setting. Default 'theme_mod'.
  3746 	 * @return WP_Customize_Setting The instance of the setting that was added.
  3647 	 *  @type string       $capability            Capability required for the setting. Default 'edit_theme_options'
       
  3648 	 *  @type string|array $theme_supports        Theme features required to support the panel. Default is none.
       
  3649 	 *  @type string       $default               Default value for the setting. Default is empty string.
       
  3650 	 *  @type string       $transport             Options for rendering the live preview of changes in Customizer.
       
  3651 	 *                                            Using 'refresh' makes the change visible by reloading the whole preview.
       
  3652 	 *                                            Using 'postMessage' allows a custom JavaScript to handle live changes.
       
  3653 	 * @link https://developer.wordpress.org/themes/customize-api
       
  3654 	 *                                            Default is 'refresh'
       
  3655 	 *  @type callable     $validate_callback     Server-side validation callback for the setting's value.
       
  3656 	 *  @type callable     $sanitize_callback     Callback to filter a Customize setting value in un-slashed form.
       
  3657 	 *  @type callable     $sanitize_js_callback  Callback to convert a Customize PHP setting value to a value that is
       
  3658 	 *                                            JSON serializable.
       
  3659 	 *  @type bool         $dirty                 Whether or not the setting is initially dirty when created.
       
  3660 	 * }
       
  3661 	 * @return WP_Customize_Setting             The instance of the setting that was added.
       
  3662 	 */
  3747 	 */
  3663 	public function add_setting( $id, $args = array() ) {
  3748 	public function add_setting( $id, $args = array() ) {
  3664 		if ( $id instanceof WP_Customize_Setting ) {
  3749 		if ( $id instanceof WP_Customize_Setting ) {
  3665 			$setting = $id;
  3750 			$setting = $id;
  3666 		} else {
  3751 		} else {
  3694 	 * @return array The WP_Customize_Setting objects added.
  3779 	 * @return array The WP_Customize_Setting objects added.
  3695 	 */
  3780 	 */
  3696 	public function add_dynamic_settings( $setting_ids ) {
  3781 	public function add_dynamic_settings( $setting_ids ) {
  3697 		$new_settings = array();
  3782 		$new_settings = array();
  3698 		foreach ( $setting_ids as $setting_id ) {
  3783 		foreach ( $setting_ids as $setting_id ) {
  3699 			// Skip settings already created
  3784 			// Skip settings already created.
  3700 			if ( $this->get_setting( $setting_id ) ) {
  3785 			if ( $this->get_setting( $setting_id ) ) {
  3701 				continue;
  3786 				continue;
  3702 			}
  3787 			}
  3703 
  3788 
  3704 			$setting_args  = false;
  3789 			$setting_args  = false;
  3755 	}
  3840 	}
  3756 
  3841 
  3757 	/**
  3842 	/**
  3758 	 * Remove a customize setting.
  3843 	 * Remove a customize setting.
  3759 	 *
  3844 	 *
       
  3845 	 * Note that removing the setting doesn't destroy the WP_Customize_Setting instance or remove its filters.
       
  3846 	 *
  3760 	 * @since 3.4.0
  3847 	 * @since 3.4.0
  3761 	 *
  3848 	 *
  3762 	 * @param string $id Customize Setting ID.
  3849 	 * @param string $id Customize Setting ID.
  3763 	 */
  3850 	 */
  3764 	public function remove_setting( $id ) {
  3851 	public function remove_setting( $id ) {
  3769 	 * Add a customize panel.
  3856 	 * Add a customize panel.
  3770 	 *
  3857 	 *
  3771 	 * @since 4.0.0
  3858 	 * @since 4.0.0
  3772 	 * @since 4.5.0 Return added WP_Customize_Panel instance.
  3859 	 * @since 4.5.0 Return added WP_Customize_Panel instance.
  3773 	 *
  3860 	 *
  3774 	 * @param WP_Customize_Panel|string $id   Customize Panel object, or Panel ID.
  3861 	 * @see WP_Customize_Panel::__construct()
  3775 	 * @param array                     $args {
  3862 	 *
  3776 	 *  Optional. Array of properties for the new Panel object. Default empty array.
  3863 	 * @param WP_Customize_Panel|string $id   Customize Panel object, or ID.
  3777 	 *  @type int          $priority              Priority of the panel, defining the display order of panels and sections.
  3864 	 * @param array                     $args Optional. Array of properties for the new Panel object.
  3778 	 *                                            Default 160.
  3865 	 *                                        See WP_Customize_Panel::__construct() for information
  3779 	 *  @type string       $capability            Capability required for the panel. Default `edit_theme_options`
  3866 	 *                                        on accepted arguments. Default empty array.
  3780 	 *  @type string|array $theme_supports        Theme features required to support the panel.
  3867 	 * @return WP_Customize_Panel The instance of the panel that was added.
  3781 	 *  @type string       $title                 Title of the panel to show in UI.
       
  3782 	 *  @type string       $description           Description to show in the UI.
       
  3783 	 *  @type string       $type                  Type of the panel.
       
  3784 	 *  @type callable     $active_callback       Active callback.
       
  3785 	 * }
       
  3786 	 * @return WP_Customize_Panel             The instance of the panel that was added.
       
  3787 	 */
  3868 	 */
  3788 	public function add_panel( $id, $args = array() ) {
  3869 	public function add_panel( $id, $args = array() ) {
  3789 		if ( $id instanceof WP_Customize_Panel ) {
  3870 		if ( $id instanceof WP_Customize_Panel ) {
  3790 			$panel = $id;
  3871 			$panel = $id;
  3791 		} else {
  3872 		} else {
  3811 	}
  3892 	}
  3812 
  3893 
  3813 	/**
  3894 	/**
  3814 	 * Remove a customize panel.
  3895 	 * Remove a customize panel.
  3815 	 *
  3896 	 *
       
  3897 	 * Note that removing the panel doesn't destroy the WP_Customize_Panel instance or remove its filters.
       
  3898 	 *
  3816 	 * @since 4.0.0
  3899 	 * @since 4.0.0
  3817 	 *
  3900 	 *
  3818 	 * @param string $id Panel ID to remove.
  3901 	 * @param string $id Panel ID to remove.
  3819 	 */
  3902 	 */
  3820 	public function remove_panel( $id ) {
  3903 	public function remove_panel( $id ) {
  3821 		// Removing core components this way is _doing_it_wrong().
  3904 		// Removing core components this way is _doing_it_wrong().
  3822 		if ( in_array( $id, $this->components, true ) ) {
  3905 		if ( in_array( $id, $this->components, true ) ) {
  3823 			/* translators: 1: panel id, 2: link to 'customize_loaded_components' filter reference */
       
  3824 			$message = sprintf(
  3906 			$message = sprintf(
       
  3907 				/* translators: 1: Panel ID, 2: Link to 'customize_loaded_components' filter reference. */
  3825 				__( 'Removing %1$s manually will cause PHP warnings. Use the %2$s filter instead.' ),
  3908 				__( 'Removing %1$s manually will cause PHP warnings. Use the %2$s filter instead.' ),
  3826 				$id,
  3909 				$id,
  3827 				'<a href="' . esc_url( 'https://developer.wordpress.org/reference/hooks/customize_loaded_components/' ) . '"><code>customize_loaded_components</code></a>'
  3910 				sprintf(
       
  3911 					'<a href="%1$s">%2$s</a>',
       
  3912 					esc_url( 'https://developer.wordpress.org/reference/hooks/customize_loaded_components/' ),
       
  3913 					'<code>customize_loaded_components</code>'
       
  3914 				)
  3828 			);
  3915 			);
  3829 
  3916 
  3830 			_doing_it_wrong( __METHOD__, $message, '4.5.0' );
  3917 			_doing_it_wrong( __METHOD__, $message, '4.5.0' );
  3831 		}
  3918 		}
  3832 		unset( $this->panels[ $id ] );
  3919 		unset( $this->panels[ $id ] );
  3863 	 * Add a customize section.
  3950 	 * Add a customize section.
  3864 	 *
  3951 	 *
  3865 	 * @since 3.4.0
  3952 	 * @since 3.4.0
  3866 	 * @since 4.5.0 Return added WP_Customize_Section instance.
  3953 	 * @since 4.5.0 Return added WP_Customize_Section instance.
  3867 	 *
  3954 	 *
  3868 	 * @param WP_Customize_Section|string $id   Customize Section object, or Section ID.
  3955 	 * @see WP_Customize_Section::__construct()
  3869 	 * @param array                     $args {
  3956 	 *
  3870 	 *  Optional. Array of properties for the new Section object. Default empty array.
  3957 	 * @param WP_Customize_Section|string $id   Customize Section object, or ID.
  3871 	 *  @type int          $priority              Priority of the section, defining the display order of panels and sections.
  3958 	 * @param array                       $args Optional. Array of properties for the new Section object.
  3872 	 *                                            Default 160.
  3959 	 *                                          See WP_Customize_Section::__construct() for information
  3873 	 *  @type string       $panel                 The panel this section belongs to (if any). Default empty.
  3960 	 *                                          on accepted arguments. Default empty array.
  3874 	 *  @type string       $capability            Capability required for the section. Default 'edit_theme_options'
  3961 	 * @return WP_Customize_Section The instance of the section that was added.
  3875 	 *  @type string|array $theme_supports        Theme features required to support the section.
       
  3876 	 *  @type string       $title                 Title of the section to show in UI.
       
  3877 	 *  @type string       $description           Description to show in the UI.
       
  3878 	 *  @type string       $type                  Type of the section.
       
  3879 	 *  @type callable     $active_callback       Active callback.
       
  3880 	 *  @type bool         $description_hidden    Hide the description behind a help icon, instead of inline above the first control. Default false.
       
  3881 	 * }
       
  3882 	 * @return WP_Customize_Section             The instance of the section that was added.
       
  3883 	 */
  3962 	 */
  3884 	public function add_section( $id, $args = array() ) {
  3963 	public function add_section( $id, $args = array() ) {
  3885 		if ( $id instanceof WP_Customize_Section ) {
  3964 		if ( $id instanceof WP_Customize_Section ) {
  3886 			$section = $id;
  3965 			$section = $id;
  3887 		} else {
  3966 		} else {
  3907 	}
  3986 	}
  3908 
  3987 
  3909 	/**
  3988 	/**
  3910 	 * Remove a customize section.
  3989 	 * Remove a customize section.
  3911 	 *
  3990 	 *
       
  3991 	 * Note that removing the section doesn't destroy the WP_Customize_Section instance or remove its filters.
       
  3992 	 *
  3912 	 * @since 3.4.0
  3993 	 * @since 3.4.0
  3913 	 *
  3994 	 *
  3914 	 * @param string $id Section ID.
  3995 	 * @param string $id Section ID.
  3915 	 */
  3996 	 */
  3916 	public function remove_section( $id ) {
  3997 	public function remove_section( $id ) {
  3948 	 * Add a customize control.
  4029 	 * Add a customize control.
  3949 	 *
  4030 	 *
  3950 	 * @since 3.4.0
  4031 	 * @since 3.4.0
  3951 	 * @since 4.5.0 Return added WP_Customize_Control instance.
  4032 	 * @since 4.5.0 Return added WP_Customize_Control instance.
  3952 	 *
  4033 	 *
       
  4034 	 * @see WP_Customize_Control::__construct()
       
  4035 	 *
  3953 	 * @param WP_Customize_Control|string $id   Customize Control object, or ID.
  4036 	 * @param WP_Customize_Control|string $id   Customize Control object, or ID.
  3954 	 * @param array                       $args {
  4037 	 * @param array                       $args Optional. Array of properties for the new Control object.
  3955 	 *  Optional. Array of properties for the new Control object. Default empty array.
  4038 	 *                                          See WP_Customize_Control::__construct() for information
  3956 	 *
  4039 	 *                                          on accepted arguments. Default empty array.
  3957 	 *  @type array        $settings              All settings tied to the control. If undefined, defaults to `$setting`.
  4040 	 * @return WP_Customize_Control The instance of the control that was added.
  3958 	 *                                            IDs in the array correspond to the ID of a registered `WP_Customize_Setting`.
       
  3959 	 *  @type string       $setting               The primary setting for the control (if there is one). Default is 'default'.
       
  3960 	 *  @type string       $capability            Capability required to use this control. Normally derived from `$settings`.
       
  3961 	 *  @type int          $priority              Order priority to load the control. Default 10.
       
  3962 	 *  @type string       $section               The section this control belongs to. Default empty.
       
  3963 	 *  @type string       $label                 Label for the control. Default empty.
       
  3964 	 *  @type string       $description           Description for the control. Default empty.
       
  3965 	 *  @type array        $choices               List of choices for 'radio' or 'select' type controls, where values
       
  3966 	 *                                            are the keys, and labels are the values. Default empty array.
       
  3967 	 *  @type array        $input_attrs           List of custom input attributes for control output, where attribute
       
  3968 	 *                                            names are the keys and values are the values. Default empty array.
       
  3969 	 *  @type bool         $allow_addition        Show UI for adding new content, currently only used for the
       
  3970 	 *                                            dropdown-pages control. Default false.
       
  3971 	 *  @type string       $type                  The type of the control. Default 'text'.
       
  3972 	 *  @type callback     $active_callback       Active callback.
       
  3973 	 * }
       
  3974 	 * @return WP_Customize_Control             The instance of the control that was added.
       
  3975 	 */
  4041 	 */
  3976 	public function add_control( $id, $args = array() ) {
  4042 	public function add_control( $id, $args = array() ) {
  3977 		if ( $id instanceof WP_Customize_Control ) {
  4043 		if ( $id instanceof WP_Customize_Control ) {
  3978 			$control = $id;
  4044 			$control = $id;
  3979 		} else {
  4045 		} else {
  3998 		}
  4064 		}
  3999 	}
  4065 	}
  4000 
  4066 
  4001 	/**
  4067 	/**
  4002 	 * Remove a customize control.
  4068 	 * Remove a customize control.
       
  4069 	 *
       
  4070 	 * Note that removing the control doesn't destroy the WP_Customize_Control instance or remove its filters.
  4003 	 *
  4071 	 *
  4004 	 * @since 3.4.0
  4072 	 * @since 3.4.0
  4005 	 *
  4073 	 *
  4006 	 * @param string $id ID of the control.
  4074 	 * @param string $id ID of the control.
  4007 	 */
  4075 	 */
  4465 	 *
  4533 	 *
  4466 	 * @return string The template string for the document title.
  4534 	 * @return string The template string for the document title.
  4467 	 */
  4535 	 */
  4468 	public function get_document_title_template() {
  4536 	public function get_document_title_template() {
  4469 		if ( $this->is_theme_active() ) {
  4537 		if ( $this->is_theme_active() ) {
  4470 			/* translators: %s: document title from the preview */
  4538 			/* translators: %s: Document title from the preview. */
  4471 			$document_title_tmpl = __( 'Customize: %s' );
  4539 			$document_title_tmpl = __( 'Customize: %s' );
  4472 		} else {
  4540 		} else {
  4473 			/* translators: %s: document title from the preview */
  4541 			/* translators: %s: Document title from the preview. */
  4474 			$document_title_tmpl = __( 'Live Preview: %s' );
  4542 			$document_title_tmpl = __( 'Live Preview: %s' );
  4475 		}
  4543 		}
  4476 		$document_title_tmpl = html_entity_decode( $document_title_tmpl, ENT_QUOTES, 'UTF-8' ); // Because exported to JS and assigned to document.title.
  4544 		$document_title_tmpl = html_entity_decode( $document_title_tmpl, ENT_QUOTES, 'UTF-8' ); // Because exported to JS and assigned to document.title.
  4477 		return $document_title_tmpl;
  4545 		return $document_title_tmpl;
  4478 	}
  4546 	}
  4531 	 * ssl certs. Domain mapping plugins can allow other urls in these conditions
  4599 	 * ssl certs. Domain mapping plugins can allow other urls in these conditions
  4532 	 * using the customize_allowed_urls filter.
  4600 	 * using the customize_allowed_urls filter.
  4533 	 *
  4601 	 *
  4534 	 * @since 4.7.0
  4602 	 * @since 4.7.0
  4535 	 *
  4603 	 *
  4536 	 * @returns array Allowed URLs.
  4604 	 * @return array Allowed URLs.
  4537 	 */
  4605 	 */
  4538 	public function get_allowed_urls() {
  4606 	public function get_allowed_urls() {
  4539 		$allowed_urls = array( home_url( '/' ) );
  4607 		$allowed_urls = array( home_url( '/' ) );
  4540 
  4608 
  4541 		if ( is_ssl() && ! $this->is_cross_domain() ) {
  4609 		if ( is_ssl() && ! $this->is_cross_domain() ) {
  4584 	/**
  4652 	/**
  4585 	 * Get URL to link the user to when closing the Customizer.
  4653 	 * Get URL to link the user to when closing the Customizer.
  4586 	 *
  4654 	 *
  4587 	 * @since 4.4.0
  4655 	 * @since 4.4.0
  4588 	 *
  4656 	 *
       
  4657 	 * @global array $_registered_pages
       
  4658 	 *
  4589 	 * @return string URL for link to close Customizer.
  4659 	 * @return string URL for link to close Customizer.
  4590 	 */
  4660 	 */
  4591 	public function get_return_url() {
  4661 	public function get_return_url() {
       
  4662 		global $_registered_pages;
       
  4663 
  4592 		$referer                    = wp_get_referer();
  4664 		$referer                    = wp_get_referer();
  4593 		$excluded_referer_basenames = array( 'customize.php', 'wp-login.php' );
  4665 		$excluded_referer_basenames = array( 'customize.php', 'wp-login.php' );
  4594 
  4666 
  4595 		if ( $this->return_url ) {
  4667 		if ( $this->return_url ) {
  4596 			$return_url = $this->return_url;
  4668 			$return_url = $this->return_url;
  4599 		} elseif ( $this->preview_url ) {
  4671 		} elseif ( $this->preview_url ) {
  4600 			$return_url = $this->preview_url;
  4672 			$return_url = $this->preview_url;
  4601 		} else {
  4673 		} else {
  4602 			$return_url = home_url( '/' );
  4674 			$return_url = home_url( '/' );
  4603 		}
  4675 		}
       
  4676 
       
  4677 		$return_url_basename = wp_basename( parse_url( $this->return_url, PHP_URL_PATH ) );
       
  4678 		$return_url_query    = parse_url( $this->return_url, PHP_URL_QUERY );
       
  4679 
       
  4680 		if ( 'themes.php' === $return_url_basename && $return_url_query ) {
       
  4681 			parse_str( $return_url_query, $query_vars );
       
  4682 
       
  4683 			/*
       
  4684 			 * If the return URL is a page added by a theme to the Appearance menu via add_submenu_page(),
       
  4685 			 * verify that belongs to the active theme, otherwise fall back to the Themes screen.
       
  4686 			 */
       
  4687 			if ( isset( $query_vars['page'] ) && ! isset( $_registered_pages[ "appearance_page_{$query_vars['page']}" ] ) ) {
       
  4688 				$return_url = admin_url( 'themes.php' );
       
  4689 			}
       
  4690 		}
       
  4691 
  4604 		return $return_url;
  4692 		return $return_url;
  4605 	}
  4693 	}
  4606 
  4694 
  4607 	/**
  4695 	/**
  4608 	 * Set the autofocused constructs.
  4696 	 * Set the autofocused constructs.
  4610 	 * @since 4.4.0
  4698 	 * @since 4.4.0
  4611 	 *
  4699 	 *
  4612 	 * @param array $autofocus {
  4700 	 * @param array $autofocus {
  4613 	 *     Mapping of 'panel', 'section', 'control' to the ID which should be autofocused.
  4701 	 *     Mapping of 'panel', 'section', 'control' to the ID which should be autofocused.
  4614 	 *
  4702 	 *
  4615 	 *     @type string [$control]  ID for control to be autofocused.
  4703 	 *     @type string $control ID for control to be autofocused.
  4616 	 *     @type string [$section]  ID for section to be autofocused.
  4704 	 *     @type string $section ID for section to be autofocused.
  4617 	 *     @type string [$panel]    ID for panel to be autofocused.
  4705 	 *     @type string $panel   ID for panel to be autofocused.
  4618 	 * }
  4706 	 * }
  4619 	 */
  4707 	 */
  4620 	public function set_autofocus( $autofocus ) {
  4708 	public function set_autofocus( $autofocus ) {
  4621 		$this->autofocus = array_filter( wp_array_slice_assoc( $autofocus, array( 'panel', 'section', 'control' ) ), 'is_string' );
  4709 		$this->autofocus = array_filter( wp_array_slice_assoc( $autofocus, array( 'panel', 'section', 'control' ) ), 'is_string' );
  4622 	}
  4710 	}
  4627 	 * @since 4.4.0
  4715 	 * @since 4.4.0
  4628 	 *
  4716 	 *
  4629 	 * @return array {
  4717 	 * @return array {
  4630 	 *     Mapping of 'panel', 'section', 'control' to the ID which should be autofocused.
  4718 	 *     Mapping of 'panel', 'section', 'control' to the ID which should be autofocused.
  4631 	 *
  4719 	 *
  4632 	 *     @type string [$control]  ID for control to be autofocused.
  4720 	 *     @type string $control ID for control to be autofocused.
  4633 	 *     @type string [$section]  ID for section to be autofocused.
  4721 	 *     @type string $section ID for section to be autofocused.
  4634 	 *     @type string [$panel]    ID for panel to be autofocused.
  4722 	 *     @type string $panel   ID for panel to be autofocused.
  4635 	 * }
  4723 	 * }
  4636 	 */
  4724 	 */
  4637 	public function get_autofocus() {
  4725 	public function get_autofocus() {
  4638 		return $this->autofocus;
  4726 		return $this->autofocus;
  4639 	}
  4727 	}
  4807 			'autofocus'              => $this->get_autofocus(),
  4895 			'autofocus'              => $this->get_autofocus(),
  4808 			'documentTitleTmpl'      => $this->get_document_title_template(),
  4896 			'documentTitleTmpl'      => $this->get_document_title_template(),
  4809 			'previewableDevices'     => $this->get_previewable_devices(),
  4897 			'previewableDevices'     => $this->get_previewable_devices(),
  4810 			'l10n'                   => array(
  4898 			'l10n'                   => array(
  4811 				'confirmDeleteTheme'   => __( 'Are you sure you want to delete this theme?' ),
  4899 				'confirmDeleteTheme'   => __( 'Are you sure you want to delete this theme?' ),
  4812 				/* translators: %d: number of theme search results, which cannot currently consider singular vs. plural forms */
  4900 				/* translators: %d: Number of theme search results, which cannot currently consider singular vs. plural forms. */
  4813 				'themeSearchResults'   => __( '%d themes found' ),
  4901 				'themeSearchResults'   => __( '%d themes found' ),
  4814 				/* translators: %d: number of themes being displayed, which cannot currently consider singular vs. plural forms */
  4902 				/* translators: %d: Number of themes being displayed, which cannot currently consider singular vs. plural forms. */
  4815 				'announceThemeCount'   => __( 'Displaying %d themes' ),
  4903 				'announceThemeCount'   => __( 'Displaying %d themes' ),
  4816 				/* translators: %s: theme name */
  4904 				/* translators: %s: Theme name. */
  4817 				'announceThemeDetails' => __( 'Showing details for theme: %s' ),
  4905 				'announceThemeDetails' => __( 'Showing details for theme: %s' ),
  4818 			),
  4906 			),
  4819 		);
  4907 		);
  4820 
  4908 
  4821 		// Temporarily disable installation in Customizer. See #42184.
  4909 		// Temporarily disable installation in Customizer. See #42184.
  5067 				'site_icon',
  5155 				'site_icon',
  5068 				array(
  5156 				array(
  5069 					'label'       => __( 'Site Icon' ),
  5157 					'label'       => __( 'Site Icon' ),
  5070 					'description' => sprintf(
  5158 					'description' => sprintf(
  5071 						'<p>' . __( 'Site Icons are what you see in browser tabs, bookmark bars, and within the WordPress mobile apps. Upload one here!' ) . '</p>' .
  5159 						'<p>' . __( 'Site Icons are what you see in browser tabs, bookmark bars, and within the WordPress mobile apps. Upload one here!' ) . '</p>' .
  5072 						/* translators: %s: site icon size in pixels */
  5160 						/* translators: %s: Site icon size in pixels. */
  5073 						'<p>' . __( 'Site Icons should be square and at least %s pixels.' ) . '</p>',
  5161 						'<p>' . __( 'Site Icons should be square and at least %s pixels.' ) . '</p>',
  5074 						'<strong>512 &times; 512</strong>'
  5162 						'<strong>512 &times; 512</strong>'
  5075 					),
  5163 					),
  5076 					'section'     => 'title_tagline',
  5164 					'section'     => 'title_tagline',
  5077 					'priority'    => 60,
  5165 					'priority'    => 60,
  5096 				'custom_logo',
  5184 				'custom_logo',
  5097 				array(
  5185 				array(
  5098 					'label'         => __( 'Logo' ),
  5186 					'label'         => __( 'Logo' ),
  5099 					'section'       => 'title_tagline',
  5187 					'section'       => 'title_tagline',
  5100 					'priority'      => 8,
  5188 					'priority'      => 8,
  5101 					'height'        => $custom_logo_args[0]['height'],
  5189 					'height'        => isset( $custom_logo_args[0]['height'] ) ? $custom_logo_args[0]['height'] : null,
  5102 					'width'         => $custom_logo_args[0]['width'],
  5190 					'width'         => isset( $custom_logo_args[0]['width'] ) ? $custom_logo_args[0]['width'] : null,
  5103 					'flex_height'   => $custom_logo_args[0]['flex-height'],
  5191 					'flex_height'   => isset( $custom_logo_args[0]['flex-height'] ) ? $custom_logo_args[0]['flex-height'] : null,
  5104 					'flex_width'    => $custom_logo_args[0]['flex-width'],
  5192 					'flex_width'    => isset( $custom_logo_args[0]['flex-width'] ) ? $custom_logo_args[0]['flex-width'] : null,
  5105 					'button_labels' => array(
  5193 					'button_labels' => array(
  5106 						'select'       => __( 'Select logo' ),
  5194 						'select'       => __( 'Select logo' ),
  5107 						'change'       => __( 'Change logo' ),
  5195 						'change'       => __( 'Change logo' ),
  5108 						'remove'       => __( 'Remove' ),
  5196 						'remove'       => __( 'Remove' ),
  5109 						'default'      => __( 'Default' ),
  5197 						'default'      => __( 'Default' ),
  5144 				'sanitize_callback'    => array( $this, '_sanitize_header_textcolor' ),
  5232 				'sanitize_callback'    => array( $this, '_sanitize_header_textcolor' ),
  5145 				'sanitize_js_callback' => 'maybe_hash_hex_color',
  5233 				'sanitize_js_callback' => 'maybe_hash_hex_color',
  5146 			)
  5234 			)
  5147 		);
  5235 		);
  5148 
  5236 
  5149 		// Input type: checkbox
  5237 		// Input type: checkbox.
  5150 		// With custom value
  5238 		// With custom value.
  5151 		$this->add_control(
  5239 		$this->add_control(
  5152 			'display_header_text',
  5240 			'display_header_text',
  5153 			array(
  5241 			array(
  5154 				'settings' => 'header_textcolor',
  5242 				'settings' => 'header_textcolor',
  5155 				'label'    => __( 'Display Site Title and Tagline' ),
  5243 				'label'    => __( 'Display Site Title and Tagline' ),
  5168 					'section' => 'colors',
  5256 					'section' => 'colors',
  5169 				)
  5257 				)
  5170 			)
  5258 			)
  5171 		);
  5259 		);
  5172 
  5260 
  5173 		// Input type: Color
  5261 		// Input type: color.
  5174 		// With sanitize_callback
  5262 		// With sanitize_callback.
  5175 		$this->add_setting(
  5263 		$this->add_setting(
  5176 			'background_color',
  5264 			'background_color',
  5177 			array(
  5265 			array(
  5178 				'default'              => get_theme_support( 'custom-background', 'default-color' ),
  5266 				'default'              => get_theme_support( 'custom-background', 'default-color' ),
  5179 				'theme_supports'       => 'custom-background',
  5267 				'theme_supports'       => 'custom-background',
  5202 
  5290 
  5203 			$width  = absint( get_theme_support( 'custom-header', 'width' ) );
  5291 			$width  = absint( get_theme_support( 'custom-header', 'width' ) );
  5204 			$height = absint( get_theme_support( 'custom-header', 'height' ) );
  5292 			$height = absint( get_theme_support( 'custom-header', 'height' ) );
  5205 			if ( $width && $height ) {
  5293 			if ( $width && $height ) {
  5206 				$control_description = sprintf(
  5294 				$control_description = sprintf(
  5207 					/* translators: 1: .mp4, 2: header size in pixels */
  5295 					/* translators: 1: .mp4, 2: Header size in pixels. */
  5208 					__( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends dimensions of %2$s pixels.' ),
  5296 					__( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends dimensions of %2$s pixels.' ),
  5209 					'<code>.mp4</code>',
  5297 					'<code>.mp4</code>',
  5210 					sprintf( '<strong>%s &times; %s</strong>', $width, $height )
  5298 					sprintf( '<strong>%s &times; %s</strong>', $width, $height )
  5211 				);
  5299 				);
  5212 			} elseif ( $width ) {
  5300 			} elseif ( $width ) {
  5213 				$control_description = sprintf(
  5301 				$control_description = sprintf(
  5214 					/* translators: 1: .mp4, 2: header width in pixels */
  5302 					/* translators: 1: .mp4, 2: Header width in pixels. */
  5215 					__( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends a width of %2$s pixels.' ),
  5303 					__( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends a width of %2$s pixels.' ),
  5216 					'<code>.mp4</code>',
  5304 					'<code>.mp4</code>',
  5217 					sprintf( '<strong>%s</strong>', $width )
  5305 					sprintf( '<strong>%s</strong>', $width )
  5218 				);
  5306 				);
  5219 			} else {
  5307 			} else {
  5220 				$control_description = sprintf(
  5308 				$control_description = sprintf(
  5221 					/* translators: 1: .mp4, 2: header height in pixels */
  5309 					/* translators: 1: .mp4, 2: Header height in pixels. */
  5222 					__( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends a height of %2$s pixels.' ),
  5310 					__( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends a height of %2$s pixels.' ),
  5223 					'<code>.mp4</code>',
  5311 					'<code>.mp4</code>',
  5224 					sprintf( '<strong>%s</strong>', $height )
  5312 					sprintf( '<strong>%s</strong>', $height )
  5225 				);
  5313 				);
  5226 			}
  5314 			}
  5434 			array(
  5522 			array(
  5435 				'label'   => __( 'Image Size' ),
  5523 				'label'   => __( 'Image Size' ),
  5436 				'section' => 'background_image',
  5524 				'section' => 'background_image',
  5437 				'type'    => 'select',
  5525 				'type'    => 'select',
  5438 				'choices' => array(
  5526 				'choices' => array(
  5439 					'auto'    => __( 'Original' ),
  5527 					'auto'    => _x( 'Original', 'Original Size' ),
  5440 					'contain' => __( 'Fit to Screen' ),
  5528 					'contain' => __( 'Fit to Screen' ),
  5441 					'cover'   => __( 'Fill Screen' ),
  5529 					'cover'   => __( 'Fill Screen' ),
  5442 				),
  5530 				),
  5443 			)
  5531 			)
  5444 		);
  5532 		);
  5566 		$section_description .= __( 'Add your own CSS code here to customize the appearance and layout of your site.' );
  5654 		$section_description .= __( 'Add your own CSS code here to customize the appearance and layout of your site.' );
  5567 		$section_description .= sprintf(
  5655 		$section_description .= sprintf(
  5568 			' <a href="%1$s" class="external-link" target="_blank">%2$s<span class="screen-reader-text"> %3$s</span></a>',
  5656 			' <a href="%1$s" class="external-link" target="_blank">%2$s<span class="screen-reader-text"> %3$s</span></a>',
  5569 			esc_url( __( 'https://codex.wordpress.org/CSS' ) ),
  5657 			esc_url( __( 'https://codex.wordpress.org/CSS' ) ),
  5570 			__( 'Learn more about CSS' ),
  5658 			__( 'Learn more about CSS' ),
  5571 			/* translators: accessibility text */
  5659 			/* translators: Accessibility text. */
  5572 			__( '(opens in a new tab)' )
  5660 			__( '(opens in a new tab)' )
  5573 		);
  5661 		);
  5574 		$section_description .= '</p>';
  5662 		$section_description .= '</p>';
  5575 
  5663 
  5576 		$section_description .= '<p id="editor-keyboard-trap-help-1">' . __( 'When using a keyboard to navigate:' ) . '</p>';
  5664 		$section_description .= '<p id="editor-keyboard-trap-help-1">' . __( 'When using a keyboard to navigate:' ) . '</p>';
  5577 		$section_description .= '<ul>';
  5665 		$section_description .= '<ul>';
  5578 		$section_description .= '<li id="editor-keyboard-trap-help-2">' . __( 'In the editing area, the Tab key enters a tab character.' ) . '</li>';
  5666 		$section_description .= '<li id="editor-keyboard-trap-help-2">' . __( 'In the editing area, the Tab key enters a tab character.' ) . '</li>';
  5579 		$section_description .= '<li id="editor-keyboard-trap-help-3">' . __( 'To move away from this area, press the Esc key followed by the Tab key.' ) . '</li>';
  5667 		$section_description .= '<li id="editor-keyboard-trap-help-3">' . __( 'To move away from this area, press the Esc key followed by the Tab key.' ) . '</li>';
  5580 		$section_description .= '<li id="editor-keyboard-trap-help-4">' . __( 'Screen reader users: when in forms mode, you may need to press the escape key twice.' ) . '</li>';
  5668 		$section_description .= '<li id="editor-keyboard-trap-help-4">' . __( 'Screen reader users: when in forms mode, you may need to press the Esc key twice.' ) . '</li>';
  5581 		$section_description .= '</ul>';
  5669 		$section_description .= '</ul>';
  5582 
  5670 
  5583 		if ( 'false' !== wp_get_current_user()->syntax_highlighting ) {
  5671 		if ( 'false' !== wp_get_current_user()->syntax_highlighting ) {
  5584 			$section_description .= '<p>';
  5672 			$section_description .= '<p>';
  5585 			$section_description .= sprintf(
  5673 			$section_description .= sprintf(
  5586 				/* translators: 1: link to user profile, 2: additional link attributes, 3: accessibility text */
  5674 				/* translators: 1: Link to user profile, 2: Additional link attributes, 3: Accessibility text. */
  5587 				__( 'The edit field automatically highlights code syntax. You can disable this in your <a href="%1$s" %2$s>user profile%3$s</a> to work in plain text mode.' ),
  5675 				__( 'The edit field automatically highlights code syntax. You can disable this in your <a href="%1$s" %2$s>user profile%3$s</a> to work in plain text mode.' ),
  5588 				esc_url( get_edit_profile_url() ),
  5676 				esc_url( get_edit_profile_url() ),
  5589 				'class="external-link" target="_blank"',
  5677 				'class="external-link" target="_blank"',
  5590 				sprintf(
  5678 				sprintf(
  5591 					'<span class="screen-reader-text"> %s</span>',
  5679 					'<span class="screen-reader-text"> %s</span>',
  5592 					/* translators: accessibility text */
  5680 					/* translators: Accessibility text. */
  5593 					__( '(opens in a new tab)' )
  5681 					__( '(opens in a new tab)' )
  5594 				)
  5682 				)
  5595 			);
  5683 			);
  5596 			$section_description .= '</p>';
  5684 			$section_description .= '</p>';
  5597 		}
  5685 		}
  5642 	 *
  5730 	 *
  5643 	 * Used as active callback for static front page section and controls.
  5731 	 * Used as active callback for static front page section and controls.
  5644 	 *
  5732 	 *
  5645 	 * @since 4.7.0
  5733 	 * @since 4.7.0
  5646 	 *
  5734 	 *
  5647 	 * @returns bool Whether there are published (or to be published) pages.
  5735 	 * @return bool Whether there are published (or to be published) pages.
  5648 	 */
  5736 	 */
  5649 	public function has_published_pages() {
  5737 	public function has_published_pages() {
  5650 
  5738 
  5651 		$setting = $this->get_setting( 'nav_menus_created_posts' );
  5739 		$setting = $this->get_setting( 'nav_menus_created_posts' );
  5652 		if ( $setting ) {
  5740 		if ( $setting ) {
  5797 
  5885 
  5798 				// Set active based on customized theme.
  5886 				// Set active based on customized theme.
  5799 				$theme->active = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme->slug );
  5887 				$theme->active = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme->slug );
  5800 
  5888 
  5801 				// Map available theme properties to installed theme properties.
  5889 				// Map available theme properties to installed theme properties.
  5802 				$theme->id           = $theme->slug;
  5890 				$theme->id            = $theme->slug;
  5803 				$theme->screenshot   = array( $theme->screenshot_url );
  5891 				$theme->screenshot    = array( $theme->screenshot_url );
  5804 				$theme->authorAndUri = wp_kses( $theme->author['display_name'], $themes_allowedtags );
  5892 				$theme->authorAndUri  = wp_kses( $theme->author['display_name'], $themes_allowedtags );
       
  5893 				$theme->compatibleWP  = is_wp_version_compatible( $theme->requires ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
       
  5894 				$theme->compatiblePHP = is_php_version_compatible( $theme->requires_php ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
  5805 
  5895 
  5806 				if ( isset( $theme->parent ) ) {
  5896 				if ( isset( $theme->parent ) ) {
  5807 					$theme->parent = $theme->parent['slug'];
  5897 					$theme->parent = $theme->parent['slug'];
  5808 				} else {
  5898 				} else {
  5809 					$theme->parent = false;
  5899 					$theme->parent = false;
  5864 	/**
  5954 	/**
  5865 	 * Callback for validating a background setting value.
  5955 	 * Callback for validating a background setting value.
  5866 	 *
  5956 	 *
  5867 	 * @since 4.7.0
  5957 	 * @since 4.7.0
  5868 	 *
  5958 	 *
  5869 	 * @param string $value Repeat value.
  5959 	 * @param string               $value   Repeat value.
  5870 	 * @param WP_Customize_Setting $setting Setting.
  5960 	 * @param WP_Customize_Setting $setting Setting.
  5871 	 * @return string|WP_Error Background value or validation error.
  5961 	 * @return string|WP_Error Background value or validation error.
  5872 	 */
  5962 	 */
  5873 	public function _sanitize_background_setting( $value, $setting ) {
  5963 	public function _sanitize_background_setting( $value, $setting ) {
  5874 		if ( 'background_repeat' === $setting->id ) {
  5964 		if ( 'background_repeat' === $setting->id ) {
  5875 			if ( ! in_array( $value, array( 'repeat-x', 'repeat-y', 'repeat', 'no-repeat' ) ) ) {
  5965 			if ( ! in_array( $value, array( 'repeat-x', 'repeat-y', 'repeat', 'no-repeat' ), true ) ) {
  5876 				return new WP_Error( 'invalid_value', __( 'Invalid value for background repeat.' ) );
  5966 				return new WP_Error( 'invalid_value', __( 'Invalid value for background repeat.' ) );
  5877 			}
  5967 			}
  5878 		} elseif ( 'background_attachment' === $setting->id ) {
  5968 		} elseif ( 'background_attachment' === $setting->id ) {
  5879 			if ( ! in_array( $value, array( 'fixed', 'scroll' ) ) ) {
  5969 			if ( ! in_array( $value, array( 'fixed', 'scroll' ), true ) ) {
  5880 				return new WP_Error( 'invalid_value', __( 'Invalid value for background attachment.' ) );
  5970 				return new WP_Error( 'invalid_value', __( 'Invalid value for background attachment.' ) );
  5881 			}
  5971 			}
  5882 		} elseif ( 'background_position_x' === $setting->id ) {
  5972 		} elseif ( 'background_position_x' === $setting->id ) {
  5883 			if ( ! in_array( $value, array( 'left', 'center', 'right' ), true ) ) {
  5973 			if ( ! in_array( $value, array( 'left', 'center', 'right' ), true ) ) {
  5884 				return new WP_Error( 'invalid_value', __( 'Invalid value for background position X.' ) );
  5974 				return new WP_Error( 'invalid_value', __( 'Invalid value for background position X.' ) );
  5906 	/**
  5996 	/**
  5907 	 * Export header video settings to facilitate selective refresh.
  5997 	 * Export header video settings to facilitate selective refresh.
  5908 	 *
  5998 	 *
  5909 	 * @since 4.7.0
  5999 	 * @since 4.7.0
  5910 	 *
  6000 	 *
  5911 	 * @param array $response Response.
  6001 	 * @param array                          $response          Response.
  5912 	 * @param WP_Customize_Selective_Refresh $selective_refresh Selective refresh component.
  6002 	 * @param WP_Customize_Selective_Refresh $selective_refresh Selective refresh component.
  5913 	 * @param array $partials Array of partials.
  6003 	 * @param array                          $partials          Array of partials.
  5914 	 * @return array
  6004 	 * @return array
  5915 	 */
  6005 	 */
  5916 	public function export_header_video_settings( $response, $selective_refresh, $partials ) {
  6006 	public function export_header_video_settings( $response, $selective_refresh, $partials ) {
  5917 		if ( isset( $partials['custom_header'] ) ) {
  6007 		if ( isset( $partials['custom_header'] ) ) {
  5918 			$response['custom_header_settings'] = get_header_video_settings();
  6008 			$response['custom_header_settings'] = get_header_video_settings();
  5927 	 * Ensures that the selected video is less than 8MB and provides an error message.
  6017 	 * Ensures that the selected video is less than 8MB and provides an error message.
  5928 	 *
  6018 	 *
  5929 	 * @since 4.7.0
  6019 	 * @since 4.7.0
  5930 	 *
  6020 	 *
  5931 	 * @param WP_Error $validity
  6021 	 * @param WP_Error $validity
  5932 	 * @param mixed $value
  6022 	 * @param mixed    $value
  5933 	 * @return mixed
  6023 	 * @return mixed
  5934 	 */
  6024 	 */
  5935 	public function _validate_header_video( $validity, $value ) {
  6025 	public function _validate_header_video( $validity, $value ) {
  5936 		$video = get_attached_file( absint( $value ) );
  6026 		$video = get_attached_file( absint( $value ) );
  5937 		if ( $video ) {
  6027 		if ( $video ) {
  5938 			$size = filesize( $video );
  6028 			$size = filesize( $video );
  5939 			if ( 8 < $size / pow( 1024, 2 ) ) { // Check whether the size is larger than 8MB.
  6029 			if ( $size > 8 * MB_IN_BYTES ) {
  5940 				$validity->add(
  6030 				$validity->add(
  5941 					'size_too_large',
  6031 					'size_too_large',
  5942 					__( 'This video file is too large to use as a header video. Try a shorter video or optimize the compression settings and re-upload a file that is less than 8MB. Or, upload your video to YouTube and link it with the option below.' )
  6032 					__( 'This video file is too large to use as a header video. Try a shorter video or optimize the compression settings and re-upload a file that is less than 8MB. Or, upload your video to YouTube and link it with the option below.' )
  5943 				);
  6033 				);
  5944 			}
  6034 			}
  5963 	 * Ensures that the provided URL is supported.
  6053 	 * Ensures that the provided URL is supported.
  5964 	 *
  6054 	 *
  5965 	 * @since 4.7.0
  6055 	 * @since 4.7.0
  5966 	 *
  6056 	 *
  5967 	 * @param WP_Error $validity
  6057 	 * @param WP_Error $validity
  5968 	 * @param mixed $value
  6058 	 * @param mixed    $value
  5969 	 * @return mixed
  6059 	 * @return mixed
  5970 	 */
  6060 	 */
  5971 	public function _validate_external_header_video( $validity, $value ) {
  6061 	public function _validate_external_header_video( $validity, $value ) {
  5972 		$video = esc_url_raw( $value );
  6062 		$video = esc_url_raw( $value );
  5973 		if ( $video ) {
  6063 		if ( $video ) {