wp/wp-includes/widgets.php
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 19 3d72ae0968f4
--- a/wp/wp-includes/widgets.php	Tue Dec 15 15:52:01 2020 +0100
+++ b/wp/wp-includes/widgets.php	Wed Sep 21 18:19:35 2022 +0200
@@ -219,28 +219,37 @@
  * called, it will be automatically enabled through the use of add_theme_support()
  *
  * @since 2.2.0
+ * @since 5.6.0 Added the `before_sidebar` and `after_sidebar` arguments.
  *
  * @global array $wp_registered_sidebars Registered sidebars.
  *
  * @param array|string $args {
  *     Optional. Array or string of arguments for the sidebar being registered.
  *
- *     @type string $name          The name or title of the sidebar displayed in the Widgets
- *                                 interface. Default 'Sidebar $instance'.
- *     @type string $id            The unique identifier by which the sidebar will be called.
- *                                 Default 'sidebar-$instance'.
- *     @type string $description   Description of the sidebar, displayed in the Widgets interface.
- *                                 Default empty string.
- *     @type string $class         Extra CSS class to assign to the sidebar in the Widgets interface.
- *                                 Default empty.
- *     @type string $before_widget HTML content to prepend to each widget's HTML output when
- *                                 assigned to this sidebar. Default is an opening list item element.
- *     @type string $after_widget  HTML content to append to each widget's HTML output when
- *                                 assigned to this sidebar. Default is a closing list item element.
- *     @type string $before_title  HTML content to prepend to the sidebar title when displayed.
- *                                 Default is an opening h2 element.
- *     @type string $after_title   HTML content to append to the sidebar title when displayed.
- *                                 Default is a closing h2 element.
+ *     @type string $name           The name or title of the sidebar displayed in the Widgets
+ *                                  interface. Default 'Sidebar $instance'.
+ *     @type string $id             The unique identifier by which the sidebar will be called.
+ *                                  Default 'sidebar-$instance'.
+ *     @type string $description    Description of the sidebar, displayed in the Widgets interface.
+ *                                  Default empty string.
+ *     @type string $class          Extra CSS class to assign to the sidebar in the Widgets interface.
+ *                                  Default empty.
+ *     @type string $before_widget  HTML content to prepend to each widget's HTML output when assigned
+ *                                  to this sidebar. Receives the widget's ID attribute as `%1$s`
+ *                                  and class name as `%2$s`. Default is an opening list item element.
+ *     @type string $after_widget   HTML content to append to each widget's HTML output when assigned
+ *                                  to this sidebar. Default is a closing list item element.
+ *     @type string $before_title   HTML content to prepend to the sidebar title when displayed.
+ *                                  Default is an opening h2 element.
+ *     @type string $after_title    HTML content to append to the sidebar title when displayed.
+ *                                  Default is a closing h2 element.
+ *     @type string $before_sidebar HTML content to prepend to the sidebar when displayed.
+ *                                  Receives the `$id` argument as `%1$s` and `$class` as `%2$s`.
+ *                                  Outputs after the {@see 'dynamic_sidebar_before'} action.
+ *                                  Default empty string.
+ *     @type string $after_sidebar  HTML content to append to the sidebar when displayed.
+ *                                  Outputs before the {@see 'dynamic_sidebar_after'} action.
+ *                                  Default empty string.
  * }
  * @return string Sidebar ID added to $wp_registered_sidebars global.
  */
