]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-includes/class-wp-customize-manager.php
WordPress 4.1.4-scripts
[autoinstalls/wordpress.git] / wp-includes / class-wp-customize-manager.php
index 8ed4e699a5b5da4072108dccde2c6e73aceb2125..c14d61652ab28e114cb7b66d8e57b1e6f0d4125d 100644 (file)
@@ -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();
+               }
 
                ?>
                <script type="text/javascript">
-                       var _wpCustomizeSettings = <?php echo json_encode( $settings ); ?>;
+                       var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>;
                </script>
                <?php
        }
 
        /**
-        * Prints a signature so we can ensure the customizer was properly executed.
+        * Prints a signature so we can ensure the Customizer was properly executed.
         *
         * @since 3.4.0
         */
@@ -481,7 +568,7 @@ final class WP_Customize_Manager {
        }
 
        /**
-        * Removes the signature in case we experience a case where the customizer was not properly executed.
+        * Removes the signature in case we experience a case where the Customizer was not properly executed.
         *
         * @since 3.4.0
         */
@@ -647,6 +734,53 @@ final class WP_Customize_Manager {
                unset( $this->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