wp/wp-includes/class-wp-customize-widgets.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    92 	/**
    92 	/**
    93 	 * Initial loader.
    93 	 * Initial loader.
    94 	 *
    94 	 *
    95 	 * @since 3.9.0
    95 	 * @since 3.9.0
    96 	 *
    96 	 *
    97 	 * @param WP_Customize_Manager $manager Customize manager bootstrap instance.
    97 	 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
    98 	 */
    98 	 */
    99 	public function __construct( $manager ) {
    99 	public function __construct( $manager ) {
   100 		$this->manager = $manager;
   100 		$this->manager = $manager;
   101 
   101 
   102 		// See https://github.com/xwp/wp-customize-snapshots/blob/962586659688a5b1fd9ae93618b7ce2d4e7a421c/php/class-customize-snapshot-manager.php#L420-L449
   102 		// See https://github.com/xwp/wp-customize-snapshots/blob/962586659688a5b1fd9ae93618b7ce2d4e7a421c/php/class-customize-snapshot-manager.php#L420-L449
   171 	/**
   171 	/**
   172 	 * Retrieves the widget setting type given a setting ID.
   172 	 * Retrieves the widget setting type given a setting ID.
   173 	 *
   173 	 *
   174 	 * @since 4.2.0
   174 	 * @since 4.2.0
   175 	 *
   175 	 *
   176 	 * @staticvar array $cache
       
   177 	 *
       
   178 	 * @param string $setting_id Setting ID.
   176 	 * @param string $setting_id Setting ID.
   179 	 * @return string|void Setting type.
   177 	 * @return string|void Setting type.
   180 	 */
   178 	 */
   181 	protected function get_setting_type( $setting_id ) {
   179 	protected function get_setting_type( $setting_id ) {
   182 		static $cache = array();
   180 		static $cache = array();
   223 	 *
   221 	 *
   224 	 * @since 4.2.0
   222 	 * @since 4.2.0
   225 	 *
   223 	 *
   226 	 * @param false|array $args       The arguments to the WP_Customize_Setting constructor.
   224 	 * @param false|array $args       The arguments to the WP_Customize_Setting constructor.
   227 	 * @param string      $setting_id ID for dynamic setting, usually coming from `$_POST['customized']`.
   225 	 * @param string      $setting_id ID for dynamic setting, usually coming from `$_POST['customized']`.
   228 	 * @return false|array Setting arguments, false otherwise.
   226 	 * @return array|false Setting arguments, false otherwise.
   229 	 */
   227 	 */
   230 	public function filter_customize_dynamic_setting_args( $args, $setting_id ) {
   228 	public function filter_customize_dynamic_setting_args( $args, $setting_id ) {
   231 		if ( $this->get_setting_type( $setting_id ) ) {
   229 		if ( $this->get_setting_type( $setting_id ) ) {
   232 			$args = $this->get_setting_args( $setting_id );
   230 			$args = $this->get_setting_args( $setting_id );
   233 		}
   231 		}
   274 
   272 
   275 		$this->old_sidebars_widgets = wp_get_sidebars_widgets();
   273 		$this->old_sidebars_widgets = wp_get_sidebars_widgets();
   276 		add_filter( 'customize_value_old_sidebars_widgets_data', array( $this, 'filter_customize_value_old_sidebars_widgets_data' ) );
   274 		add_filter( 'customize_value_old_sidebars_widgets_data', array( $this, 'filter_customize_value_old_sidebars_widgets_data' ) );
   277 		$this->manager->set_post_value( 'old_sidebars_widgets_data', $this->old_sidebars_widgets ); // Override any value cached in changeset.
   275 		$this->manager->set_post_value( 'old_sidebars_widgets_data', $this->old_sidebars_widgets ); // Override any value cached in changeset.
   278 
   276 
   279 		// retrieve_widgets() looks at the global $sidebars_widgets
   277 		// retrieve_widgets() looks at the global $sidebars_widgets.
   280 		$sidebars_widgets = $this->old_sidebars_widgets;
   278 		$sidebars_widgets = $this->old_sidebars_widgets;
   281 		$sidebars_widgets = retrieve_widgets( 'customize' );
   279 		$sidebars_widgets = retrieve_widgets( 'customize' );
   282 		add_filter( 'option_sidebars_widgets', array( $this, 'filter_option_sidebars_widgets_for_theme_switch' ), 1 );
   280 		add_filter( 'option_sidebars_widgets', array( $this, 'filter_option_sidebars_widgets_for_theme_switch' ), 1 );
   283 		// reset global cache var used by wp_get_sidebars_widgets()
   281 		// Reset global cache var used by wp_get_sidebars_widgets().
   284 		unset( $GLOBALS['_wp_sidebars_widgets'] );
   282 		unset( $GLOBALS['_wp_sidebars_widgets'] );
   285 	}
   283 	}
   286 
   284 
   287 	/**
   285 	/**
   288 	 * Filters old_sidebars_widgets_data Customizer setting.
   286 	 * Filters old_sidebars_widgets_data Customizer setting.
   331 	 *
   329 	 *
   332 	 * @since 3.9.0
   330 	 * @since 3.9.0
   333 	 */
   331 	 */
   334 	public function customize_controls_init() {
   332 	public function customize_controls_init() {
   335 		/** This action is documented in wp-admin/includes/ajax-actions.php */
   333 		/** This action is documented in wp-admin/includes/ajax-actions.php */
   336 		do_action( 'load-widgets.php' );
   334 		do_action( 'load-widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
   337 
   335 
   338 		/** This action is documented in wp-admin/includes/ajax-actions.php */
   336 		/** This action is documented in wp-admin/includes/ajax-actions.php */
   339 		do_action( 'widgets.php' );
   337 		do_action( 'widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
   340 
   338 
   341 		/** This action is documented in wp-admin/widgets.php */
   339 		/** This action is documented in wp-admin/widgets.php */
   342 		do_action( 'sidebar_admin_setup' );
   340 		do_action( 'sidebar_admin_setup' );
   343 	}
   341 	}
   344 
   342 
   448 				if ( $is_active_sidebar ) {
   446 				if ( $is_active_sidebar ) {
   449 
   447 
   450 					$section_args = array(
   448 					$section_args = array(
   451 						'title'       => $wp_registered_sidebars[ $sidebar_id ]['name'],
   449 						'title'       => $wp_registered_sidebars[ $sidebar_id ]['name'],
   452 						'description' => $wp_registered_sidebars[ $sidebar_id ]['description'],
   450 						'description' => $wp_registered_sidebars[ $sidebar_id ]['description'],
   453 						'priority'    => array_search( $sidebar_id, array_keys( $wp_registered_sidebars ) ),
   451 						'priority'    => array_search( $sidebar_id, array_keys( $wp_registered_sidebars ), true ),
   454 						'panel'       => 'widgets',
   452 						'panel'       => 'widgets',
   455 						'sidebar_id'  => $sidebar_id,
   453 						'sidebar_id'  => $sidebar_id,
   456 					);
   454 					);
   457 
   455 
   458 					/**
   456 					/**
   565 	 * This method will return all Core widgets as being not wide, but this can be
   563 	 * This method will return all Core widgets as being not wide, but this can be
   566 	 * overridden with the {@see 'is_wide_widget_in_customizer'} filter.
   564 	 * overridden with the {@see 'is_wide_widget_in_customizer'} filter.
   567 	 *
   565 	 *
   568 	 * @since 3.9.0
   566 	 * @since 3.9.0
   569 	 *
   567 	 *
   570 	 * @global $wp_registered_widget_controls
   568 	 * @global array $wp_registered_widget_controls
   571 	 *
   569 	 *
   572 	 * @param string $widget_id Widget ID.
   570 	 * @param string $widget_id Widget ID.
   573 	 * @return bool Whether or not the widget is a "wide" widget.
   571 	 * @return bool Whether or not the widget is a "wide" widget.
   574 	 */
   572 	 */
   575 	public function is_wide_widget( $widget_id ) {
   573 	public function is_wide_widget( $widget_id ) {
   576 		global $wp_registered_widget_controls;
   574 		global $wp_registered_widget_controls;
   577 
   575 
   578 		$parsed_widget_id = $this->parse_widget_id( $widget_id );
   576 		$parsed_widget_id = $this->parse_widget_id( $widget_id );
   579 		$width            = $wp_registered_widget_controls[ $widget_id ]['width'];
   577 		$width            = $wp_registered_widget_controls[ $widget_id ]['width'];
   580 		$is_core          = in_array( $parsed_widget_id['id_base'], $this->core_widget_id_bases );
   578 		$is_core          = in_array( $parsed_widget_id['id_base'], $this->core_widget_id_bases, true );
   581 		$is_wide          = ( $width > 250 && ! $is_core );
   579 		$is_wide          = ( $width > 250 && ! $is_core );
   582 
   580 
   583 		/**
   581 		/**
   584 		 * Filters whether the given widget is considered "wide".
   582 		 * Filters whether the given widget is considered "wide".
   585 		 *
   583 		 *
   607 
   605 
   608 		if ( preg_match( '/^(.+)-(\d+)$/', $widget_id, $matches ) ) {
   606 		if ( preg_match( '/^(.+)-(\d+)$/', $widget_id, $matches ) ) {
   609 			$parsed['id_base'] = $matches[1];
   607 			$parsed['id_base'] = $matches[1];
   610 			$parsed['number']  = intval( $matches[2] );
   608 			$parsed['number']  = intval( $matches[2] );
   611 		} else {
   609 		} else {
   612 			// likely an old single widget
   610 			// Likely an old single widget.
   613 			$parsed['id_base'] = $widget_id;
   611 			$parsed['id_base'] = $widget_id;
   614 		}
   612 		}
   615 		return $parsed;
   613 		return $parsed;
   616 	}
   614 	}
   617 
   615 
   619 	 * Converts a widget setting ID (option path) to its id_base and number components.
   617 	 * Converts a widget setting ID (option path) to its id_base and number components.
   620 	 *
   618 	 *
   621 	 * @since 3.9.0
   619 	 * @since 3.9.0
   622 	 *
   620 	 *
   623 	 * @param string $setting_id Widget setting ID.
   621 	 * @param string $setting_id Widget setting ID.
   624 	 * @return WP_Error|array Array containing a widget's id_base and number components,
   622 	 * @return array|WP_Error Array containing a widget's id_base and number components,
   625 	 *                        or a WP_Error object.
   623 	 *                        or a WP_Error object.
   626 	 */
   624 	 */
   627 	public function parse_widget_setting_id( $setting_id ) {
   625 	public function parse_widget_setting_id( $setting_id ) {
   628 		if ( ! preg_match( '/^(widget_(.+?))(?:\[(\d+)\])?$/', $setting_id, $matches ) ) {
   626 		if ( ! preg_match( '/^(widget_(.+?))(?:\[(\d+)\])?$/', $setting_id, $matches ) ) {
   629 			return new WP_Error( 'widget_setting_invalid_id' );
   627 			return new WP_Error( 'widget_setting_invalid_id' );
   641 	 *
   639 	 *
   642 	 * @since 3.9.0
   640 	 * @since 3.9.0
   643 	 */
   641 	 */
   644 	public function print_styles() {
   642 	public function print_styles() {
   645 		/** This action is documented in wp-admin/admin-header.php */
   643 		/** This action is documented in wp-admin/admin-header.php */
   646 		do_action( 'admin_print_styles-widgets.php' );
   644 		do_action( 'admin_print_styles-widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
   647 
   645 
   648 		/** This action is documented in wp-admin/admin-header.php */
   646 		/** This action is documented in wp-admin/admin-header.php */
   649 		do_action( 'admin_print_styles' );
   647 		do_action( 'admin_print_styles' );
   650 	}
   648 	}
   651 
   649 
   655 	 *
   653 	 *
   656 	 * @since 3.9.0
   654 	 * @since 3.9.0
   657 	 */
   655 	 */
   658 	public function print_scripts() {
   656 	public function print_scripts() {
   659 		/** This action is documented in wp-admin/admin-header.php */
   657 		/** This action is documented in wp-admin/admin-header.php */
   660 		do_action( 'admin_print_scripts-widgets.php' );
   658 		do_action( 'admin_print_scripts-widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
   661 
   659 
   662 		/** This action is documented in wp-admin/admin-header.php */
   660 		/** This action is documented in wp-admin/admin-header.php */
   663 		do_action( 'admin_print_scripts' );
   661 		do_action( 'admin_print_scripts' );
   664 	}
   662 	}
   665 
   663 
   730 		);
   728 		);
   731 		$registered_sidebar_count            = count( $wp_registered_sidebars );
   729 		$registered_sidebar_count            = count( $wp_registered_sidebars );
   732 		for ( $non_rendered_count = 2; $non_rendered_count < $registered_sidebar_count; $non_rendered_count++ ) {
   730 		for ( $non_rendered_count = 2; $non_rendered_count < $registered_sidebar_count; $non_rendered_count++ ) {
   733 			$some_non_rendered_areas_messages[ $non_rendered_count ] = html_entity_decode(
   731 			$some_non_rendered_areas_messages[ $non_rendered_count ] = html_entity_decode(
   734 				sprintf(
   732 				sprintf(
   735 					/* translators: %s: the number of other widget areas registered but not rendered */
   733 					/* translators: %s: The number of other widget areas registered but not rendered. */
   736 					_n(
   734 					_n(
   737 						'Your theme has %s other widget area, but this particular page doesn&#8217;t display it.',
   735 						'Your theme has %s other widget area, but this particular page doesn&#8217;t display it.',
   738 						'Your theme has %s other widget areas, but this particular page doesn&#8217;t display them.',
   736 						'Your theme has %s other widget areas, but this particular page doesn&#8217;t display them.',
   739 						$non_rendered_count
   737 						$non_rendered_count
   740 					),
   738 					),
   754 				get_bloginfo( 'charset' )
   752 				get_bloginfo( 'charset' )
   755 			);
   753 			);
   756 		} else {
   754 		} else {
   757 			$no_areas_shown_message = html_entity_decode(
   755 			$no_areas_shown_message = html_entity_decode(
   758 				sprintf(
   756 				sprintf(
   759 					/* translators: %s: the total number of widget areas registered */
   757 					/* translators: %s: The total number of widget areas registered. */
   760 					_n(
   758 					_n(
   761 						'Your theme has %s widget area, but this particular page doesn&#8217;t display it.',
   759 						'Your theme has %s widget area, but this particular page doesn&#8217;t display it.',
   762 						'Your theme has %s widget areas, but this particular page doesn&#8217;t display them.',
   760 						'Your theme has %s widget areas, but this particular page doesn&#8217;t display them.',
   763 						$registered_sidebar_count
   761 						$registered_sidebar_count
   764 					),
   762 					),
   770 		}
   768 		}
   771 
   769 
   772 		$settings = array(
   770 		$settings = array(
   773 			'registeredSidebars'          => array_values( $wp_registered_sidebars ),
   771 			'registeredSidebars'          => array_values( $wp_registered_sidebars ),
   774 			'registeredWidgets'           => $wp_registered_widgets,
   772 			'registeredWidgets'           => $wp_registered_widgets,
   775 			'availableWidgets'            => $available_widgets, // @todo Merge this with registered_widgets
   773 			'availableWidgets'            => $available_widgets, // @todo Merge this with registered_widgets.
   776 			'l10n'                        => array(
   774 			'l10n'                        => array(
   777 				'saveBtnLabel'     => __( 'Apply' ),
   775 				'saveBtnLabel'     => __( 'Apply' ),
   778 				'saveBtnTooltip'   => __( 'Save and preview changes before publishing them.' ),
   776 				'saveBtnTooltip'   => __( 'Save and preview changes before publishing them.' ),
   779 				'removeBtnLabel'   => __( 'Remove' ),
   777 				'removeBtnLabel'   => __( 'Remove' ),
   780 				'removeBtnTooltip' => __( 'Keep widget settings and move it to the inactive widgets' ),
   778 				'removeBtnTooltip' => __( 'Keep widget settings and move it to the inactive widgets' ),
   785 				'someAreasShown'   => $some_non_rendered_areas_messages,
   783 				'someAreasShown'   => $some_non_rendered_areas_messages,
   786 				'noAreasShown'     => $no_areas_shown_message,
   784 				'noAreasShown'     => $no_areas_shown_message,
   787 				'reorderModeOn'    => __( 'Reorder mode enabled' ),
   785 				'reorderModeOn'    => __( 'Reorder mode enabled' ),
   788 				'reorderModeOff'   => __( 'Reorder mode closed' ),
   786 				'reorderModeOff'   => __( 'Reorder mode closed' ),
   789 				'reorderLabelOn'   => esc_attr__( 'Reorder widgets' ),
   787 				'reorderLabelOn'   => esc_attr__( 'Reorder widgets' ),
   790 				/* translators: %d: the number of widgets found */
   788 				/* translators: %d: The number of widgets found. */
   791 				'widgetsFound'     => __( 'Number of widgets found: %d' ),
   789 				'widgetsFound'     => __( 'Number of widgets found: %d' ),
   792 				'noWidgetsFound'   => __( 'No widgets found.' ),
   790 				'noWidgetsFound'   => __( 'No widgets found.' ),
   793 			),
   791 			),
   794 			'tpl'                         => array(
   792 			'tpl'                         => array(
   795 				'widgetReorderNav' => $widget_reorder_nav_tpl,
   793 				'widgetReorderNav' => $widget_reorder_nav_tpl,
   797 			),
   795 			),
   798 			'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(),
   796 			'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(),
   799 		);
   797 		);
   800 
   798 
   801 		foreach ( $settings['registeredWidgets'] as &$registered_widget ) {
   799 		foreach ( $settings['registeredWidgets'] as &$registered_widget ) {
   802 			unset( $registered_widget['callback'] ); // may not be JSON-serializeable
   800 			unset( $registered_widget['callback'] ); // May not be JSON-serializeable.
   803 		}
   801 		}
   804 
   802 
   805 		$wp_scripts->add_data(
   803 		$wp_scripts->add_data(
   806 			'customize-widgets',
   804 			'customize-widgets',
   807 			'data',
   805 			'data',
   823 					<span class="screen-reader-text"><?php _e( 'Back' ); ?></span>
   821 					<span class="screen-reader-text"><?php _e( 'Back' ); ?></span>
   824 				</button>
   822 				</button>
   825 				<h3>
   823 				<h3>
   826 					<span class="customize-action">
   824 					<span class="customize-action">
   827 					<?php
   825 					<?php
   828 						/* translators: &#9656; is the unicode right-pointing triangle, and %s is the section title in the Customizer */
   826 						/* translators: &#9656; is the unicode right-pointing triangle. %s: Section title in the Customizer. */
   829 						echo sprintf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( 'widgets' )->title ) );
   827 						printf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( 'widgets' )->title ) );
   830 					?>
   828 					?>
   831 					</span>
   829 					</span>
   832 					<?php _e( 'Add a Widget' ); ?>
   830 					<?php _e( 'Add a Widget' ); ?>
   833 				</h3>
   831 				</h3>
   834 			</div>
   832 			</div>
   858 	 *
   856 	 *
   859 	 * @since 3.9.0
   857 	 * @since 3.9.0
   860 	 */
   858 	 */
   861 	public function print_footer_scripts() {
   859 	public function print_footer_scripts() {
   862 		/** This action is documented in wp-admin/admin-footer.php */
   860 		/** This action is documented in wp-admin/admin-footer.php */
   863 		do_action( 'admin_print_footer_scripts-widgets.php' );
   861 		do_action( 'admin_print_footer_scripts-widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
   864 
   862 
   865 		/** This action is documented in wp-admin/admin-footer.php */
   863 		/** This action is documented in wp-admin/admin-footer.php */
   866 		do_action( 'admin_print_footer_scripts' );
   864 		do_action( 'admin_print_footer_scripts' );
   867 
   865 
   868 		/** This action is documented in wp-admin/admin-footer.php */
   866 		/** This action is documented in wp-admin/admin-footer.php */
   869 		do_action( 'admin_footer-widgets.php' );
   867 		do_action( 'admin_footer-widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
   870 	}
   868 	}
   871 
   869 
   872 	/**
   870 	/**
   873 	 * Retrieves common arguments to supply when constructing a Customizer setting.
   871 	 * Retrieves common arguments to supply when constructing a Customizer setting.
   874 	 *
   872 	 *
   934 	 *
   932 	 *
   935 	 * @since 3.9.0
   933 	 * @since 3.9.0
   936 	 *
   934 	 *
   937 	 * @global array $wp_registered_widgets
   935 	 * @global array $wp_registered_widgets
   938 	 * @global array $wp_registered_widget_controls
   936 	 * @global array $wp_registered_widget_controls
   939 	 * @staticvar array $available_widgets
       
   940 	 *
   937 	 *
   941 	 * @see wp_list_widgets()
   938 	 * @see wp_list_widgets()
   942 	 *
   939 	 *
   943 	 * @return array List of available widgets.
   940 	 * @return array List of available widgets.
   944 	 */
   941 	 */
   947 		if ( ! empty( $available_widgets ) ) {
   944 		if ( ! empty( $available_widgets ) ) {
   948 			return $available_widgets;
   945 			return $available_widgets;
   949 		}
   946 		}
   950 
   947 
   951 		global $wp_registered_widgets, $wp_registered_widget_controls;
   948 		global $wp_registered_widgets, $wp_registered_widget_controls;
   952 		require_once ABSPATH . 'wp-admin/includes/widgets.php'; // for next_widget_id_number()
   949 		require_once ABSPATH . 'wp-admin/includes/widgets.php'; // For next_widget_id_number().
   953 
   950 
   954 		$sort = $wp_registered_widgets;
   951 		$sort = $wp_registered_widgets;
   955 		usort( $sort, array( $this, '_sort_name_callback' ) );
   952 		usort( $sort, array( $this, '_sort_name_callback' ) );
   956 		$done = array();
   953 		$done = array();
   957 
   954 
   958 		foreach ( $sort as $widget ) {
   955 		foreach ( $sort as $widget ) {
   959 			if ( in_array( $widget['callback'], $done, true ) ) { // We already showed this multi-widget
   956 			if ( in_array( $widget['callback'], $done, true ) ) { // We already showed this multi-widget.
   960 				continue;
   957 				continue;
   961 			}
   958 			}
   962 
   959 
   963 			$sidebar = is_active_widget( $widget['callback'], $widget['id'], false, false );
   960 			$sidebar = is_active_widget( $widget['callback'], $widget['id'], false, false );
   964 			$done[]  = $widget['callback'];
   961 			$done[]  = $widget['callback'];
   966 			if ( ! isset( $widget['params'][0] ) ) {
   963 			if ( ! isset( $widget['params'][0] ) ) {
   967 				$widget['params'][0] = array();
   964 				$widget['params'][0] = array();
   968 			}
   965 			}
   969 
   966 
   970 			$available_widget = $widget;
   967 			$available_widget = $widget;
   971 			unset( $available_widget['callback'] ); // not serializable to JSON
   968 			unset( $available_widget['callback'] ); // Not serializable to JSON.
   972 
   969 
   973 			$args = array(
   970 			$args = array(
   974 				'widget_id'   => $widget['id'],
   971 				'widget_id'   => $widget['id'],
   975 				'widget_name' => $widget['name'],
   972 				'widget_name' => $widget['name'],
   976 				'_display'    => 'template',
   973 				'_display'    => 'template',
  1005 				$available_widget,
  1002 				$available_widget,
  1006 				array(
  1003 				array(
  1007 					'temp_id'      => isset( $args['_temp_id'] ) ? $args['_temp_id'] : null,
  1004 					'temp_id'      => isset( $args['_temp_id'] ) ? $args['_temp_id'] : null,
  1008 					'is_multi'     => $is_multi_widget,
  1005 					'is_multi'     => $is_multi_widget,
  1009 					'control_tpl'  => $control_tpl,
  1006 					'control_tpl'  => $control_tpl,
  1010 					'multi_number' => ( $args['_add'] === 'multi' ) ? $args['_multi_num'] : false,
  1007 					'multi_number' => ( 'multi' === $args['_add'] ) ? $args['_multi_num'] : false,
  1011 					'is_disabled'  => $is_disabled,
  1008 					'is_disabled'  => $is_disabled,
  1012 					'id_base'      => $id_base,
  1009 					'id_base'      => $id_base,
  1013 					'transport'    => $this->is_widget_selective_refreshable( $id_base ) ? 'postMessage' : 'refresh',
  1010 					'transport'    => $this->is_widget_selective_refreshable( $id_base ) ? 'postMessage' : 'refresh',
  1014 					'width'        => $wp_registered_widget_controls[ $widget['id'] ]['width'],
  1011 					'width'        => $wp_registered_widget_controls[ $widget['id'] ]['width'],
  1015 					'height'       => $wp_registered_widget_controls[ $widget['id'] ]['height'],
  1012 					'height'       => $wp_registered_widget_controls[ $widget['id'] ]['height'],
  1048 		$args[0]['before_form']           = '<div class="form">';
  1045 		$args[0]['before_form']           = '<div class="form">';
  1049 		$args[0]['after_form']            = '</div><!-- .form -->';
  1046 		$args[0]['after_form']            = '</div><!-- .form -->';
  1050 		$args[0]['before_widget_content'] = '<div class="widget-content">';
  1047 		$args[0]['before_widget_content'] = '<div class="widget-content">';
  1051 		$args[0]['after_widget_content']  = '</div><!-- .widget-content -->';
  1048 		$args[0]['after_widget_content']  = '</div><!-- .widget-content -->';
  1052 		ob_start();
  1049 		ob_start();
  1053 		call_user_func_array( 'wp_widget_control', $args );
  1050 		wp_widget_control( ...$args );
  1054 		$control_tpl = ob_get_clean();
  1051 		$control_tpl = ob_get_clean();
  1055 		return $control_tpl;
  1052 		return $control_tpl;
  1056 	}
  1053 	}
  1057 
  1054 
  1058 	/**
  1055 	/**
  1101 	/**
  1098 	/**
  1102 	 * Refreshes the nonce for widget updates.
  1099 	 * Refreshes the nonce for widget updates.
  1103 	 *
  1100 	 *
  1104 	 * @since 4.2.0
  1101 	 * @since 4.2.0
  1105 	 *
  1102 	 *
  1106 	 * @param  array $nonces Array of nonces.
  1103 	 * @param array $nonces Array of nonces.
  1107 	 * @return array $nonces Array of nonces.
  1104 	 * @return array Array of nonces.
  1108 	 */
  1105 	 */
  1109 	public function refresh_nonces( $nonces ) {
  1106 	public function refresh_nonces( $nonces ) {
  1110 		$nonces['update-widget'] = wp_create_nonce( 'update-widget' );
  1107 		$nonces['update-widget'] = wp_create_nonce( 'update-widget' );
  1111 		return $nonces;
  1108 		return $nonces;
  1112 	}
  1109 	}
  1171 	 */
  1168 	 */
  1172 	public function export_preview_data() {
  1169 	public function export_preview_data() {
  1173 		global $wp_registered_sidebars, $wp_registered_widgets;
  1170 		global $wp_registered_sidebars, $wp_registered_widgets;
  1174 
  1171 
  1175 		$switched_locale = switch_to_locale( get_user_locale() );
  1172 		$switched_locale = switch_to_locale( get_user_locale() );
  1176 		$l10n            = array(
  1173 
       
  1174 		$l10n = array(
  1177 			'widgetTooltip' => __( 'Shift-click to edit this widget.' ),
  1175 			'widgetTooltip' => __( 'Shift-click to edit this widget.' ),
  1178 		);
  1176 		);
       
  1177 
  1179 		if ( $switched_locale ) {
  1178 		if ( $switched_locale ) {
  1180 			restore_previous_locale();
  1179 			restore_previous_locale();
  1181 		}
  1180 		}
  1182 
  1181 
       
  1182 		$rendered_sidebars = array_filter( $this->rendered_sidebars );
       
  1183 		$rendered_widgets  = array_filter( $this->rendered_widgets );
       
  1184 
  1183 		// Prepare Customizer settings to pass to JavaScript.
  1185 		// Prepare Customizer settings to pass to JavaScript.
  1184 		$settings = array(
  1186 		$settings = array(
  1185 			'renderedSidebars'            => array_fill_keys( array_unique( $this->rendered_sidebars ), true ),
  1187 			'renderedSidebars'            => array_fill_keys( array_keys( $rendered_sidebars ), true ),
  1186 			'renderedWidgets'             => array_fill_keys( array_keys( $this->rendered_widgets ), true ),
  1188 			'renderedWidgets'             => array_fill_keys( array_keys( $rendered_widgets ), true ),
  1187 			'registeredSidebars'          => array_values( $wp_registered_sidebars ),
  1189 			'registeredSidebars'          => array_values( $wp_registered_sidebars ),
  1188 			'registeredWidgets'           => $wp_registered_widgets,
  1190 			'registeredWidgets'           => $wp_registered_widgets,
  1189 			'l10n'                        => $l10n,
  1191 			'l10n'                        => $l10n,
  1190 			'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(),
  1192 			'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(),
  1191 		);
  1193 		);
       
  1194 
  1192 		foreach ( $settings['registeredWidgets'] as &$registered_widget ) {
  1195 		foreach ( $settings['registeredWidgets'] as &$registered_widget ) {
  1193 			unset( $registered_widget['callback'] ); // may not be JSON-serializeable
  1196 			unset( $registered_widget['callback'] ); // May not be JSON-serializeable.
  1194 		}
  1197 		}
  1195 
  1198 
  1196 		?>
  1199 		?>
  1197 		<script type="text/javascript">
  1200 		<script type="text/javascript">
  1198 			var _wpWidgetCustomizerPreviewSettings = <?php echo wp_json_encode( $settings ); ?>;
  1201 			var _wpWidgetCustomizerPreviewSettings = <?php echo wp_json_encode( $settings ); ?>;
  1218 	 *
  1221 	 *
  1219 	 * @param string $widget_id Widget ID to check.
  1222 	 * @param string $widget_id Widget ID to check.
  1220 	 * @return bool Whether the widget is rendered.
  1223 	 * @return bool Whether the widget is rendered.
  1221 	 */
  1224 	 */
  1222 	public function is_widget_rendered( $widget_id ) {
  1225 	public function is_widget_rendered( $widget_id ) {
  1223 		return in_array( $widget_id, $this->rendered_widgets );
  1226 		return ! empty( $this->rendered_widgets[ $widget_id ] );
  1224 	}
  1227 	}
  1225 
  1228 
  1226 	/**
  1229 	/**
  1227 	 * Determines if a sidebar is rendered on the page.
  1230 	 * Determines if a sidebar is rendered on the page.
  1228 	 *
  1231 	 *
  1230 	 *
  1233 	 *
  1231 	 * @param string $sidebar_id Sidebar ID to check.
  1234 	 * @param string $sidebar_id Sidebar ID to check.
  1232 	 * @return bool Whether the sidebar is rendered.
  1235 	 * @return bool Whether the sidebar is rendered.
  1233 	 */
  1236 	 */
  1234 	public function is_sidebar_rendered( $sidebar_id ) {
  1237 	public function is_sidebar_rendered( $sidebar_id ) {
  1235 		return in_array( $sidebar_id, $this->rendered_sidebars );
  1238 		return ! empty( $this->rendered_sidebars[ $sidebar_id ] );
  1236 	}
  1239 	}
  1237 
  1240 
  1238 	/**
  1241 	/**
  1239 	 * Tallies the sidebars rendered via is_active_sidebar().
  1242 	 * Tallies the sidebars rendered via is_active_sidebar().
  1240 	 *
  1243 	 *
  1248 	 * @param string $sidebar_id Sidebar ID.
  1251 	 * @param string $sidebar_id Sidebar ID.
  1249 	 * @return bool Whether the sidebar is active.
  1252 	 * @return bool Whether the sidebar is active.
  1250 	 */
  1253 	 */
  1251 	public function tally_sidebars_via_is_active_sidebar_calls( $is_active, $sidebar_id ) {
  1254 	public function tally_sidebars_via_is_active_sidebar_calls( $is_active, $sidebar_id ) {
  1252 		if ( is_registered_sidebar( $sidebar_id ) ) {
  1255 		if ( is_registered_sidebar( $sidebar_id ) ) {
  1253 			$this->rendered_sidebars[] = $sidebar_id;
  1256 			$this->rendered_sidebars[ $sidebar_id ] = true;
  1254 		}
  1257 		}
       
  1258 
  1255 		/*
  1259 		/*
  1256 		 * We may need to force this to true, and also force-true the value
  1260 		 * We may need to force this to true, and also force-true the value
  1257 		 * for 'dynamic_sidebar_has_widgets' if we want to ensure that there
  1261 		 * for 'dynamic_sidebar_has_widgets' if we want to ensure that there
  1258 		 * is an area to drop widgets into, if the sidebar is empty.
  1262 		 * is an area to drop widgets into, if the sidebar is empty.
  1259 		 */
  1263 		 */
  1273 	 * @param string $sidebar_id  Sidebar ID.
  1277 	 * @param string $sidebar_id  Sidebar ID.
  1274 	 * @return bool Whether the current sidebar has widgets.
  1278 	 * @return bool Whether the current sidebar has widgets.
  1275 	 */
  1279 	 */
  1276 	public function tally_sidebars_via_dynamic_sidebar_calls( $has_widgets, $sidebar_id ) {
  1280 	public function tally_sidebars_via_dynamic_sidebar_calls( $has_widgets, $sidebar_id ) {
  1277 		if ( is_registered_sidebar( $sidebar_id ) ) {
  1281 		if ( is_registered_sidebar( $sidebar_id ) ) {
  1278 			$this->rendered_sidebars[] = $sidebar_id;
  1282 			$this->rendered_sidebars[ $sidebar_id ] = true;
  1279 		}
  1283 		}
  1280 
  1284 
  1281 		/*
  1285 		/*
  1282 		 * We may need to force this to true, and also force-true the value
  1286 		 * We may need to force this to true, and also force-true the value
  1283 		 * for 'is_active_sidebar' if we want to ensure there is an area to
  1287 		 * for 'is_active_sidebar' if we want to ensure there is an area to
  1311 	 *
  1315 	 *
  1312 	 * @param array $value Widget instance to sanitize.
  1316 	 * @param array $value Widget instance to sanitize.
  1313 	 * @return array|void Sanitized widget instance.
  1317 	 * @return array|void Sanitized widget instance.
  1314 	 */
  1318 	 */
  1315 	public function sanitize_widget_instance( $value ) {
  1319 	public function sanitize_widget_instance( $value ) {
  1316 		if ( $value === array() ) {
  1320 		if ( array() === $value ) {
  1317 			return $value;
  1321 			return $value;
  1318 		}
  1322 		}
  1319 
  1323 
  1320 		if ( empty( $value['is_widget_customizer_js_value'] )
  1324 		if ( empty( $value['is_widget_customizer_js_value'] )
  1321 			|| empty( $value['instance_hash_key'] )
  1325 			|| empty( $value['instance_hash_key'] )
  1389 	 * @since 3.9.0
  1393 	 * @since 3.9.0
  1390 	 *
  1394 	 *
  1391 	 * @global array $wp_registered_widget_updates
  1395 	 * @global array $wp_registered_widget_updates
  1392 	 * @global array $wp_registered_widget_controls
  1396 	 * @global array $wp_registered_widget_controls
  1393 	 *
  1397 	 *
  1394 	 * @param  string $widget_id Widget ID.
  1398 	 * @param string $widget_id Widget ID.
  1395 	 * @return WP_Error|array Array containing the updated widget information.
  1399 	 * @return array|WP_Error Array containing the updated widget information.
  1396 	 *                        A WP_Error object, otherwise.
  1400 	 *                        A WP_Error object, otherwise.
  1397 	 */
  1401 	 */
  1398 	public function call_widget_update( $widget_id ) {
  1402 	public function call_widget_update( $widget_id ) {
  1399 		global $wp_registered_widget_updates, $wp_registered_widget_controls;
  1403 		global $wp_registered_widget_updates, $wp_registered_widget_controls;
  1400 
  1404 
  1436 
  1440 
  1437 			if ( ! is_null( $parsed_id['number'] ) ) {
  1441 			if ( ! is_null( $parsed_id['number'] ) ) {
  1438 				$value                         = array();
  1442 				$value                         = array();
  1439 				$value[ $parsed_id['number'] ] = $instance;
  1443 				$value[ $parsed_id['number'] ] = $instance;
  1440 				$key                           = 'widget-' . $parsed_id['id_base'];
  1444 				$key                           = 'widget-' . $parsed_id['id_base'];
  1441 				$_REQUEST[ $key ]              = $_POST[ $key ] = wp_slash( $value );
  1445 				$_REQUEST[ $key ]              = wp_slash( $value );
       
  1446 				$_POST[ $key ]                 = $_REQUEST[ $key ];
  1442 				$added_input_vars[]            = $key;
  1447 				$added_input_vars[]            = $key;
  1443 			} else {
  1448 			} else {
  1444 				foreach ( $instance as $key => $value ) {
  1449 				foreach ( $instance as $key => $value ) {
  1445 					$_REQUEST[ $key ]   = $_POST[ $key ] = wp_slash( $value );
  1450 					$_REQUEST[ $key ]   = wp_slash( $value );
       
  1451 					$_POST[ $key ]      = $_REQUEST[ $key ];
  1446 					$added_input_vars[] = $key;
  1452 					$added_input_vars[] = $key;
  1447 				}
  1453 				}
  1448 			}
  1454 			}
  1449 		}
  1455 		}
  1450 
  1456 
  1456 				ob_end_clean();
  1462 				ob_end_clean();
  1457 				break;
  1463 				break;
  1458 			}
  1464 			}
  1459 		}
  1465 		}
  1460 
  1466 
  1461 		// Clean up any input vars that were manually added
  1467 		// Clean up any input vars that were manually added.
  1462 		foreach ( $added_input_vars as $key ) {
  1468 		foreach ( $added_input_vars as $key ) {
  1463 			unset( $_POST[ $key ] );
  1469 			unset( $_POST[ $key ] );
  1464 			unset( $_REQUEST[ $key ] );
  1470 			unset( $_REQUEST[ $key ] );
  1465 		}
  1471 		}
  1466 
  1472 
  1534 		if ( empty( $_POST['widget-id'] ) ) {
  1540 		if ( empty( $_POST['widget-id'] ) ) {
  1535 			wp_send_json_error( 'missing_widget-id' );
  1541 			wp_send_json_error( 'missing_widget-id' );
  1536 		}
  1542 		}
  1537 
  1543 
  1538 		/** This action is documented in wp-admin/includes/ajax-actions.php */
  1544 		/** This action is documented in wp-admin/includes/ajax-actions.php */
  1539 		do_action( 'load-widgets.php' );
  1545 		do_action( 'load-widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
  1540 
  1546 
  1541 		/** This action is documented in wp-admin/includes/ajax-actions.php */
  1547 		/** This action is documented in wp-admin/includes/ajax-actions.php */
  1542 		do_action( 'widgets.php' );
  1548 		do_action( 'widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
  1543 
  1549 
  1544 		/** This action is documented in wp-admin/widgets.php */
  1550 		/** This action is documented in wp-admin/widgets.php */
  1545 		do_action( 'sidebar_admin_setup' );
  1551 		do_action( 'sidebar_admin_setup' );
  1546 
  1552 
  1547 		$widget_id = $this->get_post_value( 'widget-id' );
  1553 		$widget_id = $this->get_post_value( 'widget-id' );
  1683 
  1689 
  1684 	/**
  1690 	/**
  1685 	 * List of the tag names seen for before_widget strings.
  1691 	 * List of the tag names seen for before_widget strings.
  1686 	 *
  1692 	 *
  1687 	 * This is used in the {@see 'filter_wp_kses_allowed_html'} filter to ensure that the
  1693 	 * This is used in the {@see 'filter_wp_kses_allowed_html'} filter to ensure that the
  1688 	 * data-* attributes can be whitelisted.
  1694 	 * data-* attributes can be allowed.
  1689 	 *
  1695 	 *
  1690 	 * @since 4.5.0
  1696 	 * @since 4.5.0
  1691 	 * @var array
  1697 	 * @var array
  1692 	 */
  1698 	 */
  1693 	protected $before_widget_tags_seen = array();
  1699 	protected $before_widget_tags_seen = array();
  1736 
  1742 
  1737 	/**
  1743 	/**
  1738 	 * The current request's sidebar_instance_number context.
  1744 	 * The current request's sidebar_instance_number context.
  1739 	 *
  1745 	 *
  1740 	 * @since 4.5.0
  1746 	 * @since 4.5.0
  1741 	 * @var int
  1747 	 * @var int|null
  1742 	 */
  1748 	 */
  1743 	protected $context_sidebar_instance_number;
  1749 	protected $context_sidebar_instance_number;
  1744 
  1750 
  1745 	/**
  1751 	/**
  1746 	 * Current sidebar ID being rendered.
  1752 	 * Current sidebar ID being rendered.
  1788 
  1794 
  1789 	/**
  1795 	/**
  1790 	 * Current sidebar being rendered.
  1796 	 * Current sidebar being rendered.
  1791 	 *
  1797 	 *
  1792 	 * @since 4.5.0
  1798 	 * @since 4.5.0
  1793 	 * @var string
  1799 	 * @var string|null
  1794 	 */
  1800 	 */
  1795 	protected $rendering_widget_id;
  1801 	protected $rendering_widget_id;
  1796 
  1802 
  1797 	/**
  1803 	/**
  1798 	 * Current widget being rendered.
  1804 	 * Current widget being rendered.
  1799 	 *
  1805 	 *
  1800 	 * @since 4.5.0
  1806 	 * @since 4.5.0
  1801 	 * @var string
  1807 	 * @var string|null
  1802 	 */
  1808 	 */
  1803 	protected $rendering_sidebar_id;
  1809 	protected $rendering_sidebar_id;
  1804 
  1810 
  1805 	/**
  1811 	/**
  1806 	 * Filters sidebars_widgets to ensure the currently-rendered widget is the only widget in the current sidebar.
  1812 	 * Filters sidebars_widgets to ensure the currently-rendered widget is the only widget in the current sidebar.
  1854 		$filter_callback = array( $this, 'filter_sidebars_widgets_for_rendering_widget' );
  1860 		$filter_callback = array( $this, 'filter_sidebars_widgets_for_rendering_widget' );
  1855 		add_filter( 'sidebars_widgets', $filter_callback, 1000 );
  1861 		add_filter( 'sidebars_widgets', $filter_callback, 1000 );
  1856 
  1862 
  1857 		// Render the widget.
  1863 		// Render the widget.
  1858 		ob_start();
  1864 		ob_start();
  1859 		dynamic_sidebar( $this->rendering_sidebar_id = $context['sidebar_id'] );
  1865 		$this->rendering_sidebar_id = $context['sidebar_id'];
  1860 		$container                                   = ob_get_clean();
  1866 		dynamic_sidebar( $this->rendering_sidebar_id );
       
  1867 		$container = ob_get_clean();
  1861 
  1868 
  1862 		// Reset variables for next partial render.
  1869 		// Reset variables for next partial render.
  1863 		remove_filter( 'sidebars_widgets', $filter_callback, 1000 );
  1870 		remove_filter( 'sidebars_widgets', $filter_callback, 1000 );
  1864 
  1871 
  1865 		$this->context_sidebar_instance_number = null;
  1872 		$this->context_sidebar_instance_number = null;
  1868 
  1875 
  1869 		return $container;
  1876 		return $container;
  1870 	}
  1877 	}
  1871 
  1878 
  1872 	//
  1879 	//
  1873 	// Option Update Capturing
  1880 	// Option Update Capturing.
  1874 	//
  1881 	//
  1875 
  1882 
  1876 	/**
  1883 	/**
  1877 	 * List of captured widget option updates.
  1884 	 * List of captured widget option updates.
  1878 	 *
  1885 	 *
  1966 	 * @param mixed  $old_value   The old option value.
  1973 	 * @param mixed  $old_value   The old option value.
  1967 	 * @return mixed Filtered option value.
  1974 	 * @return mixed Filtered option value.
  1968 	 */
  1975 	 */
  1969 	public function capture_filter_pre_update_option( $new_value, $option_name, $old_value ) {
  1976 	public function capture_filter_pre_update_option( $new_value, $option_name, $old_value ) {
  1970 		if ( $this->is_option_capture_ignored( $option_name ) ) {
  1977 		if ( $this->is_option_capture_ignored( $option_name ) ) {
  1971 			return;
  1978 			return $new_value;
  1972 		}
  1979 		}
  1973 
  1980 
  1974 		if ( ! isset( $this->_captured_options[ $option_name ] ) ) {
  1981 		if ( ! isset( $this->_captured_options[ $option_name ] ) ) {
  1975 			add_filter( "pre_option_{$option_name}", array( $this, 'capture_filter_pre_get_option' ) );
  1982 			add_filter( "pre_option_{$option_name}", array( $this, 'capture_filter_pre_get_option' ) );
  1976 		}
  1983 		}