wp/wp-includes/class-wp-customize-manager.php
changeset 5 5e2f62d02dcd
parent 0 d970ebf37754
child 7 cf61fcea0001
equal deleted inserted replaced
4:346c88efed21 5:5e2f62d02dcd
     1 <?php
     1 <?php
     2 /**
     2 /**
     3  * Customize Manager.
     3  * WordPress Customize Manager classes
     4  *
     4  *
     5  * @package WordPress
     5  * @package WordPress
     6  * @subpackage Customize
     6  * @subpackage Customize
     7  * @since 3.4.0
     7  * @since 3.4.0
     8  */
     8  */
       
     9 
       
    10 /**
       
    11  * Customize Manager class.
       
    12  *
       
    13  * Bootstraps the Customize experience on the server-side.
       
    14  *
       
    15  * Sets up the theme-switching process if a theme other than the active one is
       
    16  * being previewed and customized.
       
    17  *
       
    18  * Serves as a factory for Customize Controls and Settings, and
       
    19  * instantiates default Customize Controls and Settings.
       
    20  *
       
    21  * @since 3.4.0
       
    22  */
     9 final class WP_Customize_Manager {
    23 final class WP_Customize_Manager {
       
    24 	/**
       
    25 	 * An instance of the theme being previewed.
       
    26 	 *
       
    27 	 * @var WP_Theme
       
    28 	 */
    10 	protected $theme;
    29 	protected $theme;
       
    30 
       
    31 	/**
       
    32 	 * The directory name of the previously active theme (within the theme_root).
       
    33 	 *
       
    34 	 * @var string
       
    35 	 */
    11 	protected $original_stylesheet;
    36 	protected $original_stylesheet;
    12 
    37 
       
    38 	/**
       
    39 	 * Whether this is a Customizer pageload.
       
    40 	 *
       
    41 	 * @var boolean
       
    42 	 */
    13 	protected $previewing = false;
    43 	protected $previewing = false;
    14 
    44 
    15 	protected $settings = array();
    45 	/**
    16 	protected $sections = array();
    46 	 * Methods and properties deailing with managing widgets in the Customizer.
    17 	protected $controls = array();
    47 	 *
       
    48 	 * @var WP_Customize_Widgets
       
    49 	 */
       
    50 	public $widgets;
       
    51 
       
    52 	protected $settings   = array();
       
    53 	protected $containers = array();
       
    54 	protected $panels     = array();
       
    55 	protected $sections   = array();
       
    56 	protected $controls   = array();
    18 
    57 
    19 	protected $nonce_tick;
    58 	protected $nonce_tick;
    20 
    59 
    21 	protected $customized;
    60 	protected $customized;
    22 
    61 
       
    62 	/**
       
    63 	 * Controls that may be rendered from JS templates.
       
    64 	 *
       
    65 	 * @since 4.1.0
       
    66 	 * @access protected
       
    67 	 * @var array
       
    68 	 */
       
    69 	protected $registered_control_types = array();
       
    70 
       
    71 	/**
       
    72 	 * Unsanitized values for Customize Settings parsed from $_POST['customized'].
       
    73 	 *
       
    74 	 * @var array
       
    75 	 */
    23 	private $_post_values;
    76 	private $_post_values;
    24 
    77 
    25 	/**
    78 	/**
    26 	 * Constructor.
    79 	 * Constructor.
    27 	 *
    80 	 *
    28 	 * @since 3.4.0
    81 	 * @since 3.4.0
    29 	 */
    82 	 */
    30 	public function __construct() {
    83 	public function __construct() {
    31 		require( ABSPATH . WPINC . '/class-wp-customize-setting.php' );
    84 		require_once( ABSPATH . WPINC . '/class-wp-customize-setting.php' );
    32 		require( ABSPATH . WPINC . '/class-wp-customize-section.php' );
    85 		require_once( ABSPATH . WPINC . '/class-wp-customize-panel.php' );
    33 		require( ABSPATH . WPINC . '/class-wp-customize-control.php' );
    86 		require_once( ABSPATH . WPINC . '/class-wp-customize-section.php' );
       
    87 		require_once( ABSPATH . WPINC . '/class-wp-customize-control.php' );
       
    88 		require_once( ABSPATH . WPINC . '/class-wp-customize-widgets.php' );
       
    89 
       
    90 		$this->widgets = new WP_Customize_Widgets( $this );
    34 
    91 
    35 		add_filter( 'wp_die_handler', array( $this, 'wp_die_handler' ) );
    92 		add_filter( 'wp_die_handler', array( $this, 'wp_die_handler' ) );
    36 
    93 
    37 		add_action( 'setup_theme',  array( $this, 'setup_theme' ) );
    94 		add_action( 'setup_theme', array( $this, 'setup_theme' ) );
    38 		add_action( 'wp_loaded',    array( $this, 'wp_loaded' ) );
    95 		add_action( 'wp_loaded',   array( $this, 'wp_loaded' ) );
    39 
    96 
    40 		// Run wp_redirect_status late to make sure we override the status last.
    97 		// Run wp_redirect_status late to make sure we override the status last.
    41 		add_action( 'wp_redirect_status', array( $this, 'wp_redirect_status' ), 1000 );
    98 		add_action( 'wp_redirect_status', array( $this, 'wp_redirect_status' ), 1000 );
    42 
    99 
    43 		// Do not spawn cron (especially the alternate cron) while running the customizer.
   100 		// Do not spawn cron (especially the alternate cron) while running the Customizer.
    44 		remove_action( 'init', 'wp_cron' );
   101 		remove_action( 'init', 'wp_cron' );
    45 
   102 
    46 		// Do not run update checks when rendering the controls.
   103 		// Do not run update checks when rendering the controls.
    47 		remove_action( 'admin_init', '_maybe_update_core' );
   104 		remove_action( 'admin_init', '_maybe_update_core' );
    48 		remove_action( 'admin_init', '_maybe_update_plugins' );
   105 		remove_action( 'admin_init', '_maybe_update_plugins' );
    49 		remove_action( 'admin_init', '_maybe_update_themes' );
   106 		remove_action( 'admin_init', '_maybe_update_themes' );
    50 
   107 
    51 		add_action( 'wp_ajax_customize_save', array( $this, 'save' ) );
   108 		add_action( 'wp_ajax_customize_save',           array( $this, 'save' ) );
       
   109 		add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) );
    52 
   110 
    53 		add_action( 'customize_register',                 array( $this, 'register_controls' ) );
   111 		add_action( 'customize_register',                 array( $this, 'register_controls' ) );
       
   112 		add_action( 'customize_register',                 array( $this, 'register_dynamic_settings' ), 11 ); // allow code to create settings first
    54 		add_action( 'customize_controls_init',            array( $this, 'prepare_controls' ) );
   113 		add_action( 'customize_controls_init',            array( $this, 'prepare_controls' ) );
    55 		add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) );
   114 		add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) );
    56 	}
   115 	}
    57 
   116 
    58 	/**
   117 	/**
    59 	 * Return true if it's an AJAX request.
   118 	 * Return true if it's an AJAX request.
    60 	 *
   119 	 *
    61 	 * @since 3.4.0
   120 	 * @since 3.4.0
    62 	 *
   121 	 * @since 4.2.0 Added `$action` param.
    63 	 * @return bool
   122 	 * @access public
    64 	 */
   123 	 *
    65 	public function doing_ajax() {
   124 	 * @param string|null $action Whether the supplied AJAX action is being run.
    66 		return isset( $_POST['customized'] ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX );
   125 	 * @return bool True if it's an AJAX request, false otherwise.
       
   126 	 */
       
   127 	public function doing_ajax( $action = null ) {
       
   128 		$doing_ajax = ( defined( 'DOING_AJAX' ) && DOING_AJAX );
       
   129 		if ( ! $doing_ajax ) {
       
   130 			return false;
       
   131 		}
       
   132 
       
   133 		if ( ! $action ) {
       
   134 			return true;
       
   135 		} else {
       
   136 			/*
       
   137 			 * Note: we can't just use doing_action( "wp_ajax_{$action}" ) because we need
       
   138 			 * to check before admin-ajax.php gets to that point.
       
   139 			 */
       
   140 			return isset( $_REQUEST['action'] ) && wp_unslash( $_REQUEST['action'] ) === $action;
       
   141 		}
    67 	}
   142 	}
    68 
   143 
    69 	/**
   144 	/**
    70 	 * Custom wp_die wrapper. Returns either the standard message for UI
   145 	 * Custom wp_die wrapper. Returns either the standard message for UI
    71 	 * or the AJAX message.
   146 	 * or the AJAX message.
    74 	 *
   149 	 *
    75 	 * @param mixed $ajax_message AJAX return
   150 	 * @param mixed $ajax_message AJAX return
    76 	 * @param mixed $message UI message
   151 	 * @param mixed $message UI message
    77 	 */
   152 	 */
    78 	protected function wp_die( $ajax_message, $message = null ) {
   153 	protected function wp_die( $ajax_message, $message = null ) {
    79 		if ( $this->doing_ajax() )
   154 		if ( $this->doing_ajax() || isset( $_POST['customized'] ) ) {
    80 			wp_die( $ajax_message );
   155 			wp_die( $ajax_message );
    81 
   156 		}
    82 		if ( ! $message )
   157 
       
   158 		if ( ! $message ) {
    83 			$message = __( 'Cheatin&#8217; uh?' );
   159 			$message = __( 'Cheatin&#8217; uh?' );
       
   160 		}
    84 
   161 
    85 		wp_die( $message );
   162 		wp_die( $message );
    86 	}
   163 	}
    87 
   164 
    88 	/**
   165 	/**
    91 	 * @since 3.4.0
   168 	 * @since 3.4.0
    92 	 *
   169 	 *
    93 	 * @return string
   170 	 * @return string
    94 	 */
   171 	 */
    95 	public function wp_die_handler() {
   172 	public function wp_die_handler() {
    96 		if ( $this->doing_ajax() )
   173 		if ( $this->doing_ajax() || isset( $_POST['customized'] ) ) {
    97 			return '_ajax_wp_die_handler';
   174 			return '_ajax_wp_die_handler';
       
   175 		}
    98 
   176 
    99 		return '_default_wp_die_handler';
   177 		return '_default_wp_die_handler';
   100 	}
   178 	}
   101 
   179 
   102 	/**
   180 	/**
   107 	 * @since 3.4.0
   185 	 * @since 3.4.0
   108 	 */
   186 	 */
   109 	public function setup_theme() {
   187 	public function setup_theme() {
   110 		send_origin_headers();
   188 		send_origin_headers();
   111 
   189 
   112 		if ( is_admin() && ! $this->doing_ajax() )
   190 		$doing_ajax_or_is_customized = ( $this->doing_ajax() || isset( $_POST['customized'] ) );
   113 		    auth_redirect();
   191 		if ( is_admin() && ! $doing_ajax_or_is_customized ) {
   114 		elseif ( $this->doing_ajax() && ! is_user_logged_in() )
   192 			auth_redirect();
   115 		    $this->wp_die( 0 );
   193 		} elseif ( $doing_ajax_or_is_customized && ! is_user_logged_in() ) {
       
   194 			$this->wp_die( 0 );
       
   195 		}
   116 
   196 
   117 		show_admin_bar( false );
   197 		show_admin_bar( false );
   118 
   198 
   119 		if ( ! current_user_can( 'edit_theme_options' ) )
   199 		if ( ! current_user_can( 'customize' ) ) {
   120 			$this->wp_die( -1 );
   200 			$this->wp_die( -1 );
       
   201 		}
   121 
   202 
   122 		$this->original_stylesheet = get_stylesheet();
   203 		$this->original_stylesheet = get_stylesheet();
   123 
   204 
   124 		$this->theme = wp_get_theme( isset( $_REQUEST['theme'] ) ? $_REQUEST['theme'] : null );
   205 		$this->theme = wp_get_theme( isset( $_REQUEST['theme'] ) ? $_REQUEST['theme'] : null );
   125 
   206 
   126 		if ( $this->is_theme_active() ) {
   207 		if ( $this->is_theme_active() ) {
   127 			// Once the theme is loaded, we'll validate it.
   208 			// Once the theme is loaded, we'll validate it.
   128 			add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) );
   209 			add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) );
   129 		} else {
   210 		} else {
   130 			if ( ! current_user_can( 'switch_themes' ) )
   211 			// If the requested theme is not the active theme and the user doesn't have the
       
   212 			// switch_themes cap, bail.
       
   213 			if ( ! current_user_can( 'switch_themes' ) ) {
   131 				$this->wp_die( -1 );
   214 				$this->wp_die( -1 );
   132 
   215 			}
   133 			// If the theme isn't active, you can't preview it if it is not allowed or has errors.
   216 
   134 			if ( $this->theme()->errors() )
   217 			// If the theme has errors while loading, bail.
       
   218 			if ( $this->theme()->errors() ) {
   135 				$this->wp_die( -1 );
   219 				$this->wp_die( -1 );
   136 
   220 			}
   137 			if ( ! $this->theme()->is_allowed() )
   221 
       
   222 			// If the theme isn't allowed per multisite settings, bail.
       
   223 			if ( ! $this->theme()->is_allowed() ) {
   138 				$this->wp_die( -1 );
   224 				$this->wp_die( -1 );
       
   225 			}
   139 		}
   226 		}
   140 
   227 
   141 		$this->start_previewing_theme();
   228 		$this->start_previewing_theme();
   142 	}
   229 	}
   143 
   230 
   144 	/**
   231 	/**
   145 	 * Callback to validate a theme once it is loaded
   232 	 * Callback to validate a theme once it is loaded
   146 	 *
   233 	 *
   147 	 * @since 3.4.0
   234 	 * @since 3.4.0
   148 	 */
   235 	 */
   149 	function after_setup_theme() {
   236 	public function after_setup_theme() {
   150 		if ( ! $this->doing_ajax() && ! validate_current_theme() ) {
   237 		$doing_ajax_or_is_customized = ( $this->doing_ajax() || isset( $_SERVER['customized'] ) );
       
   238 		if ( ! $doing_ajax_or_is_customized && ! validate_current_theme() ) {
   151 			wp_redirect( 'themes.php?broken=true' );
   239 			wp_redirect( 'themes.php?broken=true' );
   152 			exit;
   240 			exit;
   153 		}
   241 		}
   154 	}
   242 	}
   155 
   243 
   156 	/**
   244 	/**
   157 	 * Start previewing the selected theme.
   245 	 * If the theme to be previewed isn't the active theme, add filter callbacks
   158 	 *
   246 	 * to swap it out at runtime.
   159 	 * Adds filters to change the current theme.
       
   160 	 *
   247 	 *
   161 	 * @since 3.4.0
   248 	 * @since 3.4.0
   162 	 */
   249 	 */
   163 	public function start_previewing_theme() {
   250 	public function start_previewing_theme() {
   164 		// Bail if we're already previewing.
   251 		// Bail if we're already previewing.
   165 		if ( $this->is_preview() )
   252 		if ( $this->is_preview() ) {
   166 			return;
   253 			return;
       
   254 		}
   167 
   255 
   168 		$this->previewing = true;
   256 		$this->previewing = true;
   169 
   257 
   170 		if ( ! $this->is_theme_active() ) {
   258 		if ( ! $this->is_theme_active() ) {
   171 			add_filter( 'template', array( $this, 'get_template' ) );
   259 			add_filter( 'template', array( $this, 'get_template' ) );
   172 			add_filter( 'stylesheet', array( $this, 'get_stylesheet' ) );
   260 			add_filter( 'stylesheet', array( $this, 'get_stylesheet' ) );
   173 			add_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) );
   261 			add_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) );
   174 
   262 
   175 			// @link: http://core.trac.wordpress.org/ticket/20027
   263 			// @link: https://core.trac.wordpress.org/ticket/20027
   176 			add_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) );
   264 			add_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) );
   177 			add_filter( 'pre_option_template', array( $this, 'get_template' ) );
   265 			add_filter( 'pre_option_template', array( $this, 'get_template' ) );
   178 
   266 
   179 			// Handle custom theme roots.
   267 			// Handle custom theme roots.
   180 			add_filter( 'pre_option_stylesheet_root', array( $this, 'get_stylesheet_root' ) );
   268 			add_filter( 'pre_option_stylesheet_root', array( $this, 'get_stylesheet_root' ) );
   181 			add_filter( 'pre_option_template_root', array( $this, 'get_template_root' ) );
   269 			add_filter( 'pre_option_template_root', array( $this, 'get_template_root' ) );
   182 		}
   270 		}
   183 
   271 
       
   272 		/**
       
   273 		 * Fires once the Customizer theme preview has started.
       
   274 		 *
       
   275 		 * @since 3.4.0
       
   276 		 *
       
   277 		 * @param WP_Customize_Manager $this WP_Customize_Manager instance.
       
   278 		 */
   184 		do_action( 'start_previewing_theme', $this );
   279 		do_action( 'start_previewing_theme', $this );
   185 	}
   280 	}
   186 
   281 
   187 	/**
   282 	/**
   188 	 * Stop previewing the selected theme.
   283 	 * Stop previewing the selected theme.
   190 	 * Removes filters to change the current theme.
   285 	 * Removes filters to change the current theme.
   191 	 *
   286 	 *
   192 	 * @since 3.4.0
   287 	 * @since 3.4.0
   193 	 */
   288 	 */
   194 	public function stop_previewing_theme() {
   289 	public function stop_previewing_theme() {
   195 		if ( ! $this->is_preview() )
   290 		if ( ! $this->is_preview() ) {
   196 			return;
   291 			return;
       
   292 		}
   197 
   293 
   198 		$this->previewing = false;
   294 		$this->previewing = false;
   199 
   295 
   200 		if ( ! $this->is_theme_active() ) {
   296 		if ( ! $this->is_theme_active() ) {
   201 			remove_filter( 'template', array( $this, 'get_template' ) );
   297 			remove_filter( 'template', array( $this, 'get_template' ) );
   202 			remove_filter( 'stylesheet', array( $this, 'get_stylesheet' ) );
   298 			remove_filter( 'stylesheet', array( $this, 'get_stylesheet' ) );
   203 			remove_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) );
   299 			remove_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) );
   204 
   300 
   205 			// @link: http://core.trac.wordpress.org/ticket/20027
   301 			// @link: https://core.trac.wordpress.org/ticket/20027
   206 			remove_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) );
   302 			remove_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) );
   207 			remove_filter( 'pre_option_template', array( $this, 'get_template' ) );
   303 			remove_filter( 'pre_option_template', array( $this, 'get_template' ) );
   208 
   304 
   209 			// Handle custom theme roots.
   305 			// Handle custom theme roots.
   210 			remove_filter( 'pre_option_stylesheet_root', array( $this, 'get_stylesheet_root' ) );
   306 			remove_filter( 'pre_option_stylesheet_root', array( $this, 'get_stylesheet_root' ) );
   211 			remove_filter( 'pre_option_template_root', array( $this, 'get_template_root' ) );
   307 			remove_filter( 'pre_option_template_root', array( $this, 'get_template_root' ) );
   212 		}
   308 		}
   213 
   309 
       
   310 		/**
       
   311 		 * Fires once the Customizer theme preview has stopped.
       
   312 		 *
       
   313 		 * @since 3.4.0
       
   314 		 *
       
   315 		 * @param WP_Customize_Manager $this WP_Customize_Manager instance.
       
   316 		 */
   214 		do_action( 'stop_previewing_theme', $this );
   317 		do_action( 'stop_previewing_theme', $this );
   215 	}
   318 	}
   216 
   319 
   217 	/**
   320 	/**
   218 	 * Get the theme being customized.
   321 	 * Get the theme being customized.
   220 	 * @since 3.4.0
   323 	 * @since 3.4.0
   221 	 *
   324 	 *
   222 	 * @return WP_Theme
   325 	 * @return WP_Theme
   223 	 */
   326 	 */
   224 	public function theme() {
   327 	public function theme() {
       
   328 		if ( ! $this->theme ) {
       
   329 			$this->theme = wp_get_theme();
       
   330 		}
   225 		return $this->theme;
   331 		return $this->theme;
   226 	}
   332 	}
   227 
   333 
   228 	/**
   334 	/**
   229 	 * Get the registered settings.
   335 	 * Get the registered settings.
   246 	public function controls() {
   352 	public function controls() {
   247 		return $this->controls;
   353 		return $this->controls;
   248 	}
   354 	}
   249 
   355 
   250 	/**
   356 	/**
       
   357 	 * Get the registered containers.
       
   358 	 *
       
   359 	 * @since 4.0.0
       
   360 	 *
       
   361 	 * @return array
       
   362 	 */
       
   363 	public function containers() {
       
   364 		return $this->containers;
       
   365 	}
       
   366 
       
   367 	/**
   251 	 * Get the registered sections.
   368 	 * Get the registered sections.
   252 	 *
   369 	 *
   253 	 * @since 3.4.0
   370 	 * @since 3.4.0
   254 	 *
   371 	 *
   255 	 * @return array
   372 	 * @return array
   257 	public function sections() {
   374 	public function sections() {
   258 		return $this->sections;
   375 		return $this->sections;
   259 	}
   376 	}
   260 
   377 
   261 	/**
   378 	/**
       
   379 	 * Get the registered panels.
       
   380 	 *
       
   381 	 * @since 4.0.0
       
   382 	 * @access public
       
   383 	 *
       
   384 	 * @return array Panels.
       
   385 	 */
       
   386 	public function panels() {
       
   387 		return $this->panels;
       
   388 	}
       
   389 
       
   390 	/**
   262 	 * Checks if the current theme is active.
   391 	 * Checks if the current theme is active.
   263 	 *
   392 	 *
   264 	 * @since 3.4.0
   393 	 * @since 3.4.0
   265 	 *
   394 	 *
   266 	 * @return bool
   395 	 * @return bool
   273 	 * Register styles/scripts and initialize the preview of each setting
   402 	 * Register styles/scripts and initialize the preview of each setting
   274 	 *
   403 	 *
   275 	 * @since 3.4.0
   404 	 * @since 3.4.0
   276 	 */
   405 	 */
   277 	public function wp_loaded() {
   406 	public function wp_loaded() {
       
   407 
       
   408 		/**
       
   409 		 * Fires once WordPress has loaded, allowing scripts and styles to be initialized.
       
   410 		 *
       
   411 		 * @since 3.4.0
       
   412 		 *
       
   413 		 * @param WP_Customize_Manager $this WP_Customize_Manager instance.
       
   414 		 */
   278 		do_action( 'customize_register', $this );
   415 		do_action( 'customize_register', $this );
   279 
   416 
   280 		if ( $this->is_preview() && ! is_admin() )
   417 		if ( $this->is_preview() && ! is_admin() )
   281 			$this->customize_preview_init();
   418 			$this->customize_preview_init();
   282 	}
   419 	}
   298 
   435 
   299 		return $status;
   436 		return $status;
   300 	}
   437 	}
   301 
   438 
   302 	/**
   439 	/**
   303 	 * Decode the $_POST attribute used to override the WP_Customize_Setting values.
   440 	 * Parse the incoming $_POST['customized'] JSON data and store the unsanitized
   304 	 *
   441 	 * settings for subsequent post_value() lookups.
   305 	 * @since 3.4.0
   442 	 *
   306 	 *
   443 	 * @since 4.1.1
   307 	 * @param mixed $setting A WP_Customize_Setting derived object
   444 	 *
   308 	 * @return string Sanitized attribute
   445 	 * @return array
   309 	 */
   446 	 */
   310 	public function post_value( $setting ) {
   447 	public function unsanitized_post_values() {
   311 		if ( ! isset( $this->_post_values ) ) {
   448 		if ( ! isset( $this->_post_values ) ) {
   312 			if ( isset( $_POST['customized'] ) )
   449 			if ( isset( $_POST['customized'] ) ) {
   313 				$this->_post_values = json_decode( wp_unslash( $_POST['customized'] ), true );
   450 				$this->_post_values = json_decode( wp_unslash( $_POST['customized'] ), true );
   314 			else
   451 			}
   315 				$this->_post_values = false;
   452 			if ( empty( $this->_post_values ) ) { // if not isset or if JSON error
   316 		}
   453 				$this->_post_values = array();
   317 
   454 			}
   318 		if ( isset( $this->_post_values[ $setting->id ] ) )
   455 		}
   319 			return $setting->sanitize( $this->_post_values[ $setting->id ] );
   456 		if ( empty( $this->_post_values ) ) {
   320 	}
   457 			return array();
   321 
   458 		} else {
   322 	/**
   459 			return $this->_post_values;
   323 	 * Print javascript settings.
   460 		}
       
   461 	}
       
   462 
       
   463 	/**
       
   464 	 * Return the sanitized value for a given setting from the request's POST data.
       
   465 	 *
       
   466 	 * @since 3.4.0
       
   467 	 * @since 4.1.1 Introduced 'default' parameter.
       
   468 	 *
       
   469 	 * @param WP_Customize_Setting $setting A WP_Customize_Setting derived object
       
   470 	 * @param mixed $default value returned $setting has no post value (added in 4.2.0).
       
   471 	 * @return string|mixed $post_value Sanitized value or the $default provided
       
   472 	 */
       
   473 	public function post_value( $setting, $default = null ) {
       
   474 		$post_values = $this->unsanitized_post_values();
       
   475 		if ( array_key_exists( $setting->id, $post_values ) ) {
       
   476 			return $setting->sanitize( $post_values[ $setting->id ] );
       
   477 		} else {
       
   478 			return $default;
       
   479 		}
       
   480 	}
       
   481 
       
   482 	/**
       
   483 	 * Override a setting's (unsanitized) value as found in any incoming $_POST['customized'].
       
   484 	 *
       
   485 	 * @since 4.2.0
       
   486 	 * @access public
       
   487 	 *
       
   488 	 * @param string $setting_id ID for the WP_Customize_Setting instance.
       
   489 	 * @param mixed  $value      Post value.
       
   490 	 */
       
   491 	public function set_post_value( $setting_id, $value ) {
       
   492 		$this->unsanitized_post_values();
       
   493 		$this->_post_values[ $setting_id ] = $value;
       
   494 	}
       
   495 
       
   496 	/**
       
   497 	 * Print JavaScript settings.
   324 	 *
   498 	 *
   325 	 * @since 3.4.0
   499 	 * @since 3.4.0
   326 	 */
   500 	 */
   327 	public function customize_preview_init() {
   501 	public function customize_preview_init() {
   328 		$this->nonce_tick = check_ajax_referer( 'preview-customize_' . $this->get_stylesheet(), 'nonce' );
   502 		$this->nonce_tick = check_ajax_referer( 'preview-customize_' . $this->get_stylesheet(), 'nonce' );
   329 
   503 
   330 		$this->prepare_controls();
   504 		$this->prepare_controls();
   331 
   505 
   332 		wp_enqueue_script( 'customize-preview' );
   506 		wp_enqueue_script( 'customize-preview' );
       
   507 		add_action( 'wp', array( $this, 'customize_preview_override_404_status' ) );
   333 		add_action( 'wp_head', array( $this, 'customize_preview_base' ) );
   508 		add_action( 'wp_head', array( $this, 'customize_preview_base' ) );
   334 		add_action( 'wp_head', array( $this, 'customize_preview_html5' ) );
   509 		add_action( 'wp_head', array( $this, 'customize_preview_html5' ) );
       
   510 		add_action( 'wp_head', array( $this, 'customize_preview_loading_style' ) );
   335 		add_action( 'wp_footer', array( $this, 'customize_preview_settings' ), 20 );
   511 		add_action( 'wp_footer', array( $this, 'customize_preview_settings' ), 20 );
   336 		add_action( 'shutdown', array( $this, 'customize_preview_signature' ), 1000 );
   512 		add_action( 'shutdown', array( $this, 'customize_preview_signature' ), 1000 );
   337 		add_filter( 'wp_die_handler', array( $this, 'remove_preview_signature' ) );
   513 		add_filter( 'wp_die_handler', array( $this, 'remove_preview_signature' ) );
   338 
   514 
   339 		foreach ( $this->settings as $setting ) {
   515 		foreach ( $this->settings as $setting ) {
   340 			$setting->preview();
   516 			$setting->preview();
   341 		}
   517 		}
   342 
   518 
       
   519 		/**
       
   520 		 * Fires once the Customizer preview has initialized and JavaScript
       
   521 		 * settings have been printed.
       
   522 		 *
       
   523 		 * @since 3.4.0
       
   524 		 *
       
   525 		 * @param WP_Customize_Manager $this WP_Customize_Manager instance.
       
   526 		 */
   343 		do_action( 'customize_preview_init', $this );
   527 		do_action( 'customize_preview_init', $this );
       
   528 	}
       
   529 
       
   530 	/**
       
   531 	 * Prevent sending a 404 status when returning the response for the customize
       
   532 	 * preview, since it causes the jQuery AJAX to fail. Send 200 instead.
       
   533 	 *
       
   534 	 * @since 4.0.0
       
   535 	 * @access public
       
   536 	 */
       
   537 	public function customize_preview_override_404_status() {
       
   538 		if ( is_404() ) {
       
   539 			status_header( 200 );
       
   540 		}
   344 	}
   541 	}
   345 
   542 
   346 	/**
   543 	/**
   347 	 * Print base element for preview frame.
   544 	 * Print base element for preview frame.
   348 	 *
   545 	 *
   351 	public function customize_preview_base() {
   548 	public function customize_preview_base() {
   352 		?><base href="<?php echo home_url( '/' ); ?>" /><?php
   549 		?><base href="<?php echo home_url( '/' ); ?>" /><?php
   353 	}
   550 	}
   354 
   551 
   355 	/**
   552 	/**
   356 	 * Print a workaround to handle HTML5 tags in IE < 9
   553 	 * Print a workaround to handle HTML5 tags in IE < 9.
   357 	 *
   554 	 *
   358 	 * @since 3.4.0
   555 	 * @since 3.4.0
   359 	 */
   556 	 */
   360 	public function customize_preview_html5() { ?>
   557 	public function customize_preview_html5() { ?>
   361 		<!--[if lt IE 9]>
   558 		<!--[if lt IE 9]>
   369 		</script>
   566 		</script>
   370 		<![endif]--><?php
   567 		<![endif]--><?php
   371 	}
   568 	}
   372 
   569 
   373 	/**
   570 	/**
   374 	 * Print javascript settings for preview frame.
   571 	 * Print CSS for loading indicators for the Customizer preview.
       
   572 	 *
       
   573 	 * @since 4.2.0
       
   574 	 * @access public
       
   575 	 */
       
   576 	public function customize_preview_loading_style() {
       
   577 		?><style>
       
   578 			body.wp-customizer-unloading {
       
   579 				opacity: 0.25;
       
   580 				cursor: progress !important;
       
   581 				-webkit-transition: opacity 0.5s;
       
   582 				transition: opacity 0.5s;
       
   583 			}
       
   584 			body.wp-customizer-unloading * {
       
   585 				pointer-events: none !important;
       
   586 			}
       
   587 		</style><?php
       
   588 	}
       
   589 
       
   590 	/**
       
   591 	 * Print JavaScript settings for preview frame.
   375 	 *
   592 	 *
   376 	 * @since 3.4.0
   593 	 * @since 3.4.0
   377 	 */
   594 	 */
   378 	public function customize_preview_settings() {
   595 	public function customize_preview_settings() {
   379 		$settings = array(
   596 		$settings = array(
   380 			'values'  => array(),
   597 			'values'  => array(),
   381 			'channel' => esc_js( $_POST['customize_messenger_channel'] ),
   598 			'channel' => wp_unslash( $_POST['customize_messenger_channel'] ),
       
   599 			'activePanels' => array(),
       
   600 			'activeSections' => array(),
       
   601 			'activeControls' => array(),
       
   602 			'l10n' => array(
       
   603 				'loading'  => __( 'Loading ...' ),
       
   604 			),
   382 		);
   605 		);
   383 
   606 
   384 		if ( 2 == $this->nonce_tick ) {
   607 		if ( 2 == $this->nonce_tick ) {
   385 			$settings['nonce'] = array(
   608 			$settings['nonce'] = array(
   386 				'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ),
   609 				'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ),
   389 		}
   612 		}
   390 
   613 
   391 		foreach ( $this->settings as $id => $setting ) {
   614 		foreach ( $this->settings as $id => $setting ) {
   392 			$settings['values'][ $id ] = $setting->js_value();
   615 			$settings['values'][ $id ] = $setting->js_value();
   393 		}
   616 		}
       
   617 		foreach ( $this->panels as $id => $panel ) {
       
   618 			$settings['activePanels'][ $id ] = $panel->active();
       
   619 			foreach ( $panel->sections as $id => $section ) {
       
   620 				$settings['activeSections'][ $id ] = $section->active();
       
   621 			}
       
   622 		}
       
   623 		foreach ( $this->sections as $id => $section ) {
       
   624 			$settings['activeSections'][ $id ] = $section->active();
       
   625 		}
       
   626 		foreach ( $this->controls as $id => $control ) {
       
   627 			$settings['activeControls'][ $id ] = $control->active();
       
   628 		}
   394 
   629 
   395 		?>
   630 		?>
   396 		<script type="text/javascript">
   631 		<script type="text/javascript">
   397 			var _wpCustomizeSettings = <?php echo json_encode( $settings ); ?>;
   632 			var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>;
   398 		</script>
   633 		</script>
   399 		<?php
   634 		<?php
   400 	}
   635 	}
   401 
   636 
   402 	/**
   637 	/**
   403 	 * Prints a signature so we can ensure the customizer was properly executed.
   638 	 * Prints a signature so we can ensure the Customizer was properly executed.
   404 	 *
   639 	 *
   405 	 * @since 3.4.0
   640 	 * @since 3.4.0
   406 	 */
   641 	 */
   407 	public function customize_preview_signature() {
   642 	public function customize_preview_signature() {
   408 		echo 'WP_CUSTOMIZER_SIGNATURE';
   643 		echo 'WP_CUSTOMIZER_SIGNATURE';
   409 	}
   644 	}
   410 
   645 
   411 	/**
   646 	/**
   412 	 * Removes the signature in case we experience a case where the customizer was not properly executed.
   647 	 * Removes the signature in case we experience a case where the Customizer was not properly executed.
   413 	 *
   648 	 *
   414 	 * @since 3.4.0
   649 	 * @since 3.4.0
   415 	 */
   650 	 */
   416 	public function remove_preview_signature( $return = null ) {
   651 	public function remove_preview_signature( $return = null ) {
   417 		remove_action( 'shutdown', array( $this, 'customize_preview_signature' ), 1000 );
   652 		remove_action( 'shutdown', array( $this, 'customize_preview_signature' ), 1000 );
   485 	public function current_theme( $current_theme ) {
   720 	public function current_theme( $current_theme ) {
   486 		return $this->theme()->display('Name');
   721 		return $this->theme()->display('Name');
   487 	}
   722 	}
   488 
   723 
   489 	/**
   724 	/**
   490 	 * Switch the theme and trigger the save action of each setting.
   725 	 * Switch the theme and trigger the save() method on each setting.
   491 	 *
   726 	 *
   492 	 * @since 3.4.0
   727 	 * @since 3.4.0
   493 	 */
   728 	 */
   494 	public function save() {
   729 	public function save() {
   495 		if ( ! $this->is_preview() )
   730 		if ( ! $this->is_preview() ) {
   496 			die;
   731 			wp_send_json_error( 'not_preview' );
   497 
   732 		}
   498 		check_ajax_referer( 'save-customize_' . $this->get_stylesheet(), 'nonce' );
   733 
       
   734 		$action = 'save-customize_' . $this->get_stylesheet();
       
   735 		if ( ! check_ajax_referer( $action, 'nonce', false ) ) {
       
   736 			wp_send_json_error( 'invalid_nonce' );
       
   737 		}
   499 
   738 
   500 		// Do we have to switch themes?
   739 		// Do we have to switch themes?
   501 		if ( ! $this->is_theme_active() ) {
   740 		if ( ! $this->is_theme_active() ) {
   502 			// Temporarily stop previewing the theme to allow switch_themes()
   741 			// Temporarily stop previewing the theme to allow switch_themes()
   503 			// to operate properly.
   742 			// to operate properly.
   504 			$this->stop_previewing_theme();
   743 			$this->stop_previewing_theme();
   505 			switch_theme( $this->get_stylesheet() );
   744 			switch_theme( $this->get_stylesheet() );
       
   745 			update_option( 'theme_switched_via_customizer', true );
   506 			$this->start_previewing_theme();
   746 			$this->start_previewing_theme();
   507 		}
   747 		}
   508 
   748 
       
   749 		/**
       
   750 		 * Fires once the theme has switched in the Customizer, but before settings
       
   751 		 * have been saved.
       
   752 		 *
       
   753 		 * @since 3.4.0
       
   754 		 *
       
   755 		 * @param WP_Customize_Manager $this WP_Customize_Manager instance.
       
   756 		 */
   509 		do_action( 'customize_save', $this );
   757 		do_action( 'customize_save', $this );
   510 
   758 
   511 		foreach ( $this->settings as $setting ) {
   759 		foreach ( $this->settings as $setting ) {
   512 			$setting->save();
   760 			$setting->save();
   513 		}
   761 		}
   514 
   762 
       
   763 		/**
       
   764 		 * Fires after Customize settings have been saved.
       
   765 		 *
       
   766 		 * @since 3.6.0
       
   767 		 *
       
   768 		 * @param WP_Customize_Manager $this WP_Customize_Manager instance.
       
   769 		 */
   515 		do_action( 'customize_save_after', $this );
   770 		do_action( 'customize_save_after', $this );
   516 
   771 
   517 		die;
   772 		/**
       
   773 		 * Filter response data for a successful customize_save AJAX request.
       
   774 		 *
       
   775 		 * This filter does not apply if there was a nonce or authentication failure.
       
   776 		 *
       
   777 		 * @since 4.2.0
       
   778 		 *
       
   779 		 * @param array                $data Additional information passed back to the 'saved'
       
   780 		 *                                   event on `wp.customize`.
       
   781 		 * @param WP_Customize_Manager $this WP_Customize_Manager instance.
       
   782 		 */
       
   783 		$response = apply_filters( 'customize_save_response', array(), $this );
       
   784 		wp_send_json_success( $response );
       
   785 	}
       
   786 
       
   787 	/**
       
   788 	 * Refresh nonces for the current preview.
       
   789 	 *
       
   790 	 * @since 4.2.0
       
   791 	 */
       
   792 	public function refresh_nonces() {
       
   793 		if ( ! $this->is_preview() ) {
       
   794 			wp_send_json_error( 'not_preview' );
       
   795 		}
       
   796 
       
   797 		$nonces = array(
       
   798 			'save'    => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ),
       
   799 			'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ),
       
   800 		);
       
   801 
       
   802 		/**
       
   803 		 * Filter nonces for a customize_refresh_nonces AJAX request.
       
   804 		 *
       
   805 		 * @since 4.2.0
       
   806 		 *
       
   807 		 * @param array                $nonces Array of refreshed nonces for save and
       
   808 		 *                                     preview actions.
       
   809 		 * @param WP_Customize_Manager $this   WP_Customize_Manager instance.
       
   810 		 */
       
   811 		$nonces = apply_filters( 'customize_refresh_nonces', $nonces, $this );
       
   812 		wp_send_json_success( $nonces );
   518 	}
   813 	}
   519 
   814 
   520 	/**
   815 	/**
   521 	 * Add a customize setting.
   816 	 * Add a customize setting.
   522 	 *
   817 	 *
   523 	 * @since 3.4.0
   818 	 * @since 3.4.0
   524 	 *
   819 	 *
   525 	 * @param string $id A specific ID of the setting. Can be a
   820 	 * @param WP_Customize_Setting|string $id Customize Setting object, or ID.
   526 	 *                   theme mod or option name.
   821 	 * @param array $args                     Setting arguments; passed to WP_Customize_Setting
   527 	 * @param array $args Setting arguments.
   822 	 *                                        constructor.
   528 	 */
   823 	 */
   529 	public function add_setting( $id, $args = array() ) {
   824 	public function add_setting( $id, $args = array() ) {
   530 		if ( is_a( $id, 'WP_Customize_Setting' ) )
   825 		if ( $id instanceof WP_Customize_Setting ) {
   531 			$setting = $id;
   826 			$setting = $id;
   532 		else
   827 		} else {
   533 			$setting = new WP_Customize_Setting( $this, $id, $args );
   828 			$setting = new WP_Customize_Setting( $this, $id, $args );
   534 
   829 		}
   535 		$this->settings[ $setting->id ] = $setting;
   830 		$this->settings[ $setting->id ] = $setting;
   536 	}
   831 	}
   537 
   832 
   538 	/**
   833 	/**
       
   834 	 * Register any dynamically-created settings, such as those from $_POST['customized']
       
   835 	 * that have no corresponding setting created.
       
   836 	 *
       
   837 	 * This is a mechanism to "wake up" settings that have been dynamically created
       
   838 	 * on the frontend and have been sent to WordPress in `$_POST['customized']`. When WP
       
   839 	 * loads, the dynamically-created settings then will get created and previewed
       
   840 	 * even though they are not directly created statically with code.
       
   841 	 *
       
   842 	 * @since 4.2.0
       
   843 	 *
       
   844 	 * @param string $setting_ids The setting IDs to add.
       
   845 	 * @return WP_Customize_Setting The settings added.
       
   846 	 */
       
   847 	public function add_dynamic_settings( $setting_ids ) {
       
   848 		$new_settings = array();
       
   849 		foreach ( $setting_ids as $setting_id ) {
       
   850 			// Skip settings already created
       
   851 			if ( $this->get_setting( $setting_id ) ) {
       
   852 				continue;
       
   853 			}
       
   854 
       
   855 			$setting_args = false;
       
   856 			$setting_class = 'WP_Customize_Setting';
       
   857 
       
   858 			/**
       
   859 			 * Filter a dynamic setting's constructor args.
       
   860 			 *
       
   861 			 * For a dynamic setting to be registered, this filter must be employed
       
   862 			 * to override the default false value with an array of args to pass to
       
   863 			 * the WP_Customize_Setting constructor.
       
   864 			 *
       
   865 			 * @since 4.2.0
       
   866 			 *
       
   867 			 * @param false|array $setting_args The arguments to the WP_Customize_Setting constructor.
       
   868 			 * @param string      $setting_id   ID for dynamic setting, usually coming from `$_POST['customized']`.
       
   869 			 */
       
   870 			$setting_args = apply_filters( 'customize_dynamic_setting_args', $setting_args, $setting_id );
       
   871 			if ( false === $setting_args ) {
       
   872 				continue;
       
   873 			}
       
   874 
       
   875 			/**
       
   876 			 * Allow non-statically created settings to be constructed with custom WP_Customize_Setting subclass.
       
   877 			 *
       
   878 			 * @since 4.2.0
       
   879 			 *
       
   880 			 * @param string $setting_class WP_Customize_Setting or a subclass.
       
   881 			 * @param string $setting_id    ID for dynamic setting, usually coming from `$_POST['customized']`.
       
   882 			 * @param string $setting_args  WP_Customize_Setting or a subclass.
       
   883 			 */
       
   884 			$setting_class = apply_filters( 'customize_dynamic_setting_class', $setting_class, $setting_id, $setting_args );
       
   885 
       
   886 			$setting = new $setting_class( $this, $setting_id, $setting_args );
       
   887 
       
   888 			$this->add_setting( $setting );
       
   889 			$new_settings[] = $setting;
       
   890 		}
       
   891 		return $new_settings;
       
   892 	}
       
   893 
       
   894 	/**
   539 	 * Retrieve a customize setting.
   895 	 * Retrieve a customize setting.
   540 	 *
   896 	 *
   541 	 * @since 3.4.0
   897 	 * @since 3.4.0
   542 	 *
   898 	 *
   543 	 * @param string $id A specific ID of the setting.
   899 	 * @param string $id Customize Setting ID.
   544 	 * @return object The settings object.
   900 	 * @return WP_Customize_Setting
   545 	 */
   901 	 */
   546 	public function get_setting( $id ) {
   902 	public function get_setting( $id ) {
   547 		if ( isset( $this->settings[ $id ] ) )
   903 		if ( isset( $this->settings[ $id ] ) ) {
   548 			return $this->settings[ $id ];
   904 			return $this->settings[ $id ];
       
   905 		}
   549 	}
   906 	}
   550 
   907 
   551 	/**
   908 	/**
   552 	 * Remove a customize setting.
   909 	 * Remove a customize setting.
   553 	 *
   910 	 *
   554 	 * @since 3.4.0
   911 	 * @since 3.4.0
   555 	 *
   912 	 *
   556 	 * @param string $id A specific ID of the setting.
   913 	 * @param string $id Customize Setting ID.
   557 	 */
   914 	 */
   558 	public function remove_setting( $id ) {
   915 	public function remove_setting( $id ) {
   559 		unset( $this->settings[ $id ] );
   916 		unset( $this->settings[ $id ] );
   560 	}
   917 	}
   561 
   918 
   562 	/**
   919 	/**
       
   920 	 * Add a customize panel.
       
   921 	 *
       
   922 	 * @since 4.0.0
       
   923 	 * @access public
       
   924 	 *
       
   925 	 * @param WP_Customize_Panel|string $id   Customize Panel object, or Panel ID.
       
   926 	 * @param array                     $args Optional. Panel arguments. Default empty array.
       
   927 	 */
       
   928 	public function add_panel( $id, $args = array() ) {
       
   929 		if ( $id instanceof WP_Customize_Panel ) {
       
   930 			$panel = $id;
       
   931 		} else {
       
   932 			$panel = new WP_Customize_Panel( $this, $id, $args );
       
   933 		}
       
   934 
       
   935 		$this->panels[ $panel->id ] = $panel;
       
   936 	}
       
   937 
       
   938 	/**
       
   939 	 * Retrieve a customize panel.
       
   940 	 *
       
   941 	 * @since 4.0.0
       
   942 	 * @access public
       
   943 	 *
       
   944 	 * @param string $id Panel ID to get.
       
   945 	 * @return WP_Customize_Panel Requested panel instance.
       
   946 	 */
       
   947 	public function get_panel( $id ) {
       
   948 		if ( isset( $this->panels[ $id ] ) ) {
       
   949 			return $this->panels[ $id ];
       
   950 		}
       
   951 	}
       
   952 
       
   953 	/**
       
   954 	 * Remove a customize panel.
       
   955 	 *
       
   956 	 * @since 4.0.0
       
   957 	 * @access public
       
   958 	 *
       
   959 	 * @param string $id Panel ID to remove.
       
   960 	 */
       
   961 	public function remove_panel( $id ) {
       
   962 		unset( $this->panels[ $id ] );
       
   963 	}
       
   964 
       
   965 	/**
   563 	 * Add a customize section.
   966 	 * Add a customize section.
   564 	 *
   967 	 *
   565 	 * @since 3.4.0
   968 	 * @since 3.4.0
   566 	 *
   969 	 *
   567 	 * @param string $id A specific ID of the section.
   970 	 * @param WP_Customize_Section|string $id   Customize Section object, or Section ID.
   568 	 * @param array $args Section arguments.
   971 	 * @param array                       $args Section arguments.
   569 	 */
   972 	 */
   570 	public function add_section( $id, $args = array() ) {
   973 	public function add_section( $id, $args = array() ) {
   571 		if ( is_a( $id, 'WP_Customize_Section' ) )
   974 		if ( $id instanceof WP_Customize_Section ) {
   572 			$section = $id;
   975 			$section = $id;
   573 		else
   976 		} else {
   574 			$section = new WP_Customize_Section( $this, $id, $args );
   977 			$section = new WP_Customize_Section( $this, $id, $args );
   575 
   978 		}
   576 		$this->sections[ $section->id ] = $section;
   979 		$this->sections[ $section->id ] = $section;
   577 	}
   980 	}
   578 
   981 
   579 	/**
   982 	/**
   580 	 * Retrieve a customize section.
   983 	 * Retrieve a customize section.
   581 	 *
   984 	 *
   582 	 * @since 3.4.0
   985 	 * @since 3.4.0
   583 	 *
   986 	 *
   584 	 * @param string $id A specific ID of the section.
   987 	 * @param string $id Section ID.
   585 	 * @return object The section object.
   988 	 * @return WP_Customize_Section
   586 	 */
   989 	 */
   587 	public function get_section( $id ) {
   990 	public function get_section( $id ) {
   588 		if ( isset( $this->sections[ $id ] ) )
   991 		if ( isset( $this->sections[ $id ] ) )
   589 			return $this->sections[ $id ];
   992 			return $this->sections[ $id ];
   590 	}
   993 	}
   592 	/**
   995 	/**
   593 	 * Remove a customize section.
   996 	 * Remove a customize section.
   594 	 *
   997 	 *
   595 	 * @since 3.4.0
   998 	 * @since 3.4.0
   596 	 *
   999 	 *
   597 	 * @param string $id A specific ID of the section.
  1000 	 * @param string $id Section ID.
   598 	 */
  1001 	 */
   599 	public function remove_section( $id ) {
  1002 	public function remove_section( $id ) {
   600 		unset( $this->sections[ $id ] );
  1003 		unset( $this->sections[ $id ] );
   601 	}
  1004 	}
   602 
  1005 
   603 	/**
  1006 	/**
   604 	 * Add a customize control.
  1007 	 * Add a customize control.
   605 	 *
  1008 	 *
   606 	 * @since 3.4.0
  1009 	 * @since 3.4.0
   607 	 *
  1010 	 *
   608 	 * @param string $id A specific ID of the control.
  1011 	 * @param WP_Customize_Control|string $id   Customize Control object, or ID.
   609 	 * @param array $args Setting arguments.
  1012 	 * @param array                       $args Control arguments; passed to WP_Customize_Control
       
  1013 	 *                                          constructor.
   610 	 */
  1014 	 */
   611 	public function add_control( $id, $args = array() ) {
  1015 	public function add_control( $id, $args = array() ) {
   612 		if ( is_a( $id, 'WP_Customize_Control' ) )
  1016 		if ( $id instanceof WP_Customize_Control ) {
   613 			$control = $id;
  1017 			$control = $id;
   614 		else
  1018 		} else {
   615 			$control = new WP_Customize_Control( $this, $id, $args );
  1019 			$control = new WP_Customize_Control( $this, $id, $args );
   616 
  1020 		}
   617 		$this->controls[ $control->id ] = $control;
  1021 		$this->controls[ $control->id ] = $control;
   618 	}
  1022 	}
   619 
  1023 
   620 	/**
  1024 	/**
   621 	 * Retrieve a customize control.
  1025 	 * Retrieve a customize control.
   622 	 *
  1026 	 *
   623 	 * @since 3.4.0
  1027 	 * @since 3.4.0
   624 	 *
  1028 	 *
   625 	 * @param string $id A specific ID of the control.
  1029 	 * @param string $id ID of the control.
   626 	 * @return object The settings object.
  1030 	 * @return WP_Customize_Control $control The control object.
   627 	 */
  1031 	 */
   628 	public function get_control( $id ) {
  1032 	public function get_control( $id ) {
   629 		if ( isset( $this->controls[ $id ] ) )
  1033 		if ( isset( $this->controls[ $id ] ) )
   630 			return $this->controls[ $id ];
  1034 			return $this->controls[ $id ];
   631 	}
  1035 	}
   632 
  1036 
   633 	/**
  1037 	/**
   634 	 * Remove a customize setting.
  1038 	 * Remove a customize control.
   635 	 *
  1039 	 *
   636 	 * @since 3.4.0
  1040 	 * @since 3.4.0
   637 	 *
  1041 	 *
   638 	 * @param string $id A specific ID of the control.
  1042 	 * @param string $id ID of the control.
   639 	 */
  1043 	 */
   640 	public function remove_control( $id ) {
  1044 	public function remove_control( $id ) {
   641 		unset( $this->controls[ $id ] );
  1045 		unset( $this->controls[ $id ] );
   642 	}
  1046 	}
   643 
  1047 
   644 	/**
  1048 	/**
   645 	 * Helper function to compare two objects by priority.
  1049 	 * Register a customize control type.
   646 	 *
  1050 	 *
   647 	 * @since 3.4.0
  1051 	 * Registered types are eligible to be rendered via JS and created dynamically.
   648 	 *
  1052 	 *
   649 	 * @param object $a Object A.
  1053 	 * @since 4.1.0
   650 	 * @param object $b Object B.
  1054 	 * @access public
       
  1055 	 *
       
  1056 	 * @param string $control Name of a custom control which is a subclass of
       
  1057 	 *                        {@see WP_Customize_Control}.
       
  1058 	 */
       
  1059 	public function register_control_type( $control ) {
       
  1060 		$this->registered_control_types[] = $control;
       
  1061 	}
       
  1062 
       
  1063 	/**
       
  1064 	 * Render JS templates for all registered control types.
       
  1065 	 *
       
  1066 	 * @since 4.1.0
       
  1067 	 * @access public
       
  1068 	 */
       
  1069 	public function render_control_templates() {
       
  1070 		foreach ( $this->registered_control_types as $control_type ) {
       
  1071 			$control = new $control_type( $this, 'temp', array() );
       
  1072 			$control->print_template();
       
  1073 		}
       
  1074 	}
       
  1075 
       
  1076 	/**
       
  1077 	 * Helper function to compare two objects by priority, ensuring sort stability via instance_number.
       
  1078 	 *
       
  1079 	 * @since 3.4.0
       
  1080 	 *
       
  1081 	 * @param WP_Customize_Panel|WP_Customize_Section|WP_Customize_Control $a Object A.
       
  1082 	 * @param WP_Customize_Panel|WP_Customize_Section|WP_Customize_Control $b Object B.
   651 	 * @return int
  1083 	 * @return int
   652 	 */
  1084 	 */
   653 	protected final function _cmp_priority( $a, $b ) {
  1085 	protected function _cmp_priority( $a, $b ) {
   654 		$ap = $a->priority;
  1086 		if ( $a->priority === $b->priority ) {
   655 		$bp = $b->priority;
  1087 			return $a->instance_number - $a->instance_number;
   656 
  1088 		} else {
   657 		if ( $ap == $bp )
  1089 			return $a->priority - $b->priority;
   658 			return 0;
  1090 		}
   659 		return ( $ap > $bp ) ? 1 : -1;
  1091 	}
   660 	}
  1092 
   661 
  1093 	/**
   662 	/**
  1094 	 * Prepare panels, sections, and controls.
   663 	 * Prepare settings and sections.
  1095 	 *
       
  1096 	 * For each, check if required related components exist,
       
  1097 	 * whether the user has the necessary capabilities,
       
  1098 	 * and sort by priority.
   664 	 *
  1099 	 *
   665 	 * @since 3.4.0
  1100 	 * @since 3.4.0
   666 	 */
  1101 	 */
   667 	public function prepare_controls() {
  1102 	public function prepare_controls() {
   668 		// Prepare controls
  1103 
   669 		// Reversing makes uasort sort by time added when conflicts occur.
       
   670 
       
   671 		$this->controls = array_reverse( $this->controls );
       
   672 		$controls = array();
  1104 		$controls = array();
       
  1105 		uasort( $this->controls, array( $this, '_cmp_priority' ) );
   673 
  1106 
   674 		foreach ( $this->controls as $id => $control ) {
  1107 		foreach ( $this->controls as $id => $control ) {
   675 			if ( ! isset( $this->sections[ $control->section ] ) || ! $control->check_capabilities() )
  1108 			if ( ! isset( $this->sections[ $control->section ] ) || ! $control->check_capabilities() ) {
   676 				continue;
  1109 				continue;
       
  1110 			}
   677 
  1111 
   678 			$this->sections[ $control->section ]->controls[] = $control;
  1112 			$this->sections[ $control->section ]->controls[] = $control;
   679 			$controls[ $id ] = $control;
  1113 			$controls[ $id ] = $control;
   680 		}
  1114 		}
   681 		$this->controls = $controls;
  1115 		$this->controls = $controls;
   682 
  1116 
   683 		// Prepare sections
  1117 		// Prepare sections.
   684 		$this->sections = array_reverse( $this->sections );
       
   685 		uasort( $this->sections, array( $this, '_cmp_priority' ) );
  1118 		uasort( $this->sections, array( $this, '_cmp_priority' ) );
   686 		$sections = array();
  1119 		$sections = array();
   687 
  1120 
   688 		foreach ( $this->sections as $section ) {
  1121 		foreach ( $this->sections as $section ) {
   689 			if ( ! $section->check_capabilities() || ! $section->controls )
  1122 			if ( ! $section->check_capabilities() || ! $section->controls ) {
   690 				continue;
  1123 				continue;
       
  1124 			}
   691 
  1125 
   692 			usort( $section->controls, array( $this, '_cmp_priority' ) );
  1126 			usort( $section->controls, array( $this, '_cmp_priority' ) );
   693 			$sections[] = $section;
  1127 
       
  1128 			if ( ! $section->panel ) {
       
  1129 				// Top-level section.
       
  1130 				$sections[ $section->id ] = $section;
       
  1131 			} else {
       
  1132 				// This section belongs to a panel.
       
  1133 				if ( isset( $this->panels [ $section->panel ] ) ) {
       
  1134 					$this->panels[ $section->panel ]->sections[ $section->id ] = $section;
       
  1135 				}
       
  1136 			}
   694 		}
  1137 		}
   695 		$this->sections = $sections;
  1138 		$this->sections = $sections;
       
  1139 
       
  1140 		// Prepare panels.
       
  1141 		uasort( $this->panels, array( $this, '_cmp_priority' ) );
       
  1142 		$panels = array();
       
  1143 
       
  1144 		foreach ( $this->panels as $panel ) {
       
  1145 			if ( ! $panel->check_capabilities() || ! $panel->sections ) {
       
  1146 				continue;
       
  1147 			}
       
  1148 
       
  1149 			uasort( $panel->sections, array( $this, '_cmp_priority' ) );
       
  1150 			$panels[ $panel->id ] = $panel;
       
  1151 		}
       
  1152 		$this->panels = $panels;
       
  1153 
       
  1154 		// Sort panels and top-level sections together.
       
  1155 		$this->containers = array_merge( $this->panels, $this->sections );
       
  1156 		uasort( $this->containers, array( $this, '_cmp_priority' ) );
   696 	}
  1157 	}
   697 
  1158 
   698 	/**
  1159 	/**
   699 	 * Enqueue scripts for customize controls.
  1160 	 * Enqueue scripts for customize controls.
   700 	 *
  1161 	 *
   710 	 * Register some default controls.
  1171 	 * Register some default controls.
   711 	 *
  1172 	 *
   712 	 * @since 3.4.0
  1173 	 * @since 3.4.0
   713 	 */
  1174 	 */
   714 	public function register_controls() {
  1175 	public function register_controls() {
       
  1176 
       
  1177 		/* Control Types (custom control classes) */
       
  1178 		$this->register_control_type( 'WP_Customize_Color_Control' );
       
  1179 		$this->register_control_type( 'WP_Customize_Media_Control' );
       
  1180 		$this->register_control_type( 'WP_Customize_Upload_Control' );
       
  1181 		$this->register_control_type( 'WP_Customize_Image_Control' );
       
  1182 		$this->register_control_type( 'WP_Customize_Background_Image_Control' );
       
  1183 		$this->register_control_type( 'WP_Customize_Theme_Control' );
       
  1184 
       
  1185 		/* Themes */
       
  1186 
       
  1187 		$this->add_section( new WP_Customize_Themes_Section( $this, 'themes', array(
       
  1188 			'title'      => $this->theme()->display( 'Name' ),
       
  1189 			'capability' => 'switch_themes',
       
  1190 			'priority'   => 0,
       
  1191 		) ) );
       
  1192 
       
  1193 		// Themes Setting (unused - the theme is considerably more fundamental to the Customizer experience).
       
  1194 		$this->add_setting( new WP_Customize_Filter_Setting( $this, 'active_theme', array(
       
  1195 			'capability' => 'switch_themes',
       
  1196 		) ) );
       
  1197 
       
  1198 		require_once( ABSPATH . 'wp-admin/includes/theme.php' );
       
  1199 
       
  1200 		// Theme Controls.
       
  1201 
       
  1202 		// Add a control for the active/original theme.
       
  1203 		if ( ! $this->is_theme_active() ) {
       
  1204 			$themes = wp_prepare_themes_for_js( array( wp_get_theme( $this->original_stylesheet ) ) );
       
  1205 			$active_theme = current( $themes );
       
  1206 			$active_theme['isActiveTheme'] = true;
       
  1207 			$this->add_control( new WP_Customize_Theme_Control( $this, $active_theme['id'], array(
       
  1208 				'theme'    => $active_theme,
       
  1209 				'section'  => 'themes',
       
  1210 				'settings' => 'active_theme',
       
  1211 			) ) );
       
  1212 		}
       
  1213 
       
  1214 		$themes = wp_prepare_themes_for_js();
       
  1215 		foreach ( $themes as $theme ) {
       
  1216 			if ( $theme['active'] || $theme['id'] === $this->original_stylesheet ) {
       
  1217 				continue;
       
  1218 			}
       
  1219 
       
  1220 			$theme_id = 'theme_' . $theme['id'];
       
  1221 			$theme['isActiveTheme'] = false;
       
  1222 			$this->add_control( new WP_Customize_Theme_Control( $this, $theme_id, array(
       
  1223 				'theme'    => $theme,
       
  1224 				'section'  => 'themes',
       
  1225 				'settings' => 'active_theme',
       
  1226 			) ) );
       
  1227 		}
   715 
  1228 
   716 		/* Site Title & Tagline */
  1229 		/* Site Title & Tagline */
   717 
  1230 
   718 		$this->add_section( 'title_tagline', array(
  1231 		$this->add_section( 'title_tagline', array(
   719 			'title'    => __( 'Site Title & Tagline' ),
  1232 			'title'    => __( 'Site Title & Tagline' ),
   825 		) ) );
  1338 		) ) );
   826 
  1339 
   827 		$this->add_control( new WP_Customize_Background_Image_Control( $this ) );
  1340 		$this->add_control( new WP_Customize_Background_Image_Control( $this ) );
   828 
  1341 
   829 		$this->add_setting( 'background_repeat', array(
  1342 		$this->add_setting( 'background_repeat', array(
   830 			'default'        => 'repeat',
  1343 			'default'        => get_theme_support( 'custom-background', 'default-repeat' ),
   831 			'theme_supports' => 'custom-background',
  1344 			'theme_supports' => 'custom-background',
   832 		) );
  1345 		) );
   833 
  1346 
   834 		$this->add_control( 'background_repeat', array(
  1347 		$this->add_control( 'background_repeat', array(
   835 			'label'      => __( 'Background Repeat' ),
  1348 			'label'      => __( 'Background Repeat' ),
   842 				'repeat-y'   => __('Tile Vertically'),
  1355 				'repeat-y'   => __('Tile Vertically'),
   843 			),
  1356 			),
   844 		) );
  1357 		) );
   845 
  1358 
   846 		$this->add_setting( 'background_position_x', array(
  1359 		$this->add_setting( 'background_position_x', array(
   847 			'default'        => 'left',
  1360 			'default'        => get_theme_support( 'custom-background', 'default-position-x' ),
   848 			'theme_supports' => 'custom-background',
  1361 			'theme_supports' => 'custom-background',
   849 		) );
  1362 		) );
   850 
  1363 
   851 		$this->add_control( 'background_position_x', array(
  1364 		$this->add_control( 'background_position_x', array(
   852 			'label'      => __( 'Background Position' ),
  1365 			'label'      => __( 'Background Position' ),
   858 				'right'      => __('Right'),
  1371 				'right'      => __('Right'),
   859 			),
  1372 			),
   860 		) );
  1373 		) );
   861 
  1374 
   862 		$this->add_setting( 'background_attachment', array(
  1375 		$this->add_setting( 'background_attachment', array(
   863 			'default'        => 'fixed',
  1376 			'default'        => get_theme_support( 'custom-background', 'default-attachment' ),
   864 			'theme_supports' => 'custom-background',
  1377 			'theme_supports' => 'custom-background',
   865 		) );
  1378 		) );
   866 
  1379 
   867 		$this->add_control( 'background_attachment', array(
  1380 		$this->add_control( 'background_attachment', array(
   868 			'label'      => __( 'Background Attachment' ),
  1381 			'label'      => __( 'Background Attachment' ),
   869 			'section'    => 'background_image',
  1382 			'section'    => 'background_image',
   870 			'type'       => 'radio',
  1383 			'type'       => 'radio',
   871 			'choices'    => array(
  1384 			'choices'    => array(
       
  1385 				'scroll'     => __('Scroll'),
   872 				'fixed'      => __('Fixed'),
  1386 				'fixed'      => __('Fixed'),
   873 				'scroll'     => __('Scroll'),
       
   874 			),
  1387 			),
   875 		) );
  1388 		) );
   876 
  1389 
   877 		// If the theme is using the default background callback, we can update
  1390 		// If the theme is using the default background callback, we can update
   878 		// the background CSS using postMessage.
  1391 		// the background CSS using postMessage.
   884 
  1397 
   885 		/* Nav Menus */
  1398 		/* Nav Menus */
   886 
  1399 
   887 		$locations      = get_registered_nav_menus();
  1400 		$locations      = get_registered_nav_menus();
   888 		$menus          = wp_get_nav_menus();
  1401 		$menus          = wp_get_nav_menus();
   889 		$menu_locations = get_nav_menu_locations();
       
   890 		$num_locations  = count( array_keys( $locations ) );
  1402 		$num_locations  = count( array_keys( $locations ) );
       
  1403 
       
  1404 		if ( 1 == $num_locations ) {
       
  1405 			$description = __( 'Your theme supports one menu. Select which menu you would like to use.' );
       
  1406 		} else {
       
  1407 			$description = sprintf( _n( 'Your theme supports %s menu. Select which menu appears in each location.', 'Your theme supports %s menus. Select which menu appears in each location.', $num_locations ), number_format_i18n( $num_locations ) );
       
  1408 		}
   891 
  1409 
   892 		$this->add_section( 'nav', array(
  1410 		$this->add_section( 'nav', array(
   893 			'title'          => __( 'Navigation' ),
  1411 			'title'          => __( 'Navigation' ),
   894 			'theme_supports' => 'menus',
  1412 			'theme_supports' => 'menus',
   895 			'priority'       => 100,
  1413 			'priority'       => 100,
   896 			'description'    => sprintf( _n('Your theme supports %s menu. Select which menu you would like to use.', 'Your theme supports %s menus. Select which menu appears in each location.', $num_locations ), number_format_i18n( $num_locations ) ) . "\n\n" . __('You can edit your menu content on the Menus screen in the Appearance section.'),
  1414 			'description'    => $description . "\n\n" . __( 'You can edit your menu content on the Menus screen in the Appearance section.' ),
   897 		) );
  1415 		) );
   898 
  1416 
   899 		if ( $menus ) {
  1417 		if ( $menus ) {
   900 			$choices = array( 0 => __( '&mdash; Select &mdash;' ) );
  1418 			$choices = array( '' => __( '&mdash; Select &mdash;' ) );
   901 			foreach ( $menus as $menu ) {
  1419 			foreach ( $menus as $menu ) {
   902 				$choices[ $menu->term_id ] = wp_html_excerpt( $menu->name, 40, '&hellip;' );
  1420 				$choices[ $menu->term_id ] = wp_html_excerpt( $menu->name, 40, '&hellip;' );
   903 			}
  1421 			}
   904 
  1422 
   905 			foreach ( $locations as $location => $description ) {
  1423 			foreach ( $locations as $location => $description ) {
   920 		}
  1438 		}
   921 
  1439 
   922 		/* Static Front Page */
  1440 		/* Static Front Page */
   923 		// #WP19627
  1441 		// #WP19627
   924 
  1442 
   925 		$this->add_section( 'static_front_page', array(
  1443 		// Replicate behavior from options-reading.php and hide front page options if there are no pages
   926 			'title'          => __( 'Static Front Page' ),
  1444 		if ( get_pages() ) {
   927 		//	'theme_supports' => 'static-front-page',
  1445 			$this->add_section( 'static_front_page', array(
   928 			'priority'       => 120,
  1446 				'title'          => __( 'Static Front Page' ),
   929 			'description'    => __( 'Your theme supports a static front page.' ),
  1447 			//	'theme_supports' => 'static-front-page',
   930 		) );
  1448 				'priority'       => 120,
   931 
  1449 				'description'    => __( 'Your theme supports a static front page.' ),
   932 		$this->add_setting( 'show_on_front', array(
  1450 			) );
   933 			'default'        => get_option( 'show_on_front' ),
  1451 
   934 			'capability'     => 'manage_options',
  1452 			$this->add_setting( 'show_on_front', array(
   935 			'type'           => 'option',
  1453 				'default'        => get_option( 'show_on_front' ),
   936 		//	'theme_supports' => 'static-front-page',
  1454 				'capability'     => 'manage_options',
   937 		) );
  1455 				'type'           => 'option',
   938 
  1456 			//	'theme_supports' => 'static-front-page',
   939 		$this->add_control( 'show_on_front', array(
  1457 			) );
   940 			'label'   => __( 'Front page displays' ),
  1458 
   941 			'section' => 'static_front_page',
  1459 			$this->add_control( 'show_on_front', array(
   942 			'type'    => 'radio',
  1460 				'label'   => __( 'Front page displays' ),
   943 			'choices' => array(
  1461 				'section' => 'static_front_page',
   944 				'posts' => __( 'Your latest posts' ),
  1462 				'type'    => 'radio',
   945 				'page'  => __( 'A static page' ),
  1463 				'choices' => array(
   946 			),
  1464 					'posts' => __( 'Your latest posts' ),
   947 		) );
  1465 					'page'  => __( 'A static page' ),
   948 
  1466 				),
   949 		$this->add_setting( 'page_on_front', array(
  1467 			) );
   950 			'type'       => 'option',
  1468 
   951 			'capability' => 'manage_options',
  1469 			$this->add_setting( 'page_on_front', array(
   952 		//	'theme_supports' => 'static-front-page',
  1470 				'type'       => 'option',
   953 		) );
  1471 				'capability' => 'manage_options',
   954 
  1472 			//	'theme_supports' => 'static-front-page',
   955 		$this->add_control( 'page_on_front', array(
  1473 			) );
   956 			'label'      => __( 'Front page' ),
  1474 
   957 			'section'    => 'static_front_page',
  1475 			$this->add_control( 'page_on_front', array(
   958 			'type'       => 'dropdown-pages',
  1476 				'label'      => __( 'Front page' ),
   959 		) );
  1477 				'section'    => 'static_front_page',
   960 
  1478 				'type'       => 'dropdown-pages',
   961 		$this->add_setting( 'page_for_posts', array(
  1479 			) );
   962 			'type'           => 'option',
  1480 
   963 			'capability'     => 'manage_options',
  1481 			$this->add_setting( 'page_for_posts', array(
   964 		//	'theme_supports' => 'static-front-page',
  1482 				'type'           => 'option',
   965 		) );
  1483 				'capability'     => 'manage_options',
   966 
  1484 			//	'theme_supports' => 'static-front-page',
   967 		$this->add_control( 'page_for_posts', array(
  1485 			) );
   968 			'label'      => __( 'Posts page' ),
  1486 
   969 			'section'    => 'static_front_page',
  1487 			$this->add_control( 'page_for_posts', array(
   970 			'type'       => 'dropdown-pages',
  1488 				'label'      => __( 'Posts page' ),
   971 		) );
  1489 				'section'    => 'static_front_page',
       
  1490 				'type'       => 'dropdown-pages',
       
  1491 			) );
       
  1492 		}
       
  1493 	}
       
  1494 
       
  1495 	/**
       
  1496 	 * Add settings from the POST data that were not added with code, e.g. dynamically-created settings for Widgets
       
  1497 	 *
       
  1498 	 * @since 4.2.0
       
  1499 	 * @access public
       
  1500 	 *
       
  1501 	 * @see add_dynamic_settings()
       
  1502 	 */
       
  1503 	public function register_dynamic_settings() {
       
  1504 		$this->add_dynamic_settings( array_keys( $this->unsanitized_post_values() ) );
   972 	}
  1505 	}
   973 
  1506 
   974 	/**
  1507 	/**
   975 	 * Callback for validating the header_textcolor value.
  1508 	 * Callback for validating the header_textcolor value.
   976 	 *
  1509 	 *
   990 		if ( empty( $color ) )
  1523 		if ( empty( $color ) )
   991 			$color = get_theme_support( 'custom-header', 'default-text-color' );
  1524 			$color = get_theme_support( 'custom-header', 'default-text-color' );
   992 
  1525 
   993 		return $color;
  1526 		return $color;
   994 	}
  1527 	}
   995 };
  1528 }
   996 
  1529 
   997 /**
  1530 /**
   998  * Validates a hex color.
  1531  * Sanitizes a hex color.
   999  *
  1532  *
  1000  * Returns either '', a 3 or 6 digit hex color (with #), or null.
  1533  * Returns either '', a 3 or 6 digit hex color (with #), or null.
  1001  * For validating values without a #, see sanitize_hex_color_no_hash().
  1534  * For sanitizing values without a #, see sanitize_hex_color_no_hash().
  1002  *
  1535  *
  1003  * @since 3.4.0
  1536  * @since 3.4.0
  1004  *
  1537  *
  1005  * @param string $color
  1538  * @param string $color
  1006  * @return string|null
  1539  * @return string|null
  1024  * rgba, hsl, rgb, and html color names.
  1557  * rgba, hsl, rgb, and html color names.
  1025  *
  1558  *
  1026  * Returns either '', a 3 or 6 digit hex color (without a #), or null.
  1559  * Returns either '', a 3 or 6 digit hex color (without a #), or null.
  1027  *
  1560  *
  1028  * @since 3.4.0
  1561  * @since 3.4.0
  1029  * @uses sanitize_hex_color()
       
  1030  *
  1562  *
  1031  * @param string $color
  1563  * @param string $color
  1032  * @return string|null
  1564  * @return string|null
  1033  */
  1565  */
  1034 function sanitize_hex_color_no_hash( $color ) {
  1566 function sanitize_hex_color_no_hash( $color ) {