WordPress Version: 6.3
/**
* Renders the `core/navigation` block on server.
*
* @param array $attributes The block attributes.
* @param string $content The saved content.
* @param WP_Block $block The parsed block.
*
* @return string Returns the post content with the legacy widget added.
*/
function render_block_core_navigation($attributes, $content, $block)
{
static $seen_menu_names = array();
// Flag used to indicate whether the rendered output is considered to be
// a fallback (i.e. the block has no menu associated with it).
$is_fallback = false;
$nav_menu_name = '';
/**
* Deprecated:
* The rgbTextColor and rgbBackgroundColor attributes
* have been deprecated in favor of
* customTextColor and customBackgroundColor ones.
* Move the values from old attrs to the new ones.
*/
if (isset($attributes['rgbTextColor']) && empty($attributes['textColor'])) {
$attributes['customTextColor'] = $attributes['rgbTextColor'];
}
if (isset($attributes['rgbBackgroundColor']) && empty($attributes['backgroundColor'])) {
$attributes['customBackgroundColor'] = $attributes['rgbBackgroundColor'];
}
unset($attributes['rgbTextColor'], $attributes['rgbBackgroundColor']);
/**
* This is for backwards compatibility after `isResponsive` attribute has been removed.
*/
$has_old_responsive_attribute = !empty($attributes['isResponsive']) && $attributes['isResponsive'];
$is_responsive_menu = isset($attributes['overlayMenu']) && 'never' !== $attributes['overlayMenu'] || $has_old_responsive_attribute;
$inner_blocks = $block->inner_blocks;
// Ensure that blocks saved with the legacy ref attribute name (navigationMenuId) continue to render.
if (array_key_exists('navigationMenuId', $attributes)) {
$attributes['ref'] = $attributes['navigationMenuId'];
}
// If:
// - the gutenberg plugin is active
// - `__unstableLocation` is defined
// - we have menu items at the defined location
// - we don't have a relationship to a `wp_navigation` Post (via `ref`).
// ...then create inner blocks from the classic menu assigned to that location.
if (defined('IS_GUTENBERG_PLUGIN') && IS_GUTENBERG_PLUGIN && array_key_exists('__unstableLocation', $attributes) && !array_key_exists('ref', $attributes) && !empty(block_core_navigation_get_menu_items_at_location($attributes['__unstableLocation']))) {
$menu_items = block_core_navigation_get_menu_items_at_location($attributes['__unstableLocation']);
if (empty($menu_items)) {
return '';
}
$menu_items_by_parent_id = block_core_navigation_sort_menu_items_by_parent_id($menu_items);
$parsed_blocks = block_core_navigation_parse_blocks_from_menu_items($menu_items_by_parent_id[0], $menu_items_by_parent_id);
$inner_blocks = new WP_Block_List($parsed_blocks, $attributes);
}
// Load inner blocks from the navigation post.
if (array_key_exists('ref', $attributes)) {
$navigation_post = get_post($attributes['ref']);
if (!isset($navigation_post)) {
return '';
}
// Only published posts are valid. If this is changed then a corresponding change
// must also be implemented in `use-navigation-menu.js`.
if ('publish' === $navigation_post->post_status) {
$nav_menu_name = $navigation_post->post_title;
if (isset($seen_menu_names[$nav_menu_name])) {
++$seen_menu_names[$nav_menu_name];
} else {
$seen_menu_names[$nav_menu_name] = 1;
}
$parsed_blocks = parse_blocks($navigation_post->post_content);
// 'parse_blocks' includes a null block with '\n\n' as the content when
// it encounters whitespace. This code strips it.
$compacted_blocks = block_core_navigation_filter_out_empty_blocks($parsed_blocks);
// TODO - this uses the full navigation block attributes for the
// context which could be refined.
$inner_blocks = new WP_Block_List($compacted_blocks, $attributes);
}
}
// If there are no inner blocks then fallback to rendering an appropriate fallback.
if (empty($inner_blocks)) {
$is_fallback = true;
// indicate we are rendering the fallback.
$fallback_blocks = block_core_navigation_get_fallback_blocks();
// Fallback my have been filtered so do basic test for validity.
if (empty($fallback_blocks) || !is_array($fallback_blocks)) {
return '';
}
$inner_blocks = new WP_Block_List($fallback_blocks, $attributes);
}
if (block_core_navigation_block_contains_core_navigation($inner_blocks)) {
return '';
}
/**
* Filter navigation block $inner_blocks.
* Allows modification of a navigation block menu items.
*
* @since 6.1.0
*
* @param \WP_Block_List $inner_blocks
*/
$inner_blocks = apply_filters('block_core_navigation_render_inner_blocks', $inner_blocks);
$layout_justification = array('left' => 'items-justified-left', 'right' => 'items-justified-right', 'center' => 'items-justified-center', 'space-between' => 'items-justified-space-between');
// Restore legacy classnames for submenu positioning.
$layout_class = '';
if (isset($attributes['layout']['justifyContent']) && isset($layout_justification[$attributes['layout']['justifyContent']])) {
$layout_class .= $layout_justification[$attributes['layout']['justifyContent']];
}
if (isset($attributes['layout']['orientation']) && 'vertical' === $attributes['layout']['orientation']) {
$layout_class .= ' is-vertical';
}
if (isset($attributes['layout']['flexWrap']) && 'nowrap' === $attributes['layout']['flexWrap']) {
$layout_class .= ' no-wrap';
}
// Manually add block support text decoration as CSS class.
$text_decoration = _wp_array_get($attributes, array('style', 'typography', 'textDecoration'), null);
$text_decoration_class = sprintf('has-text-decoration-%s', $text_decoration);
$colors = block_core_navigation_build_css_colors($attributes);
$font_sizes = block_core_navigation_build_css_font_sizes($attributes);
$classes = array_merge($colors['css_classes'], $font_sizes['css_classes'], $is_responsive_menu ? array('is-responsive') : array(), $layout_class ? array($layout_class) : array(), $is_fallback ? array('is-fallback') : array(), $text_decoration ? array($text_decoration_class) : array());
$post_ids = block_core_navigation_get_post_ids($inner_blocks);
if ($post_ids) {
_prime_post_caches($post_ids, false, false);
}
$list_item_nav_blocks = array('core/navigation-link', 'core/home-link', 'core/site-title', 'core/site-logo', 'core/navigation-submenu');
$needs_list_item_wrapper = array('core/site-title', 'core/site-logo');
$block_styles = isset($attributes['styles']) ? $attributes['styles'] : '';
$style = $block_styles . $colors['inline_styles'] . $font_sizes['inline_styles'];
$class = implode(' ', $classes);
// If the menu name has been used previously then append an ID
// to the name to ensure uniqueness across a given post.
if (isset($seen_menu_names[$nav_menu_name]) && $seen_menu_names[$nav_menu_name] > 1) {
$count = $seen_menu_names[$nav_menu_name];
$nav_menu_name = $nav_menu_name . ' ' . $count;
}
$wrapper_attributes = get_block_wrapper_attributes(array('class' => $class, 'style' => $style, 'aria-label' => $nav_menu_name));
$container_attributes = get_block_wrapper_attributes(array('class' => 'wp-block-navigation__container ' . $class, 'style' => $style));
$inner_blocks_html = '';
$is_list_open = false;
$has_submenus = false;
foreach ($inner_blocks as $inner_block) {
$is_list_item = in_array($inner_block->name, $list_item_nav_blocks, true);
if ($is_list_item && !$is_list_open) {
$is_list_open = true;
$inner_blocks_html .= sprintf('<ul %1$s>', $container_attributes);
}
if (!$is_list_item && $is_list_open) {
$is_list_open = false;
$inner_blocks_html .= '</ul>';
}
$inner_block_content = $inner_block->render();
$p = new WP_HTML_Tag_Processor($inner_block_content);
if ($p->next_tag(array('name' => 'LI', 'class_name' => 'has-child'))) {
$has_submenus = true;
}
if (!empty($inner_block_content)) {
if (in_array($inner_block->name, $needs_list_item_wrapper, true)) {
$inner_blocks_html .= '<li class="wp-block-navigation-item">' . $inner_block_content . '</li>';
} else {
$inner_blocks_html .= $inner_block_content;
}
}
}
if ($is_list_open) {
$inner_blocks_html .= '</ul>';
}
// If the script already exists, there is no point in removing it from viewScript.
$should_load_view_script = $is_responsive_menu || $has_submenus && ($attributes['openSubmenusOnClick'] || $attributes['showSubmenuIcon']);
$view_js_file = 'wp-block-navigation-view';
if (!wp_script_is($view_js_file)) {
$script_handles = $block->block_type->view_script_handles;
// If the script is not needed, and it is still in the `view_script_handles`, remove it.
if (!$should_load_view_script && in_array($view_js_file, $script_handles, true)) {
$block->block_type->view_script_handles = array_diff($script_handles, array($view_js_file, 'wp-block-navigation-view-2'));
}
// If the script is needed, but it was previously removed, add it again.
if ($should_load_view_script && !in_array($view_js_file, $script_handles, true)) {
$block->block_type->view_script_handles = array_merge($script_handles, array($view_js_file, 'wp-block-navigation-view-2'));
}
}
// Add directives to the submenu if needed.
if (defined('IS_GUTENBERG_PLUGIN') && IS_GUTENBERG_PLUGIN && $has_submenus && $should_load_view_script) {
$w = new WP_HTML_Tag_Processor($inner_blocks_html);
$inner_blocks_html = block_core_navigation_add_directives_to_submenu($w, $attributes);
}
$modal_unique_id = wp_unique_id('modal-');
// Determine whether or not navigation elements should be wrapped in the markup required to make it responsive,
// return early if they don't.
if (!$is_responsive_menu) {
return sprintf('<nav %1$s>%2$s</nav>', $wrapper_attributes, $inner_blocks_html);
}
$is_hidden_by_default = isset($attributes['overlayMenu']) && 'always' === $attributes['overlayMenu'];
$responsive_container_classes = array('wp-block-navigation__responsive-container', $is_hidden_by_default ? 'hidden-by-default' : '', implode(' ', $colors['overlay_css_classes']));
$open_button_classes = array('wp-block-navigation__responsive-container-open', $is_hidden_by_default ? 'always-shown' : '');
$should_display_icon_label = isset($attributes['hasIcon']) && true === $attributes['hasIcon'];
$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>';
if (isset($attributes['icon'])) {
if ('menu' === $attributes['icon']) {
$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>';
}
}
$toggle_button_content = $should_display_icon_label ? $toggle_button_icon : __('Menu');
$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>';
$toggle_close_button_content = $should_display_icon_label ? $toggle_close_button_icon : __('Close');
$toggle_aria_label_open = $should_display_icon_label ? 'aria-label="' . __('Open menu') . '"' : '';
// Open button label.
$toggle_aria_label_close = $should_display_icon_label ? 'aria-label="' . __('Close menu') . '"' : '';
// Close button label.
// Add Interactivity API directives to the markup if needed.
$nav_element_directives = '';
$open_button_directives = '';
$responsive_container_directives = '';
$responsive_dialog_directives = '';
$close_button_directives = '';
if (defined('IS_GUTENBERG_PLUGIN') && IS_GUTENBERG_PLUGIN && $should_load_view_script) {
$nav_element_directives = '
data-wp-interactive
data-wp-context=\'{ "core": { "navigation": { "isMenuOpen": { "click": false, "hover": false }, "overlay": true, "roleAttribute": "" } } }\'
';
$open_button_directives = '
data-wp-on--click="actions.core.navigation.openMenuOnClick"
data-wp-on--keydown="actions.core.navigation.handleMenuKeydown"
';
$responsive_container_directives = '
data-wp-class--has-modal-open="selectors.core.navigation.isMenuOpen"
data-wp-class--is-menu-open="selectors.core.navigation.isMenuOpen"
data-wp-effect="effects.core.navigation.initMenu"
data-wp-on--keydown="actions.core.navigation.handleMenuKeydown"
data-wp-on--focusout="actions.core.navigation.handleMenuFocusout"
tabindex="-1"
';
$responsive_dialog_directives = '
data-wp-bind--aria-modal="selectors.core.navigation.isMenuOpen"
data-wp-bind--role="selectors.core.navigation.roleAttribute"
data-wp-effect="effects.core.navigation.focusFirstElement"
';
$close_button_directives = '
data-wp-on--click="actions.core.navigation.closeMenuOnClick"
';
}
$responsive_container_markup = sprintf('<button aria-haspopup="true" %3$s class="%6$s" data-micromodal-trigger="%1$s" %11$s>%9$s</button>
<div class="%5$s" style="%7$s" id="%1$s" %12$s>
<div class="wp-block-navigation__responsive-close" tabindex="-1" data-micromodal-close>
<div class="wp-block-navigation__responsive-dialog" aria-label="%8$s" %13$s>
<button %4$s data-micromodal-close class="wp-block-navigation__responsive-container-close" %14$s>%10$s</button>
<div class="wp-block-navigation__responsive-container-content" id="%1$s-content">
%2$s
</div>
</div>
</div>
</div>', esc_attr($modal_unique_id), $inner_blocks_html, $toggle_aria_label_open, $toggle_aria_label_close, esc_attr(implode(' ', $responsive_container_classes)), esc_attr(implode(' ', $open_button_classes)), esc_attr(safecss_filter_attr($colors['overlay_inline_styles'])), __('Menu'), $toggle_button_content, $toggle_close_button_content, $open_button_directives, $responsive_container_directives, $responsive_dialog_directives, $close_button_directives);
return sprintf('<nav %1$s %3$s>%2$s</nav>', $wrapper_attributes, $responsive_container_markup, $nav_element_directives);
}