WP_Customize_Manager

Customize Manager class.

Defined (1)

The class is defined in the following location(s).

/wp-includes/class-wp-customize-manager.php  
  1. final class WP_Customize_Manager { 
  2. /** 
  3. * An instance of the theme being previewed. 
  4. * @since 3.4.0 
  5. * @access protected 
  6. * @var WP_Theme 
  7. */ 
  8. protected $theme; 
  9.  
  10. /** 
  11. * The directory name of the previously active theme (within the theme_root). 
  12. * @since 3.4.0 
  13. * @access protected 
  14. * @var string 
  15. */ 
  16. protected $original_stylesheet; 
  17.  
  18. /** 
  19. * Whether this is a Customizer pageload. 
  20. * @since 3.4.0 
  21. * @access protected 
  22. * @var bool 
  23. */ 
  24. protected $previewing = false; 
  25.  
  26. /** 
  27. * Methods and properties dealing with managing widgets in the Customizer. 
  28. * @since 3.9.0 
  29. * @access public 
  30. * @var WP_Customize_Widgets 
  31. */ 
  32. public $widgets; 
  33.  
  34. /** 
  35. * Methods and properties dealing with managing nav menus in the Customizer. 
  36. * @since 4.3.0 
  37. * @access public 
  38. * @var WP_Customize_Nav_Menus 
  39. */ 
  40. public $nav_menus; 
  41.  
  42. /** 
  43. * Methods and properties dealing with selective refresh in the Customizer preview. 
  44. * @since 4.5.0 
  45. * @access public 
  46. * @var WP_Customize_Selective_Refresh 
  47. */ 
  48. public $selective_refresh; 
  49.  
  50. /** 
  51. * Registered instances of WP_Customize_Setting. 
  52. * @since 3.4.0 
  53. * @access protected 
  54. * @var array 
  55. */ 
  56. protected $settings = array(); 
  57.  
  58. /** 
  59. * Sorted top-level instances of WP_Customize_Panel and WP_Customize_Section. 
  60. * @since 4.0.0 
  61. * @access protected 
  62. * @var array 
  63. */ 
  64. protected $containers = array(); 
  65.  
  66. /** 
  67. * Registered instances of WP_Customize_Panel. 
  68. * @since 4.0.0 
  69. * @access protected 
  70. * @var array 
  71. */ 
  72. protected $panels = array(); 
  73.  
  74. /** 
  75. * List of core components. 
  76. * @since 4.5.0 
  77. * @access protected 
  78. * @var array 
  79. */ 
  80. protected $components = array( 'widgets', 'nav_menus' ); 
  81.  
  82. /** 
  83. * Registered instances of WP_Customize_Section. 
  84. * @since 3.4.0 
  85. * @access protected 
  86. * @var array 
  87. */ 
  88. protected $sections = array(); 
  89.  
  90. /** 
  91. * Registered instances of WP_Customize_Control. 
  92. * @since 3.4.0 
  93. * @access protected 
  94. * @var array 
  95. */ 
  96. protected $controls = array(); 
  97.  
  98. /** 
  99. * Return value of check_ajax_referer() in customize_preview_init() method. 
  100. * @since 3.5.0 
  101. * @access protected 
  102. * @var false|int 
  103. */ 
  104. protected $nonce_tick; 
  105.  
  106. /** 
  107. * Panel types that may be rendered from JS templates. 
  108. * @since 4.3.0 
  109. * @access protected 
  110. * @var array 
  111. */ 
  112. protected $registered_panel_types = array(); 
  113.  
  114. /** 
  115. * Section types that may be rendered from JS templates. 
  116. * @since 4.3.0 
  117. * @access protected 
  118. * @var array 
  119. */ 
  120. protected $registered_section_types = array(); 
  121.  
  122. /** 
  123. * Control types that may be rendered from JS templates. 
  124. * @since 4.1.0 
  125. * @access protected 
  126. * @var array 
  127. */ 
  128. protected $registered_control_types = array(); 
  129.  
  130. /** 
  131. * Initial URL being previewed. 
  132. * @since 4.4.0 
  133. * @access protected 
  134. * @var string 
  135. */ 
  136. protected $preview_url; 
  137.  
  138. /** 
  139. * URL to link the user to when closing the Customizer. 
  140. * @since 4.4.0 
  141. * @access protected 
  142. * @var string 
  143. */ 
  144. protected $return_url; 
  145.  
  146. /** 
  147. * Mapping of 'panel', 'section', 'control' to the ID which should be autofocused. 
  148. * @since 4.4.0 
  149. * @access protected 
  150. * @var array 
  151. */ 
  152. protected $autofocus = array(); 
  153.  
  154. /** 
  155. * Unsanitized values for Customize Settings parsed from $_POST['customized']. 
  156. * @var array 
  157. */ 
  158. private $_post_values; 
  159.  
  160. /** 
  161. * Constructor. 
  162. * @since 3.4.0 
  163. */ 
  164. public function __construct() { 
  165. require_once( ABSPATH . WPINC . '/class-wp-customize-setting.php' ); 
  166. require_once( ABSPATH . WPINC . '/class-wp-customize-panel.php' ); 
  167. require_once( ABSPATH . WPINC . '/class-wp-customize-section.php' ); 
  168. require_once( ABSPATH . WPINC . '/class-wp-customize-control.php' ); 
  169.  
  170. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-color-control.php' ); 
  171. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-media-control.php' ); 
  172. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-upload-control.php' ); 
  173. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-image-control.php' ); 
  174. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-control.php' ); 
  175. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-cropped-image-control.php' ); 
  176. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php' ); 
  177. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-control.php' ); 
  178. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-theme-control.php' ); 
  179. require_once( ABSPATH . WPINC . '/customize/class-wp-widget-area-customize-control.php' ); 
  180. require_once( ABSPATH . WPINC . '/customize/class-wp-widget-form-customize-control.php' ); 
  181. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-control.php' ); 
  182. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-control.php' ); 
  183. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-location-control.php' ); 
  184. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php' ); 
  185. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php' ); 
  186. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-control.php' ); 
  187.  
  188. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' ); 
  189.  
  190. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php' ); 
  191. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php' ); 
  192. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php' ); 
  193. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-section.php' ); 
  194.  
  195. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php' ); 
  196. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php' ); 
  197. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php' ); 
  198. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php' ); 
  199. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php' ); 
  200.  
  201. /** 
  202. * Filters the core Customizer components to load. 
  203. * This allows Core components to be excluded from being instantiated by 
  204. * filtering them out of the array. Note that this filter generally runs 
  205. * during the {@see 'plugins_loaded'} action, so it cannot be added 
  206. * in a theme. 
  207. * @since 4.4.0 
  208. * @see WP_Customize_Manager::__construct() 
  209. * @param array $components List of core components to load. 
  210. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  211. */ 
  212. $components = apply_filters( 'customize_loaded_components', $this->components, $this ); 
  213.  
  214. require_once( ABSPATH . WPINC . '/customize/class-wp-customize-selective-refresh.php' ); 
  215. $this->selective_refresh = new WP_Customize_Selective_Refresh( $this ); 
  216.  
  217. if ( in_array( 'widgets', $components, true ) ) { 
  218. require_once( ABSPATH . WPINC . '/class-wp-customize-widgets.php' ); 
  219. $this->widgets = new WP_Customize_Widgets( $this ); 
  220.  
  221. if ( in_array( 'nav_menus', $components, true ) ) { 
  222. require_once( ABSPATH . WPINC . '/class-wp-customize-nav-menus.php' ); 
  223. $this->nav_menus = new WP_Customize_Nav_Menus( $this ); 
  224.  
  225. add_filter( 'wp_die_handler', array( $this, 'wp_die_handler' ) ); 
  226.  
  227. add_action( 'setup_theme', array( $this, 'setup_theme' ) ); 
  228. add_action( 'wp_loaded', array( $this, 'wp_loaded' ) ); 
  229.  
  230. // Run wp_redirect_status late to make sure we override the status last. 
  231. add_action( 'wp_redirect_status', array( $this, 'wp_redirect_status' ), 1000 ); 
  232.  
  233. // Do not spawn cron (especially the alternate cron) while running the Customizer. 
  234. remove_action( 'init', 'wp_cron' ); 
  235.  
  236. // Do not run update checks when rendering the controls. 
  237. remove_action( 'admin_init', '_maybe_update_core' ); 
  238. remove_action( 'admin_init', '_maybe_update_plugins' ); 
  239. remove_action( 'admin_init', '_maybe_update_themes' ); 
  240.  
  241. add_action( 'wp_ajax_customize_save', array( $this, 'save' ) ); 
  242. add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) ); 
  243.  
  244. add_action( 'customize_register', array( $this, 'register_controls' ) ); 
  245. add_action( 'customize_register', array( $this, 'register_dynamic_settings' ), 11 ); // allow code to create settings first 
  246. add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) ); 
  247. add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) ); 
  248.  
  249. // Render Panel, Section, and Control templates. 
  250. add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_panel_templates' ), 1 ); 
  251. add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_section_templates' ), 1 ); 
  252. add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_control_templates' ), 1 ); 
  253.  
  254. // Export the settings to JS via the _wpCustomizeSettings variable. 
  255. add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_pane_settings' ), 1000 ); 
  256.  
  257. /** 
  258. * Return true if it's an Ajax request. 
  259. * @since 3.4.0 
  260. * @since 4.2.0 Added `$action` param. 
  261. * @access public 
  262. * @param string|null $action Whether the supplied Ajax action is being run. 
  263. * @return bool True if it's an Ajax request, false otherwise. 
  264. */ 
  265. public function doing_ajax( $action = null ) { 
  266. $doing_ajax = ( defined( 'DOING_AJAX' ) && DOING_AJAX ); 
  267. if ( ! $doing_ajax ) { 
  268. return false; 
  269.  
  270. if ( ! $action ) { 
  271. return true; 
  272. } else { 
  273. /** 
  274. * Note: we can't just use doing_action( "wp_ajax_{$action}" ) because we need 
  275. * to check before admin-ajax.php gets to that point. 
  276. */ 
  277. return isset( $_REQUEST['action'] ) && wp_unslash( $_REQUEST['action'] ) === $action; 
  278.  
  279. /** 
  280. * Custom wp_die wrapper. Returns either the standard message for UI 
  281. * or the Ajax message. 
  282. * @since 3.4.0 
  283. * @param mixed $ajax_message Ajax return 
  284. * @param mixed $message UI message 
  285. */ 
  286. protected function wp_die( $ajax_message, $message = null ) { 
  287. if ( $this->doing_ajax() || isset( $_POST['customized'] ) ) { 
  288. wp_die( $ajax_message ); 
  289.  
  290. if ( ! $message ) { 
  291. $message = __( 'Cheatin’ uh?' ); 
  292.  
  293. wp_die( $message ); 
  294.  
  295. /** 
  296. * Return the Ajax wp_die() handler if it's a customized request. 
  297. * @since 3.4.0 
  298. * @return string 
  299. */ 
  300. public function wp_die_handler() { 
  301. if ( $this->doing_ajax() || isset( $_POST['customized'] ) ) { 
  302. return '_ajax_wp_die_handler'; 
  303.  
  304. return '_default_wp_die_handler'; 
  305.  
  306. /** 
  307. * Start preview and customize theme. 
  308. * Check if customize query variable exist. Init filters to filter the current theme. 
  309. * @since 3.4.0 
  310. */ 
  311. public function setup_theme() { 
  312. send_origin_headers(); 
  313.  
  314. $doing_ajax_or_is_customized = ( $this->doing_ajax() || isset( $_POST['customized'] ) ); 
  315. if ( is_admin() && ! $doing_ajax_or_is_customized ) { 
  316. auth_redirect(); 
  317. } elseif ( $doing_ajax_or_is_customized && ! is_user_logged_in() ) { 
  318. $this->wp_die( 0, __( 'You must be logged in to complete this action.' ) ); 
  319.  
  320. show_admin_bar( false ); 
  321.  
  322. if ( ! current_user_can( 'customize' ) ) { 
  323. $this->wp_die( -1, __( 'Sorry, you are not allowed to customize this site.' ) ); 
  324.  
  325. $this->original_stylesheet = get_stylesheet(); 
  326.  
  327. $this->theme = wp_get_theme( isset( $_REQUEST['theme'] ) ? $_REQUEST['theme'] : null ); 
  328.  
  329. if ( $this->is_theme_active() ) { 
  330. // Once the theme is loaded, we'll validate it. 
  331. add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) ); 
  332. } else { 
  333. // If the requested theme is not the active theme and the user doesn't have the 
  334. // switch_themes cap, bail. 
  335. if ( ! current_user_can( 'switch_themes' ) ) { 
  336. $this->wp_die( -1, __( 'Sorry, you are not allowed to edit theme options on this site.' ) ); 
  337.  
  338. // If the theme has errors while loading, bail. 
  339. if ( $this->theme()->errors() ) { 
  340. $this->wp_die( -1, $this->theme()->errors()->get_error_message() ); 
  341.  
  342. // If the theme isn't allowed per multisite settings, bail. 
  343. if ( ! $this->theme()->is_allowed() ) { 
  344. $this->wp_die( -1, __( 'The requested theme does not exist.' ) ); 
  345.  
  346. $this->start_previewing_theme(); 
  347.  
  348. /** 
  349. * Callback to validate a theme once it is loaded 
  350. * @since 3.4.0 
  351. */ 
  352. public function after_setup_theme() { 
  353. $doing_ajax_or_is_customized = ( $this->doing_ajax() || isset( $_POST['customized'] ) ); 
  354. if ( ! $doing_ajax_or_is_customized && ! validate_current_theme() ) { 
  355. wp_redirect( 'themes.php?broken=true' ); 
  356. exit; 
  357.  
  358. /** 
  359. * If the theme to be previewed isn't the active theme, add filter callbacks 
  360. * to swap it out at runtime. 
  361. * @since 3.4.0 
  362. */ 
  363. public function start_previewing_theme() { 
  364. // Bail if we're already previewing. 
  365. if ( $this->is_preview() ) { 
  366. return; 
  367.  
  368. $this->previewing = true; 
  369.  
  370. if ( ! $this->is_theme_active() ) { 
  371. add_filter( 'template', array( $this, 'get_template' ) ); 
  372. add_filter( 'stylesheet', array( $this, 'get_stylesheet' ) ); 
  373. add_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) ); 
  374.  
  375. // @link: https://core.trac.wordpress.org/ticket/20027 
  376. add_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) ); 
  377. add_filter( 'pre_option_template', array( $this, 'get_template' ) ); 
  378.  
  379. // Handle custom theme roots. 
  380. add_filter( 'pre_option_stylesheet_root', array( $this, 'get_stylesheet_root' ) ); 
  381. add_filter( 'pre_option_template_root', array( $this, 'get_template_root' ) ); 
  382.  
  383. /** 
  384. * Fires once the Customizer theme preview has started. 
  385. * @since 3.4.0 
  386. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  387. */ 
  388. do_action( 'start_previewing_theme', $this ); 
  389.  
  390. /** 
  391. * Stop previewing the selected theme. 
  392. * Removes filters to change the current theme. 
  393. * @since 3.4.0 
  394. */ 
  395. public function stop_previewing_theme() { 
  396. if ( ! $this->is_preview() ) { 
  397. return; 
  398.  
  399. $this->previewing = false; 
  400.  
  401. if ( ! $this->is_theme_active() ) { 
  402. remove_filter( 'template', array( $this, 'get_template' ) ); 
  403. remove_filter( 'stylesheet', array( $this, 'get_stylesheet' ) ); 
  404. remove_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) ); 
  405.  
  406. // @link: https://core.trac.wordpress.org/ticket/20027 
  407. remove_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) ); 
  408. remove_filter( 'pre_option_template', array( $this, 'get_template' ) ); 
  409.  
  410. // Handle custom theme roots. 
  411. remove_filter( 'pre_option_stylesheet_root', array( $this, 'get_stylesheet_root' ) ); 
  412. remove_filter( 'pre_option_template_root', array( $this, 'get_template_root' ) ); 
  413.  
  414. /** 
  415. * Fires once the Customizer theme preview has stopped. 
  416. * @since 3.4.0 
  417. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  418. */ 
  419. do_action( 'stop_previewing_theme', $this ); 
  420.  
  421. /** 
  422. * Get the theme being customized. 
  423. * @since 3.4.0 
  424. * @return WP_Theme 
  425. */ 
  426. public function theme() { 
  427. if ( ! $this->theme ) { 
  428. $this->theme = wp_get_theme(); 
  429. return $this->theme; 
  430.  
  431. /** 
  432. * Get the registered settings. 
  433. * @since 3.4.0 
  434. * @return array 
  435. */ 
  436. public function settings() { 
  437. return $this->settings; 
  438.  
  439. /** 
  440. * Get the registered controls. 
  441. * @since 3.4.0 
  442. * @return array 
  443. */ 
  444. public function controls() { 
  445. return $this->controls; 
  446.  
  447. /** 
  448. * Get the registered containers. 
  449. * @since 4.0.0 
  450. * @return array 
  451. */ 
  452. public function containers() { 
  453. return $this->containers; 
  454.  
  455. /** 
  456. * Get the registered sections. 
  457. * @since 3.4.0 
  458. * @return array 
  459. */ 
  460. public function sections() { 
  461. return $this->sections; 
  462.  
  463. /** 
  464. * Get the registered panels. 
  465. * @since 4.0.0 
  466. * @access public 
  467. * @return array Panels. 
  468. */ 
  469. public function panels() { 
  470. return $this->panels; 
  471.  
  472. /** 
  473. * Checks if the current theme is active. 
  474. * @since 3.4.0 
  475. * @return bool 
  476. */ 
  477. public function is_theme_active() { 
  478. return $this->get_stylesheet() == $this->original_stylesheet; 
  479.  
  480. /** 
  481. * Register styles/scripts and initialize the preview of each setting 
  482. * @since 3.4.0 
  483. */ 
  484. public function wp_loaded() { 
  485.  
  486. /** 
  487. * Fires once WordPress has loaded, allowing scripts and styles to be initialized. 
  488. * @since 3.4.0 
  489. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  490. */ 
  491. do_action( 'customize_register', $this ); 
  492.  
  493. if ( $this->is_preview() && ! is_admin() ) 
  494. $this->customize_preview_init(); 
  495.  
  496. /** 
  497. * Prevents Ajax requests from following redirects when previewing a theme 
  498. * by issuing a 200 response instead of a 30x. 
  499. * Instead, the JS will sniff out the location header. 
  500. * @since 3.4.0 
  501. * @param $status 
  502. * @return int 
  503. */ 
  504. public function wp_redirect_status( $status ) { 
  505. if ( $this->is_preview() && ! is_admin() ) 
  506. return 200; 
  507.  
  508. return $status; 
  509.  
  510. /** 
  511. * Parse the incoming $_POST['customized'] JSON data and store the unsanitized 
  512. * settings for subsequent post_value() lookups. 
  513. * @since 4.1.1 
  514. * @return array 
  515. */ 
  516. public function unsanitized_post_values() { 
  517. if ( ! isset( $this->_post_values ) ) { 
  518. if ( isset( $_POST['customized'] ) ) { 
  519. $this->_post_values = json_decode( wp_unslash( $_POST['customized'] ), true ); 
  520. if ( empty( $this->_post_values ) ) { // if not isset or if JSON error 
  521. $this->_post_values = array(); 
  522. if ( empty( $this->_post_values ) ) { 
  523. return array(); 
  524. } else { 
  525. return $this->_post_values; 
  526.  
  527. /** 
  528. * Returns the sanitized value for a given setting from the request's POST data. 
  529. * @since 3.4.0 
  530. * @since 4.1.1 Introduced the `$default` parameter. 
  531. * @since 4.6.0 `$default` is now returned early when the setting post value is invalid. 
  532. * @access public 
  533. * @see WP_REST_Server::dispatch() 
  534. * @see WP_Rest_Request::sanitize_params() 
  535. * @see WP_Rest_Request::has_valid_params() 
  536. * @param WP_Customize_Setting $setting A WP_Customize_Setting derived object. 
  537. * @param mixed $default Value returned $setting has no post value (added in 4.2.0) 
  538. * or the post value is invalid (added in 4.6.0). 
  539. * @return string|mixed $post_value Sanitized value or the $default provided. 
  540. */ 
  541. public function post_value( $setting, $default = null ) { 
  542. $post_values = $this->unsanitized_post_values(); 
  543. if ( ! array_key_exists( $setting->id, $post_values ) ) { 
  544. return $default; 
  545. $value = $post_values[ $setting->id ]; 
  546. $valid = $setting->validate( $value ); 
  547. if ( is_wp_error( $valid ) ) { 
  548. return $default; 
  549. $value = $setting->sanitize( $value ); 
  550. if ( is_null( $value ) || is_wp_error( $value ) ) { 
  551. return $default; 
  552. return $value; 
  553.  
  554. /** 
  555. * Override a setting's (unsanitized) value as found in any incoming $_POST['customized']. 
  556. * @since 4.2.0 
  557. * @access public 
  558. * @param string $setting_id ID for the WP_Customize_Setting instance. 
  559. * @param mixed $value Post value. 
  560. */ 
  561. public function set_post_value( $setting_id, $value ) { 
  562. $this->unsanitized_post_values(); 
  563. $this->_post_values[ $setting_id ] = $value; 
  564.  
  565. /** 
  566. * Announce when a specific setting's unsanitized post value has been set. 
  567. * Fires when the WP_Customize_Manager::set_post_value() method is called. 
  568. * The dynamic portion of the hook name, `$setting_id`, refers to the setting ID. 
  569. * @since 4.4.0 
  570. * @param mixed $value Unsanitized setting post value. 
  571. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  572. */ 
  573. do_action( "customize_post_value_set_{$setting_id}", $value, $this ); 
  574.  
  575. /** 
  576. * Announce when any setting's unsanitized post value has been set. 
  577. * Fires when the WP_Customize_Manager::set_post_value() method is called. 
  578. * This is useful for `WP_Customize_Setting` instances to watch 
  579. * in order to update a cached previewed value. 
  580. * @since 4.4.0 
  581. * @param string $setting_id Setting ID. 
  582. * @param mixed $value Unsanitized setting post value. 
  583. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  584. */ 
  585. do_action( 'customize_post_value_set', $setting_id, $value, $this ); 
  586.  
  587. /** 
  588. * Print JavaScript settings. 
  589. * @since 3.4.0 
  590. */ 
  591. public function customize_preview_init() { 
  592. $this->nonce_tick = check_ajax_referer( 'preview-customize_' . $this->get_stylesheet(), 'nonce' ); 
  593.  
  594. $this->prepare_controls(); 
  595.  
  596. wp_enqueue_script( 'customize-preview' ); 
  597. add_action( 'wp', array( $this, 'customize_preview_override_404_status' ) ); 
  598. add_action( 'wp_head', array( $this, 'customize_preview_base' ) ); 
  599. add_action( 'wp_head', array( $this, 'customize_preview_html5' ) ); 
  600. add_action( 'wp_head', array( $this, 'customize_preview_loading_style' ) ); 
  601. add_action( 'wp_footer', array( $this, 'customize_preview_settings' ), 20 ); 
  602. add_action( 'shutdown', array( $this, 'customize_preview_signature' ), 1000 ); 
  603. add_filter( 'wp_die_handler', array( $this, 'remove_preview_signature' ) ); 
  604.  
  605. foreach ( $this->settings as $setting ) { 
  606. $setting->preview(); 
  607.  
  608. /** 
  609. * Fires once the Customizer preview has initialized and JavaScript 
  610. * settings have been printed. 
  611. * @since 3.4.0 
  612. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  613. */ 
  614. do_action( 'customize_preview_init', $this ); 
  615.  
  616. /** 
  617. * Prevent sending a 404 status when returning the response for the customize 
  618. * preview, since it causes the jQuery Ajax to fail. Send 200 instead. 
  619. * @since 4.0.0 
  620. * @access public 
  621. */ 
  622. public function customize_preview_override_404_status() { 
  623. if ( is_404() ) { 
  624. status_header( 200 ); 
  625.  
  626. /** 
  627. * Print base element for preview frame. 
  628. * @since 3.4.0 
  629. */ 
  630. public function customize_preview_base() { 
  631. ?><base href="<?php echo home_url( '/' ); ?>" /><?php 
  632.  
  633. /** 
  634. * Print a workaround to handle HTML5 tags in IE < 9. 
  635. * @since 3.4.0 
  636. */ 
  637. public function customize_preview_html5() { ?> 
  638. <!--[if lt IE 9]> 
  639. <script type="text/javascript"> 
  640. var e = [ 'abbr', 'article', 'aside', 'audio', 'canvas', 'datalist', 'details',  
  641. 'figure', 'footer', 'header', 'hgroup', 'mark', 'menu', 'meter', 'nav',  
  642. 'output', 'progress', 'section', 'time', 'video' ]; 
  643. for ( var i = 0; i < e.length; i++ ) { 
  644. document.createElement( e[i] ); 
  645. </script> 
  646. <![endif]--><?php 
  647.  
  648. /** 
  649. * Print CSS for loading indicators for the Customizer preview. 
  650. * @since 4.2.0 
  651. * @access public 
  652. */ 
  653. public function customize_preview_loading_style() { 
  654. ?><style> 
  655. body.wp-customizer-unloading { 
  656. opacity: 0.25; 
  657. cursor: progress !important; 
  658. -webkit-transition: opacity 0.5s; 
  659. transition: opacity 0.5s; 
  660. body.wp-customizer-unloading * { 
  661. pointer-events: none !important; 
  662. </style><?php 
  663.  
  664. /** 
  665. * Print JavaScript settings for preview frame. 
  666. * @since 3.4.0 
  667. */ 
  668. public function customize_preview_settings() { 
  669. $setting_validities = $this->validate_setting_values( $this->unsanitized_post_values() ); 
  670. $exported_setting_validities = array_map( array( $this, 'prepare_setting_validity_for_js' ), $setting_validities ); 
  671.  
  672. $settings = array( 
  673. 'theme' => array( 
  674. 'stylesheet' => $this->get_stylesheet(),  
  675. 'active' => $this->is_theme_active(),  
  676. ),  
  677. 'url' => array( 
  678. 'self' => empty( $_SERVER['REQUEST_URI'] ) ? home_url( '/' ) : esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ),  
  679. ),  
  680. 'channel' => wp_unslash( $_POST['customize_messenger_channel'] ),  
  681. 'activePanels' => array(),  
  682. 'activeSections' => array(),  
  683. 'activeControls' => array(),  
  684. 'settingValidities' => $exported_setting_validities,  
  685. 'nonce' => $this->get_nonces(),  
  686. 'l10n' => array( 
  687. 'shiftClickToEdit' => __( 'Shift-click to edit this element.' ),  
  688. ),  
  689. '_dirty' => array_keys( $this->unsanitized_post_values() ),  
  690. ); 
  691.  
  692. foreach ( $this->panels as $panel_id => $panel ) { 
  693. if ( $panel->check_capabilities() ) { 
  694. $settings['activePanels'][ $panel_id ] = $panel->active(); 
  695. foreach ( $panel->sections as $section_id => $section ) { 
  696. if ( $section->check_capabilities() ) { 
  697. $settings['activeSections'][ $section_id ] = $section->active(); 
  698. foreach ( $this->sections as $id => $section ) { 
  699. if ( $section->check_capabilities() ) { 
  700. $settings['activeSections'][ $id ] = $section->active(); 
  701. foreach ( $this->controls as $id => $control ) { 
  702. if ( $control->check_capabilities() ) { 
  703. $settings['activeControls'][ $id ] = $control->active(); 
  704.  
  705. ?> 
  706. <script type="text/javascript"> 
  707. var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>; 
  708. _wpCustomizeSettings.values = {}; 
  709. (function( v ) { 
  710. <?php 
  711. /** 
  712. * Serialize settings separately from the initial _wpCustomizeSettings 
  713. * serialization in order to avoid a peak memory usage spike. 
  714. * @todo We may not even need to export the values at all since the pane syncs them anyway. 
  715. */ 
  716. foreach ( $this->settings as $id => $setting ) { 
  717. if ( $setting->check_capabilities() ) { 
  718. printf( 
  719. "v[%s] = %s;\n",  
  720. wp_json_encode( $id ),  
  721. wp_json_encode( $setting->js_value() ) 
  722. ); 
  723. ?> 
  724. })( _wpCustomizeSettings.values ); 
  725. </script> 
  726. <?php 
  727.  
  728. /** 
  729. * Prints a signature so we can ensure the Customizer was properly executed. 
  730. * @since 3.4.0 
  731. */ 
  732. public function customize_preview_signature() { 
  733. echo 'WP_CUSTOMIZER_SIGNATURE'; 
  734.  
  735. /** 
  736. * Removes the signature in case we experience a case where the Customizer was not properly executed. 
  737. * @since 3.4.0 
  738. * @param mixed $return Value passed through for {@see 'wp_die_handler'} filter. 
  739. * @return mixed Value passed through for {@see 'wp_die_handler'} filter. 
  740. */ 
  741. public function remove_preview_signature( $return = null ) { 
  742. remove_action( 'shutdown', array( $this, 'customize_preview_signature' ), 1000 ); 
  743.  
  744. return $return; 
  745.  
  746. /** 
  747. * Is it a theme preview? 
  748. * @since 3.4.0 
  749. * @return bool True if it's a preview, false if not. 
  750. */ 
  751. public function is_preview() { 
  752. return (bool) $this->previewing; 
  753.  
  754. /** 
  755. * Retrieve the template name of the previewed theme. 
  756. * @since 3.4.0 
  757. * @return string Template name. 
  758. */ 
  759. public function get_template() { 
  760. return $this->theme()->get_template(); 
  761.  
  762. /** 
  763. * Retrieve the stylesheet name of the previewed theme. 
  764. * @since 3.4.0 
  765. * @return string Stylesheet name. 
  766. */ 
  767. public function get_stylesheet() { 
  768. return $this->theme()->get_stylesheet(); 
  769.  
  770. /** 
  771. * Retrieve the template root of the previewed theme. 
  772. * @since 3.4.0 
  773. * @return string Theme root. 
  774. */ 
  775. public function get_template_root() { 
  776. return get_raw_theme_root( $this->get_template(), true ); 
  777.  
  778. /** 
  779. * Retrieve the stylesheet root of the previewed theme. 
  780. * @since 3.4.0 
  781. * @return string Theme root. 
  782. */ 
  783. public function get_stylesheet_root() { 
  784. return get_raw_theme_root( $this->get_stylesheet(), true ); 
  785.  
  786. /** 
  787. * Filters the current theme and return the name of the previewed theme. 
  788. * @since 3.4.0 
  789. * @param $current_theme {@internal Parameter is not used} 
  790. * @return string Theme name. 
  791. */ 
  792. public function current_theme( $current_theme ) { 
  793. return $this->theme()->display('Name'); 
  794.  
  795. /** 
  796. * Validates setting values. 
  797. * Sanitization is applied to the values before being passed for validation. 
  798. * Validation is skipped for unregistered settings or for values that are 
  799. * already null since they will be skipped anyway. 
  800. * @since 4.6.0 
  801. * @access public 
  802. * @see WP_REST_Request::has_valid_params() 
  803. * @see WP_Customize_Setting::validate() 
  804. * @param array $setting_values Mapping of setting IDs to values to sanitize and validate. 
  805. * @return array Mapping of setting IDs to return value of validate method calls, either `true` or `WP_Error`. 
  806. */ 
  807. public function validate_setting_values( $setting_values ) { 
  808. $validities = array(); 
  809. foreach ( $setting_values as $setting_id => $unsanitized_value ) { 
  810. $setting = $this->get_setting( $setting_id ); 
  811. if ( ! $setting || is_null( $unsanitized_value ) ) { 
  812. continue; 
  813. $validity = $setting->validate( $unsanitized_value ); 
  814. if ( ! is_wp_error( $validity ) ) { 
  815. $value = $setting->sanitize( $unsanitized_value ); 
  816. if ( is_null( $value ) ) { 
  817. $validity = false; 
  818. } elseif ( is_wp_error( $value ) ) { 
  819. $validity = $value; 
  820. if ( false === $validity ) { 
  821. $validity = new WP_Error( 'invalid_value', __( 'Invalid value.' ) ); 
  822. $validities[ $setting_id ] = $validity; 
  823. return $validities; 
  824.  
  825. /** 
  826. * Prepares setting validity for exporting to the client (JS). 
  827. * Converts `WP_Error` instance into array suitable for passing into the 
  828. * `wp.customize.Notification` JS model. 
  829. * @since 4.6.0 
  830. * @access public 
  831. * @param true|WP_Error $validity Setting validity. 
  832. * @return true|array If `$validity` was a WP_Error, the error codes will be array-mapped 
  833. * to their respective `message` and `data` to pass into the 
  834. * `wp.customize.Notification` JS model. 
  835. */ 
  836. public function prepare_setting_validity_for_js( $validity ) { 
  837. if ( is_wp_error( $validity ) ) { 
  838. $notification = array(); 
  839. foreach ( $validity->errors as $error_code => $error_messages ) { 
  840. $error_data = $validity->get_error_data( $error_code ); 
  841. if ( is_null( $error_data ) ) { 
  842. $error_data = array(); 
  843. $error_data = array_merge( 
  844. $error_data,  
  845. array( 'from_server' => true ) 
  846. ); 
  847. $notification[ $error_code ] = array( 
  848. 'message' => join( ' ', $error_messages ),  
  849. 'data' => $error_data,  
  850. ); 
  851. return $notification; 
  852. } else { 
  853. return true; 
  854.  
  855. /** 
  856. * Switch the theme and trigger the save() method on each setting. 
  857. * @since 3.4.0 
  858. */ 
  859. public function save() { 
  860. if ( ! $this->is_preview() ) { 
  861. wp_send_json_error( 'not_preview' ); 
  862.  
  863. $action = 'save-customize_' . $this->get_stylesheet(); 
  864. if ( ! check_ajax_referer( $action, 'nonce', false ) ) { 
  865. wp_send_json_error( 'invalid_nonce' ); 
  866.  
  867. /** 
  868. * Fires before save validation happens. 
  869. * Plugins can add just-in-time {@see 'customize_validate_{$this->ID}'} filters 
  870. * at this point to catch any settings registered after `customize_register`. 
  871. * The dynamic portion of the hook name, `$this->ID` refers to the setting ID. 
  872. * @since 4.6.0 
  873. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  874. */ 
  875. do_action( 'customize_save_validation_before', $this ); 
  876.  
  877. // Validate settings. 
  878. $setting_validities = $this->validate_setting_values( $this->unsanitized_post_values() ); 
  879. $invalid_setting_count = count( array_filter( $setting_validities, 'is_wp_error' ) ); 
  880. $exported_setting_validities = array_map( array( $this, 'prepare_setting_validity_for_js' ), $setting_validities ); 
  881. if ( $invalid_setting_count > 0 ) { 
  882. $response = array( 
  883. 'setting_validities' => $exported_setting_validities,  
  884. 'message' => sprintf( _n( 'There is %s invalid setting.', 'There are %s invalid settings.', $invalid_setting_count ), number_format_i18n( $invalid_setting_count ) ),  
  885. ); 
  886.  
  887. /** This filter is documented in wp-includes/class-wp-customize-manager.php */ 
  888. $response = apply_filters( 'customize_save_response', $response, $this ); 
  889. wp_send_json_error( $response ); 
  890.  
  891. // Do we have to switch themes? 
  892. if ( ! $this->is_theme_active() ) { 
  893. // Temporarily stop previewing the theme to allow switch_themes() 
  894. // to operate properly. 
  895. $this->stop_previewing_theme(); 
  896. switch_theme( $this->get_stylesheet() ); 
  897. update_option( 'theme_switched_via_customizer', true ); 
  898. $this->start_previewing_theme(); 
  899.  
  900. /** 
  901. * Fires once the theme has switched in the Customizer, but before settings 
  902. * have been saved. 
  903. * @since 3.4.0 
  904. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  905. */ 
  906. do_action( 'customize_save', $this ); 
  907.  
  908. foreach ( $this->settings as $setting ) { 
  909. $setting->save(); 
  910.  
  911. /** 
  912. * Fires after Customize settings have been saved. 
  913. * @since 3.6.0 
  914. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  915. */ 
  916. do_action( 'customize_save_after', $this ); 
  917.  
  918. $data = array( 
  919. 'setting_validities' => $exported_setting_validities,  
  920. ); 
  921.  
  922. /** 
  923. * Filters response data for a successful customize_save Ajax request. 
  924. * This filter does not apply if there was a nonce or authentication failure. 
  925. * @since 4.2.0 
  926. * @param array $data Additional information passed back to the 'saved' 
  927. * event on `wp.customize`. 
  928. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  929. */ 
  930. $response = apply_filters( 'customize_save_response', $data, $this ); 
  931. wp_send_json_success( $response ); 
  932.  
  933. /** 
  934. * Refresh nonces for the current preview. 
  935. * @since 4.2.0 
  936. */ 
  937. public function refresh_nonces() { 
  938. if ( ! $this->is_preview() ) { 
  939. wp_send_json_error( 'not_preview' ); 
  940.  
  941. wp_send_json_success( $this->get_nonces() ); 
  942.  
  943. /** 
  944. * Add a customize setting. 
  945. * @since 3.4.0 
  946. * @since 4.5.0 Return added WP_Customize_Setting instance. 
  947. * @access public 
  948. * @param WP_Customize_Setting|string $id Customize Setting object, or ID. 
  949. * @param array $args Setting arguments; passed to WP_Customize_Setting 
  950. * constructor. 
  951. * @return WP_Customize_Setting The instance of the setting that was added. 
  952. */ 
  953. public function add_setting( $id, $args = array() ) { 
  954. if ( $id instanceof WP_Customize_Setting ) { 
  955. $setting = $id; 
  956. } else { 
  957. $class = 'WP_Customize_Setting'; 
  958.  
  959. /** This filter is documented in wp-includes/class-wp-customize-manager.php */ 
  960. $args = apply_filters( 'customize_dynamic_setting_args', $args, $id ); 
  961.  
  962. /** This filter is documented in wp-includes/class-wp-customize-manager.php */ 
  963. $class = apply_filters( 'customize_dynamic_setting_class', $class, $id, $args ); 
  964.  
  965. $setting = new $class( $this, $id, $args ); 
  966.  
  967. $this->settings[ $setting->id ] = $setting; 
  968. return $setting; 
  969.  
  970. /** 
  971. * Register any dynamically-created settings, such as those from $_POST['customized'] 
  972. * that have no corresponding setting created. 
  973. * This is a mechanism to "wake up" settings that have been dynamically created 
  974. * on the front end and have been sent to WordPress in `$_POST['customized']`. When WP 
  975. * loads, the dynamically-created settings then will get created and previewed 
  976. * even though they are not directly created statically with code. 
  977. * @since 4.2.0 
  978. * @access public 
  979. * @param array $setting_ids The setting IDs to add. 
  980. * @return array The WP_Customize_Setting objects added. 
  981. */ 
  982. public function add_dynamic_settings( $setting_ids ) { 
  983. $new_settings = array(); 
  984. foreach ( $setting_ids as $setting_id ) { 
  985. // Skip settings already created 
  986. if ( $this->get_setting( $setting_id ) ) { 
  987. continue; 
  988.  
  989. $setting_args = false; 
  990. $setting_class = 'WP_Customize_Setting'; 
  991.  
  992. /** 
  993. * Filters a dynamic setting's constructor args. 
  994. * For a dynamic setting to be registered, this filter must be employed 
  995. * to override the default false value with an array of args to pass to 
  996. * the WP_Customize_Setting constructor. 
  997. * @since 4.2.0 
  998. * @param false|array $setting_args The arguments to the WP_Customize_Setting constructor. 
  999. * @param string $setting_id ID for dynamic setting, usually coming from `$_POST['customized']`. 
  1000. */ 
  1001. $setting_args = apply_filters( 'customize_dynamic_setting_args', $setting_args, $setting_id ); 
  1002. if ( false === $setting_args ) { 
  1003. continue; 
  1004.  
  1005. /** 
  1006. * Allow non-statically created settings to be constructed with custom WP_Customize_Setting subclass. 
  1007. * @since 4.2.0 
  1008. * @param string $setting_class WP_Customize_Setting or a subclass. 
  1009. * @param string $setting_id ID for dynamic setting, usually coming from `$_POST['customized']`. 
  1010. * @param array $setting_args WP_Customize_Setting or a subclass. 
  1011. */ 
  1012. $setting_class = apply_filters( 'customize_dynamic_setting_class', $setting_class, $setting_id, $setting_args ); 
  1013.  
  1014. $setting = new $setting_class( $this, $setting_id, $setting_args ); 
  1015.  
  1016. $this->add_setting( $setting ); 
  1017. $new_settings[] = $setting; 
  1018. return $new_settings; 
  1019.  
  1020. /** 
  1021. * Retrieve a customize setting. 
  1022. * @since 3.4.0 
  1023. * @param string $id Customize Setting ID. 
  1024. * @return WP_Customize_Setting|void The setting, if set. 
  1025. */ 
  1026. public function get_setting( $id ) { 
  1027. if ( isset( $this->settings[ $id ] ) ) { 
  1028. return $this->settings[ $id ]; 
  1029.  
  1030. /** 
  1031. * Remove a customize setting. 
  1032. * @since 3.4.0 
  1033. * @param string $id Customize Setting ID. 
  1034. */ 
  1035. public function remove_setting( $id ) { 
  1036. unset( $this->settings[ $id ] ); 
  1037.  
  1038. /** 
  1039. * Add a customize panel. 
  1040. * @since 4.0.0 
  1041. * @since 4.5.0 Return added WP_Customize_Panel instance. 
  1042. * @access public 
  1043. * @param WP_Customize_Panel|string $id Customize Panel object, or Panel ID. 
  1044. * @param array $args Optional. Panel arguments. Default empty array. 
  1045. * @return WP_Customize_Panel The instance of the panel that was added. 
  1046. */ 
  1047. public function add_panel( $id, $args = array() ) { 
  1048. if ( $id instanceof WP_Customize_Panel ) { 
  1049. $panel = $id; 
  1050. } else { 
  1051. $panel = new WP_Customize_Panel( $this, $id, $args ); 
  1052.  
  1053. $this->panels[ $panel->id ] = $panel; 
  1054. return $panel; 
  1055.  
  1056. /** 
  1057. * Retrieve a customize panel. 
  1058. * @since 4.0.0 
  1059. * @access public 
  1060. * @param string $id Panel ID to get. 
  1061. * @return WP_Customize_Panel|void Requested panel instance, if set. 
  1062. */ 
  1063. public function get_panel( $id ) { 
  1064. if ( isset( $this->panels[ $id ] ) ) { 
  1065. return $this->panels[ $id ]; 
  1066.  
  1067. /** 
  1068. * Remove a customize panel. 
  1069. * @since 4.0.0 
  1070. * @access public 
  1071. * @param string $id Panel ID to remove. 
  1072. */ 
  1073. public function remove_panel( $id ) { 
  1074. // Removing core components this way is _doing_it_wrong(). 
  1075. if ( in_array( $id, $this->components, true ) ) { 
  1076. /** translators: 1: panel id, 2: link to 'customize_loaded_components' filter reference */ 
  1077. $message = sprintf( __( 'Removing %1$s manually will cause PHP warnings. Use the %2$s filter instead.' ),  
  1078. $id,  
  1079. '<a href="' . esc_url( 'https://developer.wordpress.org/reference/hooks/customize_loaded_components/' ) . '"><code>customize_loaded_components</code></a>' 
  1080. ); 
  1081.  
  1082. _doing_it_wrong( __METHOD__, $message, '4.5.0' ); 
  1083. unset( $this->panels[ $id ] ); 
  1084.  
  1085. /** 
  1086. * Register a customize panel type. 
  1087. * Registered types are eligible to be rendered via JS and created dynamically. 
  1088. * @since 4.3.0 
  1089. * @access public 
  1090. * @see WP_Customize_Panel 
  1091. * @param string $panel Name of a custom panel which is a subclass of WP_Customize_Panel. 
  1092. */ 
  1093. public function register_panel_type( $panel ) { 
  1094. $this->registered_panel_types[] = $panel; 
  1095.  
  1096. /** 
  1097. * Render JS templates for all registered panel types. 
  1098. * @since 4.3.0 
  1099. * @access public 
  1100. */ 
  1101. public function render_panel_templates() { 
  1102. foreach ( $this->registered_panel_types as $panel_type ) { 
  1103. $panel = new $panel_type( $this, 'temp', array() ); 
  1104. $panel->print_template(); 
  1105.  
  1106. /** 
  1107. * Add a customize section. 
  1108. * @since 3.4.0 
  1109. * @since 4.5.0 Return added WP_Customize_Section instance. 
  1110. * @access public 
  1111. * @param WP_Customize_Section|string $id Customize Section object, or Section ID. 
  1112. * @param array $args Section arguments. 
  1113. * @return WP_Customize_Section The instance of the section that was added. 
  1114. */ 
  1115. public function add_section( $id, $args = array() ) { 
  1116. if ( $id instanceof WP_Customize_Section ) { 
  1117. $section = $id; 
  1118. } else { 
  1119. $section = new WP_Customize_Section( $this, $id, $args ); 
  1120.  
  1121. $this->sections[ $section->id ] = $section; 
  1122. return $section; 
  1123.  
  1124. /** 
  1125. * Retrieve a customize section. 
  1126. * @since 3.4.0 
  1127. * @param string $id Section ID. 
  1128. * @return WP_Customize_Section|void The section, if set. 
  1129. */ 
  1130. public function get_section( $id ) { 
  1131. if ( isset( $this->sections[ $id ] ) ) 
  1132. return $this->sections[ $id ]; 
  1133.  
  1134. /** 
  1135. * Remove a customize section. 
  1136. * @since 3.4.0 
  1137. * @param string $id Section ID. 
  1138. */ 
  1139. public function remove_section( $id ) { 
  1140. unset( $this->sections[ $id ] ); 
  1141.  
  1142. /** 
  1143. * Register a customize section type. 
  1144. * Registered types are eligible to be rendered via JS and created dynamically. 
  1145. * @since 4.3.0 
  1146. * @access public 
  1147. * @see WP_Customize_Section 
  1148. * @param string $section Name of a custom section which is a subclass of WP_Customize_Section. 
  1149. */ 
  1150. public function register_section_type( $section ) { 
  1151. $this->registered_section_types[] = $section; 
  1152.  
  1153. /** 
  1154. * Render JS templates for all registered section types. 
  1155. * @since 4.3.0 
  1156. * @access public 
  1157. */ 
  1158. public function render_section_templates() { 
  1159. foreach ( $this->registered_section_types as $section_type ) { 
  1160. $section = new $section_type( $this, 'temp', array() ); 
  1161. $section->print_template(); 
  1162.  
  1163. /** 
  1164. * Add a customize control. 
  1165. * @since 3.4.0 
  1166. * @since 4.5.0 Return added WP_Customize_Control instance. 
  1167. * @access public 
  1168. * @param WP_Customize_Control|string $id Customize Control object, or ID. 
  1169. * @param array $args Control arguments; passed to WP_Customize_Control 
  1170. * constructor. 
  1171. * @return WP_Customize_Control The instance of the control that was added. 
  1172. */ 
  1173. public function add_control( $id, $args = array() ) { 
  1174. if ( $id instanceof WP_Customize_Control ) { 
  1175. $control = $id; 
  1176. } else { 
  1177. $control = new WP_Customize_Control( $this, $id, $args ); 
  1178.  
  1179. $this->controls[ $control->id ] = $control; 
  1180. return $control; 
  1181.  
  1182. /** 
  1183. * Retrieve a customize control. 
  1184. * @since 3.4.0 
  1185. * @param string $id ID of the control. 
  1186. * @return WP_Customize_Control|void The control object, if set. 
  1187. */ 
  1188. public function get_control( $id ) { 
  1189. if ( isset( $this->controls[ $id ] ) ) 
  1190. return $this->controls[ $id ]; 
  1191.  
  1192. /** 
  1193. * Remove a customize control. 
  1194. * @since 3.4.0 
  1195. * @param string $id ID of the control. 
  1196. */ 
  1197. public function remove_control( $id ) { 
  1198. unset( $this->controls[ $id ] ); 
  1199.  
  1200. /** 
  1201. * Register a customize control type. 
  1202. * Registered types are eligible to be rendered via JS and created dynamically. 
  1203. * @since 4.1.0 
  1204. * @access public 
  1205. * @param string $control Name of a custom control which is a subclass of 
  1206. * WP_Customize_Control. 
  1207. */ 
  1208. public function register_control_type( $control ) { 
  1209. $this->registered_control_types[] = $control; 
  1210.  
  1211. /** 
  1212. * Render JS templates for all registered control types. 
  1213. * @since 4.1.0 
  1214. * @access public 
  1215. */ 
  1216. public function render_control_templates() { 
  1217. foreach ( $this->registered_control_types as $control_type ) { 
  1218. $control = new $control_type( $this, 'temp', array( 
  1219. 'settings' => array(),  
  1220. ) ); 
  1221. $control->print_template(); 
  1222. ?> 
  1223. <script type="text/html" id="tmpl-customize-control-notifications"> 
  1224. <ul> 
  1225. <# _.each( data.notifications, function( notification ) { #> 
  1226. <li class="notice notice-{{ notification.type || 'info' }} {{ data.altNotice ? 'notice-alt' : '' }}" data-code="{{ notification.code }}" data-type="{{ notification.type }}">{{ notification.message || notification.code }}</li> 
  1227. <# } ); #> 
  1228. </ul> 
  1229. </script> 
  1230. <?php 
  1231.  
  1232. /** 
  1233. * Helper function to compare two objects by priority, ensuring sort stability via instance_number. 
  1234. * @since 3.4.0 
  1235. * @param WP_Customize_Panel|WP_Customize_Section|WP_Customize_Control $a Object A. 
  1236. * @param WP_Customize_Panel|WP_Customize_Section|WP_Customize_Control $b Object B. 
  1237. * @return int 
  1238. */ 
  1239. protected function _cmp_priority( $a, $b ) { 
  1240. if ( $a->priority === $b->priority ) { 
  1241. return $a->instance_number - $b->instance_number; 
  1242. } else { 
  1243. return $a->priority - $b->priority; 
  1244.  
  1245. /** 
  1246. * Prepare panels, sections, and controls. 
  1247. * For each, check if required related components exist,  
  1248. * whether the user has the necessary capabilities,  
  1249. * and sort by priority. 
  1250. * @since 3.4.0 
  1251. */ 
  1252. public function prepare_controls() { 
  1253.  
  1254. $controls = array(); 
  1255. uasort( $this->controls, array( $this, '_cmp_priority' ) ); 
  1256.  
  1257. foreach ( $this->controls as $id => $control ) { 
  1258. if ( ! isset( $this->sections[ $control->section ] ) || ! $control->check_capabilities() ) { 
  1259. continue; 
  1260.  
  1261. $this->sections[ $control->section ]->controls[] = $control; 
  1262. $controls[ $id ] = $control; 
  1263. $this->controls = $controls; 
  1264.  
  1265. // Prepare sections. 
  1266. uasort( $this->sections, array( $this, '_cmp_priority' ) ); 
  1267. $sections = array(); 
  1268.  
  1269. foreach ( $this->sections as $section ) { 
  1270. if ( ! $section->check_capabilities() ) { 
  1271. continue; 
  1272.  
  1273. usort( $section->controls, array( $this, '_cmp_priority' ) ); 
  1274.  
  1275. if ( ! $section->panel ) { 
  1276. // Top-level section. 
  1277. $sections[ $section->id ] = $section; 
  1278. } else { 
  1279. // This section belongs to a panel. 
  1280. if ( isset( $this->panels [ $section->panel ] ) ) { 
  1281. $this->panels[ $section->panel ]->sections[ $section->id ] = $section; 
  1282. $this->sections = $sections; 
  1283.  
  1284. // Prepare panels. 
  1285. uasort( $this->panels, array( $this, '_cmp_priority' ) ); 
  1286. $panels = array(); 
  1287.  
  1288. foreach ( $this->panels as $panel ) { 
  1289. if ( ! $panel->check_capabilities() ) { 
  1290. continue; 
  1291.  
  1292. uasort( $panel->sections, array( $this, '_cmp_priority' ) ); 
  1293. $panels[ $panel->id ] = $panel; 
  1294. $this->panels = $panels; 
  1295.  
  1296. // Sort panels and top-level sections together. 
  1297. $this->containers = array_merge( $this->panels, $this->sections ); 
  1298. uasort( $this->containers, array( $this, '_cmp_priority' ) ); 
  1299.  
  1300. /** 
  1301. * Enqueue scripts for customize controls. 
  1302. * @since 3.4.0 
  1303. */ 
  1304. public function enqueue_control_scripts() { 
  1305. foreach ( $this->controls as $control ) { 
  1306. $control->enqueue(); 
  1307.  
  1308. /** 
  1309. * Determine whether the user agent is iOS. 
  1310. * @since 4.4.0 
  1311. * @access public 
  1312. * @return bool Whether the user agent is iOS. 
  1313. */ 
  1314. public function is_ios() { 
  1315. return wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] ); 
  1316.  
  1317. /** 
  1318. * Get the template string for the Customizer pane document title. 
  1319. * @since 4.4.0 
  1320. * @access public 
  1321. * @return string The template string for the document title. 
  1322. */ 
  1323. public function get_document_title_template() { 
  1324. if ( $this->is_theme_active() ) { 
  1325. /** translators: %s: document title from the preview */ 
  1326. $document_title_tmpl = __( 'Customize: %s' ); 
  1327. } else { 
  1328. /** translators: %s: document title from the preview */ 
  1329. $document_title_tmpl = __( 'Live Preview: %s' ); 
  1330. $document_title_tmpl = html_entity_decode( $document_title_tmpl, ENT_QUOTES, 'UTF-8' ); // Because exported to JS and assigned to document.title. 
  1331. return $document_title_tmpl; 
  1332.  
  1333. /** 
  1334. * Set the initial URL to be previewed. 
  1335. * URL is validated. 
  1336. * @since 4.4.0 
  1337. * @access public 
  1338. * @param string $preview_url URL to be previewed. 
  1339. */ 
  1340. public function set_preview_url( $preview_url ) { 
  1341. $preview_url = esc_url_raw( $preview_url ); 
  1342. $this->preview_url = wp_validate_redirect( $preview_url, home_url( '/' ) ); 
  1343.  
  1344. /** 
  1345. * Get the initial URL to be previewed. 
  1346. * @since 4.4.0 
  1347. * @access public 
  1348. * @return string URL being previewed. 
  1349. */ 
  1350. public function get_preview_url() { 
  1351. if ( empty( $this->preview_url ) ) { 
  1352. $preview_url = home_url( '/' ); 
  1353. } else { 
  1354. $preview_url = $this->preview_url; 
  1355. return $preview_url; 
  1356.  
  1357. /** 
  1358. * Set URL to link the user to when closing the Customizer. 
  1359. * URL is validated. 
  1360. * @since 4.4.0 
  1361. * @access public 
  1362. * @param string $return_url URL for return link. 
  1363. */ 
  1364. public function set_return_url( $return_url ) { 
  1365. $return_url = esc_url_raw( $return_url ); 
  1366. $return_url = remove_query_arg( wp_removable_query_args(), $return_url ); 
  1367. $return_url = wp_validate_redirect( $return_url ); 
  1368. $this->return_url = $return_url; 
  1369.  
  1370. /** 
  1371. * Get URL to link the user to when closing the Customizer. 
  1372. * @since 4.4.0 
  1373. * @access public 
  1374. * @return string URL for link to close Customizer. 
  1375. */ 
  1376. public function get_return_url() { 
  1377. $referer = wp_get_referer(); 
  1378. $excluded_referer_basenames = array( 'customize.php', 'wp-login.php' ); 
  1379.  
  1380. if ( $this->return_url ) { 
  1381. $return_url = $this->return_url; 
  1382. } else if ( $referer && ! in_array( basename( parse_url( $referer, PHP_URL_PATH ) ), $excluded_referer_basenames, true ) ) { 
  1383. $return_url = $referer; 
  1384. } else if ( $this->preview_url ) { 
  1385. $return_url = $this->preview_url; 
  1386. } else { 
  1387. $return_url = home_url( '/' ); 
  1388. return $return_url; 
  1389.  
  1390. /** 
  1391. * Set the autofocused constructs. 
  1392. * @since 4.4.0 
  1393. * @access public 
  1394. * @param array $autofocus { 
  1395. * Mapping of 'panel', 'section', 'control' to the ID which should be autofocused. 
  1396. * @type string [$control] ID for control to be autofocused. 
  1397. * @type string [$section] ID for section to be autofocused. 
  1398. * @type string [$panel] ID for panel to be autofocused. 
  1399. * } 
  1400. */ 
  1401. public function set_autofocus( $autofocus ) { 
  1402. $this->autofocus = array_filter( wp_array_slice_assoc( $autofocus, array( 'panel', 'section', 'control' ) ), 'is_string' ); 
  1403.  
  1404. /** 
  1405. * Get the autofocused constructs. 
  1406. * @since 4.4.0 
  1407. * @access public 
  1408. * @return array { 
  1409. * Mapping of 'panel', 'section', 'control' to the ID which should be autofocused. 
  1410. * @type string [$control] ID for control to be autofocused. 
  1411. * @type string [$section] ID for section to be autofocused. 
  1412. * @type string [$panel] ID for panel to be autofocused. 
  1413. * } 
  1414. */ 
  1415. public function get_autofocus() { 
  1416. return $this->autofocus; 
  1417.  
  1418. /** 
  1419. * Get nonces for the Customizer. 
  1420. * @since 4.5.0 
  1421. * @return array Nonces. 
  1422. */ 
  1423. public function get_nonces() { 
  1424. $nonces = array( 
  1425. 'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ),  
  1426. 'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ),  
  1427. ); 
  1428.  
  1429. /** 
  1430. * Filters nonces for Customizer. 
  1431. * @since 4.2.0 
  1432. * @param array $nonces Array of refreshed nonces for save and 
  1433. * preview actions. 
  1434. * @param WP_Customize_Manager $this WP_Customize_Manager instance. 
  1435. */ 
  1436. $nonces = apply_filters( 'customize_refresh_nonces', $nonces, $this ); 
  1437.  
  1438. return $nonces; 
  1439.  
  1440. /** 
  1441. * Print JavaScript settings for parent window. 
  1442. * @since 4.4.0 
  1443. */ 
  1444. public function customize_pane_settings() { 
  1445. /** 
  1446. * If the front end and the admin are served from the same domain, load the 
  1447. * preview over ssl if the Customizer is being loaded over ssl. This avoids 
  1448. * insecure content warnings. This is not attempted if the admin and front end 
  1449. * are on different domains to avoid the case where the front end doesn't have 
  1450. * ssl certs. Domain mapping plugins can allow other urls in these conditions 
  1451. * using the customize_allowed_urls filter. 
  1452. */ 
  1453.  
  1454. $allowed_urls = array( home_url( '/' ) ); 
  1455. $admin_origin = parse_url( admin_url() ); 
  1456. $home_origin = parse_url( home_url() ); 
  1457. $cross_domain = ( strtolower( $admin_origin['host'] ) !== strtolower( $home_origin['host'] ) ); 
  1458.  
  1459. if ( is_ssl() && ! $cross_domain ) { 
  1460. $allowed_urls[] = home_url( '/', 'https' ); 
  1461.  
  1462. /** 
  1463. * Filters the list of URLs allowed to be clicked and followed in the Customizer preview. 
  1464. * @since 3.4.0 
  1465. * @param array $allowed_urls An array of allowed URLs. 
  1466. */ 
  1467. $allowed_urls = array_unique( apply_filters( 'customize_allowed_urls', $allowed_urls ) ); 
  1468.  
  1469. $login_url = add_query_arg( array( 
  1470. 'interim-login' => 1,  
  1471. 'customize-login' => 1,  
  1472. ), wp_login_url() ); 
  1473.  
  1474. // Prepare Customizer settings to pass to JavaScript. 
  1475. $settings = array( 
  1476. 'theme' => array( 
  1477. 'stylesheet' => $this->get_stylesheet(),  
  1478. 'active' => $this->is_theme_active(),  
  1479. ),  
  1480. 'url' => array( 
  1481. 'preview' => esc_url_raw( $this->get_preview_url() ),  
  1482. 'parent' => esc_url_raw( admin_url() ),  
  1483. 'activated' => esc_url_raw( home_url( '/' ) ),  
  1484. 'ajax' => esc_url_raw( admin_url( 'admin-ajax.php', 'relative' ) ),  
  1485. 'allowed' => array_map( 'esc_url_raw', $allowed_urls ),  
  1486. 'isCrossDomain' => $cross_domain,  
  1487. 'home' => esc_url_raw( home_url( '/' ) ),  
  1488. 'login' => esc_url_raw( $login_url ),  
  1489. ),  
  1490. 'browser' => array( 
  1491. 'mobile' => wp_is_mobile(),  
  1492. 'ios' => $this->is_ios(),  
  1493. ),  
  1494. 'panels' => array(),  
  1495. 'sections' => array(),  
  1496. 'nonce' => $this->get_nonces(),  
  1497. 'autofocus' => $this->get_autofocus(),  
  1498. 'documentTitleTmpl' => $this->get_document_title_template(),  
  1499. 'previewableDevices' => $this->get_previewable_devices(),  
  1500. ); 
  1501.  
  1502. // Prepare Customize Section objects to pass to JavaScript. 
  1503. foreach ( $this->sections() as $id => $section ) { 
  1504. if ( $section->check_capabilities() ) { 
  1505. $settings['sections'][ $id ] = $section->json(); 
  1506.  
  1507. // Prepare Customize Panel objects to pass to JavaScript. 
  1508. foreach ( $this->panels() as $panel_id => $panel ) { 
  1509. if ( $panel->check_capabilities() ) { 
  1510. $settings['panels'][ $panel_id ] = $panel->json(); 
  1511. foreach ( $panel->sections as $section_id => $section ) { 
  1512. if ( $section->check_capabilities() ) { 
  1513. $settings['sections'][ $section_id ] = $section->json(); 
  1514.  
  1515. ?> 
  1516. <script type="text/javascript"> 
  1517. var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>; 
  1518. _wpCustomizeSettings.controls = {}; 
  1519. _wpCustomizeSettings.settings = {}; 
  1520. <?php 
  1521.  
  1522. // Serialize settings one by one to improve memory usage. 
  1523. echo "(function ( s ) {\n"; 
  1524. foreach ( $this->settings() as $setting ) { 
  1525. if ( $setting->check_capabilities() ) { 
  1526. printf( 
  1527. "s[%s] = %s;\n",  
  1528. wp_json_encode( $setting->id ),  
  1529. wp_json_encode( $setting->json() ) 
  1530. ); 
  1531. echo "})( _wpCustomizeSettings.settings );\n"; 
  1532.  
  1533. // Serialize controls one by one to improve memory usage. 
  1534. echo "(function ( c ) {\n"; 
  1535. foreach ( $this->controls() as $control ) { 
  1536. if ( $control->check_capabilities() ) { 
  1537. printf( 
  1538. "c[%s] = %s;\n",  
  1539. wp_json_encode( $control->id ),  
  1540. wp_json_encode( $control->json() ) 
  1541. ); 
  1542. echo "})( _wpCustomizeSettings.controls );\n"; 
  1543. ?> 
  1544. </script> 
  1545. <?php 
  1546.  
  1547. /** 
  1548. * Returns a list of devices to allow previewing. 
  1549. * @access public 
  1550. * @since 4.5.0 
  1551. * @return array List of devices with labels and default setting. 
  1552. */ 
  1553. public function get_previewable_devices() { 
  1554. $devices = array( 
  1555. 'desktop' => array( 
  1556. 'label' => __( 'Enter desktop preview mode' ),  
  1557. 'default' => true,  
  1558. ),  
  1559. 'tablet' => array( 
  1560. 'label' => __( 'Enter tablet preview mode' ),  
  1561. ),  
  1562. 'mobile' => array( 
  1563. 'label' => __( 'Enter mobile preview mode' ),  
  1564. ),  
  1565. ); 
  1566.  
  1567. /** 
  1568. * Filters the available devices to allow previewing in the Customizer. 
  1569. * @since 4.5.0 
  1570. * @see WP_Customize_Manager::get_previewable_devices() 
  1571. * @param array $devices List of devices with labels and default setting. 
  1572. */ 
  1573. $devices = apply_filters( 'customize_previewable_devices', $devices ); 
  1574.  
  1575. return $devices; 
  1576.  
  1577. /** 
  1578. * Register some default controls. 
  1579. * @since 3.4.0 
  1580. */ 
  1581. public function register_controls() { 
  1582.  
  1583. /** Panel, Section, and Control Types */ 
  1584. $this->register_panel_type( 'WP_Customize_Panel' ); 
  1585. $this->register_section_type( 'WP_Customize_Section' ); 
  1586. $this->register_section_type( 'WP_Customize_Sidebar_Section' ); 
  1587. $this->register_control_type( 'WP_Customize_Color_Control' ); 
  1588. $this->register_control_type( 'WP_Customize_Media_Control' ); 
  1589. $this->register_control_type( 'WP_Customize_Upload_Control' ); 
  1590. $this->register_control_type( 'WP_Customize_Image_Control' ); 
  1591. $this->register_control_type( 'WP_Customize_Background_Image_Control' ); 
  1592. $this->register_control_type( 'WP_Customize_Cropped_Image_Control' ); 
  1593. $this->register_control_type( 'WP_Customize_Site_Icon_Control' ); 
  1594. $this->register_control_type( 'WP_Customize_Theme_Control' ); 
  1595.  
  1596. /** Themes */ 
  1597.  
  1598. $this->add_section( new WP_Customize_Themes_Section( $this, 'themes', array( 
  1599. 'title' => $this->theme()->display( 'Name' ),  
  1600. 'capability' => 'switch_themes',  
  1601. 'priority' => 0,  
  1602. ) ) ); 
  1603.  
  1604. // Themes Setting (unused - the theme is considerably more fundamental to the Customizer experience). 
  1605. $this->add_setting( new WP_Customize_Filter_Setting( $this, 'active_theme', array( 
  1606. 'capability' => 'switch_themes',  
  1607. ) ) ); 
  1608.  
  1609. require_once( ABSPATH . 'wp-admin/includes/theme.php' ); 
  1610.  
  1611. // Theme Controls. 
  1612.  
  1613. // Add a control for the active/original theme. 
  1614. if ( ! $this->is_theme_active() ) { 
  1615. $themes = wp_prepare_themes_for_js( array( wp_get_theme( $this->original_stylesheet ) ) ); 
  1616. $active_theme = current( $themes ); 
  1617. $active_theme['isActiveTheme'] = true; 
  1618. $this->add_control( new WP_Customize_Theme_Control( $this, $active_theme['id'], array( 
  1619. 'theme' => $active_theme,  
  1620. 'section' => 'themes',  
  1621. 'settings' => 'active_theme',  
  1622. ) ) ); 
  1623.  
  1624. $themes = wp_prepare_themes_for_js(); 
  1625. foreach ( $themes as $theme ) { 
  1626. if ( $theme['active'] || $theme['id'] === $this->original_stylesheet ) { 
  1627. continue; 
  1628.  
  1629. $theme_id = 'theme_' . $theme['id']; 
  1630. $theme['isActiveTheme'] = false; 
  1631. $this->add_control( new WP_Customize_Theme_Control( $this, $theme_id, array( 
  1632. 'theme' => $theme,  
  1633. 'section' => 'themes',  
  1634. 'settings' => 'active_theme',  
  1635. ) ) ); 
  1636.  
  1637. /** Site Identity */ 
  1638.  
  1639. $this->add_section( 'title_tagline', array( 
  1640. 'title' => __( 'Site Identity' ),  
  1641. 'priority' => 20,  
  1642. ) ); 
  1643.  
  1644. $this->add_setting( 'blogname', array( 
  1645. 'default' => get_option( 'blogname' ),  
  1646. 'type' => 'option',  
  1647. 'capability' => 'manage_options',  
  1648. ) ); 
  1649.  
  1650. $this->add_control( 'blogname', array( 
  1651. 'label' => __( 'Site Title' ),  
  1652. 'section' => 'title_tagline',  
  1653. ) ); 
  1654.  
  1655. $this->add_setting( 'blogdescription', array( 
  1656. 'default' => get_option( 'blogdescription' ),  
  1657. 'type' => 'option',  
  1658. 'capability' => 'manage_options',  
  1659. ) ); 
  1660.  
  1661. $this->add_control( 'blogdescription', array( 
  1662. 'label' => __( 'Tagline' ),  
  1663. 'section' => 'title_tagline',  
  1664. ) ); 
  1665.  
  1666. // Add a setting to hide header text if the theme doesn't support custom headers. 
  1667. if ( ! current_theme_supports( 'custom-header', 'header-text' ) ) { 
  1668. $this->add_setting( 'header_text', array( 
  1669. 'theme_supports' => array( 'custom-logo', 'header-text' ),  
  1670. 'default' => 1,  
  1671. 'sanitize_callback' => 'absint',  
  1672. ) ); 
  1673.  
  1674. $this->add_control( 'header_text', array( 
  1675. 'label' => __( 'Display Site Title and Tagline' ),  
  1676. 'section' => 'title_tagline',  
  1677. 'settings' => 'header_text',  
  1678. 'type' => 'checkbox',  
  1679. ) ); 
  1680.  
  1681. $this->add_setting( 'site_icon', array( 
  1682. 'type' => 'option',  
  1683. 'capability' => 'manage_options',  
  1684. 'transport' => 'postMessage', // Previewed with JS in the Customizer controls window. 
  1685. ) ); 
  1686.  
  1687. $this->add_control( new WP_Customize_Site_Icon_Control( $this, 'site_icon', array( 
  1688. 'label' => __( 'Site Icon' ),  
  1689. 'description' => sprintf( 
  1690. /** translators: %s: site icon size in pixels */ 
  1691. __( 'The Site Icon is used as a browser and app icon for your site. Icons must be square, and at least %s pixels wide and tall.' ),  
  1692. '<strong>512</strong>' 
  1693. ),  
  1694. 'section' => 'title_tagline',  
  1695. 'priority' => 60,  
  1696. 'height' => 512,  
  1697. 'width' => 512,  
  1698. ) ) ); 
  1699.  
  1700. $this->add_setting( 'custom_logo', array( 
  1701. 'theme_supports' => array( 'custom-logo' ),  
  1702. 'transport' => 'postMessage',  
  1703. ) ); 
  1704.  
  1705. $custom_logo_args = get_theme_support( 'custom-logo' ); 
  1706. $this->add_control( new WP_Customize_Cropped_Image_Control( $this, 'custom_logo', array( 
  1707. 'label' => __( 'Logo' ),  
  1708. 'section' => 'title_tagline',  
  1709. 'priority' => 8,  
  1710. 'height' => $custom_logo_args[0]['height'],  
  1711. 'width' => $custom_logo_args[0]['width'],  
  1712. 'flex_height' => $custom_logo_args[0]['flex-height'],  
  1713. 'flex_width' => $custom_logo_args[0]['flex-width'],  
  1714. 'button_labels' => array( 
  1715. 'select' => __( 'Select logo' ),  
  1716. 'change' => __( 'Change logo' ),  
  1717. 'remove' => __( 'Remove' ),  
  1718. 'default' => __( 'Default' ),  
  1719. 'placeholder' => __( 'No logo selected' ),  
  1720. 'frame_title' => __( 'Select logo' ),  
  1721. 'frame_button' => __( 'Choose logo' ),  
  1722. ),  
  1723. ) ) ); 
  1724.  
  1725. $this->selective_refresh->add_partial( 'custom_logo', array( 
  1726. 'settings' => array( 'custom_logo' ),  
  1727. 'selector' => '.custom-logo-link',  
  1728. 'render_callback' => array( $this, '_render_custom_logo_partial' ),  
  1729. 'container_inclusive' => true,  
  1730. ) ); 
  1731.  
  1732. /** Colors */ 
  1733.  
  1734. $this->add_section( 'colors', array( 
  1735. 'title' => __( 'Colors' ),  
  1736. 'priority' => 40,  
  1737. ) ); 
  1738.  
  1739. $this->add_setting( 'header_textcolor', array( 
  1740. 'theme_supports' => array( 'custom-header', 'header-text' ),  
  1741. 'default' => get_theme_support( 'custom-header', 'default-text-color' ),  
  1742.  
  1743. 'sanitize_callback' => array( $this, '_sanitize_header_textcolor' ),  
  1744. 'sanitize_js_callback' => 'maybe_hash_hex_color',  
  1745. ) ); 
  1746.  
  1747. // Input type: checkbox 
  1748. // With custom value 
  1749. $this->add_control( 'display_header_text', array( 
  1750. 'settings' => 'header_textcolor',  
  1751. 'label' => __( 'Display Site Title and Tagline' ),  
  1752. 'section' => 'title_tagline',  
  1753. 'type' => 'checkbox',  
  1754. 'priority' => 40,  
  1755. ) ); 
  1756.  
  1757. $this->add_control( new WP_Customize_Color_Control( $this, 'header_textcolor', array( 
  1758. 'label' => __( 'Header Text Color' ),  
  1759. 'section' => 'colors',  
  1760. ) ) ); 
  1761.  
  1762. // Input type: Color 
  1763. // With sanitize_callback 
  1764. $this->add_setting( 'background_color', array( 
  1765. 'default' => get_theme_support( 'custom-background', 'default-color' ),  
  1766. 'theme_supports' => 'custom-background',  
  1767.  
  1768. 'sanitize_callback' => 'sanitize_hex_color_no_hash',  
  1769. 'sanitize_js_callback' => 'maybe_hash_hex_color',  
  1770. ) ); 
  1771.  
  1772. $this->add_control( new WP_Customize_Color_Control( $this, 'background_color', array( 
  1773. 'label' => __( 'Background Color' ),  
  1774. 'section' => 'colors',  
  1775. ) ) ); 
  1776.  
  1777.  
  1778. /** Custom Header */ 
  1779.  
  1780. $this->add_section( 'header_image', array( 
  1781. 'title' => __( 'Header Image' ),  
  1782. 'theme_supports' => 'custom-header',  
  1783. 'priority' => 60,  
  1784. ) ); 
  1785.  
  1786. $this->add_setting( new WP_Customize_Filter_Setting( $this, 'header_image', array( 
  1787. 'default' => get_theme_support( 'custom-header', 'default-image' ),  
  1788. 'theme_supports' => 'custom-header',  
  1789. ) ) ); 
  1790.  
  1791. $this->add_setting( new WP_Customize_Header_Image_Setting( $this, 'header_image_data', array( 
  1792. // 'default' => get_theme_support( 'custom-header', 'default-image' ),  
  1793. 'theme_supports' => 'custom-header',  
  1794. ) ) ); 
  1795.  
  1796. $this->add_control( new WP_Customize_Header_Image_Control( $this ) ); 
  1797.  
  1798. /** Custom Background */ 
  1799.  
  1800. $this->add_section( 'background_image', array( 
  1801. 'title' => __( 'Background Image' ),  
  1802. 'theme_supports' => 'custom-background',  
  1803. 'priority' => 80,  
  1804. ) ); 
  1805.  
  1806. $this->add_setting( 'background_image', array( 
  1807. 'default' => get_theme_support( 'custom-background', 'default-image' ),  
  1808. 'theme_supports' => 'custom-background',  
  1809. ) ); 
  1810.  
  1811. $this->add_setting( new WP_Customize_Background_Image_Setting( $this, 'background_image_thumb', array( 
  1812. 'theme_supports' => 'custom-background',  
  1813. ) ) ); 
  1814.  
  1815. $this->add_control( new WP_Customize_Background_Image_Control( $this ) ); 
  1816.  
  1817. $this->add_setting( 'background_repeat', array( 
  1818. 'default' => get_theme_support( 'custom-background', 'default-repeat' ),  
  1819. 'theme_supports' => 'custom-background',  
  1820. ) ); 
  1821.  
  1822. $this->add_control( 'background_repeat', array( 
  1823. 'label' => __( 'Background Repeat' ),  
  1824. 'section' => 'background_image',  
  1825. 'type' => 'radio',  
  1826. 'choices' => array( 
  1827. 'no-repeat' => __('No Repeat'),  
  1828. 'repeat' => __('Tile'),  
  1829. 'repeat-x' => __('Tile Horizontally'),  
  1830. 'repeat-y' => __('Tile Vertically'),  
  1831. ),  
  1832. ) ); 
  1833.  
  1834. $this->add_setting( 'background_position_x', array( 
  1835. 'default' => get_theme_support( 'custom-background', 'default-position-x' ),  
  1836. 'theme_supports' => 'custom-background',  
  1837. ) ); 
  1838.  
  1839. $this->add_control( 'background_position_x', array( 
  1840. 'label' => __( 'Background Position' ),  
  1841. 'section' => 'background_image',  
  1842. 'type' => 'radio',  
  1843. 'choices' => array( 
  1844. 'left' => __('Left'),  
  1845. 'center' => __('Center'),  
  1846. 'right' => __('Right'),  
  1847. ),  
  1848. ) ); 
  1849.  
  1850. $this->add_setting( 'background_attachment', array( 
  1851. 'default' => get_theme_support( 'custom-background', 'default-attachment' ),  
  1852. 'theme_supports' => 'custom-background',  
  1853. ) ); 
  1854.  
  1855. $this->add_control( 'background_attachment', array( 
  1856. 'label' => __( 'Background Attachment' ),  
  1857. 'section' => 'background_image',  
  1858. 'type' => 'radio',  
  1859. 'choices' => array( 
  1860. 'scroll' => __('Scroll'),  
  1861. 'fixed' => __('Fixed'),  
  1862. ),  
  1863. ) ); 
  1864.  
  1865. // If the theme is using the default background callback, we can update 
  1866. // the background CSS using postMessage. 
  1867. if ( get_theme_support( 'custom-background', 'wp-head-callback' ) === '_custom_background_cb' ) { 
  1868. foreach ( array( 'color', 'image', 'position_x', 'repeat', 'attachment' ) as $prop ) { 
  1869. $this->get_setting( 'background_' . $prop )->transport = 'postMessage'; 
  1870.  
  1871. /** Static Front Page */ 
  1872. // #WP19627 
  1873.  
  1874. // Replicate behavior from options-reading.php and hide front page options if there are no pages 
  1875. if ( get_pages() ) { 
  1876. $this->add_section( 'static_front_page', array( 
  1877. 'title' => __( 'Static Front Page' ),  
  1878. // 'theme_supports' => 'static-front-page',  
  1879. 'priority' => 120,  
  1880. 'description' => __( 'Your theme supports a static front page.' ),  
  1881. ) ); 
  1882.  
  1883. $this->add_setting( 'show_on_front', array( 
  1884. 'default' => get_option( 'show_on_front' ),  
  1885. 'capability' => 'manage_options',  
  1886. 'type' => 'option',  
  1887. // 'theme_supports' => 'static-front-page',  
  1888. ) ); 
  1889.  
  1890. $this->add_control( 'show_on_front', array( 
  1891. 'label' => __( 'Front page displays' ),  
  1892. 'section' => 'static_front_page',  
  1893. 'type' => 'radio',  
  1894. 'choices' => array( 
  1895. 'posts' => __( 'Your latest posts' ),  
  1896. 'page' => __( 'A static page' ),  
  1897. ),  
  1898. ) ); 
  1899.  
  1900. $this->add_setting( 'page_on_front', array( 
  1901. 'type' => 'option',  
  1902. 'capability' => 'manage_options',  
  1903. // 'theme_supports' => 'static-front-page',  
  1904. ) ); 
  1905.  
  1906. $this->add_control( 'page_on_front', array( 
  1907. 'label' => __( 'Front page' ),  
  1908. 'section' => 'static_front_page',  
  1909. 'type' => 'dropdown-pages',  
  1910. ) ); 
  1911.  
  1912. $this->add_setting( 'page_for_posts', array( 
  1913. 'type' => 'option',  
  1914. 'capability' => 'manage_options',  
  1915. // 'theme_supports' => 'static-front-page',  
  1916. ) ); 
  1917.  
  1918. $this->add_control( 'page_for_posts', array( 
  1919. 'label' => __( 'Posts page' ),  
  1920. 'section' => 'static_front_page',  
  1921. 'type' => 'dropdown-pages',  
  1922. ) ); 
  1923.  
  1924. /** 
  1925. * Add settings from the POST data that were not added with code, e.g. dynamically-created settings for Widgets 
  1926. * @since 4.2.0 
  1927. * @access public 
  1928. * @see add_dynamic_settings() 
  1929. */ 
  1930. public function register_dynamic_settings() { 
  1931. $this->add_dynamic_settings( array_keys( $this->unsanitized_post_values() ) ); 
  1932.  
  1933. /** 
  1934. * Callback for validating the header_textcolor value. 
  1935. * Accepts 'blank', and otherwise uses sanitize_hex_color_no_hash(). 
  1936. * Returns default text color if hex color is empty. 
  1937. * @since 3.4.0 
  1938. * @param string $color 
  1939. * @return mixed 
  1940. */ 
  1941. public function _sanitize_header_textcolor( $color ) { 
  1942. if ( 'blank' === $color ) 
  1943. return 'blank'; 
  1944.  
  1945. $color = sanitize_hex_color_no_hash( $color ); 
  1946. if ( empty( $color ) ) 
  1947. $color = get_theme_support( 'custom-header', 'default-text-color' ); 
  1948.  
  1949. return $color; 
  1950.  
  1951. /** 
  1952. * Callback for rendering the custom logo, used in the custom_logo partial. 
  1953. * This method exists because the partial object and context data are passed 
  1954. * into a partial's render_callback so we cannot use get_custom_logo() as 
  1955. * the render_callback directly since it expects a blog ID as the first 
  1956. * argument. When WP no longer supports PHP 5.3, this method can be removed 
  1957. * in favor of an anonymous function. 
  1958. * @see WP_Customize_Manager::register_controls() 
  1959. * @since 4.5.0 
  1960. * @access private 
  1961. * @return string Custom logo. 
  1962. */ 
  1963. public function _render_custom_logo_partial() { 
  1964. return get_custom_logo();