widgets.php (67278B)
1 <?php 2 /** 3 * Core Widgets API 4 * 5 * This API is used for creating dynamic sidebar without hardcoding functionality into 6 * themes 7 * 8 * Includes both internal WordPress routines and theme-use routines. 9 * 10 * This functionality was found in a plugin before the WordPress 2.2 release, which 11 * included it in the core from that point on. 12 * 13 * @link https://wordpress.org/support/article/wordpress-widgets/ 14 * @link https://developer.wordpress.org/themes/functionality/widgets/ 15 * 16 * @package WordPress 17 * @subpackage Widgets 18 * @since 2.2.0 19 */ 20 21 // 22 // Global Variables. 23 // 24 25 /** @ignore */ 26 global $wp_registered_sidebars, $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates; 27 28 /** 29 * Stores the sidebars, since many themes can have more than one. 30 * 31 * @global array $wp_registered_sidebars Registered sidebars. 32 * @since 2.2.0 33 */ 34 $wp_registered_sidebars = array(); 35 36 /** 37 * Stores the registered widgets. 38 * 39 * @global array $wp_registered_widgets 40 * @since 2.2.0 41 */ 42 $wp_registered_widgets = array(); 43 44 /** 45 * Stores the registered widget controls (options). 46 * 47 * @global array $wp_registered_widget_controls 48 * @since 2.2.0 49 */ 50 $wp_registered_widget_controls = array(); 51 /** 52 * @global array $wp_registered_widget_updates 53 */ 54 $wp_registered_widget_updates = array(); 55 56 /** 57 * Private 58 * 59 * @global array $_wp_sidebars_widgets 60 */ 61 $_wp_sidebars_widgets = array(); 62 63 /** 64 * Private 65 * 66 * @global array $_wp_deprecated_widgets_callbacks 67 */ 68 $GLOBALS['_wp_deprecated_widgets_callbacks'] = array( 69 'wp_widget_pages', 70 'wp_widget_pages_control', 71 'wp_widget_calendar', 72 'wp_widget_calendar_control', 73 'wp_widget_archives', 74 'wp_widget_archives_control', 75 'wp_widget_links', 76 'wp_widget_meta', 77 'wp_widget_meta_control', 78 'wp_widget_search', 79 'wp_widget_recent_entries', 80 'wp_widget_recent_entries_control', 81 'wp_widget_tag_cloud', 82 'wp_widget_tag_cloud_control', 83 'wp_widget_categories', 84 'wp_widget_categories_control', 85 'wp_widget_text', 86 'wp_widget_text_control', 87 'wp_widget_rss', 88 'wp_widget_rss_control', 89 'wp_widget_recent_comments', 90 'wp_widget_recent_comments_control', 91 ); 92 93 // 94 // Template tags & API functions. 95 // 96 97 /** 98 * Register a widget 99 * 100 * Registers a WP_Widget widget 101 * 102 * @since 2.8.0 103 * @since 4.6.0 Updated the `$widget` parameter to also accept a WP_Widget instance object 104 * instead of simply a `WP_Widget` subclass name. 105 * 106 * @see WP_Widget 107 * 108 * @global WP_Widget_Factory $wp_widget_factory 109 * 110 * @param string|WP_Widget $widget Either the name of a `WP_Widget` subclass or an instance of a `WP_Widget` subclass. 111 */ 112 function register_widget( $widget ) { 113 global $wp_widget_factory; 114 115 $wp_widget_factory->register( $widget ); 116 } 117 118 /** 119 * Unregisters a widget. 120 * 121 * Unregisters a WP_Widget widget. Useful for un-registering default widgets. 122 * Run within a function hooked to the {@see 'widgets_init'} action. 123 * 124 * @since 2.8.0 125 * @since 4.6.0 Updated the `$widget` parameter to also accept a WP_Widget instance object 126 * instead of simply a `WP_Widget` subclass name. 127 * 128 * @see WP_Widget 129 * 130 * @global WP_Widget_Factory $wp_widget_factory 131 * 132 * @param string|WP_Widget $widget Either the name of a `WP_Widget` subclass or an instance of a `WP_Widget` subclass. 133 */ 134 function unregister_widget( $widget ) { 135 global $wp_widget_factory; 136 137 $wp_widget_factory->unregister( $widget ); 138 } 139 140 /** 141 * Creates multiple sidebars. 142 * 143 * If you wanted to quickly create multiple sidebars for a theme or internally. 144 * This function will allow you to do so. If you don't pass the 'name' and/or 145 * 'id' in `$args`, then they will be built for you. 146 * 147 * @since 2.2.0 148 * 149 * @see register_sidebar() The second parameter is documented by register_sidebar() and is the same here. 150 * 151 * @global array $wp_registered_sidebars The new sidebars are stored in this array by sidebar ID. 152 * 153 * @param int $number Optional. Number of sidebars to create. Default 1. 154 * @param array|string $args { 155 * Optional. Array or string of arguments for building a sidebar. 156 * 157 * @type string $id The base string of the unique identifier for each sidebar. If provided, and multiple 158 * sidebars are being defined, the ID will have "-2" appended, and so on. 159 * Default 'sidebar-' followed by the number the sidebar creation is currently at. 160 * @type string $name The name or title for the sidebars displayed in the admin dashboard. If registering 161 * more than one sidebar, include '%d' in the string as a placeholder for the uniquely 162 * assigned number for each sidebar. 163 * Default 'Sidebar' for the first sidebar, otherwise 'Sidebar %d'. 164 * } 165 */ 166 function register_sidebars( $number = 1, $args = array() ) { 167 global $wp_registered_sidebars; 168 $number = (int) $number; 169 170 if ( is_string( $args ) ) { 171 parse_str( $args, $args ); 172 } 173 174 for ( $i = 1; $i <= $number; $i++ ) { 175 $_args = $args; 176 177 if ( $number > 1 ) { 178 if ( isset( $args['name'] ) ) { 179 $_args['name'] = sprintf( $args['name'], $i ); 180 } else { 181 /* translators: %d: Sidebar number. */ 182 $_args['name'] = sprintf( __( 'Sidebar %d' ), $i ); 183 } 184 } else { 185 $_args['name'] = isset( $args['name'] ) ? $args['name'] : __( 'Sidebar' ); 186 } 187 188 // Custom specified ID's are suffixed if they exist already. 189 // Automatically generated sidebar names need to be suffixed regardless starting at -0. 190 if ( isset( $args['id'] ) ) { 191 $_args['id'] = $args['id']; 192 $n = 2; // Start at -2 for conflicting custom IDs. 193 while ( is_registered_sidebar( $_args['id'] ) ) { 194 $_args['id'] = $args['id'] . '-' . $n++; 195 } 196 } else { 197 $n = count( $wp_registered_sidebars ); 198 do { 199 $_args['id'] = 'sidebar-' . ++$n; 200 } while ( is_registered_sidebar( $_args['id'] ) ); 201 } 202 register_sidebar( $_args ); 203 } 204 } 205 206 /** 207 * Builds the definition for a single sidebar and returns the ID. 208 * 209 * Accepts either a string or an array and then parses that against a set 210 * of default arguments for the new sidebar. WordPress will automatically 211 * generate a sidebar ID and name based on the current number of registered 212 * sidebars if those arguments are not included. 213 * 214 * When allowing for automatic generation of the name and ID parameters, keep 215 * in mind that the incrementor for your sidebar can change over time depending 216 * on what other plugins and themes are installed. 217 * 218 * If theme support for 'widgets' has not yet been added when this function is 219 * called, it will be automatically enabled through the use of add_theme_support() 220 * 221 * @since 2.2.0 222 * @since 5.6.0 Added the `before_sidebar` and `after_sidebar` arguments. 223 * 224 * @global array $wp_registered_sidebars Registered sidebars. 225 * 226 * @param array|string $args { 227 * Optional. Array or string of arguments for the sidebar being registered. 228 * 229 * @type string $name The name or title of the sidebar displayed in the Widgets 230 * interface. Default 'Sidebar $instance'. 231 * @type string $id The unique identifier by which the sidebar will be called. 232 * Default 'sidebar-$instance'. 233 * @type string $description Description of the sidebar, displayed in the Widgets interface. 234 * Default empty string. 235 * @type string $class Extra CSS class to assign to the sidebar in the Widgets interface. 236 * Default empty. 237 * @type string $before_widget HTML content to prepend to each widget's HTML output when assigned 238 * to this sidebar. Receives the widget's ID attribute as `%1$s` 239 * and class name as `%2$s`. Default is an opening list item element. 240 * @type string $after_widget HTML content to append to each widget's HTML output when assigned 241 * to this sidebar. Default is a closing list item element. 242 * @type string $before_title HTML content to prepend to the sidebar title when displayed. 243 * Default is an opening h2 element. 244 * @type string $after_title HTML content to append to the sidebar title when displayed. 245 * Default is a closing h2 element. 246 * @type string $before_sidebar HTML content to prepend to the sidebar when displayed. 247 * Receives the `$id` argument as `%1$s` and `$class` as `%2$s`. 248 * Outputs after the {@see 'dynamic_sidebar_before'} action. 249 * Default empty string. 250 * @type string $after_sidebar HTML content to append to the sidebar when displayed. 251 * Outputs before the {@see 'dynamic_sidebar_after'} action. 252 * Default empty string. 253 * } 254 * @return string Sidebar ID added to $wp_registered_sidebars global. 255 */ 256 function register_sidebar( $args = array() ) { 257 global $wp_registered_sidebars; 258 259 $i = count( $wp_registered_sidebars ) + 1; 260 261 $id_is_empty = empty( $args['id'] ); 262 263 $defaults = array( 264 /* translators: %d: Sidebar number. */ 265 'name' => sprintf( __( 'Sidebar %d' ), $i ), 266 'id' => "sidebar-$i", 267 'description' => '', 268 'class' => '', 269 'before_widget' => '<li id="%1$s" class="widget %2$s">', 270 'after_widget' => "</li>\n", 271 'before_title' => '<h2 class="widgettitle">', 272 'after_title' => "</h2>\n", 273 'before_sidebar' => '', 274 'after_sidebar' => '', 275 ); 276 277 /** 278 * Filters the sidebar default arguments. 279 * 280 * @since 5.3.0 281 * 282 * @see register_sidebar() 283 * 284 * @param array $defaults The default sidebar arguments. 285 */ 286 $sidebar = wp_parse_args( $args, apply_filters( 'register_sidebar_defaults', $defaults ) ); 287 288 if ( $id_is_empty ) { 289 _doing_it_wrong( 290 __FUNCTION__, 291 sprintf( 292 /* translators: 1: The 'id' argument, 2: Sidebar name, 3: Recommended 'id' value. */ 293 __( 'No %1$s was set in the arguments array for the "%2$s" sidebar. Defaulting to "%3$s". Manually set the %1$s to "%3$s" to silence this notice and keep existing sidebar content.' ), 294 '<code>id</code>', 295 $sidebar['name'], 296 $sidebar['id'] 297 ), 298 '4.2.0' 299 ); 300 } 301 302 $wp_registered_sidebars[ $sidebar['id'] ] = $sidebar; 303 304 add_theme_support( 'widgets' ); 305 306 /** 307 * Fires once a sidebar has been registered. 308 * 309 * @since 3.0.0 310 * 311 * @param array $sidebar Parsed arguments for the registered sidebar. 312 */ 313 do_action( 'register_sidebar', $sidebar ); 314 315 return $sidebar['id']; 316 } 317 318 /** 319 * Removes a sidebar from the list. 320 * 321 * @since 2.2.0 322 * 323 * @global array $wp_registered_sidebars Registered sidebars. 324 * 325 * @param string|int $sidebar_id The ID of the sidebar when it was registered. 326 */ 327 function unregister_sidebar( $sidebar_id ) { 328 global $wp_registered_sidebars; 329 330 unset( $wp_registered_sidebars[ $sidebar_id ] ); 331 } 332 333 /** 334 * Checks if a sidebar is registered. 335 * 336 * @since 4.4.0 337 * 338 * @global array $wp_registered_sidebars Registered sidebars. 339 * 340 * @param string|int $sidebar_id The ID of the sidebar when it was registered. 341 * @return bool True if the sidebar is registered, false otherwise. 342 */ 343 function is_registered_sidebar( $sidebar_id ) { 344 global $wp_registered_sidebars; 345 346 return isset( $wp_registered_sidebars[ $sidebar_id ] ); 347 } 348 349 /** 350 * Register an instance of a widget. 351 * 352 * The default widget option is 'classname' that can be overridden. 353 * 354 * The function can also be used to un-register widgets when `$output_callback` 355 * parameter is an empty string. 356 * 357 * @since 2.2.0 358 * @since 5.3.0 Formalized the existing and already documented `...$params` parameter 359 * by adding it to the function signature. 360 * @since 5.8.0 Added show_instance_in_rest option. 361 * 362 * @global array $wp_registered_widgets Uses stored registered widgets. 363 * @global array $wp_registered_widget_controls Stores the registered widget controls (options). 364 * @global array $wp_registered_widget_updates 365 * @global array $_wp_deprecated_widgets_callbacks 366 * 367 * @param int|string $id Widget ID. 368 * @param string $name Widget display title. 369 * @param callable $output_callback Run when widget is called. 370 * @param array $options { 371 * Optional. An array of supplementary widget options for the instance. 372 * 373 * @type string $classname Class name for the widget's HTML container. Default is a shortened 374 * version of the output callback name. 375 * @type string $description Widget description for display in the widget administration 376 * panel and/or theme. 377 * @type bool $show_instance_in_rest Whether to show the widget's instance settings in the REST API. 378 * Only available for WP_Widget based widgets. 379 * } 380 * @param mixed ...$params Optional additional parameters to pass to the callback function when it's called. 381 */ 382 function wp_register_sidebar_widget( $id, $name, $output_callback, $options = array(), ...$params ) { 383 global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates, $_wp_deprecated_widgets_callbacks; 384 385 $id = strtolower( $id ); 386 387 if ( empty( $output_callback ) ) { 388 unset( $wp_registered_widgets[ $id ] ); 389 return; 390 } 391 392 $id_base = _get_widget_id_base( $id ); 393 if ( in_array( $output_callback, $_wp_deprecated_widgets_callbacks, true ) && ! is_callable( $output_callback ) ) { 394 unset( $wp_registered_widget_controls[ $id ] ); 395 unset( $wp_registered_widget_updates[ $id_base ] ); 396 return; 397 } 398 399 $defaults = array( 'classname' => $output_callback ); 400 $options = wp_parse_args( $options, $defaults ); 401 $widget = array( 402 'name' => $name, 403 'id' => $id, 404 'callback' => $output_callback, 405 'params' => $params, 406 ); 407 $widget = array_merge( $widget, $options ); 408 409 if ( is_callable( $output_callback ) && ( ! isset( $wp_registered_widgets[ $id ] ) || did_action( 'widgets_init' ) ) ) { 410 411 /** 412 * Fires once for each registered widget. 413 * 414 * @since 3.0.0 415 * 416 * @param array $widget An array of default widget arguments. 417 */ 418 do_action( 'wp_register_sidebar_widget', $widget ); 419 $wp_registered_widgets[ $id ] = $widget; 420 } 421 } 422 423 /** 424 * Retrieve description for widget. 425 * 426 * When registering widgets, the options can also include 'description' that 427 * describes the widget for display on the widget administration panel or 428 * in the theme. 429 * 430 * @since 2.5.0 431 * 432 * @global array $wp_registered_widgets 433 * 434 * @param int|string $id Widget ID. 435 * @return string|void Widget description, if available. 436 */ 437 function wp_widget_description( $id ) { 438 if ( ! is_scalar( $id ) ) { 439 return; 440 } 441 442 global $wp_registered_widgets; 443 444 if ( isset( $wp_registered_widgets[ $id ]['description'] ) ) { 445 return esc_html( $wp_registered_widgets[ $id ]['description'] ); 446 } 447 } 448 449 /** 450 * Retrieve description for a sidebar. 451 * 452 * When registering sidebars a 'description' parameter can be included that 453 * describes the sidebar for display on the widget administration panel. 454 * 455 * @since 2.9.0 456 * 457 * @global array $wp_registered_sidebars Registered sidebars. 458 * 459 * @param string $id sidebar ID. 460 * @return string|void Sidebar description, if available. 461 */ 462 function wp_sidebar_description( $id ) { 463 if ( ! is_scalar( $id ) ) { 464 return; 465 } 466 467 global $wp_registered_sidebars; 468 469 if ( isset( $wp_registered_sidebars[ $id ]['description'] ) ) { 470 return wp_kses( $wp_registered_sidebars[ $id ]['description'], 'sidebar_description' ); 471 } 472 } 473 474 /** 475 * Remove widget from sidebar. 476 * 477 * @since 2.2.0 478 * 479 * @param int|string $id Widget ID. 480 */ 481 function wp_unregister_sidebar_widget( $id ) { 482 483 /** 484 * Fires just before a widget is removed from a sidebar. 485 * 486 * @since 3.0.0 487 * 488 * @param int $id The widget ID. 489 */ 490 do_action( 'wp_unregister_sidebar_widget', $id ); 491 492 wp_register_sidebar_widget( $id, '', '' ); 493 wp_unregister_widget_control( $id ); 494 } 495 496 /** 497 * Registers widget control callback for customizing options. 498 * 499 * @since 2.2.0 500 * @since 5.3.0 Formalized the existing and already documented `...$params` parameter 501 * by adding it to the function signature. 502 * 503 * @global array $wp_registered_widget_controls 504 * @global array $wp_registered_widget_updates 505 * @global array $wp_registered_widgets 506 * @global array $_wp_deprecated_widgets_callbacks 507 * 508 * @param int|string $id Sidebar ID. 509 * @param string $name Sidebar display name. 510 * @param callable $control_callback Run when sidebar is displayed. 511 * @param array $options { 512 * Optional. Array or string of control options. Default empty array. 513 * 514 * @type int $height Never used. Default 200. 515 * @type int $width Width of the fully expanded control form (but try hard to use the default width). 516 * Default 250. 517 * @type int|string $id_base Required for multi-widgets, i.e widgets that allow multiple instances such as the 518 * text widget. The widget id will end up looking like `{$id_base}-{$unique_number}`. 519 * } 520 * @param mixed ...$params Optional additional parameters to pass to the callback function when it's called. 521 */ 522 function wp_register_widget_control( $id, $name, $control_callback, $options = array(), ...$params ) { 523 global $wp_registered_widget_controls, $wp_registered_widget_updates, $wp_registered_widgets, $_wp_deprecated_widgets_callbacks; 524 525 $id = strtolower( $id ); 526 $id_base = _get_widget_id_base( $id ); 527 528 if ( empty( $control_callback ) ) { 529 unset( $wp_registered_widget_controls[ $id ] ); 530 unset( $wp_registered_widget_updates[ $id_base ] ); 531 return; 532 } 533 534 if ( in_array( $control_callback, $_wp_deprecated_widgets_callbacks, true ) && ! is_callable( $control_callback ) ) { 535 unset( $wp_registered_widgets[ $id ] ); 536 return; 537 } 538 539 if ( isset( $wp_registered_widget_controls[ $id ] ) && ! did_action( 'widgets_init' ) ) { 540 return; 541 } 542 543 $defaults = array( 544 'width' => 250, 545 'height' => 200, 546 ); // Height is never used. 547 $options = wp_parse_args( $options, $defaults ); 548 $options['width'] = (int) $options['width']; 549 $options['height'] = (int) $options['height']; 550 551 $widget = array( 552 'name' => $name, 553 'id' => $id, 554 'callback' => $control_callback, 555 'params' => $params, 556 ); 557 $widget = array_merge( $widget, $options ); 558 559 $wp_registered_widget_controls[ $id ] = $widget; 560 561 if ( isset( $wp_registered_widget_updates[ $id_base ] ) ) { 562 return; 563 } 564 565 if ( isset( $widget['params'][0]['number'] ) ) { 566 $widget['params'][0]['number'] = -1; 567 } 568 569 unset( $widget['width'], $widget['height'], $widget['name'], $widget['id'] ); 570 $wp_registered_widget_updates[ $id_base ] = $widget; 571 } 572 573 /** 574 * Registers the update callback for a widget. 575 * 576 * @since 2.8.0 577 * @since 5.3.0 Formalized the existing and already documented `...$params` parameter 578 * by adding it to the function signature. 579 * 580 * @global array $wp_registered_widget_updates 581 * 582 * @param string $id_base The base ID of a widget created by extending WP_Widget. 583 * @param callable $update_callback Update callback method for the widget. 584 * @param array $options Optional. Widget control options. See wp_register_widget_control(). 585 * Default empty array. 586 * @param mixed ...$params Optional additional parameters to pass to the callback function when it's called. 587 */ 588 function _register_widget_update_callback( $id_base, $update_callback, $options = array(), ...$params ) { 589 global $wp_registered_widget_updates; 590 591 if ( isset( $wp_registered_widget_updates[ $id_base ] ) ) { 592 if ( empty( $update_callback ) ) { 593 unset( $wp_registered_widget_updates[ $id_base ] ); 594 } 595 return; 596 } 597 598 $widget = array( 599 'callback' => $update_callback, 600 'params' => $params, 601 ); 602 603 $widget = array_merge( $widget, $options ); 604 $wp_registered_widget_updates[ $id_base ] = $widget; 605 } 606 607 /** 608 * Registers the form callback for a widget. 609 * 610 * @since 2.8.0 611 * @since 5.3.0 Formalized the existing and already documented `...$params` parameter 612 * by adding it to the function signature. 613 * 614 * @global array $wp_registered_widget_controls 615 * 616 * @param int|string $id Widget ID. 617 * @param string $name Name attribute for the widget. 618 * @param callable $form_callback Form callback. 619 * @param array $options Optional. Widget control options. See wp_register_widget_control(). 620 * Default empty array. 621 * @param mixed ...$params Optional additional parameters to pass to the callback function when it's called. 622 */ 623 624 function _register_widget_form_callback( $id, $name, $form_callback, $options = array(), ...$params ) { 625 global $wp_registered_widget_controls; 626 627 $id = strtolower( $id ); 628 629 if ( empty( $form_callback ) ) { 630 unset( $wp_registered_widget_controls[ $id ] ); 631 return; 632 } 633 634 if ( isset( $wp_registered_widget_controls[ $id ] ) && ! did_action( 'widgets_init' ) ) { 635 return; 636 } 637 638 $defaults = array( 639 'width' => 250, 640 'height' => 200, 641 ); 642 $options = wp_parse_args( $options, $defaults ); 643 $options['width'] = (int) $options['width']; 644 $options['height'] = (int) $options['height']; 645 646 $widget = array( 647 'name' => $name, 648 'id' => $id, 649 'callback' => $form_callback, 650 'params' => $params, 651 ); 652 $widget = array_merge( $widget, $options ); 653 654 $wp_registered_widget_controls[ $id ] = $widget; 655 } 656 657 /** 658 * Remove control callback for widget. 659 * 660 * @since 2.2.0 661 * 662 * @param int|string $id Widget ID. 663 */ 664 function wp_unregister_widget_control( $id ) { 665 wp_register_widget_control( $id, '', '' ); 666 } 667 668 /** 669 * Display dynamic sidebar. 670 * 671 * By default this displays the default sidebar or 'sidebar-1'. If your theme specifies the 'id' or 672 * 'name' parameter for its registered sidebars you can pass an ID or name as the $index parameter. 673 * Otherwise, you can pass in a numerical index to display the sidebar at that index. 674 * 675 * @since 2.2.0 676 * 677 * @global array $wp_registered_sidebars Registered sidebars. 678 * @global array $wp_registered_widgets Registered widgets. 679 * 680 * @param int|string $index Optional. Index, name or ID of dynamic sidebar. Default 1. 681 * @return bool True, if widget sidebar was found and called. False if not found or not called. 682 */ 683 function dynamic_sidebar( $index = 1 ) { 684 global $wp_registered_sidebars, $wp_registered_widgets; 685 686 if ( is_int( $index ) ) { 687 $index = "sidebar-$index"; 688 } else { 689 $index = sanitize_title( $index ); 690 foreach ( (array) $wp_registered_sidebars as $key => $value ) { 691 if ( sanitize_title( $value['name'] ) === $index ) { 692 $index = $key; 693 break; 694 } 695 } 696 } 697 698 $sidebars_widgets = wp_get_sidebars_widgets(); 699 if ( empty( $wp_registered_sidebars[ $index ] ) || empty( $sidebars_widgets[ $index ] ) || ! is_array( $sidebars_widgets[ $index ] ) ) { 700 /** This action is documented in wp-includes/widget.php */ 701 do_action( 'dynamic_sidebar_before', $index, false ); 702 /** This action is documented in wp-includes/widget.php */ 703 do_action( 'dynamic_sidebar_after', $index, false ); 704 /** This filter is documented in wp-includes/widget.php */ 705 return apply_filters( 'dynamic_sidebar_has_widgets', false, $index ); 706 } 707 708 $sidebar = $wp_registered_sidebars[ $index ]; 709 710 $sidebar['before_sidebar'] = sprintf( $sidebar['before_sidebar'], $sidebar['id'], $sidebar['class'] ); 711 712 /** 713 * Fires before widgets are rendered in a dynamic sidebar. 714 * 715 * Note: The action also fires for empty sidebars, and on both the front end 716 * and back end, including the Inactive Widgets sidebar on the Widgets screen. 717 * 718 * @since 3.9.0 719 * 720 * @param int|string $index Index, name, or ID of the dynamic sidebar. 721 * @param bool $has_widgets Whether the sidebar is populated with widgets. 722 * Default true. 723 */ 724 do_action( 'dynamic_sidebar_before', $index, true ); 725 726 if ( ! is_admin() && ! empty( $sidebar['before_sidebar'] ) ) { 727 echo $sidebar['before_sidebar']; 728 } 729 730 $did_one = false; 731 foreach ( (array) $sidebars_widgets[ $index ] as $id ) { 732 733 if ( ! isset( $wp_registered_widgets[ $id ] ) ) { 734 continue; 735 } 736 737 $params = array_merge( 738 array( 739 array_merge( 740 $sidebar, 741 array( 742 'widget_id' => $id, 743 'widget_name' => $wp_registered_widgets[ $id ]['name'], 744 ) 745 ), 746 ), 747 (array) $wp_registered_widgets[ $id ]['params'] 748 ); 749 750 // Substitute HTML `id` and `class` attributes into `before_widget`. 751 $classname_ = ''; 752 foreach ( (array) $wp_registered_widgets[ $id ]['classname'] as $cn ) { 753 if ( is_string( $cn ) ) { 754 $classname_ .= '_' . $cn; 755 } elseif ( is_object( $cn ) ) { 756 $classname_ .= '_' . get_class( $cn ); 757 } 758 } 759 $classname_ = ltrim( $classname_, '_' ); 760 761 $params[0]['before_widget'] = sprintf( 762 $params[0]['before_widget'], 763 str_replace( '\\', '_', $id ), 764 $classname_ 765 ); 766 767 /** 768 * Filters the parameters passed to a widget's display callback. 769 * 770 * Note: The filter is evaluated on both the front end and back end, 771 * including for the Inactive Widgets sidebar on the Widgets screen. 772 * 773 * @since 2.5.0 774 * 775 * @see register_sidebar() 776 * 777 * @param array $params { 778 * @type array $args { 779 * An array of widget display arguments. 780 * 781 * @type string $name Name of the sidebar the widget is assigned to. 782 * @type string $id ID of the sidebar the widget is assigned to. 783 * @type string $description The sidebar description. 784 * @type string $class CSS class applied to the sidebar container. 785 * @type string $before_widget HTML markup to prepend to each widget in the sidebar. 786 * @type string $after_widget HTML markup to append to each widget in the sidebar. 787 * @type string $before_title HTML markup to prepend to the widget title when displayed. 788 * @type string $after_title HTML markup to append to the widget title when displayed. 789 * @type string $widget_id ID of the widget. 790 * @type string $widget_name Name of the widget. 791 * } 792 * @type array $widget_args { 793 * An array of multi-widget arguments. 794 * 795 * @type int $number Number increment used for multiples of the same widget. 796 * } 797 * } 798 */ 799 $params = apply_filters( 'dynamic_sidebar_params', $params ); 800 801 $callback = $wp_registered_widgets[ $id ]['callback']; 802 803 /** 804 * Fires before a widget's display callback is called. 805 * 806 * Note: The action fires on both the front end and back end, including 807 * for widgets in the Inactive Widgets sidebar on the Widgets screen. 808 * 809 * The action is not fired for empty sidebars. 810 * 811 * @since 3.0.0 812 * 813 * @param array $widget { 814 * An associative array of widget arguments. 815 * 816 * @type string $name Name of the widget. 817 * @type string $id Widget ID. 818 * @type callable $callback When the hook is fired on the front end, `$callback` is an array 819 * containing the widget object. Fired on the back end, `$callback` 820 * is 'wp_widget_control', see `$_callback`. 821 * @type array $params An associative array of multi-widget arguments. 822 * @type string $classname CSS class applied to the widget container. 823 * @type string $description The widget description. 824 * @type array $_callback When the hook is fired on the back end, `$_callback` is populated 825 * with an array containing the widget object, see `$callback`. 826 * } 827 */ 828 do_action( 'dynamic_sidebar', $wp_registered_widgets[ $id ] ); 829 830 if ( is_callable( $callback ) ) { 831 call_user_func_array( $callback, $params ); 832 $did_one = true; 833 } 834 } 835 836 if ( ! is_admin() && ! empty( $sidebar['after_sidebar'] ) ) { 837 echo $sidebar['after_sidebar']; 838 } 839 840 /** 841 * Fires after widgets are rendered in a dynamic sidebar. 842 * 843 * Note: The action also fires for empty sidebars, and on both the front end 844 * and back end, including the Inactive Widgets sidebar on the Widgets screen. 845 * 846 * @since 3.9.0 847 * 848 * @param int|string $index Index, name, or ID of the dynamic sidebar. 849 * @param bool $has_widgets Whether the sidebar is populated with widgets. 850 * Default true. 851 */ 852 do_action( 'dynamic_sidebar_after', $index, true ); 853 854 /** 855 * Filters whether a sidebar has widgets. 856 * 857 * Note: The filter is also evaluated for empty sidebars, and on both the front end 858 * and back end, including the Inactive Widgets sidebar on the Widgets screen. 859 * 860 * @since 3.9.0 861 * 862 * @param bool $did_one Whether at least one widget was rendered in the sidebar. 863 * Default false. 864 * @param int|string $index Index, name, or ID of the dynamic sidebar. 865 */ 866 return apply_filters( 'dynamic_sidebar_has_widgets', $did_one, $index ); 867 } 868 869 /** 870 * Determines whether a given widget is displayed on the front end. 871 * 872 * Either $callback or $id_base can be used 873 * $id_base is the first argument when extending WP_Widget class 874 * Without the optional $widget_id parameter, returns the ID of the first sidebar 875 * in which the first instance of the widget with the given callback or $id_base is found. 876 * With the $widget_id parameter, returns the ID of the sidebar where 877 * the widget with that callback/$id_base AND that ID is found. 878 * 879 * NOTE: $widget_id and $id_base are the same for single widgets. To be effective 880 * this function has to run after widgets have initialized, at action {@see 'init'} or later. 881 * 882 * For more information on this and similar theme functions, check out 883 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 884 * Conditional Tags} article in the Theme Developer Handbook. 885 * 886 * @since 2.2.0 887 * 888 * @global array $wp_registered_widgets 889 * 890 * @param callable|false $callback Optional. Widget callback to check. Default false. 891 * @param string|false $widget_id Optional. Widget ID. Optional, but needed for checking. 892 * Default false. 893 * @param string|false $id_base Optional. The base ID of a widget created by extending WP_Widget. 894 * Default false. 895 * @param bool $skip_inactive Optional. Whether to check in 'wp_inactive_widgets'. 896 * Default true. 897 * @return string|false ID of the sidebar in which the widget is active, 898 * false if the widget is not active. 899 */ 900 function is_active_widget( $callback = false, $widget_id = false, $id_base = false, $skip_inactive = true ) { 901 global $wp_registered_widgets; 902 903 $sidebars_widgets = wp_get_sidebars_widgets(); 904 905 if ( is_array( $sidebars_widgets ) ) { 906 foreach ( $sidebars_widgets as $sidebar => $widgets ) { 907 if ( $skip_inactive && ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) ) { 908 continue; 909 } 910 911 if ( is_array( $widgets ) ) { 912 foreach ( $widgets as $widget ) { 913 if ( ( $callback && isset( $wp_registered_widgets[ $widget ]['callback'] ) && $wp_registered_widgets[ $widget ]['callback'] === $callback ) || ( $id_base && _get_widget_id_base( $widget ) === $id_base ) ) { 914 if ( ! $widget_id || $widget_id === $wp_registered_widgets[ $widget ]['id'] ) { 915 return $sidebar; 916 } 917 } 918 } 919 } 920 } 921 } 922 return false; 923 } 924 925 /** 926 * Determines whether the dynamic sidebar is enabled and used by the theme. 927 * 928 * For more information on this and similar theme functions, check out 929 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 930 * Conditional Tags} article in the Theme Developer Handbook. 931 * 932 * @since 2.2.0 933 * 934 * @global array $wp_registered_widgets Registered widgets. 935 * @global array $wp_registered_sidebars Registered sidebars. 936 * 937 * @return bool True if using widgets, false otherwise. 938 */ 939 function is_dynamic_sidebar() { 940 global $wp_registered_widgets, $wp_registered_sidebars; 941 942 $sidebars_widgets = get_option( 'sidebars_widgets' ); 943 944 foreach ( (array) $wp_registered_sidebars as $index => $sidebar ) { 945 if ( ! empty( $sidebars_widgets[ $index ] ) ) { 946 foreach ( (array) $sidebars_widgets[ $index ] as $widget ) { 947 if ( array_key_exists( $widget, $wp_registered_widgets ) ) { 948 return true; 949 } 950 } 951 } 952 } 953 954 return false; 955 } 956 957 /** 958 * Determines whether a sidebar contains widgets. 959 * 960 * For more information on this and similar theme functions, check out 961 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 962 * Conditional Tags} article in the Theme Developer Handbook. 963 * 964 * @since 2.8.0 965 * 966 * @param string|int $index Sidebar name, id or number to check. 967 * @return bool True if the sidebar has widgets, false otherwise. 968 */ 969 function is_active_sidebar( $index ) { 970 $index = ( is_int( $index ) ) ? "sidebar-$index" : sanitize_title( $index ); 971 $sidebars_widgets = wp_get_sidebars_widgets(); 972 $is_active_sidebar = ! empty( $sidebars_widgets[ $index ] ); 973 974 /** 975 * Filters whether a dynamic sidebar is considered "active". 976 * 977 * @since 3.9.0 978 * 979 * @param bool $is_active_sidebar Whether or not the sidebar should be considered "active". 980 * In other words, whether the sidebar contains any widgets. 981 * @param int|string $index Index, name, or ID of the dynamic sidebar. 982 */ 983 return apply_filters( 'is_active_sidebar', $is_active_sidebar, $index ); 984 } 985 986 // 987 // Internal Functions. 988 // 989 990 /** 991 * Retrieve full list of sidebars and their widget instance IDs. 992 * 993 * Will upgrade sidebar widget list, if needed. Will also save updated list, if 994 * needed. 995 * 996 * @since 2.2.0 997 * @access private 998 * 999 * @global array $_wp_sidebars_widgets 1000 * @global array $sidebars_widgets 1001 * 1002 * @param bool $deprecated Not used (argument deprecated). 1003 * @return array Upgraded list of widgets to version 3 array format when called from the admin. 1004 */ 1005 function wp_get_sidebars_widgets( $deprecated = true ) { 1006 if ( true !== $deprecated ) { 1007 _deprecated_argument( __FUNCTION__, '2.8.1' ); 1008 } 1009 1010 global $_wp_sidebars_widgets, $sidebars_widgets; 1011 1012 // If loading from front page, consult $_wp_sidebars_widgets rather than options 1013 // to see if wp_convert_widget_settings() has made manipulations in memory. 1014 if ( ! is_admin() ) { 1015 if ( empty( $_wp_sidebars_widgets ) ) { 1016 $_wp_sidebars_widgets = get_option( 'sidebars_widgets', array() ); 1017 } 1018 1019 $sidebars_widgets = $_wp_sidebars_widgets; 1020 } else { 1021 $sidebars_widgets = get_option( 'sidebars_widgets', array() ); 1022 } 1023 1024 if ( is_array( $sidebars_widgets ) && isset( $sidebars_widgets['array_version'] ) ) { 1025 unset( $sidebars_widgets['array_version'] ); 1026 } 1027 1028 /** 1029 * Filters the list of sidebars and their widgets. 1030 * 1031 * @since 2.7.0 1032 * 1033 * @param array $sidebars_widgets An associative array of sidebars and their widgets. 1034 */ 1035 return apply_filters( 'sidebars_widgets', $sidebars_widgets ); 1036 } 1037 1038 /** 1039 * Set the sidebar widget option to update sidebars. 1040 * 1041 * @since 2.2.0 1042 * @access private 1043 * 1044 * @global array $_wp_sidebars_widgets 1045 * @param array $sidebars_widgets Sidebar widgets and their settings. 1046 */ 1047 function wp_set_sidebars_widgets( $sidebars_widgets ) { 1048 global $_wp_sidebars_widgets; 1049 1050 // Clear cached value used in wp_get_sidebars_widgets(). 1051 $_wp_sidebars_widgets = null; 1052 1053 if ( ! isset( $sidebars_widgets['array_version'] ) ) { 1054 $sidebars_widgets['array_version'] = 3; 1055 } 1056 1057 update_option( 'sidebars_widgets', $sidebars_widgets ); 1058 } 1059 1060 /** 1061 * Retrieve default registered sidebars list. 1062 * 1063 * @since 2.2.0 1064 * @access private 1065 * 1066 * @global array $wp_registered_sidebars Registered sidebars. 1067 * 1068 * @return array 1069 */ 1070 function wp_get_widget_defaults() { 1071 global $wp_registered_sidebars; 1072 1073 $defaults = array(); 1074 1075 foreach ( (array) $wp_registered_sidebars as $index => $sidebar ) { 1076 $defaults[ $index ] = array(); 1077 } 1078 1079 return $defaults; 1080 } 1081 1082 /** 1083 * Converts the widget settings from single to multi-widget format. 1084 * 1085 * @since 2.8.0 1086 * 1087 * @global array $_wp_sidebars_widgets 1088 * 1089 * @param string $base_name Root ID for all widgets of this type. 1090 * @param string $option_name Option name for this widget type. 1091 * @param array $settings The array of widget instance settings. 1092 * @return array The array of widget settings converted to multi-widget format. 1093 */ 1094 function wp_convert_widget_settings( $base_name, $option_name, $settings ) { 1095 // This test may need expanding. 1096 $single = false; 1097 $changed = false; 1098 1099 if ( empty( $settings ) ) { 1100 $single = true; 1101 } else { 1102 foreach ( array_keys( $settings ) as $number ) { 1103 if ( 'number' === $number ) { 1104 continue; 1105 } 1106 if ( ! is_numeric( $number ) ) { 1107 $single = true; 1108 break; 1109 } 1110 } 1111 } 1112 1113 if ( $single ) { 1114 $settings = array( 2 => $settings ); 1115 1116 // If loading from the front page, update sidebar in memory but don't save to options. 1117 if ( is_admin() ) { 1118 $sidebars_widgets = get_option( 'sidebars_widgets' ); 1119 } else { 1120 if ( empty( $GLOBALS['_wp_sidebars_widgets'] ) ) { 1121 $GLOBALS['_wp_sidebars_widgets'] = get_option( 'sidebars_widgets', array() ); 1122 } 1123 $sidebars_widgets = &$GLOBALS['_wp_sidebars_widgets']; 1124 } 1125 1126 foreach ( (array) $sidebars_widgets as $index => $sidebar ) { 1127 if ( is_array( $sidebar ) ) { 1128 foreach ( $sidebar as $i => $name ) { 1129 if ( $base_name === $name ) { 1130 $sidebars_widgets[ $index ][ $i ] = "$name-2"; 1131 $changed = true; 1132 break 2; 1133 } 1134 } 1135 } 1136 } 1137 1138 if ( is_admin() && $changed ) { 1139 update_option( 'sidebars_widgets', $sidebars_widgets ); 1140 } 1141 } 1142 1143 $settings['_multiwidget'] = 1; 1144 if ( is_admin() ) { 1145 update_option( $option_name, $settings ); 1146 } 1147 1148 return $settings; 1149 } 1150 1151 /** 1152 * Output an arbitrary widget as a template tag. 1153 * 1154 * @since 2.8.0 1155 * 1156 * @global WP_Widget_Factory $wp_widget_factory 1157 * 1158 * @param string $widget The widget's PHP class name (see class-wp-widget.php). 1159 * @param array $instance Optional. The widget's instance settings. Default empty array. 1160 * @param array $args { 1161 * Optional. Array of arguments to configure the display of the widget. 1162 * 1163 * @type string $before_widget HTML content that will be prepended to the widget's HTML output. 1164 * Default `<div class="widget %s">`, where `%s` is the widget's class name. 1165 * @type string $after_widget HTML content that will be appended to the widget's HTML output. 1166 * Default `</div>`. 1167 * @type string $before_title HTML content that will be prepended to the widget's title when displayed. 1168 * Default `<h2 class="widgettitle">`. 1169 * @type string $after_title HTML content that will be appended to the widget's title when displayed. 1170 * Default `</h2>`. 1171 * } 1172 */ 1173 function the_widget( $widget, $instance = array(), $args = array() ) { 1174 global $wp_widget_factory; 1175 1176 if ( ! isset( $wp_widget_factory->widgets[ $widget ] ) ) { 1177 _doing_it_wrong( 1178 __FUNCTION__, 1179 sprintf( 1180 /* translators: %s: register_widget() */ 1181 __( 'Widgets need to be registered using %s, before they can be displayed.' ), 1182 '<code>register_widget()</code>' 1183 ), 1184 '4.9.0' 1185 ); 1186 return; 1187 } 1188 1189 $widget_obj = $wp_widget_factory->widgets[ $widget ]; 1190 if ( ! ( $widget_obj instanceof WP_Widget ) ) { 1191 return; 1192 } 1193 1194 $default_args = array( 1195 'before_widget' => '<div class="widget %s">', 1196 'after_widget' => '</div>', 1197 'before_title' => '<h2 class="widgettitle">', 1198 'after_title' => '</h2>', 1199 ); 1200 $args = wp_parse_args( $args, $default_args ); 1201 $args['before_widget'] = sprintf( $args['before_widget'], $widget_obj->widget_options['classname'] ); 1202 1203 $instance = wp_parse_args( $instance ); 1204 1205 /** This filter is documented in wp-includes/class-wp-widget.php */ 1206 $instance = apply_filters( 'widget_display_callback', $instance, $widget_obj, $args ); 1207 1208 if ( false === $instance ) { 1209 return; 1210 } 1211 1212 /** 1213 * Fires before rendering the requested widget. 1214 * 1215 * @since 3.0.0 1216 * 1217 * @param string $widget The widget's class name. 1218 * @param array $instance The current widget instance's settings. 1219 * @param array $args An array of the widget's sidebar arguments. 1220 */ 1221 do_action( 'the_widget', $widget, $instance, $args ); 1222 1223 $widget_obj->_set( -1 ); 1224 $widget_obj->widget( $args, $instance ); 1225 } 1226 1227 /** 1228 * Retrieves the widget ID base value. 1229 * 1230 * @since 2.8.0 1231 * 1232 * @param string $id Widget ID. 1233 * @return string Widget ID base. 1234 */ 1235 function _get_widget_id_base( $id ) { 1236 return preg_replace( '/-[0-9]+$/', '', $id ); 1237 } 1238 1239 /** 1240 * Handle sidebars config after theme change 1241 * 1242 * @access private 1243 * @since 3.3.0 1244 * 1245 * @global array $sidebars_widgets 1246 */ 1247 function _wp_sidebars_changed() { 1248 global $sidebars_widgets; 1249 1250 if ( ! is_array( $sidebars_widgets ) ) { 1251 $sidebars_widgets = wp_get_sidebars_widgets(); 1252 } 1253 1254 retrieve_widgets( true ); 1255 } 1256 1257 /** 1258 * Look for "lost" widgets, this has to run at least on each theme change. 1259 * 1260 * @since 2.8.0 1261 * 1262 * @global array $wp_registered_sidebars Registered sidebars. 1263 * @global array $sidebars_widgets 1264 * @global array $wp_registered_widgets Registered widgets. 1265 * 1266 * @param string|bool $theme_changed Whether the theme was changed as a boolean. A value 1267 * of 'customize' defers updates for the Customizer. 1268 * @return array Updated sidebars widgets. 1269 */ 1270 function retrieve_widgets( $theme_changed = false ) { 1271 global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets; 1272 1273 $registered_sidebars_keys = array_keys( $wp_registered_sidebars ); 1274 $registered_widgets_ids = array_keys( $wp_registered_widgets ); 1275 1276 if ( ! is_array( get_theme_mod( 'sidebars_widgets' ) ) ) { 1277 if ( empty( $sidebars_widgets ) ) { 1278 return array(); 1279 } 1280 1281 unset( $sidebars_widgets['array_version'] ); 1282 1283 $sidebars_widgets_keys = array_keys( $sidebars_widgets ); 1284 sort( $sidebars_widgets_keys ); 1285 sort( $registered_sidebars_keys ); 1286 1287 if ( $sidebars_widgets_keys === $registered_sidebars_keys ) { 1288 $sidebars_widgets = _wp_remove_unregistered_widgets( $sidebars_widgets, $registered_widgets_ids ); 1289 1290 return $sidebars_widgets; 1291 } 1292 } 1293 1294 // Discard invalid, theme-specific widgets from sidebars. 1295 $sidebars_widgets = _wp_remove_unregistered_widgets( $sidebars_widgets, $registered_widgets_ids ); 1296 $sidebars_widgets = wp_map_sidebars_widgets( $sidebars_widgets ); 1297 1298 // Find hidden/lost multi-widget instances. 1299 $shown_widgets = array_merge( ...array_values( $sidebars_widgets ) ); 1300 $lost_widgets = array_diff( $registered_widgets_ids, $shown_widgets ); 1301 1302 foreach ( $lost_widgets as $key => $widget_id ) { 1303 $number = preg_replace( '/.+?-([0-9]+)$/', '$1', $widget_id ); 1304 1305 // Only keep active and default widgets. 1306 if ( is_numeric( $number ) && (int) $number < 2 ) { 1307 unset( $lost_widgets[ $key ] ); 1308 } 1309 } 1310 $sidebars_widgets['wp_inactive_widgets'] = array_merge( $lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets'] ); 1311 1312 if ( 'customize' !== $theme_changed ) { 1313 wp_set_sidebars_widgets( $sidebars_widgets ); 1314 } 1315 1316 return $sidebars_widgets; 1317 } 1318 1319 /** 1320 * Compares a list of sidebars with their widgets against an allowed list. 1321 * 1322 * @since 4.9.0 1323 * @since 4.9.2 Always tries to restore widget assignments from previous data, not just if sidebars needed mapping. 1324 * 1325 * @param array $existing_sidebars_widgets List of sidebars and their widget instance IDs. 1326 * @return array Mapped sidebars widgets. 1327 */ 1328 function wp_map_sidebars_widgets( $existing_sidebars_widgets ) { 1329 global $wp_registered_sidebars; 1330 1331 $new_sidebars_widgets = array( 1332 'wp_inactive_widgets' => array(), 1333 ); 1334 1335 // Short-circuit if there are no sidebars to map. 1336 if ( ! is_array( $existing_sidebars_widgets ) || empty( $existing_sidebars_widgets ) ) { 1337 return $new_sidebars_widgets; 1338 } 1339 1340 foreach ( $existing_sidebars_widgets as $sidebar => $widgets ) { 1341 if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) { 1342 $new_sidebars_widgets['wp_inactive_widgets'] = array_merge( $new_sidebars_widgets['wp_inactive_widgets'], (array) $widgets ); 1343 unset( $existing_sidebars_widgets[ $sidebar ] ); 1344 } 1345 } 1346 1347 // If old and new theme have just one sidebar, map it and we're done. 1348 if ( 1 === count( $existing_sidebars_widgets ) && 1 === count( $wp_registered_sidebars ) ) { 1349 $new_sidebars_widgets[ key( $wp_registered_sidebars ) ] = array_pop( $existing_sidebars_widgets ); 1350 1351 return $new_sidebars_widgets; 1352 } 1353 1354 // Map locations with the same slug. 1355 $existing_sidebars = array_keys( $existing_sidebars_widgets ); 1356 1357 foreach ( $wp_registered_sidebars as $sidebar => $name ) { 1358 if ( in_array( $sidebar, $existing_sidebars, true ) ) { 1359 $new_sidebars_widgets[ $sidebar ] = $existing_sidebars_widgets[ $sidebar ]; 1360 unset( $existing_sidebars_widgets[ $sidebar ] ); 1361 } elseif ( ! array_key_exists( $sidebar, $new_sidebars_widgets ) ) { 1362 $new_sidebars_widgets[ $sidebar ] = array(); 1363 } 1364 } 1365 1366 // If there are more sidebars, try to map them. 1367 if ( ! empty( $existing_sidebars_widgets ) ) { 1368 1369 /* 1370 * If old and new theme both have sidebars that contain phrases 1371 * from within the same group, make an educated guess and map it. 1372 */ 1373 $common_slug_groups = array( 1374 array( 'sidebar', 'primary', 'main', 'right' ), 1375 array( 'second', 'left' ), 1376 array( 'sidebar-2', 'footer', 'bottom' ), 1377 array( 'header', 'top' ), 1378 ); 1379 1380 // Go through each group... 1381 foreach ( $common_slug_groups as $slug_group ) { 1382 1383 // ...and see if any of these slugs... 1384 foreach ( $slug_group as $slug ) { 1385 1386 // ...and any of the new sidebars... 1387 foreach ( $wp_registered_sidebars as $new_sidebar => $args ) { 1388 1389 // ...actually match! 1390 if ( false === stripos( $new_sidebar, $slug ) && false === stripos( $slug, $new_sidebar ) ) { 1391 continue; 1392 } 1393 1394 // Then see if any of the existing sidebars... 1395 foreach ( $existing_sidebars_widgets as $sidebar => $widgets ) { 1396 1397 // ...and any slug in the same group... 1398 foreach ( $slug_group as $slug ) { 1399 1400 // ... have a match as well. 1401 if ( false === stripos( $sidebar, $slug ) && false === stripos( $slug, $sidebar ) ) { 1402 continue; 1403 } 1404 1405 // Make sure this sidebar wasn't mapped and removed previously. 1406 if ( ! empty( $existing_sidebars_widgets[ $sidebar ] ) ) { 1407 1408 // We have a match that can be mapped! 1409 $new_sidebars_widgets[ $new_sidebar ] = array_merge( $new_sidebars_widgets[ $new_sidebar ], $existing_sidebars_widgets[ $sidebar ] ); 1410 1411 // Remove the mapped sidebar so it can't be mapped again. 1412 unset( $existing_sidebars_widgets[ $sidebar ] ); 1413 1414 // Go back and check the next new sidebar. 1415 continue 3; 1416 } 1417 } // End foreach ( $slug_group as $slug ). 1418 } // End foreach ( $existing_sidebars_widgets as $sidebar => $widgets ). 1419 } // End foreach ( $wp_registered_sidebars as $new_sidebar => $args ). 1420 } // End foreach ( $slug_group as $slug ). 1421 } // End foreach ( $common_slug_groups as $slug_group ). 1422 } 1423 1424 // Move any left over widgets to inactive sidebar. 1425 foreach ( $existing_sidebars_widgets as $widgets ) { 1426 if ( is_array( $widgets ) && ! empty( $widgets ) ) { 1427 $new_sidebars_widgets['wp_inactive_widgets'] = array_merge( $new_sidebars_widgets['wp_inactive_widgets'], $widgets ); 1428 } 1429 } 1430 1431 // Sidebars_widgets settings from when this theme was previously active. 1432 $old_sidebars_widgets = get_theme_mod( 'sidebars_widgets' ); 1433 $old_sidebars_widgets = isset( $old_sidebars_widgets['data'] ) ? $old_sidebars_widgets['data'] : false; 1434 1435 if ( is_array( $old_sidebars_widgets ) ) { 1436 1437 // Remove empty sidebars, no need to map those. 1438 $old_sidebars_widgets = array_filter( $old_sidebars_widgets ); 1439 1440 // Only check sidebars that are empty or have not been mapped to yet. 1441 foreach ( $new_sidebars_widgets as $new_sidebar => $new_widgets ) { 1442 if ( array_key_exists( $new_sidebar, $old_sidebars_widgets ) && ! empty( $new_widgets ) ) { 1443 unset( $old_sidebars_widgets[ $new_sidebar ] ); 1444 } 1445 } 1446 1447 // Remove orphaned widgets, we're only interested in previously active sidebars. 1448 foreach ( $old_sidebars_widgets as $sidebar => $widgets ) { 1449 if ( 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) { 1450 unset( $old_sidebars_widgets[ $sidebar ] ); 1451 } 1452 } 1453 1454 $old_sidebars_widgets = _wp_remove_unregistered_widgets( $old_sidebars_widgets ); 1455 1456 if ( ! empty( $old_sidebars_widgets ) ) { 1457 1458 // Go through each remaining sidebar... 1459 foreach ( $old_sidebars_widgets as $old_sidebar => $old_widgets ) { 1460 1461 // ...and check every new sidebar... 1462 foreach ( $new_sidebars_widgets as $new_sidebar => $new_widgets ) { 1463 1464 // ...for every widget we're trying to revive. 1465 foreach ( $old_widgets as $key => $widget_id ) { 1466 $active_key = array_search( $widget_id, $new_widgets, true ); 1467 1468 // If the widget is used elsewhere... 1469 if ( false !== $active_key ) { 1470 1471 // ...and that elsewhere is inactive widgets... 1472 if ( 'wp_inactive_widgets' === $new_sidebar ) { 1473 1474 // ...remove it from there and keep the active version... 1475 unset( $new_sidebars_widgets['wp_inactive_widgets'][ $active_key ] ); 1476 } else { 1477 1478 // ...otherwise remove it from the old sidebar and keep it in the new one. 1479 unset( $old_sidebars_widgets[ $old_sidebar ][ $key ] ); 1480 } 1481 } // End if ( $active_key ). 1482 } // End foreach ( $old_widgets as $key => $widget_id ). 1483 } // End foreach ( $new_sidebars_widgets as $new_sidebar => $new_widgets ). 1484 } // End foreach ( $old_sidebars_widgets as $old_sidebar => $old_widgets ). 1485 } // End if ( ! empty( $old_sidebars_widgets ) ). 1486 1487 // Restore widget settings from when theme was previously active. 1488 $new_sidebars_widgets = array_merge( $new_sidebars_widgets, $old_sidebars_widgets ); 1489 } 1490 1491 return $new_sidebars_widgets; 1492 } 1493 1494 /** 1495 * Compares a list of sidebars with their widgets against an allowed list. 1496 * 1497 * @since 4.9.0 1498 * 1499 * @param array $sidebars_widgets List of sidebars and their widget instance IDs. 1500 * @param array $allowed_widget_ids Optional. List of widget IDs to compare against. Default: Registered widgets. 1501 * @return array Sidebars with allowed widgets. 1502 */ 1503 function _wp_remove_unregistered_widgets( $sidebars_widgets, $allowed_widget_ids = array() ) { 1504 if ( empty( $allowed_widget_ids ) ) { 1505 $allowed_widget_ids = array_keys( $GLOBALS['wp_registered_widgets'] ); 1506 } 1507 1508 foreach ( $sidebars_widgets as $sidebar => $widgets ) { 1509 if ( is_array( $widgets ) ) { 1510 $sidebars_widgets[ $sidebar ] = array_intersect( $widgets, $allowed_widget_ids ); 1511 } 1512 } 1513 1514 return $sidebars_widgets; 1515 } 1516 1517 /** 1518 * Display the RSS entries in a list. 1519 * 1520 * @since 2.5.0 1521 * 1522 * @param string|array|object $rss RSS url. 1523 * @param array $args Widget arguments. 1524 */ 1525 function wp_widget_rss_output( $rss, $args = array() ) { 1526 if ( is_string( $rss ) ) { 1527 $rss = fetch_feed( $rss ); 1528 } elseif ( is_array( $rss ) && isset( $rss['url'] ) ) { 1529 $args = $rss; 1530 $rss = fetch_feed( $rss['url'] ); 1531 } elseif ( ! is_object( $rss ) ) { 1532 return; 1533 } 1534 1535 if ( is_wp_error( $rss ) ) { 1536 if ( is_admin() || current_user_can( 'manage_options' ) ) { 1537 echo '<p><strong>' . __( 'RSS Error:' ) . '</strong> ' . $rss->get_error_message() . '</p>'; 1538 } 1539 return; 1540 } 1541 1542 $default_args = array( 1543 'show_author' => 0, 1544 'show_date' => 0, 1545 'show_summary' => 0, 1546 'items' => 0, 1547 ); 1548 $args = wp_parse_args( $args, $default_args ); 1549 1550 $items = (int) $args['items']; 1551 if ( $items < 1 || 20 < $items ) { 1552 $items = 10; 1553 } 1554 $show_summary = (int) $args['show_summary']; 1555 $show_author = (int) $args['show_author']; 1556 $show_date = (int) $args['show_date']; 1557 1558 if ( ! $rss->get_item_quantity() ) { 1559 echo '<ul><li>' . __( 'An error has occurred, which probably means the feed is down. Try again later.' ) . '</li></ul>'; 1560 $rss->__destruct(); 1561 unset( $rss ); 1562 return; 1563 } 1564 1565 echo '<ul>'; 1566 foreach ( $rss->get_items( 0, $items ) as $item ) { 1567 $link = $item->get_link(); 1568 while ( ! empty( $link ) && stristr( $link, 'http' ) !== $link ) { 1569 $link = substr( $link, 1 ); 1570 } 1571 $link = esc_url( strip_tags( $link ) ); 1572 1573 $title = esc_html( trim( strip_tags( $item->get_title() ) ) ); 1574 if ( empty( $title ) ) { 1575 $title = __( 'Untitled' ); 1576 } 1577 1578 $desc = html_entity_decode( $item->get_description(), ENT_QUOTES, get_option( 'blog_charset' ) ); 1579 $desc = esc_attr( wp_trim_words( $desc, 55, ' […]' ) ); 1580 1581 $summary = ''; 1582 if ( $show_summary ) { 1583 $summary = $desc; 1584 1585 // Change existing [...] to […]. 1586 if ( '[...]' === substr( $summary, -5 ) ) { 1587 $summary = substr( $summary, 0, -5 ) . '[…]'; 1588 } 1589 1590 $summary = '<div class="rssSummary">' . esc_html( $summary ) . '</div>'; 1591 } 1592 1593 $date = ''; 1594 if ( $show_date ) { 1595 $date = $item->get_date( 'U' ); 1596 1597 if ( $date ) { 1598 $date = ' <span class="rss-date">' . date_i18n( get_option( 'date_format' ), $date ) . '</span>'; 1599 } 1600 } 1601 1602 $author = ''; 1603 if ( $show_author ) { 1604 $author = $item->get_author(); 1605 if ( is_object( $author ) ) { 1606 $author = $author->get_name(); 1607 $author = ' <cite>' . esc_html( strip_tags( $author ) ) . '</cite>'; 1608 } 1609 } 1610 1611 if ( '' === $link ) { 1612 echo "<li>$title{$date}{$summary}{$author}</li>"; 1613 } elseif ( $show_summary ) { 1614 echo "<li><a class='rsswidget' href='$link'>$title</a>{$date}{$summary}{$author}</li>"; 1615 } else { 1616 echo "<li><a class='rsswidget' href='$link'>$title</a>{$date}{$author}</li>"; 1617 } 1618 } 1619 echo '</ul>'; 1620 $rss->__destruct(); 1621 unset( $rss ); 1622 } 1623 1624 /** 1625 * Display RSS widget options form. 1626 * 1627 * The options for what fields are displayed for the RSS form are all booleans 1628 * and are as follows: 'url', 'title', 'items', 'show_summary', 'show_author', 1629 * 'show_date'. 1630 * 1631 * @since 2.5.0 1632 * 1633 * @param array|string $args Values for input fields. 1634 * @param array $inputs Override default display options. 1635 */ 1636 function wp_widget_rss_form( $args, $inputs = null ) { 1637 $default_inputs = array( 1638 'url' => true, 1639 'title' => true, 1640 'items' => true, 1641 'show_summary' => true, 1642 'show_author' => true, 1643 'show_date' => true, 1644 ); 1645 $inputs = wp_parse_args( $inputs, $default_inputs ); 1646 1647 $args['title'] = isset( $args['title'] ) ? $args['title'] : ''; 1648 $args['url'] = isset( $args['url'] ) ? $args['url'] : ''; 1649 $args['items'] = isset( $args['items'] ) ? (int) $args['items'] : 0; 1650 1651 if ( $args['items'] < 1 || 20 < $args['items'] ) { 1652 $args['items'] = 10; 1653 } 1654 1655 $args['show_summary'] = isset( $args['show_summary'] ) ? (int) $args['show_summary'] : (int) $inputs['show_summary']; 1656 $args['show_author'] = isset( $args['show_author'] ) ? (int) $args['show_author'] : (int) $inputs['show_author']; 1657 $args['show_date'] = isset( $args['show_date'] ) ? (int) $args['show_date'] : (int) $inputs['show_date']; 1658 1659 if ( ! empty( $args['error'] ) ) { 1660 echo '<p class="widget-error"><strong>' . __( 'RSS Error:' ) . '</strong> ' . $args['error'] . '</p>'; 1661 } 1662 1663 $esc_number = esc_attr( $args['number'] ); 1664 if ( $inputs['url'] ) : 1665 ?> 1666 <p><label for="rss-url-<?php echo $esc_number; ?>"><?php _e( 'Enter the RSS feed URL here:' ); ?></label> 1667 <input class="widefat" id="rss-url-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][url]" type="text" value="<?php echo esc_url( $args['url'] ); ?>" /></p> 1668 <?php endif; if ( $inputs['title'] ) : ?> 1669 <p><label for="rss-title-<?php echo $esc_number; ?>"><?php _e( 'Give the feed a title (optional):' ); ?></label> 1670 <input class="widefat" id="rss-title-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][title]" type="text" value="<?php echo esc_attr( $args['title'] ); ?>" /></p> 1671 <?php endif; if ( $inputs['items'] ) : ?> 1672 <p><label for="rss-items-<?php echo $esc_number; ?>"><?php _e( 'How many items would you like to display?' ); ?></label> 1673 <select id="rss-items-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][items]"> 1674 <?php 1675 for ( $i = 1; $i <= 20; ++$i ) { 1676 echo "<option value='$i' " . selected( $args['items'], $i, false ) . ">$i</option>"; 1677 } 1678 ?> 1679 </select></p> 1680 <?php endif; if ( $inputs['show_summary'] || $inputs['show_author'] || $inputs['show_date'] ) : ?> 1681 <p> 1682 <?php if ( $inputs['show_summary'] ) : ?> 1683 <input id="rss-show-summary-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_summary]" type="checkbox" value="1" <?php checked( $args['show_summary'] ); ?> /> 1684 <label for="rss-show-summary-<?php echo $esc_number; ?>"><?php _e( 'Display item content?' ); ?></label><br /> 1685 <?php endif; if ( $inputs['show_author'] ) : ?> 1686 <input id="rss-show-author-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_author]" type="checkbox" value="1" <?php checked( $args['show_author'] ); ?> /> 1687 <label for="rss-show-author-<?php echo $esc_number; ?>"><?php _e( 'Display item author if available?' ); ?></label><br /> 1688 <?php endif; if ( $inputs['show_date'] ) : ?> 1689 <input id="rss-show-date-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_date]" type="checkbox" value="1" <?php checked( $args['show_date'] ); ?>/> 1690 <label for="rss-show-date-<?php echo $esc_number; ?>"><?php _e( 'Display item date?' ); ?></label><br /> 1691 <?php endif; ?> 1692 </p> 1693 <?php 1694 endif; // End of display options. 1695 foreach ( array_keys( $default_inputs ) as $input ) : 1696 if ( 'hidden' === $inputs[ $input ] ) : 1697 $id = str_replace( '_', '-', $input ); 1698 ?> 1699 <input type="hidden" id="rss-<?php echo esc_attr( $id ); ?>-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][<?php echo esc_attr( $input ); ?>]" value="<?php echo esc_attr( $args[ $input ] ); ?>" /> 1700 <?php 1701 endif; 1702 endforeach; 1703 } 1704 1705 /** 1706 * Process RSS feed widget data and optionally retrieve feed items. 1707 * 1708 * The feed widget can not have more than 20 items or it will reset back to the 1709 * default, which is 10. 1710 * 1711 * The resulting array has the feed title, feed url, feed link (from channel), 1712 * feed items, error (if any), and whether to show summary, author, and date. 1713 * All respectively in the order of the array elements. 1714 * 1715 * @since 2.5.0 1716 * 1717 * @param array $widget_rss RSS widget feed data. Expects unescaped data. 1718 * @param bool $check_feed Optional. Whether to check feed for errors. Default true. 1719 * @return array 1720 */ 1721 function wp_widget_rss_process( $widget_rss, $check_feed = true ) { 1722 $items = (int) $widget_rss['items']; 1723 if ( $items < 1 || 20 < $items ) { 1724 $items = 10; 1725 } 1726 $url = esc_url_raw( strip_tags( $widget_rss['url'] ) ); 1727 $title = isset( $widget_rss['title'] ) ? trim( strip_tags( $widget_rss['title'] ) ) : ''; 1728 $show_summary = isset( $widget_rss['show_summary'] ) ? (int) $widget_rss['show_summary'] : 0; 1729 $show_author = isset( $widget_rss['show_author'] ) ? (int) $widget_rss['show_author'] : 0; 1730 $show_date = isset( $widget_rss['show_date'] ) ? (int) $widget_rss['show_date'] : 0; 1731 1732 if ( $check_feed ) { 1733 $rss = fetch_feed( $url ); 1734 $error = false; 1735 $link = ''; 1736 if ( is_wp_error( $rss ) ) { 1737 $error = $rss->get_error_message(); 1738 } else { 1739 $link = esc_url( strip_tags( $rss->get_permalink() ) ); 1740 while ( stristr( $link, 'http' ) !== $link ) { 1741 $link = substr( $link, 1 ); 1742 } 1743 1744 $rss->__destruct(); 1745 unset( $rss ); 1746 } 1747 } 1748 1749 return compact( 'title', 'url', 'link', 'items', 'error', 'show_summary', 'show_author', 'show_date' ); 1750 } 1751 1752 /** 1753 * Registers all of the default WordPress widgets on startup. 1754 * 1755 * Calls {@see 'widgets_init'} action after all of the WordPress widgets have been registered. 1756 * 1757 * @since 2.2.0 1758 */ 1759 function wp_widgets_init() { 1760 if ( ! is_blog_installed() ) { 1761 return; 1762 } 1763 1764 register_widget( 'WP_Widget_Pages' ); 1765 1766 register_widget( 'WP_Widget_Calendar' ); 1767 1768 register_widget( 'WP_Widget_Archives' ); 1769 1770 if ( get_option( 'link_manager_enabled' ) ) { 1771 register_widget( 'WP_Widget_Links' ); 1772 } 1773 1774 register_widget( 'WP_Widget_Media_Audio' ); 1775 1776 register_widget( 'WP_Widget_Media_Image' ); 1777 1778 register_widget( 'WP_Widget_Media_Gallery' ); 1779 1780 register_widget( 'WP_Widget_Media_Video' ); 1781 1782 register_widget( 'WP_Widget_Meta' ); 1783 1784 register_widget( 'WP_Widget_Search' ); 1785 1786 register_widget( 'WP_Widget_Text' ); 1787 1788 register_widget( 'WP_Widget_Categories' ); 1789 1790 register_widget( 'WP_Widget_Recent_Posts' ); 1791 1792 register_widget( 'WP_Widget_Recent_Comments' ); 1793 1794 register_widget( 'WP_Widget_RSS' ); 1795 1796 register_widget( 'WP_Widget_Tag_Cloud' ); 1797 1798 register_widget( 'WP_Nav_Menu_Widget' ); 1799 1800 register_widget( 'WP_Widget_Custom_HTML' ); 1801 1802 register_widget( 'WP_Widget_Block' ); 1803 1804 /** 1805 * Fires after all default WordPress widgets have been registered. 1806 * 1807 * @since 2.2.0 1808 */ 1809 do_action( 'widgets_init' ); 1810 } 1811 1812 /** 1813 * Enables the widgets block editor. This is hooked into 'after_setup_theme' so 1814 * that the block editor is enabled by default but can be disabled by themes. 1815 * 1816 * @since 5.8.0 1817 * 1818 * @access private 1819 */ 1820 function wp_setup_widgets_block_editor() { 1821 add_theme_support( 'widgets-block-editor' ); 1822 } 1823 1824 /** 1825 * Whether or not to use the block editor to manage widgets. Defaults to true 1826 * unless a theme has removed support for widgets-block-editor or a plugin has 1827 * filtered the return value of this function. 1828 * 1829 * @since 5.8.0 1830 * 1831 * @return boolean Whether or not to use the block editor to manage widgets. 1832 */ 1833 function wp_use_widgets_block_editor() { 1834 /** 1835 * Filters whether or not to use the block editor to manage widgets. 1836 * 1837 * @since 5.8.0 1838 * 1839 * @param boolean $use_widgets_block_editor Whether or not to use the block editor to manage widgets. 1840 */ 1841 return apply_filters( 1842 'use_widgets_block_editor', 1843 get_theme_support( 'widgets-block-editor' ) 1844 ); 1845 } 1846 1847 /** 1848 * Converts a widget ID into its id_base and number components. 1849 * 1850 * @since 5.8.0 1851 * 1852 * @param string $id Widget ID. 1853 * @return array Array containing a widget's id_base and number components. 1854 */ 1855 function wp_parse_widget_id( $id ) { 1856 $parsed = array(); 1857 1858 if ( preg_match( '/^(.+)-(\d+)$/', $id, $matches ) ) { 1859 $parsed['id_base'] = $matches[1]; 1860 $parsed['number'] = (int) $matches[2]; 1861 } else { 1862 // Likely an old single widget. 1863 $parsed['id_base'] = $id; 1864 } 1865 1866 return $parsed; 1867 } 1868 1869 /** 1870 * Finds the sidebar that a given widget belongs to. 1871 * 1872 * @since 5.8.0 1873 * 1874 * @param string $widget_id The widget id to look for. 1875 * @return string|null The found sidebar's id, or null if it was not found. 1876 */ 1877 function wp_find_widgets_sidebar( $widget_id ) { 1878 foreach ( wp_get_sidebars_widgets() as $sidebar_id => $widget_ids ) { 1879 foreach ( $widget_ids as $maybe_widget_id ) { 1880 if ( $maybe_widget_id === $widget_id ) { 1881 return (string) $sidebar_id; 1882 } 1883 } 1884 } 1885 1886 return null; 1887 } 1888 1889 /** 1890 * Assigns a widget to the given sidebar. 1891 * 1892 * @since 5.8.0 1893 * 1894 * @param string $widget_id The widget id to assign. 1895 * @param string $sidebar_id The sidebar id to assign to. If empty, the widget won't be added to any sidebar. 1896 */ 1897 function wp_assign_widget_to_sidebar( $widget_id, $sidebar_id ) { 1898 $sidebars = wp_get_sidebars_widgets(); 1899 1900 foreach ( $sidebars as $maybe_sidebar_id => $widgets ) { 1901 foreach ( $widgets as $i => $maybe_widget_id ) { 1902 if ( $widget_id === $maybe_widget_id && $sidebar_id !== $maybe_sidebar_id ) { 1903 unset( $sidebars[ $maybe_sidebar_id ][ $i ] ); 1904 // We could technically break 2 here, but continue looping in case the id is duplicated. 1905 continue 2; 1906 } 1907 } 1908 } 1909 1910 if ( $sidebar_id ) { 1911 $sidebars[ $sidebar_id ][] = $widget_id; 1912 } 1913 1914 wp_set_sidebars_widgets( $sidebars ); 1915 } 1916 1917 /** 1918 * Calls the render callback of a widget and returns the output. 1919 * 1920 * @since 5.8.0 1921 * 1922 * @param string $widget_id Widget ID. 1923 * @param string $sidebar_id Sidebar ID. 1924 * @return string 1925 */ 1926 function wp_render_widget( $widget_id, $sidebar_id ) { 1927 global $wp_registered_widgets, $wp_registered_sidebars; 1928 1929 if ( ! isset( $wp_registered_widgets[ $widget_id ] ) ) { 1930 return ''; 1931 } 1932 1933 if ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ) { 1934 $sidebar = $wp_registered_sidebars[ $sidebar_id ]; 1935 } elseif ( 'wp_inactive_widgets' === $sidebar_id ) { 1936 $sidebar = array(); 1937 } else { 1938 return ''; 1939 } 1940 1941 $params = array_merge( 1942 array( 1943 array_merge( 1944 $sidebar, 1945 array( 1946 'widget_id' => $widget_id, 1947 'widget_name' => $wp_registered_widgets[ $widget_id ]['name'], 1948 ) 1949 ), 1950 ), 1951 (array) $wp_registered_widgets[ $widget_id ]['params'] 1952 ); 1953 1954 // Substitute HTML `id` and `class` attributes into `before_widget`. 1955 $classname_ = ''; 1956 foreach ( (array) $wp_registered_widgets[ $widget_id ]['classname'] as $cn ) { 1957 if ( is_string( $cn ) ) { 1958 $classname_ .= '_' . $cn; 1959 } elseif ( is_object( $cn ) ) { 1960 $classname_ .= '_' . get_class( $cn ); 1961 } 1962 } 1963 $classname_ = ltrim( $classname_, '_' ); 1964 $params[0]['before_widget'] = sprintf( $params[0]['before_widget'], $widget_id, $classname_ ); 1965 1966 /** This filter is documented in wp-includes/widgets.php */ 1967 $params = apply_filters( 'dynamic_sidebar_params', $params ); 1968 1969 $callback = $wp_registered_widgets[ $widget_id ]['callback']; 1970 1971 ob_start(); 1972 1973 /** This filter is documented in wp-includes/widgets.php */ 1974 do_action( 'dynamic_sidebar', $wp_registered_widgets[ $widget_id ] ); 1975 1976 if ( is_callable( $callback ) ) { 1977 call_user_func_array( $callback, $params ); 1978 } 1979 1980 return ob_get_clean(); 1981 } 1982 1983 /** 1984 * Calls the control callback of a widget and returns the output. 1985 * 1986 * @since 5.8.0 1987 * 1988 * @param string $id Widget ID. 1989 * @return string|null 1990 */ 1991 function wp_render_widget_control( $id ) { 1992 global $wp_registered_widget_controls; 1993 1994 if ( ! isset( $wp_registered_widget_controls[ $id ]['callback'] ) ) { 1995 return null; 1996 } 1997 1998 $callback = $wp_registered_widget_controls[ $id ]['callback']; 1999 $params = $wp_registered_widget_controls[ $id ]['params']; 2000 2001 ob_start(); 2002 2003 if ( is_callable( $callback ) ) { 2004 call_user_func_array( $callback, $params ); 2005 } 2006 2007 return ob_get_clean(); 2008 } 2009 2010 /** 2011 * Displays a _doing_it_wrong() message for conflicting widget editor scripts. 2012 * 2013 * The 'wp-editor' script module is exposed as window.wp.editor. This overrides 2014 * the legacy TinyMCE editor module which is required by the widgets editor. 2015 * Because of that conflict, these two shouldn't be enqueued together. 2016 * See https://core.trac.wordpress.org/ticket/53569. 2017 * 2018 * There is also another conflict related to styles where the block widgets 2019 * editor is hidden if a block enqueues 'wp-edit-post' stylesheet. 2020 * See https://core.trac.wordpress.org/ticket/53569. 2021 * 2022 * @since 5.8.0 2023 * @access private 2024 * 2025 * @global WP_Scripts $wp_scripts 2026 * @global WP_Styles $wp_styles 2027 */ 2028 function wp_check_widget_editor_deps() { 2029 global $wp_scripts, $wp_styles; 2030 2031 if ( 2032 $wp_scripts->query( 'wp-edit-widgets', 'enqueued' ) || 2033 $wp_scripts->query( 'wp-customize-widgets', 'enqueued' ) 2034 ) { 2035 if ( $wp_scripts->query( 'wp-editor', 'enqueued' ) ) { 2036 _doing_it_wrong( 2037 'wp_enqueue_script()', 2038 '"wp-editor" script should not be enqueued together with the new widgets editor (wp-edit-widgets or wp-customize-widgets).', 2039 '5.8.0' 2040 ); 2041 } 2042 if ( $wp_styles->query( 'wp-edit-post', 'enqueued' ) ) { 2043 _doing_it_wrong( 2044 'wp_enqueue_style()', 2045 '"wp-edit-post" style should not be enqueued together with the new widgets editor (wp-edit-widgets or wp-customize-widgets).', 2046 '5.8.0' 2047 ); 2048 } 2049 } 2050 }