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