X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/4713a14935b83517997f3c88f808eb41da55033d..caeaf8dc94b5e3f75dc98ec92dc7b76049cdddb6:/wp-includes/class-wp-customize-manager.php diff --git a/wp-includes/class-wp-customize-manager.php b/wp-includes/class-wp-customize-manager.php index 8ed4e699..c14d6165 100644 --- a/wp-includes/class-wp-customize-manager.php +++ b/wp-includes/class-wp-customize-manager.php @@ -16,7 +16,7 @@ */ final class WP_Customize_Manager { /** - * An instance of the theme that is being customized. + * An instance of the theme being previewed. * * @var WP_Theme */ @@ -30,33 +30,43 @@ final class WP_Customize_Manager { protected $original_stylesheet; /** - * Whether filters have been set to change the active theme to the theme being - * customized. + * Whether this is a Customizer pageload. * * @var boolean */ protected $previewing = false; /** - * Methods and properties deailing with managing widgets in the customizer. + * Methods and properties deailing with managing widgets in the Customizer. * * @var WP_Customize_Widgets */ public $widgets; - protected $settings = array(); - protected $sections = array(); - protected $controls = array(); + protected $settings = array(); + protected $containers = array(); + protected $panels = array(); + protected $sections = array(); + protected $controls = array(); protected $nonce_tick; protected $customized; /** - * $_POST values for Customize Settings. + * Controls that may be rendered from JS templates. * + * @since 4.1.0 + * @access protected * @var array */ + protected $registered_control_types = array(); + + /** + * Unsanitized values for Customize Settings parsed from $_POST['customized']. + * + * @var array|false + */ private $_post_values; /** @@ -65,10 +75,11 @@ final class WP_Customize_Manager { * @since 3.4.0 */ public function __construct() { - require( ABSPATH . WPINC . '/class-wp-customize-setting.php' ); - require( ABSPATH . WPINC . '/class-wp-customize-section.php' ); - require( ABSPATH . WPINC . '/class-wp-customize-control.php' ); - require( ABSPATH . WPINC . '/class-wp-customize-widgets.php' ); + require_once( ABSPATH . WPINC . '/class-wp-customize-setting.php' ); + require_once( ABSPATH . WPINC . '/class-wp-customize-panel.php' ); + require_once( ABSPATH . WPINC . '/class-wp-customize-section.php' ); + require_once( ABSPATH . WPINC . '/class-wp-customize-control.php' ); + require_once( ABSPATH . WPINC . '/class-wp-customize-widgets.php' ); $this->widgets = new WP_Customize_Widgets( $this ); @@ -80,7 +91,7 @@ final class WP_Customize_Manager { // Run wp_redirect_status late to make sure we override the status last. add_action( 'wp_redirect_status', array( $this, 'wp_redirect_status' ), 1000 ); - // Do not spawn cron (especially the alternate cron) while running the customizer. + // Do not spawn cron (especially the alternate cron) while running the Customizer. remove_action( 'init', 'wp_cron' ); // Do not run update checks when rendering the controls. @@ -156,8 +167,9 @@ final class WP_Customize_Manager { show_admin_bar( false ); - if ( ! current_user_can( 'edit_theme_options' ) ) + if ( ! current_user_can( 'customize' ) ) { $this->wp_die( -1 ); + } $this->original_stylesheet = get_stylesheet(); @@ -181,7 +193,6 @@ final class WP_Customize_Manager { $this->wp_die( -1 ); } - // All good, let's do some internal business to preview the theme. $this->start_previewing_theme(); } @@ -190,7 +201,7 @@ final class WP_Customize_Manager { * * @since 3.4.0 */ - function after_setup_theme() { + public function after_setup_theme() { if ( ! $this->doing_ajax() && ! validate_current_theme() ) { wp_redirect( 'themes.php?broken=true' ); exit; @@ -198,7 +209,8 @@ final class WP_Customize_Manager { } /** - * Start previewing the selected theme by adding filters to change the current theme. + * If the theme to be previewed isn't the active theme, add filter callbacks + * to swap it out at runtime. * * @since 3.4.0 */ @@ -214,7 +226,7 @@ final class WP_Customize_Manager { add_filter( 'stylesheet', array( $this, 'get_stylesheet' ) ); add_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) ); - // @link: http://core.trac.wordpress.org/ticket/20027 + // @link: https://core.trac.wordpress.org/ticket/20027 add_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) ); add_filter( 'pre_option_template', array( $this, 'get_template' ) ); @@ -251,7 +263,7 @@ final class WP_Customize_Manager { remove_filter( 'stylesheet', array( $this, 'get_stylesheet' ) ); remove_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) ); - // @link: http://core.trac.wordpress.org/ticket/20027 + // @link: https://core.trac.wordpress.org/ticket/20027 remove_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) ); remove_filter( 'pre_option_template', array( $this, 'get_template' ) ); @@ -303,6 +315,17 @@ final class WP_Customize_Manager { return $this->controls; } + /** + * Get the registered containers. + * + * @since 4.0.0 + * + * @return array + */ + public function containers() { + return $this->containers; + } + /** * Get the registered sections. * @@ -314,6 +337,18 @@ final class WP_Customize_Manager { return $this->sections; } + /** + * Get the registered panels. + * + * @since 4.0.0 + * @access public + * + * @return array Panels. + */ + public function panels() { + return $this->panels; + } + /** * Checks if the current theme is active. * @@ -364,27 +399,50 @@ final class WP_Customize_Manager { } /** - * Decode the $_POST['customized'] values for a specific Customize Setting. + * Parse the incoming $_POST['customized'] JSON data and store the unsanitized + * settings for subsequent post_value() lookups. * - * @since 3.4.0 + * @since 4.1.1 * - * @param mixed $setting A WP_Customize_Setting derived object - * @return string $post_value Sanitized value + * @return array */ - public function post_value( $setting ) { + public function unsanitized_post_values() { if ( ! isset( $this->_post_values ) ) { - if ( isset( $_POST['customized'] ) ) + if ( isset( $_POST['customized'] ) ) { $this->_post_values = json_decode( wp_unslash( $_POST['customized'] ), true ); - else + } + if ( empty( $this->_post_values ) ) { // if not isset or of JSON error $this->_post_values = false; + } + } + if ( empty( $this->_post_values ) ) { + return array(); + } else { + return $this->_post_values; } + } - if ( isset( $this->_post_values[ $setting->id ] ) ) - return $setting->sanitize( $this->_post_values[ $setting->id ] ); + /** + * Return the sanitized value for a given setting from the request's POST data. + * + * @since 3.4.0 + * @since 4.1.1 Introduced 'default' parameter. + * + * @param WP_Customize_Setting $setting A WP_Customize_Setting derived object + * @param mixed $default value returned $setting has no post value (added in 4.2.0). + * @return string|mixed $post_value Sanitized value or the $default provided + */ + public function post_value( $setting, $default = null ) { + $post_values = $this->unsanitized_post_values(); + if ( array_key_exists( $setting->id, $post_values ) ) { + return $setting->sanitize( $post_values[ $setting->id ] ); + } else { + return $default; + } } /** - * Print javascript settings. + * Print JavaScript settings. * * @since 3.4.0 */ @@ -394,6 +452,7 @@ final class WP_Customize_Manager { $this->prepare_controls(); wp_enqueue_script( 'customize-preview' ); + add_action( 'wp', array( $this, 'customize_preview_override_404_status' ) ); add_action( 'wp_head', array( $this, 'customize_preview_base' ) ); add_action( 'wp_head', array( $this, 'customize_preview_html5' ) ); add_action( 'wp_footer', array( $this, 'customize_preview_settings' ), 20 ); @@ -415,6 +474,19 @@ final class WP_Customize_Manager { do_action( 'customize_preview_init', $this ); } + /** + * Prevent sending a 404 status when returning the response for the customize + * preview, since it causes the jQuery AJAX to fail. Send 200 instead. + * + * @since 4.0.0 + * @access public + */ + public function customize_preview_override_404_status() { + if ( is_404() ) { + status_header( 200 ); + } + } + /** * Print base element for preview frame. * @@ -443,14 +515,17 @@ final class WP_Customize_Manager { } /** - * Print javascript settings for preview frame. + * Print JavaScript settings for preview frame. * * @since 3.4.0 */ public function customize_preview_settings() { $settings = array( 'values' => array(), - 'channel' => esc_js( $_POST['customize_messenger_channel'] ), + 'channel' => wp_unslash( $_POST['customize_messenger_channel'] ), + 'activePanels' => array(), + 'activeSections' => array(), + 'activeControls' => array(), ); if ( 2 == $this->nonce_tick ) { @@ -463,16 +538,28 @@ final class WP_Customize_Manager { foreach ( $this->settings as $id => $setting ) { $settings['values'][ $id ] = $setting->js_value(); } + foreach ( $this->panels as $id => $panel ) { + $settings['activePanels'][ $id ] = $panel->active(); + foreach ( $panel->sections as $id => $section ) { + $settings['activeSections'][ $id ] = $section->active(); + } + } + foreach ( $this->sections as $id => $section ) { + $settings['activeSections'][ $id ] = $section->active(); + } + foreach ( $this->controls as $id => $control ) { + $settings['activeControls'][ $id ] = $control->active(); + } ?> settings[ $id ] ); } + /** + * Add a customize panel. + * + * @since 4.0.0 + * @access public + * + * @param WP_Customize_Panel|string $id Customize Panel object, or Panel ID. + * @param array $args Optional. Panel arguments. Default empty array. + */ + public function add_panel( $id, $args = array() ) { + if ( is_a( $id, 'WP_Customize_Panel' ) ) { + $panel = $id; + } + else { + $panel = new WP_Customize_Panel( $this, $id, $args ); + } + + $this->panels[ $panel->id ] = $panel; + } + + /** + * Retrieve a customize panel. + * + * @since 4.0.0 + * @access public + * + * @param string $id Panel ID to get. + * @return WP_Customize_Panel Requested panel instance. + */ + public function get_panel( $id ) { + if ( isset( $this->panels[ $id ] ) ) { + return $this->panels[ $id ]; + } + } + + /** + * Remove a customize panel. + * + * @since 4.0.0 + * @access public + * + * @param string $id Panel ID to remove. + */ + public function remove_panel( $id ) { + unset( $this->panels[ $id ] ); + } + /** * Add a customize section. * @@ -731,25 +865,52 @@ final class WP_Customize_Manager { } /** - * Helper function to compare two objects by priority. + * Register a customize control type. + * + * Registered types are eligible to be rendered via JS and created dynamically. + * + * @since 4.1.0 + * @access public + * + * @param string $control Name of a custom control which is a subclass of + * {@see WP_Customize_Control}. + */ + public function register_control_type( $control ) { + $this->registered_control_types[] = $control; + } + + /** + * Render JS templates for all registered control types. + * + * @since 4.1.0 + * @access public + */ + public function render_control_templates() { + foreach ( $this->registered_control_types as $control_type ) { + $control = new $control_type( $this, 'temp', array() ); + $control->print_template(); + } + } + + /** + * Helper function to compare two objects by priority, ensuring sort stability via instance_number. * * @since 3.4.0 * - * @param object $a Object A. - * @param object $b Object B. + * @param {WP_Customize_Panel|WP_Customize_Section|WP_Customize_Control} $a Object A. + * @param {WP_Customize_Panel|WP_Customize_Section|WP_Customize_Control} $b Object B. * @return int */ protected final function _cmp_priority( $a, $b ) { - $ap = $a->priority; - $bp = $b->priority; - - if ( $ap == $bp ) - return 0; - return ( $ap > $bp ) ? 1 : -1; + if ( $a->priority === $b->priority ) { + return $a->instance_number - $a->instance_number; + } else { + return $a->priority - $b->priority; + } } /** - * Prepare settings and sections. + * Prepare panels, sections, and controls. * * For each, check if required related components exist, * whether the user has the necessary capabilities, @@ -759,12 +920,13 @@ final class WP_Customize_Manager { */ public function prepare_controls() { - $this->controls = array_reverse( $this->controls ); $controls = array(); + uasort( $this->controls, array( $this, '_cmp_priority' ) ); foreach ( $this->controls as $id => $control ) { - if ( ! isset( $this->sections[ $control->section ] ) || ! $control->check_capabilities() ) + if ( ! isset( $this->sections[ $control->section ] ) || ! $control->check_capabilities() ) { continue; + } $this->sections[ $control->section ]->controls[] = $control; $controls[ $id ] = $control; @@ -772,19 +934,45 @@ final class WP_Customize_Manager { $this->controls = $controls; // Prepare sections. - // Reversing makes uasort sort by time added when conflicts occur. - $this->sections = array_reverse( $this->sections ); uasort( $this->sections, array( $this, '_cmp_priority' ) ); $sections = array(); foreach ( $this->sections as $section ) { - if ( ! $section->check_capabilities() || ! $section->controls ) + if ( ! $section->check_capabilities() || ! $section->controls ) { continue; + } usort( $section->controls, array( $this, '_cmp_priority' ) ); - $sections[] = $section; + + if ( ! $section->panel ) { + // Top-level section. + $sections[ $section->id ] = $section; + } else { + // This section belongs to a panel. + if ( isset( $this->panels [ $section->panel ] ) ) { + $this->panels[ $section->panel ]->sections[ $section->id ] = $section; + } + } } $this->sections = $sections; + + // Prepare panels. + uasort( $this->panels, array( $this, '_cmp_priority' ) ); + $panels = array(); + + foreach ( $this->panels as $panel ) { + if ( ! $panel->check_capabilities() || ! $panel->sections ) { + continue; + } + + uasort( $panel->sections, array( $this, '_cmp_priority' ) ); + $panels[ $panel->id ] = $panel; + } + $this->panels = $panels; + + // Sort panels and top-level sections together. + $this->containers = array_merge( $this->panels, $this->sections ); + uasort( $this->containers, array( $this, '_cmp_priority' ) ); } /** @@ -805,6 +993,12 @@ final class WP_Customize_Manager { */ public function register_controls() { + /* Control Types (custom control classes) */ + $this->register_control_type( 'WP_Customize_Color_Control' ); + $this->register_control_type( 'WP_Customize_Upload_Control' ); + $this->register_control_type( 'WP_Customize_Image_Control' ); + $this->register_control_type( 'WP_Customize_Background_Image_Control' ); + /* Site Title & Tagline */ $this->add_section( 'title_tagline', array( @@ -919,7 +1113,7 @@ final class WP_Customize_Manager { $this->add_control( new WP_Customize_Background_Image_Control( $this ) ); $this->add_setting( 'background_repeat', array( - 'default' => 'repeat', + 'default' => get_theme_support( 'custom-background', 'default-repeat' ), 'theme_supports' => 'custom-background', ) ); @@ -936,7 +1130,7 @@ final class WP_Customize_Manager { ) ); $this->add_setting( 'background_position_x', array( - 'default' => 'left', + 'default' => get_theme_support( 'custom-background', 'default-position-x' ), 'theme_supports' => 'custom-background', ) ); @@ -952,7 +1146,7 @@ final class WP_Customize_Manager { ) ); $this->add_setting( 'background_attachment', array( - 'default' => 'fixed', + 'default' => get_theme_support( 'custom-background', 'default-attachment' ), 'theme_supports' => 'custom-background', ) ); @@ -961,8 +1155,8 @@ final class WP_Customize_Manager { 'section' => 'background_image', 'type' => 'radio', 'choices' => array( - 'fixed' => __('Fixed'), 'scroll' => __('Scroll'), + 'fixed' => __('Fixed'), ), ) ); @@ -978,7 +1172,6 @@ final class WP_Customize_Manager { $locations = get_registered_nav_menus(); $menus = wp_get_nav_menus(); - $menu_locations = get_nav_menu_locations(); $num_locations = count( array_keys( $locations ) ); $this->add_section( 'nav', array( @@ -1084,7 +1277,7 @@ final class WP_Customize_Manager { return $color; } -}; +} /** * Sanitizes a hex color. @@ -1118,7 +1311,6 @@ function sanitize_hex_color( $color ) { * Returns either '', a 3 or 6 digit hex color (without a #), or null. * * @since 3.4.0 - * @uses sanitize_hex_color() * * @param string $color * @return string|null