@@ -253,14 +262,16 @@
 
 	$defaults = array(
 		/* translators: %d: Sidebar number. */
-		'name'          => sprintf( __( 'Sidebar %d' ), $i ),
-		'id'            => "sidebar-$i",
-		'description'   => '',
-		'class'         => '',
-		'before_widget' => '<li id="%1$s" class="widget %2$s">',
-		'after_widget'  => "</li>\n",
-		'before_title'  => '<h2 class="widgettitle">',
-		'after_title'   => "</h2>\n",
+		'name'           => sprintf( __( 'Sidebar %d' ), $i ),
+		'id'             => "sidebar-$i",
+		'description'    => '',
+		'class'          => '',
+		'before_widget'  => '<li id="%1$s" class="widget %2$s">',
+		'after_widget'   => "</li>\n",
+		'before_title'   => '<h2 class="widgettitle">',
+		'after_title'    => "</h2>\n",
+		'before_sidebar' => '',
+		'after_sidebar'  => '',
 	);
 
 	/**
@@ -346,6 +357,7 @@
  * @since 2.2.0
  * @since 5.3.0 Formalized the existing and already documented `...$params` parameter
  *              by adding it to the function signature.
+ * @since 5.8.0 Added show_instance_in_rest option.
  *
  * @global array $wp_registered_widgets            Uses stored registered widgets.
  * @global array $wp_registered_widget_controls    Stores the registered widget controls (options).
@@ -358,10 +370,12 @@
  * @param array      $options {
  *     Optional. An array of supplementary widget options for the instance.
  *
- *     @type string $classname   Class name for the widget's HTML container. Default is a shortened
- *                               version of the output callback name.
- *     @type string $description Widget description for display in the widget administration
- *                               panel and/or theme.
+ *     @type string $classname             Class name for the widget's HTML container. Default is a shortened
+ *                                         version of the output callback name.
+ *     @type string $description           Widget description for display in the widget administration
+ *                                         panel and/or theme.
+ *     @type bool   $show_instance_in_rest Whether to show the widget's instance settings in the REST API.
+ *                                         Only available for WP_Widget based widgets.
  * }
  * @param mixed      ...$params       Optional additional parameters to pass to the callback function when it's called.
  */
@@ -691,6 +705,10 @@
 		return apply_filters( 'dynamic_sidebar_has_widgets', false, $index );
 	}
 
+	$sidebar = $wp_registered_sidebars[ $index ];
+
+	$sidebar['before_sidebar'] = sprintf( $sidebar['before_sidebar'], $sidebar['id'], $sidebar['class'] );
+
 	/**
 	 * Fires before widgets are rendered in a dynamic sidebar.
 	 *
@@ -704,7 +722,10 @@
 	 *                                Default true.
 	 */
 	do_action( 'dynamic_sidebar_before', $index, true );
-	$sidebar = $wp_registered_sidebars[ $index ];
+
+	if ( ! is_admin() && ! empty( $sidebar['before_sidebar'] ) ) {
+		echo $sidebar['before_sidebar'];
+	}
 
 	$did_one = false;
 	foreach ( (array) $sidebars_widgets[ $index ] as $id ) {
@@ -735,8 +756,13 @@
 				$classname_ .= '_' . get_class( $cn );
 			}
 		}
-		$classname_                 = ltrim( $classname_, '_' );
-		$params[0]['before_widget'] = sprintf( $params[0]['before_widget'], $id, $classname_ );
+		$classname_ = ltrim( $classname_, '_' );
+
+		$params[0]['before_widget'] = sprintf(
+			$params[0]['before_widget'],
+			str_replace( '\\', '_', $id ),
+			$classname_
+		);
 
 		/**
 		 * Filters the parameters passed to a widget's display callback.
@@ -784,19 +810,19 @@
 		 *
 		 * @since 3.0.0
 		 *
-		 * @param array $widget_id {
+		 * @param array $widget {
 		 *     An associative array of widget arguments.
 		 *
 		 *     @type string   $name        Name of the widget.
 		 *     @type string   $id          Widget ID.
-		 *     @type callable $callback    When the hook is fired on the front end, $callback is an array
-		 *                                 containing the widget object. Fired on the back end, $callback
-		 *                                 is 'wp_widget_control', see $_callback.
+		 *     @type callable $callback    When the hook is fired on the front end, `$callback` is an array
+		 *                                 containing the widget object. Fired on the back end, `$callback`
+		 *                                 is 'wp_widget_control', see `$_callback`.
 		 *     @type array    $params      An associative array of multi-widget arguments.
 		 *     @type string   $classname   CSS class applied to the widget container.
 		 *     @type string   $description The widget description.
-		 *     @type array    $_callback   When the hook is fired on the back end, $_callback is populated
-		 *                                 with an array containing the widget object, see $callback.
+		 *     @type array    $_callback   When the hook is fired on the back end, `$_callback` is populated
+		 *                                 with an array containing the widget object, see `$callback`.
 		 * }
 		 */
 		do_action( 'dynamic_sidebar', $wp_registered_widgets[ $id ] );
