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