Changeset 56067
- Timestamp:
- 06/27/2023 03:25:00 PM (16 months ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-walker-nav-menu.php
r55261 r56067 74 74 */ 75 75 $class_names = implode( ' ', apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth ) ); 76 $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : ''; 77 78 $output .= "{$n}{$indent}<ul$class_names>{$n}"; 76 77 $atts = array(); 78 $atts['class'] = ! empty( $class_names ) ? $class_names : ''; 79 80 /** 81 * Filters the HTML attributes applied to a menu list element. 82 * 83 * @since 6.3.0 84 * 85 * @param array $atts { 86 * The HTML attributes applied to the `<ul>` element, empty strings are ignored. 87 * 88 * @type string $class HTML CSS class attribute. 89 * } 90 * @param stdClass $args An object of `wp_nav_menu()` arguments. 91 * @param int $depth Depth of menu item. Used for padding. 92 */ 93 $atts = apply_filters( 'nav_menu_submenu_attributes', $atts, $args, $depth ); 94 $attributes = $this->build_atts( $atts ); 95 96 $output .= "{$n}{$indent}<ul{$attributes}>{$n}"; 79 97 } 80 98 … … 157 175 */ 158 176 $class_names = implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $menu_item, $args, $depth ) ); 159 $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';160 177 161 178 /** … … 171 188 */ 172 189 $id = apply_filters( 'nav_menu_item_id', 'menu-item-' . $menu_item->ID, $menu_item, $args, $depth ); 173 $id = $id ? ' id="' . esc_attr( $id ) . '"' : ''; 174 175 $output .= $indent . '<li' . $id . $class_names . '>'; 190 191 $li_atts = array(); 192 $li_atts['id'] = ! empty( $id ) ? $id : ''; 193 $li_atts['class'] = ! empty( $class_names ) ? $class_names : ''; 194 195 /** 196 * Filters the HTML attributes applied to a menu's list item element. 197 * 198 * @since 6.3.0 199 * 200 * @param array $li_atts { 201 * The HTML attributes applied to the menu item's `<li>` element, empty strings are ignored. 202 * 203 * @type string $class HTML CSS class attribute. 204 * @type string $id HTML id attribute. 205 * } 206 * @param WP_Post $menu_item The current menu item object. 207 * @param stdClass $args An object of wp_nav_menu() arguments. 208 * @param int $depth Depth of menu item. Used for padding. 209 */ 210 $li_atts = apply_filters( 'nav_menu_item_attributes', $li_atts, $menu_item, $args, $depth ); 211 $li_attributes = $this->build_atts( $li_atts ); 212 213 $output .= $indent . '<li' . $li_attributes . '>'; 176 214 177 215 $atts = array(); … … 215 253 * @param int $depth Depth of menu item. Used for padding. 216 254 */ 217 $atts = apply_filters( 'nav_menu_link_attributes', $atts, $menu_item, $args, $depth ); 218 219 $attributes = ''; 220 foreach ( $atts as $attr => $value ) { 221 if ( is_scalar( $value ) && '' !== $value && false !== $value ) { 222 $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value ); 223 $attributes .= ' ' . $attr . '="' . $value . '"'; 224 } 225 } 255 $atts = apply_filters( 'nav_menu_link_attributes', $atts, $menu_item, $args, $depth ); 256 $attributes = $this->build_atts( $atts ); 226 257 227 258 /** This filter is documented in wp-includes/post-template.php */ … … 287 318 } 288 319 320 /** 321 * Builds a string of HTML attributes from an array of key/value pairs. 322 * Empty values are ignored. 323 * 324 * @since 6.3.0 325 * 326 * @param array $atts Optional. An array of HTML attribute key/value pairs. Default empty array. 327 * @return string A string of HTML attributes. 328 */ 329 protected function build_atts( $atts = array() ) { 330 $attribute_string = ''; 331 foreach ( $atts as $attr => $value ) { 332 if ( false !== $value && '' !== $value && is_scalar( $value ) ) { 333 $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value ); 334 $attribute_string .= ' ' . $attr . '="' . $value . '"'; 335 } 336 } 337 return $attribute_string; 338 } 289 339 } -
trunk/tests/phpunit/tests/menu/walker-nav-menu.php
r55261 r56067 366 366 $this->assertStringContainsString( 'rel="privacy-policy"', $output ); 367 367 } 368 369 /** 370 * Tests that `Walker_Nav_Menu::start_lvl()` applies 'nav_menu_submenu_attributes' filters. 371 * 372 * @ticket 57278 373 * 374 * @covers Walker_Nav_Menu::start_lvl 375 */ 376 public function test_start_lvl_should_apply_nav_menu_submenu_attributes_filters() { 377 $output = ''; 378 $args = (object) array( 379 'before' => '', 380 'after' => '', 381 'link_before' => '', 382 'link_after' => '', 383 ); 384 385 $filter = new MockAction(); 386 add_filter( 'nav_menu_submenu_attributes', array( $filter, 'filter' ) ); 387 388 $this->walker->start_lvl( $output, 0, $args ); 389 390 $this->assertSame( 1, $filter->get_call_count() ); 391 } 392 393 /** 394 * Tests that `Walker_Nav_Menu::start_el()` applies 'nav_menu_item_attributes' filters. 395 * 396 * @ticket 57278 397 * 398 * @covers Walker_Nav_Menu::start_el 399 */ 400 public function test_start_el_should_apply_nav_menu_item_attributes_filters() { 401 $output = ''; 402 $post_id = self::factory()->post->create(); 403 $item = (object) array( 404 'ID' => $post_id, 405 'object_id' => $post_id, 406 'title' => get_the_title( $post_id ), 407 'target' => '', 408 'xfn' => '', 409 'current' => false, 410 ); 411 $args = (object) array( 412 'before' => '', 413 'after' => '', 414 'link_before' => '', 415 'link_after' => '', 416 ); 417 418 $filter = new MockAction(); 419 add_filter( 'nav_menu_item_attributes', array( $filter, 'filter' ) ); 420 421 $this->walker->start_el( $output, $item, 0, $args ); 422 423 $this->assertSame( 1, $filter->get_call_count() ); 424 } 425 426 /** 427 * Tests that `Walker_Nav_Menu::build_atts()` builds attributes correctly. 428 * 429 * @ticket 57278 430 * 431 * @covers Walker_Nav_Menu::build_atts 432 * 433 * @dataProvider data_build_atts_should_build_attributes 434 * 435 * @param array $atts An array of HTML attribute key/value pairs. 436 * @param string $expected The expected built attributes. 437 */ 438 public function test_build_atts_should_build_attributes( $atts, $expected ) { 439 $build_atts_reflection = new ReflectionMethod( $this->walker, 'build_atts' ); 440 441 $build_atts_reflection->setAccessible( true ); 442 $actual = $build_atts_reflection->invoke( $this->walker, $atts ); 443 $build_atts_reflection->setAccessible( false ); 444 445 $this->assertSame( $expected, $actual ); 446 } 447 448 /** 449 * Data provider. 450 * 451 * @return array[] 452 */ 453 public function data_build_atts_should_build_attributes() { 454 return array( 455 'an empty attributes array' => array( 456 'atts' => array(), 457 'expected' => '', 458 ), 459 'attributes containing a (bool) false value' => array( 460 'atts' => array( 'disabled' => false ), 461 'expected' => '', 462 ), 463 'attributes containing an empty string value' => array( 464 'atts' => array( 'id' => '' ), 465 'expected' => '', 466 ), 467 'attributes containing a non-scalar value' => array( 468 'atts' => array( 'data-items' => new stdClass() ), 469 'expected' => '', 470 ), 471 'attributes containing a "href" -> should escape the URL' => array( 472 'atts' => array( 'href' => 'https://example.org/A File With Spaces.pdf' ), 473 'expected' => ' href="https://example.org/A%20File%20With%20Spaces.pdf"', 474 ), 475 'attributes containing a non-"href" attribute -> should escape the value' => array( 476 'atts' => array( 'id' => 'hello&goodbye' ), 477 'expected' => ' id="hello&goodbye"', 478 ), 479 ); 480 } 368 481 }
Note: See TracChangeset
for help on using the changeset viewer.