@@ -807,6 +833,10 @@
 		}
 	}
 
+	if ( ! is_admin() && ! empty( $sidebar['after_sidebar'] ) ) {
+		echo $sidebar['after_sidebar'];
+	}
+
 	/**
 	 * Fires after widgets are rendered in a dynamic sidebar.
 	 *
@@ -857,11 +887,15 @@
  *
  * @global array $wp_registered_widgets
  *
- * @param callable|false $callback      Optional, Widget callback to check. Default false.
- * @param int|false      $widget_id     Optional. Widget ID. Optional, but needed for checking. Default false.
- * @param string|false   $id_base       Optional. The base ID of a widget created by extending WP_Widget. Default false.
- * @param bool           $skip_inactive Optional. Whether to check in 'wp_inactive_widgets'. Default true.
- * @return string|false False if widget is not active or id of sidebar in which the widget is active.
+ * @param callable|false $callback      Optional. Widget callback to check. Default false.
+ * @param string|false   $widget_id     Optional. Widget ID. Optional, but needed for checking.
+ *                                      Default false.
+ * @param string|false   $id_base       Optional. The base ID of a widget created by extending WP_Widget.
+ *                                      Default false.
+ * @param bool           $skip_inactive Optional. Whether to check in 'wp_inactive_widgets'.
+ *                                      Default true.
+ * @return string|false ID of the sidebar in which the widget is active,
+ *                      false if the widget is not active.
  */
 function is_active_widget( $callback = false, $widget_id = false, $id_base = false, $skip_inactive = true ) {
 	global $wp_registered_widgets;
@@ -1262,7 +1296,7 @@
 	$sidebars_widgets = wp_map_sidebars_widgets( $sidebars_widgets );
 
 	// Find hidden/lost multi-widget instances.
-	$shown_widgets = call_user_func_array( 'array_merge', $sidebars_widgets );
+	$shown_widgets = array_merge( ...array_values( $sidebars_widgets ) );
 	$lost_widgets  = array_diff( $registered_widgets_ids, $shown_widgets );
 
 	foreach ( $lost_widgets as $key => $widget_id ) {
@@ -1531,7 +1565,7 @@
 	echo '<ul>';
 	foreach ( $rss->get_items( 0, $items ) as $item ) {
 		$link = $item->get_link();
-		while ( stristr( $link, 'http' ) !== $link ) {
+		while ( ! empty( $link ) && stristr( $link, 'http' ) !== $link ) {
 			$link = substr( $link, 1 );
 		}
 		$link = esc_url( strip_tags( $link ) );
@@ -1765,6 +1799,8 @@
 
 	register_widget( 'WP_Widget_Custom_HTML' );
 
+	register_widget( 'WP_Widget_Block' );
+
 	/**
 	 * Fires after all default WordPress widgets have been registered.
 	 *
@@ -1772,3 +1808,243 @@
 	 */
 	do_action( 'widgets_init' );
 }
+
+/**
+ * Enables the widgets block editor. This is hooked into 'after_setup_theme' so
+ * that the block editor is enabled by default but can be disabled by themes.
+ *
+ * @since 5.8.0
+ *
+ * @access private
+ */
+function wp_setup_widgets_block_editor() {
+	add_theme_support( 'widgets-block-editor' );
+}
+
+/**
+ * Whether or not to use the block editor to manage widgets. Defaults to true
+ * unless a theme has removed support for widgets-block-editor or a plugin has
+ * filtered the return value of this function.
+ *
+ * @since 5.8.0
+ *
+ * @return boolean Whether or not to use the block editor to manage widgets.
+ */
+function wp_use_widgets_block_editor() {
+	/**
+	 * Filters whether or not to use the block editor to manage widgets.
+	 *
+	 * @since 5.8.0
+	 *
+	 * @param boolean $use_widgets_block_editor Whether or not to use the block editor to manage widgets.
+	 */
+	return apply_filters(
+		'use_widgets_block_editor',
+		get_theme_support( 'widgets-block-editor' )
+	);
+}
+
+/**
+ * Converts a widget ID into its id_base and number components.
+ *
+ * @since 5.8.0
+ *
+ * @param string $id Widget ID.
+ * @return array Array containing a widget's id_base and number components.
+ */
+function wp_parse_widget_id( $id ) {
+	$parsed = array();
+
+	if ( preg_match( '/^(.+)-(\d+)$/', $id, $matches ) ) {
+		$parsed['id_base'] = $matches[1];
+		$parsed['number']  = (int) $matches[2];
+	} else {
+		// Likely an old single widget.
+		$parsed['id_base'] = $id;
+	}
+
+	return $parsed;
+}
+
+/**
+ * Finds the sidebar that a given widget belongs to.
+ *
+ * @since 5.8.0
+ *
+ * @param string $widget_id The widget id to look for.
+ * @return string|null The found sidebar's id, or null if it was not found.
+ */
+function wp_find_widgets_sidebar( $widget_id ) {
+	foreach ( wp_get_sidebars_widgets() as $sidebar_id => $widget_ids ) {
+		foreach ( $widget_ids as $maybe_widget_id ) {
+			if ( $maybe_widget_id === $widget_id ) {
+				return (string) $sidebar_id;
+			}
+		}
+	}
+
+	return null;
+}
+
+/**
+ * Assigns a widget to the given sidebar.
+ *
+ * @since 5.8.0
+ *
+ * @param string $widget_id  The widget id to assign.
+ * @param string $sidebar_id The sidebar id to assign to. If empty, the widget won't be added to any sidebar.
+ */
+function wp_assign_widget_to_sidebar( $widget_id, $sidebar_id ) {
+	$sidebars = wp_get_sidebars_widgets();
+
+	foreach ( $sidebars as $maybe_sidebar_id => $widgets ) {
+		foreach ( $widgets as $i => $maybe_widget_id ) {
+			if ( $widget_id === $maybe_widget_id && $sidebar_id !== $maybe_sidebar_id ) {
+				unset( $sidebars[ $maybe_sidebar_id ][ $i ] );
+				// We could technically break 2 here, but continue looping in case the id is duplicated.
+				continue 2;
+			}
+		}
+	}
+
+	if ( $sidebar_id ) {
+		$sidebars[ $sidebar_id ][] = $widget_id;
+	}
+
+	wp_set_sidebars_widgets( $sidebars );
+}
+
+/**
+ * Calls the render callback of a widget and returns the output.
+ *
+ * @since 5.8.0
+ *
+ * @param string $widget_id Widget ID.
+ * @param string $sidebar_id Sidebar ID.
+ * @return string
+ */
+function wp_render_widget( $widget_id, $sidebar_id ) {
+	global $wp_registered_widgets, $wp_registered_sidebars;
+
+	if ( ! isset( $wp_registered_widgets[ $widget_id ] ) ) {
+		return '';
+	}
+
+	if ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ) {
+		$sidebar = $wp_registered_sidebars[ $sidebar_id ];
+	} elseif ( 'wp_inactive_widgets' === $sidebar_id ) {
+		$sidebar = array();
+	} else {
+		return '';
+	}
+
+	$params = array_merge(
+		array(
+			array_merge(
+				$sidebar,
+				array(
+					'widget_id'   => $widget_id,
+					'widget_name' => $wp_registered_widgets[ $widget_id ]['name'],
+				)
+			),
+		),
+		(array) $wp_registered_widgets[ $widget_id ]['params']
+	);
+
+	// Substitute HTML `id` and `class` attributes into `before_widget`.
+	$classname_ = '';
+	foreach ( (array) $wp_registered_widgets[ $widget_id ]['classname'] as $cn ) {
+		if ( is_string( $cn ) ) {
+			$classname_ .= '_' . $cn;
+		} elseif ( is_object( $cn ) ) {
+			$classname_ .= '_' . get_class( $cn );
+		}
+	}
+	$classname_                 = ltrim( $classname_, '_' );
+	$params[0]['before_widget'] = sprintf( $params[0]['before_widget'], $widget_id, $classname_ );
+
+	/** This filter is documented in wp-includes/widgets.php */
+	$params = apply_filters( 'dynamic_sidebar_params', $params );
+
+	$callback = $wp_registered_widgets[ $widget_id ]['callback'];
+
+	ob_start();
+
+	/** This filter is documented in wp-includes/widgets.php */
+	do_action( 'dynamic_sidebar', $wp_registered_widgets[ $widget_id ] );
+
+	if ( is_callable( $callback ) ) {
+		call_user_func_array( $callback, $params );
+	}
+
+	return ob_get_clean();
+}
+
+/**
+ * Calls the control callback of a widget and returns the output.
+ *
+ * @since 5.8.0
+ *
+ * @param string $id Widget ID.
+ * @return string|null
+ */
+function wp_render_widget_control( $id ) {
+	global $wp_registered_widget_controls;
+
+	if ( ! isset( $wp_registered_widget_controls[ $id ]['callback'] ) ) {
+		return null;
+	}
+
+	$callback = $wp_registered_widget_controls[ $id ]['callback'];
+	$params   = $wp_registered_widget_controls[ $id ]['params'];
+
+	ob_start();
+
+	if ( is_callable( $callback ) ) {
+		call_user_func_array( $callback, $params );
+	}
+
+	return ob_get_clean();
+}
+
+/**
+ * Displays a _doing_it_wrong() message for conflicting widget editor scripts.
+ *
+ * The 'wp-editor' script module is exposed as window.wp.editor. This overrides
+ * the legacy TinyMCE editor module which is required by the widgets editor.
+ * Because of that conflict, these two shouldn't be enqueued together.
+ * See https://core.trac.wordpress.org/ticket/53569.
+ *
+ * There is also another conflict related to styles where the block widgets
+ * editor is hidden if a block enqueues 'wp-edit-post' stylesheet.
+ * See https://core.trac.wordpress.org/ticket/53569.
+ *
+ * @since 5.8.0
+ * @access private
+ *
+ * @global WP_Scripts $wp_scripts
+ * @global WP_Styles  $wp_styles
+ */
+function wp_check_widget_editor_deps() {
+	global $wp_scripts, $wp_styles;
+
+	if (
+		$wp_scripts->query( 'wp-edit-widgets', 'enqueued' ) ||
+		$wp_scripts->query( 'wp-customize-widgets', 'enqueued' )
+	) {
+		if ( $wp_scripts->query( 'wp-editor', 'enqueued' ) ) {
+			_doing_it_wrong(
+				'wp_enqueue_script()',
+				'"wp-editor" script should not be enqueued together with the new widgets editor (wp-edit-widgets or wp-customize-widgets).',
+				'5.8.0'
+			);
+		}
+		if ( $wp_styles->query( 'wp-edit-post', 'enqueued' ) ) {
+			_doing_it_wrong(
+				'wp_enqueue_style()',
+				'"wp-edit-post" style should not be enqueued together with the new widgets editor (wp-edit-widgets or wp-customize-widgets).',
+				'5.8.0'
+			);
+		}
+	}
+}