[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Server-side rendering of the `core/navigation` block. 4 * 5 * @package WordPress 6 */ 7 8 // These functions are used for the __unstableLocation feature and only active 9 // when the gutenberg plugin is active. 10 if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { 11 /** 12 * Returns the menu items for a WordPress menu location. 13 * 14 * @param string $location The menu location. 15 * @return array Menu items for the location. 16 */ 17 function block_core_navigation_get_menu_items_at_location( $location ) { 18 if ( empty( $location ) ) { 19 return; 20 } 21 22 // Build menu data. The following approximates the code in 23 // `wp_nav_menu()` and `gutenberg_output_block_nav_menu`. 24 25 // Find the location in the list of locations, returning early if the 26 // location can't be found. 27 $locations = get_nav_menu_locations(); 28 if ( ! isset( $locations[ $location ] ) ) { 29 return; 30 } 31 32 // Get the menu from the location, returning early if there is no 33 // menu or there was an error. 34 $menu = wp_get_nav_menu_object( $locations[ $location ] ); 35 if ( ! $menu || is_wp_error( $menu ) ) { 36 return; 37 } 38 39 $menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'update_post_term_cache' => false ) ); 40 _wp_menu_item_classes_by_context( $menu_items ); 41 42 return $menu_items; 43 } 44 45 46 /** 47 * Sorts a standard array of menu items into a nested structure keyed by the 48 * id of the parent menu. 49 * 50 * @param array $menu_items Menu items to sort. 51 * @return array An array keyed by the id of the parent menu where each element 52 * is an array of menu items that belong to that parent. 53 */ 54 function block_core_navigation_sort_menu_items_by_parent_id( $menu_items ) { 55 $sorted_menu_items = array(); 56 foreach ( (array) $menu_items as $menu_item ) { 57 $sorted_menu_items[ $menu_item->menu_order ] = $menu_item; 58 } 59 unset( $menu_items, $menu_item ); 60 61 $menu_items_by_parent_id = array(); 62 foreach ( $sorted_menu_items as $menu_item ) { 63 $menu_items_by_parent_id[ $menu_item->menu_item_parent ][] = $menu_item; 64 } 65 66 return $menu_items_by_parent_id; 67 } 68 } 69 70 /** 71 * Turns menu item data into a nested array of parsed blocks 72 * 73 * @param array $menu_items An array of menu items that represent 74 * an individual level of a menu. 75 * @param array $menu_items_by_parent_id An array keyed by the id of the 76 * parent menu where each element is an 77 * array of menu items that belong to 78 * that parent. 79 * @return array An array of parsed block data. 80 */ 81 function block_core_navigation_parse_blocks_from_menu_items( $menu_items, $menu_items_by_parent_id ) { 82 if ( empty( $menu_items ) ) { 83 return array(); 84 } 85 86 $blocks = array(); 87 88 foreach ( $menu_items as $menu_item ) { 89 $class_name = ! empty( $menu_item->classes ) ? implode( ' ', (array) $menu_item->classes ) : null; 90 $id = ( null !== $menu_item->object_id && 'custom' !== $menu_item->object ) ? $menu_item->object_id : null; 91 $opens_in_new_tab = null !== $menu_item->target && '_blank' === $menu_item->target; 92 $rel = ( null !== $menu_item->xfn && '' !== $menu_item->xfn ) ? $menu_item->xfn : null; 93 $kind = null !== $menu_item->type ? str_replace( '_', '-', $menu_item->type ) : 'custom'; 94 95 $block = array( 96 'blockName' => isset( $menu_items_by_parent_id[ $menu_item->ID ] ) ? 'core/navigation-submenu' : 'core/navigation-link', 97 'attrs' => array( 98 'className' => $class_name, 99 'description' => $menu_item->description, 100 'id' => $id, 101 'kind' => $kind, 102 'label' => $menu_item->title, 103 'opensInNewTab' => $opens_in_new_tab, 104 'rel' => $rel, 105 'title' => $menu_item->attr_title, 106 'type' => $menu_item->object, 107 'url' => $menu_item->url, 108 ), 109 ); 110 111 $block['innerBlocks'] = isset( $menu_items_by_parent_id[ $menu_item->ID ] ) 112 ? block_core_navigation_parse_blocks_from_menu_items( $menu_items_by_parent_id[ $menu_item->ID ], $menu_items_by_parent_id ) 113 : array(); 114 $block['innerContent'] = array_map( 'serialize_block', $block['innerBlocks'] ); 115 116 $blocks[] = $block; 117 } 118 119 return $blocks; 120 } 121 122 /** 123 * Build an array with CSS classes and inline styles defining the colors 124 * which will be applied to the navigation markup in the front-end. 125 * 126 * @param array $attributes Navigation block attributes. 127 * 128 * @return array Colors CSS classes and inline styles. 129 */ 130 function block_core_navigation_build_css_colors( $attributes ) { 131 $colors = array( 132 'css_classes' => array(), 133 'inline_styles' => '', 134 'overlay_css_classes' => array(), 135 'overlay_inline_styles' => '', 136 ); 137 138 // Text color. 139 $has_named_text_color = array_key_exists( 'textColor', $attributes ); 140 $has_custom_text_color = array_key_exists( 'customTextColor', $attributes ); 141 142 // If has text color. 143 if ( $has_custom_text_color || $has_named_text_color ) { 144 // Add has-text-color class. 145 $colors['css_classes'][] = 'has-text-color'; 146 } 147 148 if ( $has_named_text_color ) { 149 // Add the color class. 150 $colors['css_classes'][] = sprintf( 'has-%s-color', $attributes['textColor'] ); 151 } elseif ( $has_custom_text_color ) { 152 // Add the custom color inline style. 153 $colors['inline_styles'] .= sprintf( 'color: %s;', $attributes['customTextColor'] ); 154 } 155 156 // Background color. 157 $has_named_background_color = array_key_exists( 'backgroundColor', $attributes ); 158 $has_custom_background_color = array_key_exists( 'customBackgroundColor', $attributes ); 159 160 // If has background color. 161 if ( $has_custom_background_color || $has_named_background_color ) { 162 // Add has-background class. 163 $colors['css_classes'][] = 'has-background'; 164 } 165 166 if ( $has_named_background_color ) { 167 // Add the background-color class. 168 $colors['css_classes'][] = sprintf( 'has-%s-background-color', $attributes['backgroundColor'] ); 169 } elseif ( $has_custom_background_color ) { 170 // Add the custom background-color inline style. 171 $colors['inline_styles'] .= sprintf( 'background-color: %s;', $attributes['customBackgroundColor'] ); 172 } 173 174 // Overlay text color. 175 $has_named_overlay_text_color = array_key_exists( 'overlayTextColor', $attributes ); 176 $has_custom_overlay_text_color = array_key_exists( 'customOverlayTextColor', $attributes ); 177 178 // If has overlay text color. 179 if ( $has_custom_overlay_text_color || $has_named_overlay_text_color ) { 180 // Add has-text-color class. 181 $colors['overlay_css_classes'][] = 'has-text-color'; 182 } 183 184 if ( $has_named_overlay_text_color ) { 185 // Add the overlay color class. 186 $colors['overlay_css_classes'][] = sprintf( 'has-%s-color', $attributes['overlayTextColor'] ); 187 } elseif ( $has_custom_overlay_text_color ) { 188 // Add the custom overlay color inline style. 189 $colors['overlay_inline_styles'] .= sprintf( 'color: %s;', $attributes['customOverlayTextColor'] ); 190 } 191 192 // Overlay background color. 193 $has_named_overlay_background_color = array_key_exists( 'overlayBackgroundColor', $attributes ); 194 $has_custom_overlay_background_color = array_key_exists( 'customOverlayBackgroundColor', $attributes ); 195 196 // If has overlay background color. 197 if ( $has_custom_overlay_background_color || $has_named_overlay_background_color ) { 198 // Add has-background class. 199 $colors['overlay_css_classes'][] = 'has-background'; 200 } 201 202 if ( $has_named_overlay_background_color ) { 203 // Add the overlay background-color class. 204 $colors['overlay_css_classes'][] = sprintf( 'has-%s-background-color', $attributes['overlayBackgroundColor'] ); 205 } elseif ( $has_custom_overlay_background_color ) { 206 // Add the custom overlay background-color inline style. 207 $colors['overlay_inline_styles'] .= sprintf( 'background-color: %s;', $attributes['customOverlayBackgroundColor'] ); 208 } 209 210 return $colors; 211 } 212 213 /** 214 * Build an array with CSS classes and inline styles defining the font sizes 215 * which will be applied to the navigation markup in the front-end. 216 * 217 * @param array $attributes Navigation block attributes. 218 * 219 * @return array Font size CSS classes and inline styles. 220 */ 221 function block_core_navigation_build_css_font_sizes( $attributes ) { 222 // CSS classes. 223 $font_sizes = array( 224 'css_classes' => array(), 225 'inline_styles' => '', 226 ); 227 228 $has_named_font_size = array_key_exists( 'fontSize', $attributes ); 229 $has_custom_font_size = array_key_exists( 'customFontSize', $attributes ); 230 231 if ( $has_named_font_size ) { 232 // Add the font size class. 233 $font_sizes['css_classes'][] = sprintf( 'has-%s-font-size', $attributes['fontSize'] ); 234 } elseif ( $has_custom_font_size ) { 235 // Add the custom font size inline style. 236 $font_sizes['inline_styles'] = sprintf( 'font-size: %spx;', $attributes['customFontSize'] ); 237 } 238 239 return $font_sizes; 240 } 241 242 /** 243 * Returns the top-level submenu SVG chevron icon. 244 * 245 * @return string 246 */ 247 function block_core_navigation_render_submenu_icon() { 248 return '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true" focusable="false"><path d="M1.50002 4L6.00002 8L10.5 4" stroke-width="1.5"></path></svg>'; 249 } 250 251 /** 252 * Get the classic navigation menu to use as a fallback. 253 * 254 * @return object WP_Term The classic navigation. 255 */ 256 function block_core_navigation_get_classic_menu_fallback() { 257 $classic_nav_menus = wp_get_nav_menus(); 258 259 // If menus exist. 260 if ( $classic_nav_menus && ! is_wp_error( $classic_nav_menus ) ) { 261 // Handles simple use case where user has a classic menu and switches to a block theme. 262 263 // Returns the menu assigned to location `primary`. 264 $locations = get_nav_menu_locations(); 265 if ( isset( $locations['primary'] ) ) { 266 $primary_menu = wp_get_nav_menu_object( $locations['primary'] ); 267 if ( $primary_menu ) { 268 return $primary_menu; 269 } 270 } 271 272 // Returns a menu if `primary` is its slug. 273 foreach ( $classic_nav_menus as $classic_nav_menu ) { 274 if ( 'primary' === $classic_nav_menu->slug ) { 275 return $classic_nav_menu; 276 } 277 } 278 279 // Otherwise return the most recently created classic menu. 280 usort( 281 $classic_nav_menus, 282 function( $a, $b ) { 283 return $b->term_id - $a->term_id; 284 } 285 ); 286 return $classic_nav_menus[0]; 287 } 288 } 289 290 /** 291 * Converts a classic navigation to blocks. 292 * 293 * @param object $classic_nav_menu WP_Term The classic navigation object to convert. 294 * @return array the normalized parsed blocks. 295 */ 296 function block_core_navigation_get_classic_menu_fallback_blocks( $classic_nav_menu ) { 297 // BEGIN: Code that already exists in wp_nav_menu(). 298 $menu_items = wp_get_nav_menu_items( $classic_nav_menu->term_id, array( 'update_post_term_cache' => false ) ); 299 300 // Set up the $menu_item variables. 301 _wp_menu_item_classes_by_context( $menu_items ); 302 303 $sorted_menu_items = array(); 304 foreach ( (array) $menu_items as $menu_item ) { 305 $sorted_menu_items[ $menu_item->menu_order ] = $menu_item; 306 } 307 308 unset( $menu_items, $menu_item ); 309 310 // END: Code that already exists in wp_nav_menu(). 311 312 $menu_items_by_parent_id = array(); 313 foreach ( $sorted_menu_items as $menu_item ) { 314 $menu_items_by_parent_id[ $menu_item->menu_item_parent ][] = $menu_item; 315 } 316 317 $inner_blocks = block_core_navigation_parse_blocks_from_menu_items( 318 isset( $menu_items_by_parent_id[0] ) 319 ? $menu_items_by_parent_id[0] 320 : array(), 321 $menu_items_by_parent_id 322 ); 323 324 return serialize_blocks( $inner_blocks ); 325 } 326 327 /** 328 * If there's a the classic menu then use it as a fallback. 329 * 330 * @return array the normalized parsed blocks. 331 */ 332 function block_core_navigation_maybe_use_classic_menu_fallback() { 333 // See if we have a classic menu. 334 $classic_nav_menu = block_core_navigation_get_classic_menu_fallback(); 335 336 if ( ! $classic_nav_menu ) { 337 return; 338 } 339 340 // If we have a classic menu then convert it to blocks. 341 $classic_nav_menu_blocks = block_core_navigation_get_classic_menu_fallback_blocks( $classic_nav_menu ); 342 343 if ( empty( $classic_nav_menu_blocks ) ) { 344 return; 345 } 346 347 // Create a new navigation menu from the classic menu. 348 $wp_insert_post_result = wp_insert_post( 349 array( 350 'post_content' => $classic_nav_menu_blocks, 351 'post_title' => $classic_nav_menu->slug, 352 'post_name' => $classic_nav_menu->slug, 353 'post_status' => 'publish', 354 'post_type' => 'wp_navigation', 355 ), 356 true // So that we can check whether the result is an error. 357 ); 358 359 if ( is_wp_error( $wp_insert_post_result ) ) { 360 return; 361 } 362 363 // Fetch the most recently published navigation which will be the classic one created above. 364 return block_core_navigation_get_most_recently_published_navigation(); 365 } 366 367 /** 368 * Finds the most recently published `wp_navigation` Post. 369 * 370 * @return WP_Post|null the first non-empty Navigation or null. 371 */ 372 function block_core_navigation_get_most_recently_published_navigation() { 373 374 // Default to the most recently created menu. 375 $parsed_args = array( 376 'post_type' => 'wp_navigation', 377 'no_found_rows' => true, 378 'update_post_meta_cache' => false, 379 'update_post_term_cache' => false, 380 'order' => 'DESC', 381 'orderby' => 'date', 382 'post_status' => 'publish', 383 'posts_per_page' => 1, // get only the most recent. 384 ); 385 386 $navigation_post = new WP_Query( $parsed_args ); 387 if ( count( $navigation_post->posts ) > 0 ) { 388 return $navigation_post->posts[0]; 389 } 390 391 return null; 392 } 393 394 /** 395 * Filter out empty "null" blocks from the block list. 396 * 'parse_blocks' includes a null block with '\n\n' as the content when 397 * it encounters whitespace. This is not a bug but rather how the parser 398 * is designed. 399 * 400 * @param array $parsed_blocks the parsed blocks to be normalized. 401 * @return array the normalized parsed blocks. 402 */ 403 function block_core_navigation_filter_out_empty_blocks( $parsed_blocks ) { 404 $filtered = array_filter( 405 $parsed_blocks, 406 function( $block ) { 407 return isset( $block['blockName'] ); 408 } 409 ); 410 411 // Reset keys. 412 return array_values( $filtered ); 413 } 414 415 /** 416 * Returns true if the navigation block contains a nested navigation block. 417 * 418 * @param WP_Block_List $inner_blocks Inner block instance to be normalized. 419 * @return bool true if the navigation block contains a nested navigation block. 420 */ 421 function block_core_navigation_block_contains_core_navigation( $inner_blocks ) { 422 foreach ( $inner_blocks as $block ) { 423 if ( 'core/navigation' === $block->name ) { 424 return true; 425 } 426 if ( $block->inner_blocks && block_core_navigation_block_contains_core_navigation( $block->inner_blocks ) ) { 427 return true; 428 } 429 } 430 431 return false; 432 } 433 434 /** 435 * Retrieves the appropriate fallback to be used on the front of the 436 * site when there is no menu assigned to the Nav block. 437 * 438 * This aims to mirror how the fallback mechanic for wp_nav_menu works. 439 * See https://developer.wordpress.org/reference/functions/wp_nav_menu/#more-information. 440 * 441 * @return array the array of blocks to be used as a fallback. 442 */ 443 function block_core_navigation_get_fallback_blocks() { 444 $page_list_fallback = array( 445 array( 446 'blockName' => 'core/page-list', 447 ), 448 ); 449 450 $registry = WP_Block_Type_Registry::get_instance(); 451 452 // If `core/page-list` is not registered then return empty blocks. 453 $fallback_blocks = $registry->is_registered( 'core/page-list' ) ? $page_list_fallback : array(); 454 455 // Default to a list of Pages. 456 457 $navigation_post = block_core_navigation_get_most_recently_published_navigation(); 458 459 // If there are no navigation posts then try to find a classic menu 460 // and convert it into a block based navigation menu. 461 if ( ! $navigation_post ) { 462 $navigation_post = block_core_navigation_maybe_use_classic_menu_fallback(); 463 } 464 465 // Use the first non-empty Navigation as fallback if available. 466 if ( $navigation_post ) { 467 $parsed_blocks = parse_blocks( $navigation_post->post_content ); 468 $maybe_fallback = block_core_navigation_filter_out_empty_blocks( $parsed_blocks ); 469 470 // Normalizing blocks may result in an empty array of blocks if they were all `null` blocks. 471 // In this case default to the (Page List) fallback. 472 $fallback_blocks = ! empty( $maybe_fallback ) ? $maybe_fallback : $fallback_blocks; 473 } 474 475 /** 476 * Filters the fallback experience for the Navigation block. 477 * 478 * Returning a falsey value will opt out of the fallback and cause the block not to render. 479 * To customise the blocks provided return an array of blocks - these should be valid 480 * children of the `core/navigation` block. 481 * 482 * @since 5.9.0 483 * 484 * @param array[] default fallback blocks provided by the default block mechanic. 485 */ 486 return apply_filters( 'block_core_navigation_render_fallback', $fallback_blocks ); 487 } 488 489 /** 490 * Iterate through all inner blocks recursively and get navigation link block's post IDs. 491 * 492 * @param WP_Block_List $inner_blocks Block list class instance. 493 * 494 * @return array Array of post IDs. 495 */ 496 function block_core_navigation_get_post_ids( $inner_blocks ) { 497 $post_ids = array_map( 'block_core_navigation_from_block_get_post_ids', iterator_to_array( $inner_blocks ) ); 498 return array_unique( array_merge( ...$post_ids ) ); 499 } 500 501 /** 502 * Get post IDs from a navigation link block instance. 503 * 504 * @param WP_Block $block Instance of a block. 505 * 506 * @return array Array of post IDs. 507 */ 508 function block_core_navigation_from_block_get_post_ids( $block ) { 509 $post_ids = array(); 510 511 if ( $block->inner_blocks ) { 512 $post_ids = block_core_navigation_get_post_ids( $block->inner_blocks ); 513 } 514 515 if ( 'core/navigation-link' === $block->name || 'core/navigation-submenu' === $block->name ) { 516 if ( $block->attributes && isset( $block->attributes['kind'] ) && 'post-type' === $block->attributes['kind'] && isset( $block->attributes['id'] ) ) { 517 $post_ids[] = $block->attributes['id']; 518 } 519 } 520 521 return $post_ids; 522 } 523 524 /** 525 * Renders the `core/navigation` block on server. 526 * 527 * @param array $attributes The block attributes. 528 * @param string $content The saved content. 529 * @param WP_Block $block The parsed block. 530 * 531 * @return string Returns the post content with the legacy widget added. 532 */ 533 function render_block_core_navigation( $attributes, $content, $block ) { 534 535 static $seen_menu_names = array(); 536 537 // Flag used to indicate whether the rendered output is considered to be 538 // a fallback (i.e. the block has no menu associated with it). 539 $is_fallback = false; 540 541 $nav_menu_name = ''; 542 543 /** 544 * Deprecated: 545 * The rgbTextColor and rgbBackgroundColor attributes 546 * have been deprecated in favor of 547 * customTextColor and customBackgroundColor ones. 548 * Move the values from old attrs to the new ones. 549 */ 550 if ( isset( $attributes['rgbTextColor'] ) && empty( $attributes['textColor'] ) ) { 551 $attributes['customTextColor'] = $attributes['rgbTextColor']; 552 } 553 554 if ( isset( $attributes['rgbBackgroundColor'] ) && empty( $attributes['backgroundColor'] ) ) { 555 $attributes['customBackgroundColor'] = $attributes['rgbBackgroundColor']; 556 } 557 558 unset( $attributes['rgbTextColor'], $attributes['rgbBackgroundColor'] ); 559 560 /** 561 * This is for backwards compatibility after `isResponsive` attribute has been removed. 562 */ 563 $has_old_responsive_attribute = ! empty( $attributes['isResponsive'] ) && $attributes['isResponsive']; 564 $is_responsive_menu = isset( $attributes['overlayMenu'] ) && 'never' !== $attributes['overlayMenu'] || $has_old_responsive_attribute; 565 $should_load_view_script = ! wp_script_is( 'wp-block-navigation-view' ) && ( $is_responsive_menu || $attributes['openSubmenusOnClick'] || $attributes['showSubmenuIcon'] ); 566 if ( $should_load_view_script ) { 567 wp_enqueue_script( 'wp-block-navigation-view' ); 568 } 569 570 $should_load_modal_view_script = isset( $attributes['overlayMenu'] ) && 'never' !== $attributes['overlayMenu']; 571 if ( $should_load_modal_view_script ) { 572 wp_enqueue_script( 'wp-block-navigation-view-modal' ); 573 } 574 575 $inner_blocks = $block->inner_blocks; 576 577 // Ensure that blocks saved with the legacy ref attribute name (navigationMenuId) continue to render. 578 if ( array_key_exists( 'navigationMenuId', $attributes ) ) { 579 $attributes['ref'] = $attributes['navigationMenuId']; 580 } 581 582 // If: 583 // - the gutenberg plugin is active 584 // - `__unstableLocation` is defined 585 // - we have menu items at the defined location 586 // - we don't have a relationship to a `wp_navigation` Post (via `ref`). 587 // ...then create inner blocks from the classic menu assigned to that location. 588 if ( 589 defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN && 590 array_key_exists( '__unstableLocation', $attributes ) && 591 ! array_key_exists( 'ref', $attributes ) && 592 ! empty( block_core_navigation_get_menu_items_at_location( $attributes['__unstableLocation'] ) ) 593 ) { 594 $menu_items = block_core_navigation_get_menu_items_at_location( $attributes['__unstableLocation'] ); 595 if ( empty( $menu_items ) ) { 596 return ''; 597 } 598 599 $menu_items_by_parent_id = block_core_navigation_sort_menu_items_by_parent_id( $menu_items ); 600 $parsed_blocks = block_core_navigation_parse_blocks_from_menu_items( $menu_items_by_parent_id[0], $menu_items_by_parent_id ); 601 $inner_blocks = new WP_Block_List( $parsed_blocks, $attributes ); 602 } 603 604 // Load inner blocks from the navigation post. 605 if ( array_key_exists( 'ref', $attributes ) ) { 606 $navigation_post = get_post( $attributes['ref'] ); 607 if ( ! isset( $navigation_post ) ) { 608 return ''; 609 } 610 611 // Only published posts are valid. If this is changed then a corresponding change 612 // must also be implemented in `use-navigation-menu.js`. 613 if ( 'publish' === $navigation_post->post_status ) { 614 $nav_menu_name = $navigation_post->post_title; 615 616 if ( isset( $seen_menu_names[ $nav_menu_name ] ) ) { 617 ++$seen_menu_names[ $nav_menu_name ]; 618 } else { 619 $seen_menu_names[ $nav_menu_name ] = 1; 620 } 621 622 $parsed_blocks = parse_blocks( $navigation_post->post_content ); 623 624 // 'parse_blocks' includes a null block with '\n\n' as the content when 625 // it encounters whitespace. This code strips it. 626 $compacted_blocks = block_core_navigation_filter_out_empty_blocks( $parsed_blocks ); 627 628 // TODO - this uses the full navigation block attributes for the 629 // context which could be refined. 630 $inner_blocks = new WP_Block_List( $compacted_blocks, $attributes ); 631 } 632 } 633 634 // If there are no inner blocks then fallback to rendering an appropriate fallback. 635 if ( empty( $inner_blocks ) ) { 636 $is_fallback = true; // indicate we are rendering the fallback. 637 638 $fallback_blocks = block_core_navigation_get_fallback_blocks(); 639 640 // Fallback my have been filtered so do basic test for validity. 641 if ( empty( $fallback_blocks ) || ! is_array( $fallback_blocks ) ) { 642 return ''; 643 } 644 645 $inner_blocks = new WP_Block_List( $fallback_blocks, $attributes ); 646 } 647 648 if ( block_core_navigation_block_contains_core_navigation( $inner_blocks ) ) { 649 return ''; 650 } 651 652 /** 653 * Filter navigation block $inner_blocks. 654 * Allows modification of a navigation block menu items. 655 * 656 * @since 6.1.0 657 * 658 * @param \WP_Block_List $inner_blocks 659 */ 660 $inner_blocks = apply_filters( 'block_core_navigation_render_inner_blocks', $inner_blocks ); 661 662 $layout_justification = array( 663 'left' => 'items-justified-left', 664 'right' => 'items-justified-right', 665 'center' => 'items-justified-center', 666 'space-between' => 'items-justified-space-between', 667 ); 668 669 // Restore legacy classnames for submenu positioning. 670 $layout_class = ''; 671 if ( isset( $attributes['layout']['justifyContent'] ) ) { 672 $layout_class .= $layout_justification[ $attributes['layout']['justifyContent'] ]; 673 } 674 if ( isset( $attributes['layout']['orientation'] ) && 'vertical' === $attributes['layout']['orientation'] ) { 675 $layout_class .= ' is-vertical'; 676 } 677 678 if ( isset( $attributes['layout']['flexWrap'] ) && 'nowrap' === $attributes['layout']['flexWrap'] ) { 679 $layout_class .= ' no-wrap'; 680 } 681 682 // Manually add block support text decoration as CSS class. 683 $text_decoration = _wp_array_get( $attributes, array( 'style', 'typography', 'textDecoration' ), null ); 684 $text_decoration_class = sprintf( 'has-text-decoration-%s', $text_decoration ); 685 686 $colors = block_core_navigation_build_css_colors( $attributes ); 687 $font_sizes = block_core_navigation_build_css_font_sizes( $attributes ); 688 $classes = array_merge( 689 $colors['css_classes'], 690 $font_sizes['css_classes'], 691 $is_responsive_menu ? array( 'is-responsive' ) : array(), 692 $layout_class ? array( $layout_class ) : array(), 693 $is_fallback ? array( 'is-fallback' ) : array(), 694 $text_decoration ? array( $text_decoration_class ) : array() 695 ); 696 697 $post_ids = block_core_navigation_get_post_ids( $inner_blocks ); 698 if ( $post_ids ) { 699 _prime_post_caches( $post_ids, false, false ); 700 } 701 702 $list_item_nav_blocks = array( 703 'core/navigation-link', 704 'core/home-link', 705 'core/site-title', 706 'core/site-logo', 707 'core/navigation-submenu', 708 ); 709 710 $needs_list_item_wrapper = array( 711 'core/site-title', 712 'core/site-logo', 713 ); 714 715 $inner_blocks_html = ''; 716 $is_list_open = false; 717 foreach ( $inner_blocks as $inner_block ) { 718 $is_list_item = in_array( $inner_block->name, $list_item_nav_blocks, true ); 719 720 if ( $is_list_item && ! $is_list_open ) { 721 $is_list_open = true; 722 $inner_blocks_html .= '<ul class="wp-block-navigation__container">'; 723 } 724 725 if ( ! $is_list_item && $is_list_open ) { 726 $is_list_open = false; 727 $inner_blocks_html .= '</ul>'; 728 } 729 730 $inner_block_content = $inner_block->render(); 731 if ( ! empty( $inner_block_content ) ) { 732 if ( in_array( $inner_block->name, $needs_list_item_wrapper, true ) ) { 733 $inner_blocks_html .= '<li class="wp-block-navigation-item">' . $inner_block_content . '</li>'; 734 } else { 735 $inner_blocks_html .= $inner_block_content; 736 } 737 } 738 } 739 740 if ( $is_list_open ) { 741 $inner_blocks_html .= '</ul>'; 742 } 743 744 $block_styles = isset( $attributes['styles'] ) ? $attributes['styles'] : ''; 745 746 // If the menu name has been used previously then append an ID 747 // to the name to ensure uniqueness across a given post. 748 if ( isset( $seen_menu_names[ $nav_menu_name ] ) && $seen_menu_names[ $nav_menu_name ] > 1 ) { 749 $count = $seen_menu_names[ $nav_menu_name ]; 750 $nav_menu_name = $nav_menu_name . ' ' . ( $count ); 751 } 752 753 $wrapper_attributes = get_block_wrapper_attributes( 754 array( 755 'class' => implode( ' ', $classes ), 756 'style' => $block_styles . $colors['inline_styles'] . $font_sizes['inline_styles'], 757 'aria-label' => $nav_menu_name, 758 ) 759 ); 760 761 $modal_unique_id = wp_unique_id( 'modal-' ); 762 763 // Determine whether or not navigation elements should be wrapped in the markup required to make it responsive, 764 // return early if they don't. 765 if ( ! $is_responsive_menu ) { 766 return sprintf( 767 '<nav %1$s>%2$s</nav>', 768 $wrapper_attributes, 769 $inner_blocks_html 770 ); 771 } 772 773 $is_hidden_by_default = isset( $attributes['overlayMenu'] ) && 'always' === $attributes['overlayMenu']; 774 775 $responsive_container_classes = array( 776 'wp-block-navigation__responsive-container', 777 $is_hidden_by_default ? 'hidden-by-default' : '', 778 implode( ' ', $colors['overlay_css_classes'] ), 779 ); 780 $open_button_classes = array( 781 'wp-block-navigation__responsive-container-open', 782 $is_hidden_by_default ? 'always-shown' : '', 783 ); 784 785 $should_display_icon_label = isset( $attributes['hasIcon'] ) && true === $attributes['hasIcon']; 786 $toggle_button_icon = '<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="4" y="7.5" width="16" height="1.5" /><rect x="4" y="15" width="16" height="1.5" /></svg>'; 787 if ( isset( $attributes['icon'] ) ) { 788 if ( 'menu' === $attributes['icon'] ) { 789 $toggle_button_icon = '<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 5v1.5h14V5H5zm0 7.8h14v-1.5H5v1.5zM5 19h14v-1.5H5V19z" /></svg>'; 790 } 791 } 792 $toggle_button_content = $should_display_icon_label ? $toggle_button_icon : __( 'Menu' ); 793 $toggle_close_button_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false"><path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path></svg>'; 794 $toggle_close_button_content = $should_display_icon_label ? $toggle_close_button_icon : __( 'Close' ); 795 $toggle_aria_label_open = $should_display_icon_label ? 'aria-label="' . __( 'Open menu' ) . '"' : ''; // Open button label. 796 $toggle_aria_label_close = $should_display_icon_label ? 'aria-label="' . __( 'Close menu' ) . '"' : ''; // Close button label. 797 798 $responsive_container_markup = sprintf( 799 '<button aria-haspopup="true" %3$s class="%6$s" data-micromodal-trigger="%1$s">%9$s</button> 800 <div class="%5$s" style="%7$s" id="%1$s"> 801 <div class="wp-block-navigation__responsive-close" tabindex="-1" data-micromodal-close> 802 <div class="wp-block-navigation__responsive-dialog" aria-label="%8$s"> 803 <button %4$s data-micromodal-close class="wp-block-navigation__responsive-container-close">%10$s</button> 804 <div class="wp-block-navigation__responsive-container-content" id="%1$s-content"> 805 %2$s 806 </div> 807 </div> 808 </div> 809 </div>', 810 esc_attr( $modal_unique_id ), 811 $inner_blocks_html, 812 $toggle_aria_label_open, 813 $toggle_aria_label_close, 814 esc_attr( implode( ' ', $responsive_container_classes ) ), 815 esc_attr( implode( ' ', $open_button_classes ) ), 816 esc_attr( safecss_filter_attr( $colors['overlay_inline_styles'] ) ), 817 __( 'Menu' ), 818 $toggle_button_content, 819 $toggle_close_button_content 820 ); 821 822 return sprintf( 823 '<nav %1$s>%2$s</nav>', 824 $wrapper_attributes, 825 $responsive_container_markup 826 ); 827 } 828 829 /** 830 * Register the navigation block. 831 * 832 * @uses render_block_core_navigation() 833 * @throws WP_Error An WP_Error exception parsing the block definition. 834 */ 835 function register_block_core_navigation() { 836 register_block_type_from_metadata( 837 __DIR__ . '/navigation', 838 array( 839 'render_callback' => 'render_block_core_navigation', 840 ) 841 ); 842 } 843 844 add_action( 'init', 'register_block_core_navigation' ); 845 846 /** 847 * Filter that changes the parsed attribute values of navigation blocks contain typographic presets to contain the values directly. 848 * 849 * @param array $parsed_block The block being rendered. 850 * 851 * @return array The block being rendered without typographic presets. 852 */ 853 function block_core_navigation_typographic_presets_backcompatibility( $parsed_block ) { 854 if ( 'core/navigation' === $parsed_block['blockName'] ) { 855 $attribute_to_prefix_map = array( 856 'fontStyle' => 'var:preset|font-style|', 857 'fontWeight' => 'var:preset|font-weight|', 858 'textDecoration' => 'var:preset|text-decoration|', 859 'textTransform' => 'var:preset|text-transform|', 860 ); 861 foreach ( $attribute_to_prefix_map as $style_attribute => $prefix ) { 862 if ( ! empty( $parsed_block['attrs']['style']['typography'][ $style_attribute ] ) ) { 863 $prefix_len = strlen( $prefix ); 864 $attribute_value = &$parsed_block['attrs']['style']['typography'][ $style_attribute ]; 865 if ( 0 === strncmp( $attribute_value, $prefix, $prefix_len ) ) { 866 $attribute_value = substr( $attribute_value, $prefix_len ); 867 } 868 if ( 'textDecoration' === $style_attribute && 'strikethrough' === $attribute_value ) { 869 $attribute_value = 'line-through'; 870 } 871 } 872 } 873 } 874 875 return $parsed_block; 876 } 877 878 add_filter( 'render_block_data', 'block_core_navigation_typographic_presets_backcompatibility' );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Sun Jun 4 08:20:02 2023 | Cross-referenced by PHPXref |