Customizer: Introduce an API to create WP_Customize_Settings for dynamically-created settings.
* Introduce WP_Customize_Manager::add_dynamic_settings() to register dynamically-created settings. * Introduce `customize_dynamic_setting_args` filter to pass an array of args to a dynamic setting's constructor. * Add unit tests for WP_Customize_Manager and WP_Customize_Widgets. * See WP_Customize_Widgets as an example. props westonruter. fixes #30936. Built from https://develop.svn.wordpress.org/trunk@31370 git-svn-id: http://core.svn.wordpress.org/trunk@31351 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
17330354bc
commit
3aea5f144b
|
@ -65,7 +65,7 @@ final class WP_Customize_Manager {
|
||||||
/**
|
/**
|
||||||
* Unsanitized values for Customize Settings parsed from $_POST['customized'].
|
* Unsanitized values for Customize Settings parsed from $_POST['customized'].
|
||||||
*
|
*
|
||||||
* @var array|false
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $_post_values;
|
private $_post_values;
|
||||||
|
|
||||||
|
@ -102,6 +102,7 @@ final class WP_Customize_Manager {
|
||||||
add_action( 'wp_ajax_customize_save', array( $this, 'save' ) );
|
add_action( 'wp_ajax_customize_save', array( $this, 'save' ) );
|
||||||
|
|
||||||
add_action( 'customize_register', array( $this, 'register_controls' ) );
|
add_action( 'customize_register', array( $this, 'register_controls' ) );
|
||||||
|
add_action( 'customize_register', array( $this, 'register_dynamic_settings' ), 11 ); // allow code to create settings first
|
||||||
add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) );
|
add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) );
|
||||||
add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) );
|
add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) );
|
||||||
}
|
}
|
||||||
|
@ -110,11 +111,23 @@ final class WP_Customize_Manager {
|
||||||
* Return true if it's an AJAX request.
|
* Return true if it's an AJAX request.
|
||||||
*
|
*
|
||||||
* @since 3.4.0
|
* @since 3.4.0
|
||||||
|
* @since 4.2.0 Added $action param.
|
||||||
*
|
*
|
||||||
|
* @param string|null $action whether the supplied Ajax action is being run.
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function doing_ajax() {
|
public function doing_ajax( $action = null ) {
|
||||||
return isset( $_POST['customized'] ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX );
|
$doing_ajax = ( defined( 'DOING_AJAX' ) && DOING_AJAX );
|
||||||
|
if ( ! $doing_ajax ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $action ) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Note: we can't just use doing_action( "wp_ajax_{$action}" ) because we need to check before admin-ajax.php gets to that point
|
||||||
|
return isset( $_REQUEST['action'] ) && wp_unslash( $_REQUEST['action'] ) === $action;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -411,8 +424,8 @@ final class WP_Customize_Manager {
|
||||||
if ( isset( $_POST['customized'] ) ) {
|
if ( isset( $_POST['customized'] ) ) {
|
||||||
$this->_post_values = json_decode( wp_unslash( $_POST['customized'] ), true );
|
$this->_post_values = json_decode( wp_unslash( $_POST['customized'] ), true );
|
||||||
}
|
}
|
||||||
if ( empty( $this->_post_values ) ) { // if not isset or of JSON error
|
if ( empty( $this->_post_values ) ) { // if not isset or if JSON error
|
||||||
$this->_post_values = false;
|
$this->_post_values = array();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( empty( $this->_post_values ) ) {
|
if ( empty( $this->_post_values ) ) {
|
||||||
|
@ -441,6 +454,19 @@ final class WP_Customize_Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override a setting's (unsanitized) value as found in any incoming $_POST['customized']
|
||||||
|
*
|
||||||
|
* @since 4.2.0
|
||||||
|
*
|
||||||
|
* @param string $setting_id The ID for the WP_Customize_Setting instance.
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public function set_post_value( $setting_id, $value ) {
|
||||||
|
$this->unsanitized_post_values();
|
||||||
|
$this->_post_values[ $setting_id ] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print JavaScript settings.
|
* Print JavaScript settings.
|
||||||
*
|
*
|
||||||
|
@ -726,6 +752,65 @@ final class WP_Customize_Manager {
|
||||||
$this->settings[ $setting->id ] = $setting;
|
$this->settings[ $setting->id ] = $setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register any dynamically-created settings, such as those from $_POST['customized'] that have no corresponding setting created.
|
||||||
|
*
|
||||||
|
* This is a mechanism to "wake up" settings that have been dynamically created
|
||||||
|
* on the frontend and have been sent to WordPress in $_POST['customized']. When WP
|
||||||
|
* loads, the dynamically-created settings then will get created and previewed
|
||||||
|
* even though they are not directly created statically with code.
|
||||||
|
*
|
||||||
|
* @since 4.2.0
|
||||||
|
*
|
||||||
|
* @param string[] $setting_ids The setting IDs to add.
|
||||||
|
* @return WP_Customize_Setting[] The settings added.
|
||||||
|
*/
|
||||||
|
public function add_dynamic_settings( $setting_ids ) {
|
||||||
|
$new_settings = array();
|
||||||
|
foreach ( $setting_ids as $setting_id ) {
|
||||||
|
// Skip settings already created
|
||||||
|
if ( $this->get_setting( $setting_id ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$setting_args = false;
|
||||||
|
$setting_class = 'WP_Customize_Setting';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a dynamic setting's constructor args.
|
||||||
|
*
|
||||||
|
* For a dynamic setting to be registered, this filter must be employed
|
||||||
|
* to override the default false value with an array of args to pass to
|
||||||
|
* the WP_Customize_Setting constructor.
|
||||||
|
*
|
||||||
|
* @since 4.2.0
|
||||||
|
*
|
||||||
|
* @param false|array $setting_args The arguments to the WP_Customize_Setting constructor.
|
||||||
|
* @param string $setting_id ID for dynamic setting, usually coming from $_POST['customized'].
|
||||||
|
*/
|
||||||
|
$setting_args = apply_filters( 'customize_dynamic_setting_args', $setting_args, $setting_id );
|
||||||
|
if ( false === $setting_args ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow non-statically created settings to be constructed with custom WP_Customize_Setting subclass.
|
||||||
|
*
|
||||||
|
* @since 4.2.0
|
||||||
|
*
|
||||||
|
* @param string $setting_class WP_Customize_Setting or a subclass.
|
||||||
|
* @param string $setting_id ID for dynamic setting, usually coming from $_POST['customized'].
|
||||||
|
* @param string $setting_args WP_Customize_Setting or a subclass.
|
||||||
|
*/
|
||||||
|
$setting_class = apply_filters( 'customize_dynamic_setting_class', $setting_class, $setting_id, $setting_args );
|
||||||
|
|
||||||
|
$setting = new $setting_class( $this, $setting_id, $setting_args );
|
||||||
|
$this->add_setting( $setting );
|
||||||
|
$new_settings[] = $setting;
|
||||||
|
}
|
||||||
|
return $new_settings;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a customize setting.
|
* Retrieve a customize setting.
|
||||||
*
|
*
|
||||||
|
@ -735,9 +820,10 @@ final class WP_Customize_Manager {
|
||||||
* @return WP_Customize_Setting
|
* @return WP_Customize_Setting
|
||||||
*/
|
*/
|
||||||
public function get_setting( $id ) {
|
public function get_setting( $id ) {
|
||||||
if ( isset( $this->settings[ $id ] ) )
|
if ( isset( $this->settings[ $id ] ) ) {
|
||||||
return $this->settings[ $id ];
|
return $this->settings[ $id ];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a customize setting.
|
* Remove a customize setting.
|
||||||
|
@ -1274,6 +1360,15 @@ final class WP_Customize_Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add settings from the POST data that were not added with code, e.g. dynamically-created settings for Widgets
|
||||||
|
*
|
||||||
|
* @since 4.2.0
|
||||||
|
*/
|
||||||
|
public function register_dynamic_settings() {
|
||||||
|
$this->add_dynamic_settings( array_keys( $this->unsanitized_post_values() ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback for validating the header_textcolor value.
|
* Callback for validating the header_textcolor value.
|
||||||
*
|
*
|
||||||
|
|
|
@ -54,14 +54,6 @@ class WP_Customize_Setting {
|
||||||
|
|
||||||
protected $id_data = array();
|
protected $id_data = array();
|
||||||
|
|
||||||
/**
|
|
||||||
* Cached and sanitized $_POST value for the setting.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
private $_post_value;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
|
@ -163,7 +155,7 @@ class WP_Customize_Setting {
|
||||||
*/
|
*/
|
||||||
public function _preview_filter( $original ) {
|
public function _preview_filter( $original ) {
|
||||||
$undefined = new stdClass(); // symbol hack
|
$undefined = new stdClass(); // symbol hack
|
||||||
$post_value = $this->manager->post_value( $this, $undefined );
|
$post_value = $this->post_value( $undefined );
|
||||||
if ( $undefined === $post_value ) {
|
if ( $undefined === $post_value ) {
|
||||||
$value = $this->_original_value;
|
$value = $this->_original_value;
|
||||||
} else {
|
} else {
|
||||||
|
@ -211,17 +203,7 @@ class WP_Customize_Setting {
|
||||||
* @return mixed The default value on failure, otherwise the sanitized value.
|
* @return mixed The default value on failure, otherwise the sanitized value.
|
||||||
*/
|
*/
|
||||||
final public function post_value( $default = null ) {
|
final public function post_value( $default = null ) {
|
||||||
// Check for a cached value
|
return $this->manager->post_value( $this, $default );
|
||||||
if ( isset( $this->_post_value ) )
|
|
||||||
return $this->_post_value;
|
|
||||||
|
|
||||||
// Call the manager for the post value
|
|
||||||
$result = $this->manager->post_value( $this );
|
|
||||||
|
|
||||||
if ( isset( $result ) )
|
|
||||||
return $this->_post_value = $result;
|
|
||||||
else
|
|
||||||
return $default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,20 +32,6 @@ final class WP_Customize_Widgets {
|
||||||
'rss', 'search', 'tag_cloud', 'text',
|
'rss', 'search', 'tag_cloud', 'text',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 3.9.0
|
|
||||||
* @access protected
|
|
||||||
* @var
|
|
||||||
*/
|
|
||||||
protected $_customized;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 3.9.0
|
|
||||||
* @access protected
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_prepreview_added_filters = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 3.9.0
|
* @since 3.9.0
|
||||||
* @access protected
|
* @access protected
|
||||||
|
@ -67,6 +53,18 @@ final class WP_Customize_Widgets {
|
||||||
*/
|
*/
|
||||||
protected $old_sidebars_widgets = array();
|
protected $old_sidebars_widgets = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping of setting type to setting ID pattern.
|
||||||
|
*
|
||||||
|
* @since 4.2.0
|
||||||
|
* @access protected
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $setting_id_patterns = array(
|
||||||
|
'widget_instance' => '/^(widget_.+?)(?:\[(\d+)\])?$/',
|
||||||
|
'sidebar_widgets' => '/^sidebars_widgets\[(.+?)\]$/',
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initial loader.
|
* Initial loader.
|
||||||
*
|
*
|
||||||
|
@ -78,7 +76,8 @@ final class WP_Customize_Widgets {
|
||||||
public function __construct( $manager ) {
|
public function __construct( $manager ) {
|
||||||
$this->manager = $manager;
|
$this->manager = $manager;
|
||||||
|
|
||||||
add_action( 'after_setup_theme', array( $this, 'setup_widget_addition_previews' ) );
|
add_filter( 'customize_dynamic_setting_args', array( $this, 'filter_customize_dynamic_setting_args' ), 10, 2 );
|
||||||
|
add_action( 'after_setup_theme', array( $this, 'register_settings' ) );
|
||||||
add_action( 'wp_loaded', array( $this, 'override_sidebars_widgets_for_theme_switch' ) );
|
add_action( 'wp_loaded', array( $this, 'override_sidebars_widgets_for_theme_switch' ) );
|
||||||
add_action( 'customize_controls_init', array( $this, 'customize_controls_init' ) );
|
add_action( 'customize_controls_init', array( $this, 'customize_controls_init' ) );
|
||||||
add_action( 'customize_register', array( $this, 'schedule_customize_register' ), 1 );
|
add_action( 'customize_register', array( $this, 'schedule_customize_register' ), 1 );
|
||||||
|
@ -94,6 +93,76 @@ final class WP_Customize_Widgets {
|
||||||
add_filter( 'dynamic_sidebar_has_widgets', array( $this, 'tally_sidebars_via_dynamic_sidebar_calls' ), 10, 2 );
|
add_filter( 'dynamic_sidebar_has_widgets', array( $this, 'tally_sidebars_via_dynamic_sidebar_calls' ), 10, 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the widget setting type given a setting ID.
|
||||||
|
*
|
||||||
|
* @since 4.2.0
|
||||||
|
*
|
||||||
|
* @param $setting_id
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
protected function get_setting_type( $setting_id ) {
|
||||||
|
static $cache = array();
|
||||||
|
if ( isset( $cache[ $setting_id ] ) ) {
|
||||||
|
return $cache[ $setting_id ];
|
||||||
|
}
|
||||||
|
foreach ( $this->setting_id_patterns as $type => $pattern ) {
|
||||||
|
if ( preg_match( $pattern, $setting_id ) ) {
|
||||||
|
$cache[ $setting_id ] = $type;
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inspect the incoming customized data for any widget settings, and dynamically add them up-front so widgets will be initialized properly.
|
||||||
|
*
|
||||||
|
* @since 4.2.0
|
||||||
|
*/
|
||||||
|
public function register_settings() {
|
||||||
|
$widget_setting_ids = array();
|
||||||
|
$incoming_setting_ids = array_keys( $this->manager->unsanitized_post_values() );
|
||||||
|
foreach ( $incoming_setting_ids as $setting_id ) {
|
||||||
|
if ( ! is_null( $this->get_setting_type( $setting_id ) ) ) {
|
||||||
|
$widget_setting_ids[] = $setting_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( $this->manager->doing_ajax( 'update-widget' ) && isset( $_REQUEST['widget-id'] ) ) {
|
||||||
|
$widget_setting_ids[] = $this->get_setting_id( wp_unslash( $_REQUEST['widget-id'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = $this->manager->add_dynamic_settings( array_unique( $widget_setting_ids ) );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Preview settings right away so that widgets and sidebars will get registered properly.
|
||||||
|
* But don't do this if a customize_save because this will cause WP to think there is nothing
|
||||||
|
* changed that needs to be saved.
|
||||||
|
*/
|
||||||
|
if ( ! $this->manager->doing_ajax( 'customize_save' ) ) {
|
||||||
|
foreach ( $settings as $setting ) {
|
||||||
|
$setting->preview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the arguments for a dynamically-created setting.
|
||||||
|
*
|
||||||
|
* @since 4.2.0
|
||||||
|
*
|
||||||
|
* @param false|array $args
|
||||||
|
* @param string $setting_id
|
||||||
|
* @return false|array
|
||||||
|
*/
|
||||||
|
public function filter_customize_dynamic_setting_args( $args, $setting_id ) {
|
||||||
|
if ( $this->get_setting_type( $setting_id ) ) {
|
||||||
|
$args = $this->get_setting_args( $setting_id );
|
||||||
|
}
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an unslashed post value or return a default.
|
* Get an unslashed post value or return a default.
|
||||||
*
|
*
|
||||||
|
@ -113,177 +182,6 @@ final class WP_Customize_Widgets {
|
||||||
return wp_unslash( $_POST[ $name ] );
|
return wp_unslash( $_POST[ $name ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up widget addition previews.
|
|
||||||
*
|
|
||||||
* Since the widgets get registered on 'widgets_init' before the Customizer
|
|
||||||
* settings are set up on 'customize_register', we have to filter the options
|
|
||||||
* similarly to how the setting previewer will filter the options later.
|
|
||||||
*
|
|
||||||
* @since 3.9.0
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
public function setup_widget_addition_previews() {
|
|
||||||
$is_customize_preview = false;
|
|
||||||
|
|
||||||
if ( ! empty( $this->manager ) && ! is_admin() && 'on' === $this->get_post_value( 'wp_customize' ) ) {
|
|
||||||
$is_customize_preview = check_ajax_referer( 'preview-customize_' . $this->manager->get_stylesheet(), 'nonce', false );
|
|
||||||
}
|
|
||||||
|
|
||||||
$is_ajax_widget_update = false;
|
|
||||||
if ( $this->manager->doing_ajax() && 'update-widget' === $this->get_post_value( 'action' ) ) {
|
|
||||||
$is_ajax_widget_update = check_ajax_referer( 'update-widget', 'nonce', false );
|
|
||||||
}
|
|
||||||
|
|
||||||
$is_ajax_customize_save = false;
|
|
||||||
if ( $this->manager->doing_ajax() && 'customize_save' === $this->get_post_value( 'action' ) ) {
|
|
||||||
$is_ajax_customize_save = check_ajax_referer( 'save-customize_' . $this->manager->get_stylesheet(), 'nonce', false );
|
|
||||||
}
|
|
||||||
|
|
||||||
$is_valid_request = ( $is_ajax_widget_update || $is_customize_preview || $is_ajax_customize_save );
|
|
||||||
if ( ! $is_valid_request ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Input from Customizer preview.
|
|
||||||
if ( isset( $_POST['customized'] ) ) {
|
|
||||||
$this->_customized = json_decode( $this->get_post_value( 'customized' ), true );
|
|
||||||
} else { // Input from ajax widget update request.
|
|
||||||
$this->_customized = array();
|
|
||||||
$id_base = $this->get_post_value( 'id_base' );
|
|
||||||
$widget_number = $this->get_post_value( 'widget_number', false );
|
|
||||||
$option_name = 'widget_' . $id_base;
|
|
||||||
$this->_customized[ $option_name ] = array();
|
|
||||||
if ( preg_match( '/^[0-9]+$/', $widget_number ) ) {
|
|
||||||
$option_name .= '[' . $widget_number . ']';
|
|
||||||
$this->_customized[ $option_name ][ $widget_number ] = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$function = array( $this, 'prepreview_added_sidebars_widgets' );
|
|
||||||
|
|
||||||
$hook = 'option_sidebars_widgets';
|
|
||||||
add_filter( $hook, $function );
|
|
||||||
$this->_prepreview_added_filters[] = compact( 'hook', 'function' );
|
|
||||||
|
|
||||||
$hook = 'default_option_sidebars_widgets';
|
|
||||||
add_filter( $hook, $function );
|
|
||||||
$this->_prepreview_added_filters[] = compact( 'hook', 'function' );
|
|
||||||
|
|
||||||
$function = array( $this, 'prepreview_added_widget_instance' );
|
|
||||||
foreach ( $this->_customized as $setting_id => $value ) {
|
|
||||||
if ( preg_match( '/^(widget_.+?)(?:\[(\d+)\])?$/', $setting_id, $matches ) ) {
|
|
||||||
$option = $matches[1];
|
|
||||||
|
|
||||||
$hook = sprintf( 'option_%s', $option );
|
|
||||||
if ( ! has_filter( $hook, $function ) ) {
|
|
||||||
add_filter( $hook, $function );
|
|
||||||
$this->_prepreview_added_filters[] = compact( 'hook', 'function' );
|
|
||||||
}
|
|
||||||
|
|
||||||
$hook = sprintf( 'default_option_%s', $option );
|
|
||||||
if ( ! has_filter( $hook, $function ) ) {
|
|
||||||
add_filter( $hook, $function );
|
|
||||||
$this->_prepreview_added_filters[] = compact( 'hook', 'function' );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure the option is registered so that the update_option()
|
|
||||||
* won't fail due to the filters providing a default value, which
|
|
||||||
* causes the update_option() to get confused.
|
|
||||||
*/
|
|
||||||
add_option( $option, array() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that newly-added widgets will appear in the widgets_sidebars.
|
|
||||||
*
|
|
||||||
* This is necessary because the Customizer's setting preview filters
|
|
||||||
* are added after the widgets_init action, which is too late for the
|
|
||||||
* widgets to be set up properly.
|
|
||||||
*
|
|
||||||
* @since 3.9.0
|
|
||||||
* @access public
|
|
||||||
*
|
|
||||||
* @param array $sidebars_widgets Associative array of sidebars and their widgets.
|
|
||||||
* @return array Filtered array of sidebars and their widgets.
|
|
||||||
*/
|
|
||||||
public function prepreview_added_sidebars_widgets( $sidebars_widgets ) {
|
|
||||||
foreach ( $this->_customized as $setting_id => $value ) {
|
|
||||||
if ( preg_match( '/^sidebars_widgets\[(.+?)\]$/', $setting_id, $matches ) ) {
|
|
||||||
$sidebar_id = $matches[1];
|
|
||||||
$sidebars_widgets[ $sidebar_id ] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $sidebars_widgets;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure newly-added widgets have empty instances so they
|
|
||||||
* will be recognized.
|
|
||||||
*
|
|
||||||
* This is necessary because the Customizer's setting preview
|
|
||||||
* filters are added after the widgets_init action, which is
|
|
||||||
* too late for the widgets to be set up properly.
|
|
||||||
*
|
|
||||||
* @since 3.9.0
|
|
||||||
* @access public
|
|
||||||
*
|
|
||||||
* @param array|bool|mixed $value Widget instance(s), false if open was empty.
|
|
||||||
* @return array|mixed Widget instance(s) with additions.
|
|
||||||
*/
|
|
||||||
public function prepreview_added_widget_instance( $value = false ) {
|
|
||||||
if ( ! preg_match( '/^(?:default_)?option_(widget_(.+))/', current_filter(), $matches ) ) {
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
$id_base = $matches[2];
|
|
||||||
|
|
||||||
foreach ( $this->_customized as $setting_id => $setting ) {
|
|
||||||
$parsed_setting_id = $this->parse_widget_setting_id( $setting_id );
|
|
||||||
if ( is_wp_error( $parsed_setting_id ) || $id_base !== $parsed_setting_id['id_base'] ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$widget_number = $parsed_setting_id['number'];
|
|
||||||
|
|
||||||
if ( is_null( $widget_number ) ) {
|
|
||||||
// Single widget.
|
|
||||||
if ( false === $value ) {
|
|
||||||
$value = array();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Multi widget.
|
|
||||||
if ( empty( $value ) ) {
|
|
||||||
$value = array( '_multiwidget' => 1 );
|
|
||||||
}
|
|
||||||
if ( ! isset( $value[ $widget_number ] ) ) {
|
|
||||||
$value[ $widget_number ] = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove pre-preview filters.
|
|
||||||
*
|
|
||||||
* Removes filters added in setup_widget_addition_previews()
|
|
||||||
* to ensure widgets are populating the options during
|
|
||||||
* 'widgets_init'.
|
|
||||||
*
|
|
||||||
* @since 3.9.0
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
public function remove_prepreview_filters() {
|
|
||||||
foreach ( $this->_prepreview_added_filters as $prepreview_added_filter ) {
|
|
||||||
remove_filter( $prepreview_added_filter['hook'], $prepreview_added_filter['function'] );
|
|
||||||
}
|
|
||||||
$this->_prepreview_added_filters = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override sidebars_widgets for theme switch.
|
* Override sidebars_widgets for theme switch.
|
||||||
*
|
*
|
||||||
|
@ -380,7 +278,7 @@ final class WP_Customize_Widgets {
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
public function schedule_customize_register() {
|
public function schedule_customize_register() {
|
||||||
if ( is_admin() ) { // @todo for some reason, $wp_customize->is_preview() is true here?
|
if ( is_admin() ) {
|
||||||
$this->customize_register();
|
$this->customize_register();
|
||||||
} else {
|
} else {
|
||||||
add_action( 'wp', array( $this, 'customize_register' ) );
|
add_action( 'wp', array( $this, 'customize_register' ) );
|
||||||
|
@ -412,12 +310,9 @@ final class WP_Customize_Widgets {
|
||||||
foreach ( array_keys( $wp_registered_widgets ) as $widget_id ) {
|
foreach ( array_keys( $wp_registered_widgets ) as $widget_id ) {
|
||||||
$setting_id = $this->get_setting_id( $widget_id );
|
$setting_id = $this->get_setting_id( $widget_id );
|
||||||
$setting_args = $this->get_setting_args( $setting_id );
|
$setting_args = $this->get_setting_args( $setting_id );
|
||||||
|
if ( ! $this->manager->get_setting( $setting_id ) ) {
|
||||||
$setting_args['sanitize_callback'] = array( $this, 'sanitize_widget_instance' );
|
|
||||||
$setting_args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' );
|
|
||||||
|
|
||||||
$this->manager->add_setting( $setting_id, $setting_args );
|
$this->manager->add_setting( $setting_id, $setting_args );
|
||||||
|
}
|
||||||
$new_setting_ids[] = $setting_id;
|
$new_setting_ids[] = $setting_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,11 +347,9 @@ final class WP_Customize_Widgets {
|
||||||
if ( $is_registered_sidebar || $is_inactive_widgets ) {
|
if ( $is_registered_sidebar || $is_inactive_widgets ) {
|
||||||
$setting_id = sprintf( 'sidebars_widgets[%s]', $sidebar_id );
|
$setting_id = sprintf( 'sidebars_widgets[%s]', $sidebar_id );
|
||||||
$setting_args = $this->get_setting_args( $setting_id );
|
$setting_args = $this->get_setting_args( $setting_id );
|
||||||
|
if ( ! $this->manager->get_setting( $setting_id ) ) {
|
||||||
$setting_args['sanitize_callback'] = array( $this, 'sanitize_sidebar_widgets' );
|
|
||||||
$setting_args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' );
|
|
||||||
|
|
||||||
$this->manager->add_setting( $setting_id, $setting_args );
|
$this->manager->add_setting( $setting_id, $setting_args );
|
||||||
|
}
|
||||||
$new_setting_ids[] = $setting_id;
|
$new_setting_ids[] = $setting_id;
|
||||||
|
|
||||||
// Add section to contain controls.
|
// Add section to contain controls.
|
||||||
|
@ -523,16 +416,13 @@ final class WP_Customize_Widgets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if ( ! $this->manager->doing_ajax( 'customize_save' ) ) {
|
||||||
* We have to register these settings later than customize_preview_init
|
|
||||||
* so that other filters have had a chance to run.
|
|
||||||
*/
|
|
||||||
if ( did_action( 'customize_preview_init' ) ) {
|
|
||||||
foreach ( $new_setting_ids as $new_setting_id ) {
|
foreach ( $new_setting_ids as $new_setting_id ) {
|
||||||
$this->manager->get_setting( $new_setting_id )->preview();
|
$this->manager->get_setting( $new_setting_id )->preview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->remove_prepreview_filters();
|
|
||||||
|
add_filter( 'sidebars_widgets', array( $this, 'preview_sidebars_widgets' ), 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -804,6 +694,15 @@ final class WP_Customize_Widgets {
|
||||||
'transport' => 'refresh',
|
'transport' => 'refresh',
|
||||||
'default' => array(),
|
'default' => array(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( preg_match( $this->setting_id_patterns['sidebar_widgets'], $id, $matches ) ) {
|
||||||
|
$args['sanitize_callback'] = array( $this, 'sanitize_sidebar_widgets' );
|
||||||
|
$args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' );
|
||||||
|
} else if ( preg_match( $this->setting_id_patterns['widget_instance'], $id, $matches ) ) {
|
||||||
|
$args['sanitize_callback'] = array( $this, 'sanitize_widget_instance' );
|
||||||
|
$args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' );
|
||||||
|
}
|
||||||
|
|
||||||
$args = array_merge( $args, $overrides );
|
$args = array_merge( $args, $overrides );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -831,15 +730,10 @@ final class WP_Customize_Widgets {
|
||||||
* @return array Array of sanitized widget IDs.
|
* @return array Array of sanitized widget IDs.
|
||||||
*/
|
*/
|
||||||
public function sanitize_sidebar_widgets( $widget_ids ) {
|
public function sanitize_sidebar_widgets( $widget_ids ) {
|
||||||
global $wp_registered_widgets;
|
|
||||||
|
|
||||||
$widget_ids = array_map( 'strval', (array) $widget_ids );
|
$widget_ids = array_map( 'strval', (array) $widget_ids );
|
||||||
$sanitized_widget_ids = array();
|
$sanitized_widget_ids = array();
|
||||||
|
|
||||||
foreach ( $widget_ids as $widget_id ) {
|
foreach ( $widget_ids as $widget_id ) {
|
||||||
if ( array_key_exists( $widget_id, $wp_registered_widgets ) ) {
|
$sanitized_widget_ids[] = preg_replace( '/[^a-z0-9_\-]/', '', $widget_id );
|
||||||
$sanitized_widget_ids[] = $widget_id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return $sanitized_widget_ids;
|
return $sanitized_widget_ids;
|
||||||
}
|
}
|
||||||
|
@ -974,7 +868,6 @@ final class WP_Customize_Widgets {
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
public function customize_preview_init() {
|
public function customize_preview_init() {
|
||||||
add_filter( 'sidebars_widgets', array( $this, 'preview_sidebars_widgets' ), 1 );
|
|
||||||
add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue' ) );
|
add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue' ) );
|
||||||
add_action( 'wp_print_styles', array( $this, 'print_preview_css' ), 1 );
|
add_action( 'wp_print_styles', array( $this, 'print_preview_css' ), 1 );
|
||||||
add_action( 'wp_footer', array( $this, 'export_preview_data' ), 20 );
|
add_action( 'wp_footer', array( $this, 'export_preview_data' ), 20 );
|
||||||
|
@ -1333,25 +1226,31 @@ final class WP_Customize_Widgets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain the widget control with the updated instance in place.
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
$form = $wp_registered_widget_controls[$widget_id];
|
|
||||||
if ( $form ) {
|
|
||||||
call_user_func_array( $form['callback'], $form['params'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
$form = ob_get_clean();
|
|
||||||
|
|
||||||
// Obtain the widget instance.
|
// Obtain the widget instance.
|
||||||
$option = get_option( $option_name );
|
$option = $this->get_captured_option( $option_name );
|
||||||
|
|
||||||
if ( null !== $parsed_id['number'] ) {
|
if ( null !== $parsed_id['number'] ) {
|
||||||
$instance = $option[ $parsed_id['number'] ];
|
$instance = $option[ $parsed_id['number'] ];
|
||||||
} else {
|
} else {
|
||||||
$instance = $option;
|
$instance = $option;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Override the incoming $_POST['customized'] for a newly-created widget's
|
||||||
|
* setting with the new $instance so that the preview filter currently
|
||||||
|
* in place from WP_Customize_Setting::preview() will use this value
|
||||||
|
* instead of the default widget instance value (an empty array).
|
||||||
|
*/
|
||||||
|
$setting_id = $this->get_setting_id( $widget_id );
|
||||||
|
$this->manager->set_post_value( $setting_id, $instance );
|
||||||
|
|
||||||
|
// Obtain the widget control with the updated instance in place.
|
||||||
|
ob_start();
|
||||||
|
$form = $wp_registered_widget_controls[ $widget_id ];
|
||||||
|
if ( $form ) {
|
||||||
|
call_user_func_array( $form['callback'], $form['params'] );
|
||||||
|
}
|
||||||
|
$form = ob_get_clean();
|
||||||
|
|
||||||
$this->stop_capturing_option_updates();
|
$this->stop_capturing_option_updates();
|
||||||
|
|
||||||
return compact( 'instance', 'form' );
|
return compact( 'instance', 'form' );
|
||||||
|
@ -1383,8 +1282,8 @@ final class WP_Customize_Widgets {
|
||||||
wp_die( -1 );
|
wp_die( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! isset( $_POST['widget-id'] ) ) {
|
if ( empty( $_POST['widget-id'] ) ) {
|
||||||
wp_send_json_error();
|
wp_send_json_error( 'missing_widget-id' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This action is documented in wp-admin/includes/ajax-actions.php */
|
/** This action is documented in wp-admin/includes/ajax-actions.php */
|
||||||
|
@ -1400,13 +1299,20 @@ final class WP_Customize_Widgets {
|
||||||
$parsed_id = $this->parse_widget_id( $widget_id );
|
$parsed_id = $this->parse_widget_id( $widget_id );
|
||||||
$id_base = $parsed_id['id_base'];
|
$id_base = $parsed_id['id_base'];
|
||||||
|
|
||||||
if ( isset( $_POST['widget-' . $id_base] ) && is_array( $_POST['widget-' . $id_base] ) && preg_match( '/__i__|%i%/', key( $_POST['widget-' . $id_base] ) ) ) {
|
$is_updating_widget_template = (
|
||||||
wp_send_json_error();
|
isset( $_POST[ 'widget-' . $id_base ] )
|
||||||
|
&&
|
||||||
|
is_array( $_POST[ 'widget-' . $id_base ] )
|
||||||
|
&&
|
||||||
|
preg_match( '/__i__|%i%/', key( $_POST[ 'widget-' . $id_base ] ) )
|
||||||
|
);
|
||||||
|
if ( $is_updating_widget_template ) {
|
||||||
|
wp_send_json_error( 'template_widget_not_updatable' );
|
||||||
}
|
}
|
||||||
|
|
||||||
$updated_widget = $this->call_widget_update( $widget_id ); // => {instance,form}
|
$updated_widget = $this->call_widget_update( $widget_id ); // => {instance,form}
|
||||||
if ( is_wp_error( $updated_widget ) ) {
|
if ( is_wp_error( $updated_widget ) ) {
|
||||||
wp_send_json_error();
|
wp_send_json_error( $updated_widget->get_error_message() );
|
||||||
}
|
}
|
||||||
|
|
||||||
$form = $updated_widget['form'];
|
$form = $updated_widget['form'];
|
||||||
|
@ -1462,6 +1368,25 @@ final class WP_Customize_Widgets {
|
||||||
return $this->_captured_options;
|
return $this->_captured_options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the option that was captured from being saved.
|
||||||
|
*
|
||||||
|
* @since 4.2.0
|
||||||
|
* @access protected
|
||||||
|
*
|
||||||
|
* @param string $option_name Option name.
|
||||||
|
* @param mixed $default Optional. Default value to return if the option does not exist.
|
||||||
|
* @return mixed Value set for the option.
|
||||||
|
*/
|
||||||
|
protected function get_captured_option( $option_name, $default = false ) {
|
||||||
|
if ( array_key_exists( $option_name, $this->_captured_options ) ) {
|
||||||
|
$value = $this->_captured_options[ $option_name ];
|
||||||
|
} else {
|
||||||
|
$value = $default;
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of captured widget option updates.
|
* Get the number of captured widget option updates.
|
||||||
*
|
*
|
||||||
|
@ -1496,10 +1421,10 @@ final class WP_Customize_Widgets {
|
||||||
* @since 3.9.0
|
* @since 3.9.0
|
||||||
* @access public
|
* @access public
|
||||||
*
|
*
|
||||||
* @param mixed $new_value
|
* @param mixed $new_value The new option value.
|
||||||
* @param string $option_name
|
* @param string $option_name Name of the option.
|
||||||
* @param mixed $old_value
|
* @param mixed $old_value The old option value.
|
||||||
* @return mixed
|
* @return mixed Filtered option value.
|
||||||
*/
|
*/
|
||||||
public function capture_filter_pre_update_option( $new_value, $option_name, $old_value ) {
|
public function capture_filter_pre_update_option( $new_value, $option_name, $old_value ) {
|
||||||
if ( $this->is_option_capture_ignored( $option_name ) ) {
|
if ( $this->is_option_capture_ignored( $option_name ) ) {
|
||||||
|
@ -1521,8 +1446,8 @@ final class WP_Customize_Widgets {
|
||||||
* @since 3.9.0
|
* @since 3.9.0
|
||||||
* @access public
|
* @access public
|
||||||
*
|
*
|
||||||
* @param mixed $value Option
|
* @param mixed $value Value to return instead of the option value.
|
||||||
* @return mixed
|
* @return mixed Filtered option value.
|
||||||
*/
|
*/
|
||||||
public function capture_filter_pre_get_option( $value ) {
|
public function capture_filter_pre_get_option( $value ) {
|
||||||
$option_name = preg_replace( '/^pre_option_/', '', current_filter() );
|
$option_name = preg_replace( '/^pre_option_/', '', current_filter() );
|
||||||
|
@ -1557,4 +1482,36 @@ final class WP_Customize_Widgets {
|
||||||
$this->_captured_options = array();
|
$this->_captured_options = array();
|
||||||
$this->_is_capturing_option_updates = false;
|
$this->_is_capturing_option_updates = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.9.0
|
||||||
|
* @deprecated 4.2.0 Deprecated in favor of customize_dynamic_setting_args filter.
|
||||||
|
*/
|
||||||
|
public function setup_widget_addition_previews() {
|
||||||
|
_deprecated_function( __METHOD__, '4.2.0' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.9.0
|
||||||
|
* @deprecated 4.2.0 Deprecated in favor of customize_dynamic_setting_args filter.
|
||||||
|
*/
|
||||||
|
public function prepreview_added_sidebars_widgets() {
|
||||||
|
_deprecated_function( __METHOD__, '4.2.0' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.9.0
|
||||||
|
* @deprecated 4.2.0 Deprecated in favor of customize_dynamic_setting_args filter.
|
||||||
|
*/
|
||||||
|
public function prepreview_added_widget_instance() {
|
||||||
|
_deprecated_function( __METHOD__, '4.2.0' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.9.0
|
||||||
|
* @deprecated 4.2.0 Deprecated in favor of customize_dynamic_setting_args filter.
|
||||||
|
*/
|
||||||
|
public function remove_prepreview_filters() {
|
||||||
|
_deprecated_function( __METHOD__, '4.2.0' );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*
|
*
|
||||||
* @global string $wp_version
|
* @global string $wp_version
|
||||||
*/
|
*/
|
||||||
$wp_version = '4.2-alpha-31369';
|
$wp_version = '4.2-alpha-31370';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||||
|
|
Loading…
Reference in New Issue