wp/wp-includes/class-wp-customize-widgets.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    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
   103 		add_filter( 'customize_dynamic_setting_args',          array( $this, 'filter_customize_dynamic_setting_args' ), 10, 2 );
   103 		add_filter( 'customize_dynamic_setting_args', array( $this, 'filter_customize_dynamic_setting_args' ), 10, 2 );
   104 		add_action( 'widgets_init',                            array( $this, 'register_settings' ), 95 );
   104 		add_action( 'widgets_init', array( $this, 'register_settings' ), 95 );
   105 		add_action( 'customize_register',                      array( $this, 'schedule_customize_register' ), 1 );
   105 		add_action( 'customize_register', array( $this, 'schedule_customize_register' ), 1 );
   106 
   106 
   107 		// Skip remaining hooks when the user can't manage widgets anyway.
   107 		// Skip remaining hooks when the user can't manage widgets anyway.
   108 		if ( ! current_user_can( 'edit_theme_options' ) ) {
   108 		if ( ! current_user_can( 'edit_theme_options' ) ) {
   109 			return;
   109 			return;
   110 		}
   110 		}
   111 
   111 
   112 		add_action( 'wp_loaded',                               array( $this, 'override_sidebars_widgets_for_theme_switch' ) );
   112 		add_action( 'wp_loaded', array( $this, 'override_sidebars_widgets_for_theme_switch' ) );
   113 		add_action( 'customize_controls_init',                 array( $this, 'customize_controls_init' ) );
   113 		add_action( 'customize_controls_init', array( $this, 'customize_controls_init' ) );
   114 		add_action( 'customize_controls_enqueue_scripts',      array( $this, 'enqueue_scripts' ) );
   114 		add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
   115 		add_action( 'customize_controls_print_styles',         array( $this, 'print_styles' ) );
   115 		add_action( 'customize_controls_print_styles', array( $this, 'print_styles' ) );
   116 		add_action( 'customize_controls_print_scripts',        array( $this, 'print_scripts' ) );
   116 		add_action( 'customize_controls_print_scripts', array( $this, 'print_scripts' ) );
   117 		add_action( 'customize_controls_print_footer_scripts', array( $this, 'print_footer_scripts' ) );
   117 		add_action( 'customize_controls_print_footer_scripts', array( $this, 'print_footer_scripts' ) );
   118 		add_action( 'customize_controls_print_footer_scripts', array( $this, 'output_widget_control_templates' ) );
   118 		add_action( 'customize_controls_print_footer_scripts', array( $this, 'output_widget_control_templates' ) );
   119 		add_action( 'customize_preview_init',                  array( $this, 'customize_preview_init' ) );
   119 		add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) );
   120 		add_filter( 'customize_refresh_nonces',                array( $this, 'refresh_nonces' ) );
   120 		add_filter( 'customize_refresh_nonces', array( $this, 'refresh_nonces' ) );
   121 
   121 
   122 		add_action( 'dynamic_sidebar',                         array( $this, 'tally_rendered_widgets' ) );
   122 		add_action( 'dynamic_sidebar', array( $this, 'tally_rendered_widgets' ) );
   123 		add_filter( 'is_active_sidebar',                       array( $this, 'tally_sidebars_via_is_active_sidebar_calls' ), 10, 2 );
   123 		add_filter( 'is_active_sidebar', array( $this, 'tally_sidebars_via_is_active_sidebar_calls' ), 10, 2 );
   124 		add_filter( 'dynamic_sidebar_has_widgets',             array( $this, 'tally_sidebars_via_dynamic_sidebar_calls' ), 10, 2 );
   124 		add_filter( 'dynamic_sidebar_has_widgets', array( $this, 'tally_sidebars_via_dynamic_sidebar_calls' ), 10, 2 );
   125 
   125 
   126 		// Selective Refresh.
   126 		// Selective Refresh.
   127 		add_filter( 'customize_dynamic_partial_args',          array( $this, 'customize_dynamic_partial_args' ), 10, 2 );
   127 		add_filter( 'customize_dynamic_partial_args', array( $this, 'customize_dynamic_partial_args' ), 10, 2 );
   128 		add_action( 'customize_preview_init',                  array( $this, 'selective_refresh_init' ) );
   128 		add_action( 'customize_preview_init', array( $this, 'selective_refresh_init' ) );
   129 	}
   129 	}
   130 
   130 
   131 	/**
   131 	/**
   132 	 * List whether each registered widget can be use selective refresh.
   132 	 * List whether each registered widget can be use selective refresh.
   133 	 *
   133 	 *
   196 	 * them up-front so widgets will be initialized properly.
   196 	 * them up-front so widgets will be initialized properly.
   197 	 *
   197 	 *
   198 	 * @since 4.2.0
   198 	 * @since 4.2.0
   199 	 */
   199 	 */
   200 	public function register_settings() {
   200 	public function register_settings() {
   201 		$widget_setting_ids = array();
   201 		$widget_setting_ids   = array();
   202 		$incoming_setting_ids = array_keys( $this->manager->unsanitized_post_values() );
   202 		$incoming_setting_ids = array_keys( $this->manager->unsanitized_post_values() );
   203 		foreach ( $incoming_setting_ids as $setting_id ) {
   203 		foreach ( $incoming_setting_ids as $setting_id ) {
   204 			if ( ! is_null( $this->get_setting_type( $setting_id ) ) ) {
   204 			if ( ! is_null( $this->get_setting_type( $setting_id ) ) ) {
   205 				$widget_setting_ids[] = $setting_id;
   205 				$widget_setting_ids[] = $setting_id;
   206 			}
   206 			}
   317 	 *
   317 	 *
   318 	 * @param array $sidebars_widgets
   318 	 * @param array $sidebars_widgets
   319 	 * @return array
   319 	 * @return array
   320 	 */
   320 	 */
   321 	public function filter_option_sidebars_widgets_for_theme_switch( $sidebars_widgets ) {
   321 	public function filter_option_sidebars_widgets_for_theme_switch( $sidebars_widgets ) {
   322 		$sidebars_widgets = $GLOBALS['sidebars_widgets'];
   322 		$sidebars_widgets                  = $GLOBALS['sidebars_widgets'];
   323 		$sidebars_widgets['array_version'] = 3;
   323 		$sidebars_widgets['array_version'] = 3;
   324 		return $sidebars_widgets;
   324 		return $sidebars_widgets;
   325 	}
   325 	}
   326 
   326 
   327 	/**
   327 	/**
   397 		/*
   397 		/*
   398 		 * Add a setting which will be supplied for the theme's sidebars_widgets
   398 		 * Add a setting which will be supplied for the theme's sidebars_widgets
   399 		 * theme_mod when the theme is switched.
   399 		 * theme_mod when the theme is switched.
   400 		 */
   400 		 */
   401 		if ( ! $this->manager->is_theme_active() ) {
   401 		if ( ! $this->manager->is_theme_active() ) {
   402 			$setting_id = 'old_sidebars_widgets_data';
   402 			$setting_id   = 'old_sidebars_widgets_data';
   403 			$setting_args = $this->get_setting_args( $setting_id, array(
   403 			$setting_args = $this->get_setting_args(
   404 				'type' => 'global_variable',
   404 				$setting_id,
   405 				'dirty' => true,
   405 				array(
   406 			) );
   406 					'type'  => 'global_variable',
       
   407 					'dirty' => true,
       
   408 				)
       
   409 			);
   407 			$this->manager->add_setting( $setting_id, $setting_args );
   410 			$this->manager->add_setting( $setting_id, $setting_args );
   408 		}
   411 		}
   409 
   412 
   410 		$this->manager->add_panel( 'widgets', array(
   413 		$this->manager->add_panel(
   411 			'type'            => 'widgets',
   414 			'widgets',
   412 			'title'           => __( 'Widgets' ),
   415 			array(
   413 			'description'     => __( 'Widgets are independent sections of content that can be placed into widgetized areas provided by your theme (commonly called sidebars).' ),
   416 				'type'                     => 'widgets',
   414 			'priority'        => 110,
   417 				'title'                    => __( 'Widgets' ),
   415 			'active_callback' => array( $this, 'is_panel_active' ),
   418 				'description'              => __( 'Widgets are independent sections of content that can be placed into widgetized areas provided by your theme (commonly called sidebars).' ),
   416 			'auto_expand_sole_section' => true,
   419 				'priority'                 => 110,
   417 		) );
   420 				'active_callback'          => array( $this, 'is_panel_active' ),
       
   421 				'auto_expand_sole_section' => true,
       
   422 			)
       
   423 		);
   418 
   424 
   419 		foreach ( $sidebars_widgets as $sidebar_id => $sidebar_widget_ids ) {
   425 		foreach ( $sidebars_widgets as $sidebar_id => $sidebar_widget_ids ) {
   420 			if ( empty( $sidebar_widget_ids ) ) {
   426 			if ( empty( $sidebar_widget_ids ) ) {
   421 				$sidebar_widget_ids = array();
   427 				$sidebar_widget_ids = array();
   422 			}
   428 			}
   440 				// Add section to contain controls.
   446 				// Add section to contain controls.
   441 				$section_id = sprintf( 'sidebar-widgets-%s', $sidebar_id );
   447 				$section_id = sprintf( 'sidebar-widgets-%s', $sidebar_id );
   442 				if ( $is_active_sidebar ) {
   448 				if ( $is_active_sidebar ) {
   443 
   449 
   444 					$section_args = array(
   450 					$section_args = array(
   445 						'title' => $wp_registered_sidebars[ $sidebar_id ]['name'],
   451 						'title'       => $wp_registered_sidebars[ $sidebar_id ]['name'],
   446 						'description' => $wp_registered_sidebars[ $sidebar_id ]['description'],
   452 						'description' => $wp_registered_sidebars[ $sidebar_id ]['description'],
   447 						'priority' => array_search( $sidebar_id, array_keys( $wp_registered_sidebars ) ),
   453 						'priority'    => array_search( $sidebar_id, array_keys( $wp_registered_sidebars ) ),
   448 						'panel' => 'widgets',
   454 						'panel'       => 'widgets',
   449 						'sidebar_id' => $sidebar_id,
   455 						'sidebar_id'  => $sidebar_id,
   450 					);
   456 					);
   451 
   457 
   452 					/**
   458 					/**
   453 					 * Filters Customizer widget section arguments for a given sidebar.
   459 					 * Filters Customizer widget section arguments for a given sidebar.
   454 					 *
   460 					 *
   461 					$section_args = apply_filters( 'customizer_widgets_section_args', $section_args, $section_id, $sidebar_id );
   467 					$section_args = apply_filters( 'customizer_widgets_section_args', $section_args, $section_id, $sidebar_id );
   462 
   468 
   463 					$section = new WP_Customize_Sidebar_Section( $this->manager, $section_id, $section_args );
   469 					$section = new WP_Customize_Sidebar_Section( $this->manager, $section_id, $section_args );
   464 					$this->manager->add_section( $section );
   470 					$this->manager->add_section( $section );
   465 
   471 
   466 					$control = new WP_Widget_Area_Customize_Control( $this->manager, $setting_id, array(
   472 					$control           = new WP_Widget_Area_Customize_Control(
   467 						'section'    => $section_id,
   473 						$this->manager,
   468 						'sidebar_id' => $sidebar_id,
   474 						$setting_id,
   469 						'priority'   => count( $sidebar_widget_ids ), // place 'Add Widget' and 'Reorder' buttons at end.
   475 						array(
   470 					) );
   476 							'section'    => $section_id,
       
   477 							'sidebar_id' => $sidebar_id,
       
   478 							'priority'   => count( $sidebar_widget_ids ), // place 'Add Widget' and 'Reorder' buttons at end.
       
   479 						)
       
   480 					);
   471 					$new_setting_ids[] = $setting_id;
   481 					$new_setting_ids[] = $setting_id;
   472 
   482 
   473 					$this->manager->add_control( $control );
   483 					$this->manager->add_control( $control );
   474 				}
   484 				}
   475 			}
   485 			}
   476 
   486 
   477 			// Add a control for each active widget (located in a sidebar).
   487 			// Add a control for each active widget (located in a sidebar).
   478 			foreach ( $sidebar_widget_ids as $i => $widget_id ) {
   488 			foreach ( $sidebar_widget_ids as $i => $widget_id ) {
   479 
   489 
   480 				// Skip widgets that may have gone away due to a plugin being deactivated.
   490 				// Skip widgets that may have gone away due to a plugin being deactivated.
   481 				if ( ! $is_active_sidebar || ! isset( $wp_registered_widgets[$widget_id] ) ) {
   491 				if ( ! $is_active_sidebar || ! isset( $wp_registered_widgets[ $widget_id ] ) ) {
   482 					continue;
   492 					continue;
   483 				}
   493 				}
   484 
   494 
   485 				$registered_widget = $wp_registered_widgets[$widget_id];
   495 				$registered_widget = $wp_registered_widgets[ $widget_id ];
   486 				$setting_id        = $this->get_setting_id( $widget_id );
   496 				$setting_id        = $this->get_setting_id( $widget_id );
   487 				$id_base           = $wp_registered_widget_controls[$widget_id]['id_base'];
   497 				$id_base           = $wp_registered_widget_controls[ $widget_id ]['id_base'];
   488 
   498 
   489 				$control = new WP_Widget_Form_Customize_Control( $this->manager, $setting_id, array(
   499 				$control = new WP_Widget_Form_Customize_Control(
   490 					'label'          => $registered_widget['name'],
   500 					$this->manager,
   491 					'section'        => $section_id,
   501 					$setting_id,
   492 					'sidebar_id'     => $sidebar_id,
   502 					array(
   493 					'widget_id'      => $widget_id,
   503 						'label'          => $registered_widget['name'],
   494 					'widget_id_base' => $id_base,
   504 						'section'        => $section_id,
   495 					'priority'       => $i,
   505 						'sidebar_id'     => $sidebar_id,
   496 					'width'          => $wp_registered_widget_controls[$widget_id]['width'],
   506 						'widget_id'      => $widget_id,
   497 					'height'         => $wp_registered_widget_controls[$widget_id]['height'],
   507 						'widget_id_base' => $id_base,
   498 					'is_wide'        => $this->is_wide_widget( $widget_id ),
   508 						'priority'       => $i,
   499 				) );
   509 						'width'          => $wp_registered_widget_controls[ $widget_id ]['width'],
       
   510 						'height'         => $wp_registered_widget_controls[ $widget_id ]['height'],
       
   511 						'is_wide'        => $this->is_wide_widget( $widget_id ),
       
   512 					)
       
   513 				);
   500 				$this->manager->add_control( $control );
   514 				$this->manager->add_control( $control );
   501 			}
   515 			}
   502 		}
   516 		}
   503 
   517 
   504 		if ( $this->manager->settings_previewed() ) {
   518 		if ( $this->manager->settings_previewed() ) {
   560 	 */
   574 	 */
   561 	public function is_wide_widget( $widget_id ) {
   575 	public function is_wide_widget( $widget_id ) {
   562 		global $wp_registered_widget_controls;
   576 		global $wp_registered_widget_controls;
   563 
   577 
   564 		$parsed_widget_id = $this->parse_widget_id( $widget_id );
   578 		$parsed_widget_id = $this->parse_widget_id( $widget_id );
   565 		$width            = $wp_registered_widget_controls[$widget_id]['width'];
   579 		$width            = $wp_registered_widget_controls[ $widget_id ]['width'];
   566 		$is_core          = in_array( $parsed_widget_id['id_base'], $this->core_widget_id_bases );
   580 		$is_core          = in_array( $parsed_widget_id['id_base'], $this->core_widget_id_bases );
   567 		$is_wide          = ( $width > 250 && ! $is_core );
   581 		$is_wide          = ( $width > 250 && ! $is_core );
   568 
   582 
   569 		/**
   583 		/**
   570 		 * Filters whether the given widget is considered "wide".
   584 		 * Filters whether the given widget is considered "wide".
   585 	 * @param string $widget_id Widget ID.
   599 	 * @param string $widget_id Widget ID.
   586 	 * @return array Array containing a widget's id_base and number components.
   600 	 * @return array Array containing a widget's id_base and number components.
   587 	 */
   601 	 */
   588 	public function parse_widget_id( $widget_id ) {
   602 	public function parse_widget_id( $widget_id ) {
   589 		$parsed = array(
   603 		$parsed = array(
   590 			'number' => null,
   604 			'number'  => null,
   591 			'id_base' => null,
   605 			'id_base' => null,
   592 		);
   606 		);
   593 
   607 
   594 		if ( preg_match( '/^(.+)-(\d+)$/', $widget_id, $matches ) ) {
   608 		if ( preg_match( '/^(.+)-(\d+)$/', $widget_id, $matches ) ) {
   595 			$parsed['id_base'] = $matches[1];
   609 			$parsed['id_base'] = $matches[1];
   706 
   720 
   707 		/*
   721 		/*
   708 		 * Gather all strings in PHP that may be needed by JS on the client.
   722 		 * Gather all strings in PHP that may be needed by JS on the client.
   709 		 * Once JS i18n is implemented (in #20491), this can be removed.
   723 		 * Once JS i18n is implemented (in #20491), this can be removed.
   710 		 */
   724 		 */
   711 		$some_non_rendered_areas_messages = array();
   725 		$some_non_rendered_areas_messages    = array();
   712 		$some_non_rendered_areas_messages[1] = html_entity_decode(
   726 		$some_non_rendered_areas_messages[1] = html_entity_decode(
   713 			__( 'Your theme has 1 other widget area, but this particular page doesn’t display it.' ),
   727 			__( 'Your theme has 1 other widget area, but this particular page doesn’t display it.' ),
   714 			ENT_QUOTES,
   728 			ENT_QUOTES,
   715 			get_bloginfo( 'charset' )
   729 			get_bloginfo( 'charset' )
   716 		);
   730 		);
   717 		$registered_sidebar_count = count( $wp_registered_sidebars );
   731 		$registered_sidebar_count            = count( $wp_registered_sidebars );
   718 		for ( $non_rendered_count = 2; $non_rendered_count < $registered_sidebar_count; $non_rendered_count++ ) {
   732 		for ( $non_rendered_count = 2; $non_rendered_count < $registered_sidebar_count; $non_rendered_count++ ) {
   719 			$some_non_rendered_areas_messages[ $non_rendered_count ] = html_entity_decode( sprintf(
   733 			$some_non_rendered_areas_messages[ $non_rendered_count ] = html_entity_decode(
   720 				/* translators: %s: the number of other widget areas registered but not rendered */
   734 				sprintf(
   721 				_n(
   735 					/* translators: %s: the number of other widget areas registered but not rendered */
   722 					'Your theme has %s other widget area, but this particular page doesn&#8217;t display it.',
   736 					_n(
   723 					'Your theme has %s other widget areas, but this particular page doesn&#8217;t display them.',
   737 						'Your theme has %s other widget area, but this particular page doesn&#8217;t display it.',
   724 					$non_rendered_count
   738 						'Your theme has %s other widget areas, but this particular page doesn&#8217;t display them.',
       
   739 						$non_rendered_count
       
   740 					),
       
   741 					number_format_i18n( $non_rendered_count )
   725 				),
   742 				),
   726 				number_format_i18n( $non_rendered_count )
   743 				ENT_QUOTES,
   727 			), ENT_QUOTES, get_bloginfo( 'charset' ) );
   744 				get_bloginfo( 'charset' )
       
   745 			);
   728 		}
   746 		}
   729 
   747 
   730 		if ( 1 === $registered_sidebar_count ) {
   748 		if ( 1 === $registered_sidebar_count ) {
   731 			$no_areas_shown_message = html_entity_decode( sprintf(
   749 			$no_areas_shown_message = html_entity_decode(
   732 				__( 'Your theme has 1 widget area, but this particular page doesn&#8217;t display it.' )
   750 				sprintf(
   733 			), ENT_QUOTES, get_bloginfo( 'charset' ) );
   751 					__( 'Your theme has 1 widget area, but this particular page doesn&#8217;t display it.' )
       
   752 				),
       
   753 				ENT_QUOTES,
       
   754 				get_bloginfo( 'charset' )
       
   755 			);
   734 		} else {
   756 		} else {
   735 			$no_areas_shown_message = html_entity_decode( sprintf(
   757 			$no_areas_shown_message = html_entity_decode(
   736 				/* translators: %s: the total number of widget areas registered */
   758 				sprintf(
   737 				_n(
   759 					/* translators: %s: the total number of widget areas registered */
   738 					'Your theme has %s widget area, but this particular page doesn&#8217;t display it.',
   760 					_n(
   739 					'Your theme has %s widget areas, but this particular page doesn&#8217;t display them.',
   761 						'Your theme has %s widget area, but this particular page doesn&#8217;t display it.',
   740 					$registered_sidebar_count
   762 						'Your theme has %s widget areas, but this particular page doesn&#8217;t display them.',
       
   763 						$registered_sidebar_count
       
   764 					),
       
   765 					number_format_i18n( $registered_sidebar_count )
   741 				),
   766 				),
   742 				number_format_i18n( $registered_sidebar_count )
   767 				ENT_QUOTES,
   743 			), ENT_QUOTES, get_bloginfo( 'charset' ) );
   768 				get_bloginfo( 'charset' )
       
   769 			);
   744 		}
   770 		}
   745 
   771 
   746 		$settings = array(
   772 		$settings = array(
   747 			'registeredSidebars'   => array_values( $wp_registered_sidebars ),
   773 			'registeredSidebars'          => array_values( $wp_registered_sidebars ),
   748 			'registeredWidgets'    => $wp_registered_widgets,
   774 			'registeredWidgets'           => $wp_registered_widgets,
   749 			'availableWidgets'     => $available_widgets, // @todo Merge this with registered_widgets
   775 			'availableWidgets'            => $available_widgets, // @todo Merge this with registered_widgets
   750 			'l10n' => array(
   776 			'l10n'                        => array(
   751 				'saveBtnLabel'     => __( 'Apply' ),
   777 				'saveBtnLabel'     => __( 'Apply' ),
   752 				'saveBtnTooltip'   => __( 'Save and preview changes before publishing them.' ),
   778 				'saveBtnTooltip'   => __( 'Save and preview changes before publishing them.' ),
   753 				'removeBtnLabel'   => __( 'Remove' ),
   779 				'removeBtnLabel'   => __( 'Remove' ),
   754 				'removeBtnTooltip' => __( 'Trash widget by moving it to the inactive widgets sidebar.' ),
   780 				'removeBtnTooltip' => __( 'Keep widget settings and move it to the inactive widgets' ),
   755 				'error'            => __( 'An error has occurred. Please reload the page and try again.' ),
   781 				'error'            => __( 'An error has occurred. Please reload the page and try again.' ),
   756 				'widgetMovedUp'    => __( 'Widget moved up' ),
   782 				'widgetMovedUp'    => __( 'Widget moved up' ),
   757 				'widgetMovedDown'  => __( 'Widget moved down' ),
   783 				'widgetMovedDown'  => __( 'Widget moved down' ),
   758 				'navigatePreview'  => __( 'You can navigate to other pages on your site while using the Customizer to view and edit the widgets displayed on those pages.' ),
   784 				'navigatePreview'  => __( 'You can navigate to other pages on your site while using the Customizer to view and edit the widgets displayed on those pages.' ),
   759 				'someAreasShown'   => $some_non_rendered_areas_messages,
   785 				'someAreasShown'   => $some_non_rendered_areas_messages,
   763 				'reorderLabelOn'   => esc_attr__( 'Reorder widgets' ),
   789 				'reorderLabelOn'   => esc_attr__( 'Reorder widgets' ),
   764 				/* translators: %d: the number of widgets found */
   790 				/* translators: %d: the number of widgets found */
   765 				'widgetsFound'     => __( 'Number of widgets found: %d' ),
   791 				'widgetsFound'     => __( 'Number of widgets found: %d' ),
   766 				'noWidgetsFound'   => __( 'No widgets found.' ),
   792 				'noWidgetsFound'   => __( 'No widgets found.' ),
   767 			),
   793 			),
   768 			'tpl' => array(
   794 			'tpl'                         => array(
   769 				'widgetReorderNav' => $widget_reorder_nav_tpl,
   795 				'widgetReorderNav' => $widget_reorder_nav_tpl,
   770 				'moveWidgetArea'   => $move_widget_area_tpl,
   796 				'moveWidgetArea'   => $move_widget_area_tpl,
   771 			),
   797 			),
   772 			'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(),
   798 			'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(),
   773 		);
   799 		);
   795 			<div class="customize-section-title">
   821 			<div class="customize-section-title">
   796 				<button class="customize-section-back" tabindex="-1">
   822 				<button class="customize-section-back" tabindex="-1">
   797 					<span class="screen-reader-text"><?php _e( 'Back' ); ?></span>
   823 					<span class="screen-reader-text"><?php _e( 'Back' ); ?></span>
   798 				</button>
   824 				</button>
   799 				<h3>
   825 				<h3>
   800 					<span class="customize-action"><?php
   826 					<span class="customize-action">
       
   827 					<?php
   801 						/* translators: &#9656; is the unicode right-pointing triangle, and %s is the section title in the Customizer */
   828 						/* translators: &#9656; is the unicode right-pointing triangle, and %s is the section title in the Customizer */
   802 						echo sprintf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( 'widgets' )->title ) );
   829 						echo sprintf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( 'widgets' )->title ) );
   803 					?></span>
   830 					?>
       
   831 					</span>
   804 					<?php _e( 'Add a Widget' ); ?>
   832 					<?php _e( 'Add a Widget' ); ?>
   805 				</h3>
   833 				</h3>
   806 			</div>
   834 			</div>
   807 			<div id="available-widgets-filter">
   835 			<div id="available-widgets-filter">
   808 				<label class="screen-reader-text" for="widgets-search"><?php _e( 'Search Widgets' ); ?></label>
   836 				<label class="screen-reader-text" for="widgets-search"><?php _e( 'Search Widgets' ); ?></label>
   809 				<input type="text" id="widgets-search" placeholder="<?php esc_attr_e( 'Search widgets&hellip;' ) ?>" aria-describedby="widgets-search-desc" />
   837 				<input type="text" id="widgets-search" placeholder="<?php esc_attr_e( 'Search widgets&hellip;' ); ?>" aria-describedby="widgets-search-desc" />
   810 				<div class="search-icon" aria-hidden="true"></div>
   838 				<div class="search-icon" aria-hidden="true"></div>
   811 				<button type="button" class="clear-results"><span class="screen-reader-text"><?php _e( 'Clear Results' ); ?></span></button>
   839 				<button type="button" class="clear-results"><span class="screen-reader-text"><?php _e( 'Clear Results' ); ?></span></button>
   812 				<p class="screen-reader-text" id="widgets-search-desc"><?php _e( 'The search results will be updated as you type.' ); ?></p>
   840 				<p class="screen-reader-text" id="widgets-search-desc"><?php _e( 'The search results will be updated as you type.' ); ?></p>
   813 			</div>
   841 			</div>
   814 			<div id="available-widgets-list">
   842 			<div id="available-widgets-list">
   815 			<?php foreach ( $this->get_available_widgets() as $available_widget ): ?>
   843 			<?php foreach ( $this->get_available_widgets() as $available_widget ) : ?>
   816 				<div id="widget-tpl-<?php echo esc_attr( $available_widget['id'] ) ?>" data-widget-id="<?php echo esc_attr( $available_widget['id'] ) ?>" class="widget-tpl <?php echo esc_attr( $available_widget['id'] ) ?>" tabindex="0">
   844 				<div id="widget-tpl-<?php echo esc_attr( $available_widget['id'] ); ?>" data-widget-id="<?php echo esc_attr( $available_widget['id'] ); ?>" class="widget-tpl <?php echo esc_attr( $available_widget['id'] ); ?>" tabindex="0">
   817 					<?php echo $available_widget['control_tpl']; ?>
   845 					<?php echo $available_widget['control_tpl']; ?>
   818 				</div>
   846 				</div>
   819 			<?php endforeach; ?>
   847 			<?php endforeach; ?>
   820 			<p class="no-widgets-found-message"><?php _e( 'No widgets found.' ); ?></p>
   848 			<p class="no-widgets-found-message"><?php _e( 'No widgets found.' ); ?></p>
   821 			</div><!-- #available-widgets-list -->
   849 			</div><!-- #available-widgets-list -->
   856 			'capability' => 'edit_theme_options',
   884 			'capability' => 'edit_theme_options',
   857 			'default'    => array(),
   885 			'default'    => array(),
   858 		);
   886 		);
   859 
   887 
   860 		if ( preg_match( $this->setting_id_patterns['sidebar_widgets'], $id, $matches ) ) {
   888 		if ( preg_match( $this->setting_id_patterns['sidebar_widgets'], $id, $matches ) ) {
   861 			$args['sanitize_callback'] = array( $this, 'sanitize_sidebar_widgets' );
   889 			$args['sanitize_callback']    = array( $this, 'sanitize_sidebar_widgets' );
   862 			$args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' );
   890 			$args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' );
   863 			$args['transport'] = current_theme_supports( 'customize-selective-refresh-widgets' ) ? 'postMessage' : 'refresh';
   891 			$args['transport']            = current_theme_supports( 'customize-selective-refresh-widgets' ) ? 'postMessage' : 'refresh';
   864 		} elseif ( preg_match( $this->setting_id_patterns['widget_instance'], $id, $matches ) ) {
   892 		} elseif ( preg_match( $this->setting_id_patterns['widget_instance'], $id, $matches ) ) {
   865 			$args['sanitize_callback'] = array( $this, 'sanitize_widget_instance' );
   893 			$args['sanitize_callback']    = array( $this, 'sanitize_widget_instance' );
   866 			$args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' );
   894 			$args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' );
   867 			$args['transport'] = $this->is_widget_selective_refreshable( $matches['id_base'] ) ? 'postMessage' : 'refresh';
   895 			$args['transport']            = $this->is_widget_selective_refreshable( $matches['id_base'] ) ? 'postMessage' : 'refresh';
   868 		}
   896 		}
   869 
   897 
   870 		$args = array_merge( $args, $overrides );
   898 		$args = array_merge( $args, $overrides );
   871 
   899 
   872 		/**
   900 		/**
   887 	 *
   915 	 *
   888 	 * Used as the 'sanitize_callback' for each $sidebars_widgets setting.
   916 	 * Used as the 'sanitize_callback' for each $sidebars_widgets setting.
   889 	 *
   917 	 *
   890 	 * @since 3.9.0
   918 	 * @since 3.9.0
   891 	 *
   919 	 *
   892 	 * @param array $widget_ids Array of widget IDs.
   920 	 * @param string[] $widget_ids Array of widget IDs.
   893 	 * @return array Array of sanitized widget IDs.
   921 	 * @return string[] Array of sanitized widget IDs.
   894 	 */
   922 	 */
   895 	public function sanitize_sidebar_widgets( $widget_ids ) {
   923 	public function sanitize_sidebar_widgets( $widget_ids ) {
   896 		$widget_ids = array_map( 'strval', (array) $widget_ids );
   924 		$widget_ids           = array_map( 'strval', (array) $widget_ids );
   897 		$sanitized_widget_ids = array();
   925 		$sanitized_widget_ids = array();
   898 		foreach ( $widget_ids as $widget_id ) {
   926 		foreach ( $widget_ids as $widget_id ) {
   899 			$sanitized_widget_ids[] = preg_replace( '/[^a-z0-9_\-]/', '', $widget_id );
   927 			$sanitized_widget_ids[] = preg_replace( '/[^a-z0-9_\-]/', '', $widget_id );
   900 		}
   928 		}
   901 		return $sanitized_widget_ids;
   929 		return $sanitized_widget_ids;
   919 		if ( ! empty( $available_widgets ) ) {
   947 		if ( ! empty( $available_widgets ) ) {
   920 			return $available_widgets;
   948 			return $available_widgets;
   921 		}
   949 		}
   922 
   950 
   923 		global $wp_registered_widgets, $wp_registered_widget_controls;
   951 		global $wp_registered_widgets, $wp_registered_widget_controls;
   924 		require_once ABSPATH . '/wp-admin/includes/widgets.php'; // for next_widget_id_number()
   952 		require_once ABSPATH . 'wp-admin/includes/widgets.php'; // for next_widget_id_number()
   925 
   953 
   926 		$sort = $wp_registered_widgets;
   954 		$sort = $wp_registered_widgets;
   927 		usort( $sort, array( $this, '_sort_name_callback' ) );
   955 		usort( $sort, array( $this, '_sort_name_callback' ) );
   928 		$done = array();
   956 		$done = array();
   929 
   957 
   947 				'widget_name' => $widget['name'],
   975 				'widget_name' => $widget['name'],
   948 				'_display'    => 'template',
   976 				'_display'    => 'template',
   949 			);
   977 			);
   950 
   978 
   951 			$is_disabled     = false;
   979 			$is_disabled     = false;
   952 			$is_multi_widget = ( isset( $wp_registered_widget_controls[$widget['id']]['id_base'] ) && isset( $widget['params'][0]['number'] ) );
   980 			$is_multi_widget = ( isset( $wp_registered_widget_controls[ $widget['id'] ]['id_base'] ) && isset( $widget['params'][0]['number'] ) );
   953 			if ( $is_multi_widget ) {
   981 			if ( $is_multi_widget ) {
   954 				$id_base            = $wp_registered_widget_controls[$widget['id']]['id_base'];
   982 				$id_base            = $wp_registered_widget_controls[ $widget['id'] ]['id_base'];
   955 				$args['_temp_id']   = "$id_base-__i__";
   983 				$args['_temp_id']   = "$id_base-__i__";
   956 				$args['_multi_num'] = next_widget_id_number( $id_base );
   984 				$args['_multi_num'] = next_widget_id_number( $id_base );
   957 				$args['_add']       = 'multi';
   985 				$args['_add']       = 'multi';
   958 			} else {
   986 			} else {
   959 				$args['_add'] = 'single';
   987 				$args['_add'] = 'single';
   962 					$is_disabled = true;
   990 					$is_disabled = true;
   963 				}
   991 				}
   964 				$id_base = $widget['id'];
   992 				$id_base = $widget['id'];
   965 			}
   993 			}
   966 
   994 
   967 			$list_widget_controls_args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) );
   995 			$list_widget_controls_args = wp_list_widget_controls_dynamic_sidebar(
   968 			$control_tpl = $this->get_widget_control( $list_widget_controls_args );
   996 				array(
       
   997 					0 => $args,
       
   998 					1 => $widget['params'][0],
       
   999 				)
       
  1000 			);
       
  1001 			$control_tpl               = $this->get_widget_control( $list_widget_controls_args );
   969 
  1002 
   970 			// The properties here are mapped to the Backbone Widget model.
  1003 			// The properties here are mapped to the Backbone Widget model.
   971 			$available_widget = array_merge( $available_widget, array(
  1004 			$available_widget = array_merge(
   972 				'temp_id'      => isset( $args['_temp_id'] ) ? $args['_temp_id'] : null,
  1005 				$available_widget,
   973 				'is_multi'     => $is_multi_widget,
  1006 				array(
   974 				'control_tpl'  => $control_tpl,
  1007 					'temp_id'      => isset( $args['_temp_id'] ) ? $args['_temp_id'] : null,
   975 				'multi_number' => ( $args['_add'] === 'multi' ) ? $args['_multi_num'] : false,
  1008 					'is_multi'     => $is_multi_widget,
   976 				'is_disabled'  => $is_disabled,
  1009 					'control_tpl'  => $control_tpl,
   977 				'id_base'      => $id_base,
  1010 					'multi_number' => ( $args['_add'] === 'multi' ) ? $args['_multi_num'] : false,
   978 				'transport'    => $this->is_widget_selective_refreshable( $id_base ) ? 'postMessage' : 'refresh',
  1011 					'is_disabled'  => $is_disabled,
   979 				'width'        => $wp_registered_widget_controls[$widget['id']]['width'],
  1012 					'id_base'      => $id_base,
   980 				'height'       => $wp_registered_widget_controls[$widget['id']]['height'],
  1013 					'transport'    => $this->is_widget_selective_refreshable( $id_base ) ? 'postMessage' : 'refresh',
   981 				'is_wide'      => $this->is_wide_widget( $widget['id'] ),
  1014 					'width'        => $wp_registered_widget_controls[ $widget['id'] ]['width'],
   982 			) );
  1015 					'height'       => $wp_registered_widget_controls[ $widget['id'] ]['height'],
       
  1016 					'is_wide'      => $this->is_wide_widget( $widget['id'] ),
       
  1017 				)
       
  1018 			);
   983 
  1019 
   984 			$available_widgets[] = $available_widget;
  1020 			$available_widgets[] = $available_widget;
   985 		}
  1021 		}
   986 
  1022 
   987 		return $available_widgets;
  1023 		return $available_widgets;
  1007 	 *
  1043 	 *
  1008 	 * @param array $args Widget control arguments.
  1044 	 * @param array $args Widget control arguments.
  1009 	 * @return string Widget control form HTML markup.
  1045 	 * @return string Widget control form HTML markup.
  1010 	 */
  1046 	 */
  1011 	public function get_widget_control( $args ) {
  1047 	public function get_widget_control( $args ) {
  1012 		$args[0]['before_form'] = '<div class="form">';
  1048 		$args[0]['before_form']           = '<div class="form">';
  1013 		$args[0]['after_form'] = '</div><!-- .form -->';
  1049 		$args[0]['after_form']            = '</div><!-- .form -->';
  1014 		$args[0]['before_widget_content'] = '<div class="widget-content">';
  1050 		$args[0]['before_widget_content'] = '<div class="widget-content">';
  1015 		$args[0]['after_widget_content'] = '</div><!-- .widget-content -->';
  1051 		$args[0]['after_widget_content']  = '</div><!-- .widget-content -->';
  1016 		ob_start();
  1052 		ob_start();
  1017 		call_user_func_array( 'wp_widget_control', $args );
  1053 		call_user_func_array( 'wp_widget_control', $args );
  1018 		$control_tpl = ob_get_clean();
  1054 		$control_tpl = ob_get_clean();
  1019 		return $control_tpl;
  1055 		return $control_tpl;
  1020 	}
  1056 	}
  1030 	 *     @type string $content The contents of the widget form itself.
  1066 	 *     @type string $content The contents of the widget form itself.
  1031 	 * }
  1067 	 * }
  1032 	 */
  1068 	 */
  1033 	public function get_widget_control_parts( $args ) {
  1069 	public function get_widget_control_parts( $args ) {
  1034 		$args[0]['before_widget_content'] = '<div class="widget-content">';
  1070 		$args[0]['before_widget_content'] = '<div class="widget-content">';
  1035 		$args[0]['after_widget_content'] = '</div><!-- .widget-content -->';
  1071 		$args[0]['after_widget_content']  = '</div><!-- .widget-content -->';
  1036 		$control_markup = $this->get_widget_control( $args );
  1072 		$control_markup                   = $this->get_widget_control( $args );
  1037 
  1073 
  1038 		$content_start_pos = strpos( $control_markup, $args[0]['before_widget_content'] );
  1074 		$content_start_pos = strpos( $control_markup, $args[0]['before_widget_content'] );
  1039 		$content_end_pos = strrpos( $control_markup, $args[0]['after_widget_content'] );
  1075 		$content_end_pos   = strrpos( $control_markup, $args[0]['after_widget_content'] );
  1040 
  1076 
  1041 		$control = substr( $control_markup, 0, $content_start_pos + strlen( $args[0]['before_widget_content'] ) );
  1077 		$control  = substr( $control_markup, 0, $content_start_pos + strlen( $args[0]['before_widget_content'] ) );
  1042 		$control .= substr( $control_markup, $content_end_pos );
  1078 		$control .= substr( $control_markup, $content_end_pos );
  1043 		$content = trim( substr(
  1079 		$content  = trim(
  1044 			$control_markup,
  1080 			substr(
  1045 			$content_start_pos + strlen( $args[0]['before_widget_content'] ),
  1081 				$control_markup,
  1046 			$content_end_pos - $content_start_pos - strlen( $args[0]['before_widget_content'] )
  1082 				$content_start_pos + strlen( $args[0]['before_widget_content'] ),
  1047 		) );
  1083 				$content_end_pos - $content_start_pos - strlen( $args[0]['before_widget_content'] )
       
  1084 			)
       
  1085 		);
  1048 
  1086 
  1049 		return compact( 'control', 'content' );
  1087 		return compact( 'control', 'content' );
  1050 	}
  1088 	}
  1051 
  1089 
  1052 	/**
  1090 	/**
  1054 	 *
  1092 	 *
  1055 	 * @since 3.9.0
  1093 	 * @since 3.9.0
  1056 	 */
  1094 	 */
  1057 	public function customize_preview_init() {
  1095 	public function customize_preview_init() {
  1058 		add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue' ) );
  1096 		add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue' ) );
  1059 		add_action( 'wp_print_styles',    array( $this, 'print_preview_css' ), 1 );
  1097 		add_action( 'wp_print_styles', array( $this, 'print_preview_css' ), 1 );
  1060 		add_action( 'wp_footer',          array( $this, 'export_preview_data' ), 20 );
  1098 		add_action( 'wp_footer', array( $this, 'export_preview_data' ), 20 );
  1061 	}
  1099 	}
  1062 
  1100 
  1063 	/**
  1101 	/**
  1064 	 * Refreshes the nonce for widget updates.
  1102 	 * Refreshes the nonce for widget updates.
  1065 	 *
  1103 	 *
  1111 	public function print_preview_css() {
  1149 	public function print_preview_css() {
  1112 		?>
  1150 		?>
  1113 		<style>
  1151 		<style>
  1114 		.widget-customizer-highlighted-widget {
  1152 		.widget-customizer-highlighted-widget {
  1115 			outline: none;
  1153 			outline: none;
  1116 			-webkit-box-shadow: 0 0 2px rgba(30,140,190,0.8);
  1154 			-webkit-box-shadow: 0 0 2px rgba(30, 140, 190, 0.8);
  1117 			box-shadow: 0 0 2px rgba(30,140,190,0.8);
  1155 			box-shadow: 0 0 2px rgba(30, 140, 190, 0.8);
  1118 			position: relative;
  1156 			position: relative;
  1119 			z-index: 1;
  1157 			z-index: 1;
  1120 		}
  1158 		}
  1121 		</style>
  1159 		</style>
  1122 		<?php
  1160 		<?php
  1125 	/**
  1163 	/**
  1126 	 * Communicates the sidebars that appeared on the page at the very end of the page,
  1164 	 * Communicates the sidebars that appeared on the page at the very end of the page,
  1127 	 * and at the very end of the wp_footer,
  1165 	 * and at the very end of the wp_footer,
  1128 	 *
  1166 	 *
  1129 	 * @since 3.9.0
  1167 	 * @since 3.9.0
  1130      *
  1168 	 *
  1131 	 * @global array $wp_registered_sidebars
  1169 	 * @global array $wp_registered_sidebars
  1132 	 * @global array $wp_registered_widgets
  1170 	 * @global array $wp_registered_widgets
  1133 	 */
  1171 	 */
  1134 	public function export_preview_data() {
  1172 	public function export_preview_data() {
  1135 		global $wp_registered_sidebars, $wp_registered_widgets;
  1173 		global $wp_registered_sidebars, $wp_registered_widgets;
  1136 
  1174 
  1137 		$switched_locale = switch_to_locale( get_user_locale() );
  1175 		$switched_locale = switch_to_locale( get_user_locale() );
  1138 		$l10n = array(
  1176 		$l10n            = array(
  1139 			'widgetTooltip'  => __( 'Shift-click to edit this widget.' ),
  1177 			'widgetTooltip' => __( 'Shift-click to edit this widget.' ),
  1140 		);
  1178 		);
  1141 		if ( $switched_locale ) {
  1179 		if ( $switched_locale ) {
  1142 			restore_previous_locale();
  1180 			restore_previous_locale();
  1143 		}
  1181 		}
  1144 
  1182 
  1145 		// Prepare Customizer settings to pass to JavaScript.
  1183 		// Prepare Customizer settings to pass to JavaScript.
  1146 		$settings = array(
  1184 		$settings = array(
  1147 			'renderedSidebars'   => array_fill_keys( array_unique( $this->rendered_sidebars ), true ),
  1185 			'renderedSidebars'            => array_fill_keys( array_unique( $this->rendered_sidebars ), true ),
  1148 			'renderedWidgets'    => array_fill_keys( array_keys( $this->rendered_widgets ), true ),
  1186 			'renderedWidgets'             => array_fill_keys( array_keys( $this->rendered_widgets ), true ),
  1149 			'registeredSidebars' => array_values( $wp_registered_sidebars ),
  1187 			'registeredSidebars'          => array_values( $wp_registered_sidebars ),
  1150 			'registeredWidgets'  => $wp_registered_widgets,
  1188 			'registeredWidgets'           => $wp_registered_widgets,
  1151 			'l10n'               => $l10n,
  1189 			'l10n'                        => $l10n,
  1152 			'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(),
  1190 			'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(),
  1153 		);
  1191 		);
  1154 		foreach ( $settings['registeredWidgets'] as &$registered_widget ) {
  1192 		foreach ( $settings['registeredWidgets'] as &$registered_widget ) {
  1155 			unset( $registered_widget['callback'] ); // may not be JSON-serializeable
  1193 			unset( $registered_widget['callback'] ); // may not be JSON-serializeable
  1156 		}
  1194 		}
  1279 			return $value;
  1317 			return $value;
  1280 		}
  1318 		}
  1281 
  1319 
  1282 		if ( empty( $value['is_widget_customizer_js_value'] )
  1320 		if ( empty( $value['is_widget_customizer_js_value'] )
  1283 			|| empty( $value['instance_hash_key'] )
  1321 			|| empty( $value['instance_hash_key'] )
  1284 			|| empty( $value['encoded_serialized_instance'] ) )
  1322 			|| empty( $value['encoded_serialized_instance'] ) ) {
  1285 		{
       
  1286 			return;
  1323 			return;
  1287 		}
  1324 		}
  1288 
  1325 
  1289 		$decoded = base64_decode( $value['encoded_serialized_instance'], true );
  1326 		$decoded = base64_decode( $value['encoded_serialized_instance'], true );
  1290 		if ( false === $decoded ) {
  1327 		if ( false === $decoded ) {
  1396 				$this->stop_capturing_option_updates();
  1433 				$this->stop_capturing_option_updates();
  1397 				return new WP_Error( 'widget_setting_unsanitized' );
  1434 				return new WP_Error( 'widget_setting_unsanitized' );
  1398 			}
  1435 			}
  1399 
  1436 
  1400 			if ( ! is_null( $parsed_id['number'] ) ) {
  1437 			if ( ! is_null( $parsed_id['number'] ) ) {
  1401 				$value = array();
  1438 				$value                         = array();
  1402 				$value[$parsed_id['number']] = $instance;
  1439 				$value[ $parsed_id['number'] ] = $instance;
  1403 				$key = 'widget-' . $parsed_id['id_base'];
  1440 				$key                           = 'widget-' . $parsed_id['id_base'];
  1404 				$_REQUEST[$key] = $_POST[$key] = wp_slash( $value );
  1441 				$_REQUEST[ $key ]              = $_POST[ $key ] = wp_slash( $value );
  1405 				$added_input_vars[] = $key;
  1442 				$added_input_vars[]            = $key;
  1406 			} else {
  1443 			} else {
  1407 				foreach ( $instance as $key => $value ) {
  1444 				foreach ( $instance as $key => $value ) {
  1408 					$_REQUEST[$key] = $_POST[$key] = wp_slash( $value );
  1445 					$_REQUEST[ $key ]   = $_POST[ $key ] = wp_slash( $value );
  1409 					$added_input_vars[] = $key;
  1446 					$added_input_vars[] = $key;
  1410 				}
  1447 				}
  1411 			}
  1448 			}
  1412 		}
  1449 		}
  1413 
  1450 
  1507 		/** This action is documented in wp-admin/widgets.php */
  1544 		/** This action is documented in wp-admin/widgets.php */
  1508 		do_action( 'sidebar_admin_setup' );
  1545 		do_action( 'sidebar_admin_setup' );
  1509 
  1546 
  1510 		$widget_id = $this->get_post_value( 'widget-id' );
  1547 		$widget_id = $this->get_post_value( 'widget-id' );
  1511 		$parsed_id = $this->parse_widget_id( $widget_id );
  1548 		$parsed_id = $this->parse_widget_id( $widget_id );
  1512 		$id_base = $parsed_id['id_base'];
  1549 		$id_base   = $parsed_id['id_base'];
  1513 
  1550 
  1514 		$is_updating_widget_template = (
  1551 		$is_updating_widget_template = (
  1515 			isset( $_POST[ 'widget-' . $id_base ] )
  1552 			isset( $_POST[ 'widget-' . $id_base ] )
  1516 			&&
  1553 			&&
  1517 			is_array( $_POST[ 'widget-' . $id_base ] )
  1554 			is_array( $_POST[ 'widget-' . $id_base ] )
  1525 		$updated_widget = $this->call_widget_update( $widget_id ); // => {instance,form}
  1562 		$updated_widget = $this->call_widget_update( $widget_id ); // => {instance,form}
  1526 		if ( is_wp_error( $updated_widget ) ) {
  1563 		if ( is_wp_error( $updated_widget ) ) {
  1527 			wp_send_json_error( $updated_widget->get_error_code() );
  1564 			wp_send_json_error( $updated_widget->get_error_code() );
  1528 		}
  1565 		}
  1529 
  1566 
  1530 		$form = $updated_widget['form'];
  1567 		$form     = $updated_widget['form'];
  1531 		$instance = $this->sanitize_widget_js_instance( $updated_widget['instance'] );
  1568 		$instance = $this->sanitize_widget_js_instance( $updated_widget['instance'] );
  1532 
  1569 
  1533 		wp_send_json_success( compact( 'form', 'instance' ) );
  1570 		wp_send_json_success( compact( 'form', 'instance' ) );
  1534 	}
  1571 	}
  1535 
  1572 
  1586 	}
  1623 	}
  1587 
  1624 
  1588 	/**
  1625 	/**
  1589 	 * Inject selective refresh data attributes into widget container elements.
  1626 	 * Inject selective refresh data attributes into widget container elements.
  1590 	 *
  1627 	 *
       
  1628 	 * @since 4.5.0
       
  1629 	 *
  1591 	 * @param array $params {
  1630 	 * @param array $params {
  1592 	 *     Dynamic sidebar params.
  1631 	 *     Dynamic sidebar params.
  1593 	 *
  1632 	 *
  1594 	 *     @type array $args        Sidebar args.
  1633 	 *     @type array $args        Sidebar args.
  1595 	 *     @type array $widget_args Widget args.
  1634 	 *     @type array $widget_args Widget args.
  1596 	 * }
  1635 	 * }
  1597 	 * @see WP_Customize_Nav_Menus_Partial_Refresh::filter_wp_nav_menu_args()
  1636 	 * @see WP_Customize_Nav_Menus::filter_wp_nav_menu_args()
  1598 	 *
  1637 	 *
  1599 	 * @return array Params.
  1638 	 * @return array Params.
  1600 	 */
  1639 	 */
  1601 	public function filter_dynamic_sidebar_params( $params ) {
  1640 	public function filter_dynamic_sidebar_params( $params ) {
  1602 		$sidebar_args = array_merge(
  1641 		$sidebar_args = array_merge(
  1603 			array(
  1642 			array(
  1604 				'before_widget' => '',
  1643 				'before_widget' => '',
  1605 				'after_widget' => '',
  1644 				'after_widget'  => '',
  1606 			),
  1645 			),
  1607 			$params[0]
  1646 			$params[0]
  1608 		);
  1647 		);
  1609 
  1648 
  1610 		// Skip widgets not in a registered sidebar or ones which lack a proper wrapper element to attach the data-* attributes to.
  1649 		// Skip widgets not in a registered sidebar or ones which lack a proper wrapper element to attach the data-* attributes to.
  1611 		$matches = array();
  1650 		$matches  = array();
  1612 		$is_valid = (
  1651 		$is_valid = (
  1613 			isset( $sidebar_args['id'] )
  1652 			isset( $sidebar_args['id'] )
  1614 			&&
  1653 			&&
  1615 			is_registered_sidebar( $sidebar_args['id'] )
  1654 			is_registered_sidebar( $sidebar_args['id'] )
  1616 			&&
  1655 			&&
  1626 		$context = array(
  1665 		$context = array(
  1627 			'sidebar_id' => $sidebar_args['id'],
  1666 			'sidebar_id' => $sidebar_args['id'],
  1628 		);
  1667 		);
  1629 		if ( isset( $this->context_sidebar_instance_number ) ) {
  1668 		if ( isset( $this->context_sidebar_instance_number ) ) {
  1630 			$context['sidebar_instance_number'] = $this->context_sidebar_instance_number;
  1669 			$context['sidebar_instance_number'] = $this->context_sidebar_instance_number;
  1631 		} else if ( isset( $sidebar_args['id'] ) && isset( $this->sidebar_instance_count[ $sidebar_args['id'] ] ) ) {
  1670 		} elseif ( isset( $sidebar_args['id'] ) && isset( $this->sidebar_instance_count[ $sidebar_args['id'] ] ) ) {
  1632 			$context['sidebar_instance_number'] = $this->sidebar_instance_count[ $sidebar_args['id'] ];
  1671 			$context['sidebar_instance_number'] = $this->sidebar_instance_count[ $sidebar_args['id'] ];
  1633 		}
  1672 		}
  1634 
  1673 
  1635 		$attributes = sprintf( ' data-customize-partial-id="%s"', esc_attr( 'widget[' . $sidebar_args['widget_id'] . ']' ) );
  1674 		$attributes                    = sprintf( ' data-customize-partial-id="%s"', esc_attr( 'widget[' . $sidebar_args['widget_id'] . ']' ) );
  1636 		$attributes .= ' data-customize-partial-type="widget"';
  1675 		$attributes                   .= ' data-customize-partial-type="widget"';
  1637 		$attributes .= sprintf( ' data-customize-partial-placement-context="%s"', esc_attr( wp_json_encode( $context ) ) );
  1676 		$attributes                   .= sprintf( ' data-customize-partial-placement-context="%s"', esc_attr( wp_json_encode( $context ) ) );
  1638 		$attributes .= sprintf( ' data-customize-widget-id="%s"', esc_attr( $sidebar_args['widget_id'] ) );
  1677 		$attributes                   .= sprintf( ' data-customize-widget-id="%s"', esc_attr( $sidebar_args['widget_id'] ) );
  1639 		$sidebar_args['before_widget'] = preg_replace( '#^(<\w+)#', '$1 ' . $attributes, $sidebar_args['before_widget'] );
  1678 		$sidebar_args['before_widget'] = preg_replace( '#^(<\w+)#', '$1 ' . $attributes, $sidebar_args['before_widget'] );
  1640 
  1679 
  1641 		$params[0] = $sidebar_args;
  1680 		$params[0] = $sidebar_args;
  1642 		return $params;
  1681 		return $params;
  1643 	}
  1682 	}
  1668 			if ( ! isset( $allowed_html[ $tag_name ] ) ) {
  1707 			if ( ! isset( $allowed_html[ $tag_name ] ) ) {
  1669 				$allowed_html[ $tag_name ] = array();
  1708 				$allowed_html[ $tag_name ] = array();
  1670 			}
  1709 			}
  1671 			$allowed_html[ $tag_name ] = array_merge(
  1710 			$allowed_html[ $tag_name ] = array_merge(
  1672 				$allowed_html[ $tag_name ],
  1711 				$allowed_html[ $tag_name ],
  1673 				array_fill_keys( array(
  1712 				array_fill_keys(
  1674 					'data-customize-partial-id',
  1713 					array(
  1675 					'data-customize-partial-type',
  1714 						'data-customize-partial-id',
  1676 					'data-customize-partial-placement-context',
  1715 						'data-customize-partial-type',
  1677 					'data-customize-partial-widget-id',
  1716 						'data-customize-partial-placement-context',
  1678 					'data-customize-partial-options',
  1717 						'data-customize-partial-widget-id',
  1679 				), true )
  1718 						'data-customize-partial-options',
       
  1719 					),
       
  1720 					true
       
  1721 				)
  1680 			);
  1722 			);
  1681 		}
  1723 		}
  1682 		return $allowed_html;
  1724 		return $allowed_html;
  1683 	}
  1725 	}
  1684 
  1726 
  1813 		add_filter( 'sidebars_widgets', $filter_callback, 1000 );
  1855 		add_filter( 'sidebars_widgets', $filter_callback, 1000 );
  1814 
  1856 
  1815 		// Render the widget.
  1857 		// Render the widget.
  1816 		ob_start();
  1858 		ob_start();
  1817 		dynamic_sidebar( $this->rendering_sidebar_id = $context['sidebar_id'] );
  1859 		dynamic_sidebar( $this->rendering_sidebar_id = $context['sidebar_id'] );
  1818 		$container = ob_get_clean();
  1860 		$container                                   = ob_get_clean();
  1819 
  1861 
  1820 		// Reset variables for next partial render.
  1862 		// Reset variables for next partial render.
  1821 		remove_filter( 'sidebars_widgets', $filter_callback, 1000 );
  1863 		remove_filter( 'sidebars_widgets', $filter_callback, 1000 );
  1822 
  1864 
  1823 		$this->context_sidebar_instance_number = null;
  1865 		$this->context_sidebar_instance_number = null;
  1824 		$this->rendering_sidebar_id = null;
  1866 		$this->rendering_sidebar_id            = null;
  1825 		$this->rendering_widget_id = null;
  1867 		$this->rendering_widget_id             = null;
  1826 
  1868 
  1827 		return $container;
  1869 		return $container;
  1828 	}
  1870 	}
  1829 
  1871 
  1830 	//
  1872 	//
  1951 
  1993 
  1952 		if ( isset( $this->_captured_options[ $option_name ] ) ) {
  1994 		if ( isset( $this->_captured_options[ $option_name ] ) ) {
  1953 			$value = $this->_captured_options[ $option_name ];
  1995 			$value = $this->_captured_options[ $option_name ];
  1954 
  1996 
  1955 			/** This filter is documented in wp-includes/option.php */
  1997 			/** This filter is documented in wp-includes/option.php */
  1956 			$value = apply_filters( 'option_' . $option_name, $value );
  1998 			$value = apply_filters( 'option_' . $option_name, $value, $option_name );
  1957 		}
  1999 		}
  1958 
  2000 
  1959 		return $value;
  2001 		return $value;
  1960 	}
  2002 	}
  1961 
  2003 
  1973 
  2015 
  1974 		foreach ( array_keys( $this->_captured_options ) as $option_name ) {
  2016 		foreach ( array_keys( $this->_captured_options ) as $option_name ) {
  1975 			remove_filter( "pre_option_{$option_name}", array( $this, 'capture_filter_pre_get_option' ) );
  2017 			remove_filter( "pre_option_{$option_name}", array( $this, 'capture_filter_pre_get_option' ) );
  1976 		}
  2018 		}
  1977 
  2019 
  1978 		$this->_captured_options = array();
  2020 		$this->_captured_options            = array();
  1979 		$this->_is_capturing_option_updates = false;
  2021 		$this->_is_capturing_option_updates = false;
  1980 	}
  2022 	}
  1981 
  2023 
  1982 	/**
  2024 	/**
  1983 	 * {@internal Missing Summary}
  2025 	 * {@internal Missing Summary}