WordPress/wp-includes/navigation-areas.php

263 lines
8.0 KiB
PHP

<?php
/**
* Block navigation areas functions.
*
* @package WordPress
*/
/**
* Registers the navigation areas supported by the current theme. The expected
* shape of the argument is:
* array(
* 'primary' => 'Primary',
* 'secondary' => 'Secondary',
* 'tertiary' => 'Tertiary',
* )
*
* @since 5.9.0
*
* @param array $new_areas Supported navigation areas.
*/
function register_navigation_areas( $new_areas ) {
global $navigation_areas;
$navigation_areas = $new_areas;
}
/**
* Register the default navigation areas.
*
* @since 5.9.0
* @access private
*/
function _wp_register_default_navigation_areas() {
register_navigation_areas(
array(
'primary' => _x( 'Primary', 'navigation area' ),
'secondary' => _x( 'Secondary', 'navigation area' ),
'tertiary' => _x( 'Tertiary', 'navigation area' ),
)
);
}
/**
* Returns the available navigation areas.
*
* @since 5.9.0
*
* @return array Registered navigation areas.
*/
function get_navigation_areas() {
global $navigation_areas;
return $navigation_areas;
}
/**
* Migrates classic menus to a block-based navigation post on theme switch.
* Assigns the created navigation post to the corresponding navigation area.
*
* @since 5.9.0
* @access private
*
* @param string $new_name Name of the new theme.
* @param WP_Theme $new_theme New theme.
* @param WP_Theme $old_theme Old theme.
*/
function _wp_migrate_menu_to_navigation_post( $new_name, WP_Theme $new_theme, WP_Theme $old_theme ) {
// Do nothing when switching to a theme that does not support site editor.
if ( ! wp_is_block_template_theme() ) {
return;
}
// get_nav_menu_locations() calls get_theme_mod() which depends on the stylesheet option.
// At the same time, switch_theme runs only after the stylesheet option was updated to $new_theme.
// To retrieve theme mods of the old theme, the getter is hooked to get_option( 'stylesheet' ) so that we
// get the old theme, which causes the get_nav_menu_locations to get the locations of the old theme.
$get_old_theme_stylesheet = static function() use ( $old_theme ) {
return $old_theme->get_stylesheet();
};
add_filter( 'option_stylesheet', $get_old_theme_stylesheet );
$locations = get_nav_menu_locations();
$area_mapping = get_option( 'wp_navigation_areas', array() );
foreach ( $locations as $location_name => $menu_id ) {
// Get the menu from the location, skipping if there is no
// menu or there was an error.
$menu = wp_get_nav_menu_object( $menu_id );
if ( ! $menu || is_wp_error( $menu ) ) {
continue;
}
$menu_items = _wp_get_menu_items_at_location( $location_name );
if ( empty( $menu_items ) ) {
continue;
}
$post_name = 'classic_menu_' . $menu_id;
// Get or create to avoid creating too many wp_navigation posts.
$query = new WP_Query;
$matching_posts = $query->query(
array(
'name' => $post_name,
'post_status' => 'publish',
'post_type' => 'wp_navigation',
'posts_per_page' => 1,
'fields' => 'ids',
)
);
if ( ! empty( $matching_posts ) ) {
$navigation_post_id = $matching_posts[0];
} else {
$menu_items_by_parent_id = _wp_sort_menu_items_by_parent_id( $menu_items );
$parsed_blocks = _wp_parse_blocks_from_menu_items( $menu_items_by_parent_id[0], $menu_items_by_parent_id );
$post_data = array(
'post_type' => 'wp_navigation',
'post_title' => sprintf(
/* translators: %s: the name of the menu, e.g. "Main Menu". */
__( 'Classic menu: %s' ),
$menu->name
),
'post_name' => $post_name,
'post_content' => serialize_blocks( $parsed_blocks ),
'post_status' => 'publish',
);
$navigation_post_id = wp_insert_post( $post_data, true );
// If wp_insert_post fails *at any time*, then bail out of the
// entire migration attempt returning the WP_Error object.
if ( is_wp_error( $navigation_post_id ) ) {
return $navigation_post_id;
}
}
$area_mapping[ $location_name ] = $navigation_post_id;
}
remove_filter( 'option_stylesheet', $get_old_theme_stylesheet );
update_option( 'wp_navigation_areas', $area_mapping );
}
/**
* Returns the menu items for a WordPress menu location.
*
* @since 5.9.0
* @access private
*
* @param string $location The menu location.
* @return array Menu items for the location.
*/
function _wp_get_menu_items_at_location( $location ) {
if ( empty( $location ) ) {
return;
}
// Build menu data. The following approximates the code in `wp_nav_menu()`.
// Find the location in the list of locations, returning early if the
// location can't be found.
$locations = get_nav_menu_locations();
if ( ! isset( $locations[ $location ] ) ) {
return;
}
// Get the menu from the location, returning early if there is no
// menu or there was an error.
$menu = wp_get_nav_menu_object( $locations[ $location ] );
if ( ! $menu || is_wp_error( $menu ) ) {
return;
}
$menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'update_post_term_cache' => false ) );
_wp_menu_item_classes_by_context( $menu_items );
return $menu_items;
}
/**
* Sorts a standard array of menu items into a nested structure keyed by the
* id of the parent menu.
*
* @since 5.9.0
* @access private
*
* @param array $menu_items Menu items to sort.
* @return array An array keyed by the id of the parent menu where each element
* is an array of menu items that belong to that parent.
*/
function _wp_sort_menu_items_by_parent_id( $menu_items ) {
$sorted_menu_items = array();
foreach ( $menu_items as $menu_item ) {
$sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
}
unset( $menu_items, $menu_item );
$menu_items_by_parent_id = array();
foreach ( $sorted_menu_items as $menu_item ) {
$menu_items_by_parent_id[ $menu_item->menu_item_parent ][] = $menu_item;
}
return $menu_items_by_parent_id;
}
/**
* Turns menu item data into a nested array of parsed blocks
*
* @since 5.9.0
* @access private
*
* @param array $menu_items An array of menu items that represent
* an individual level of a menu.
* @param array $menu_items_by_parent_id An array keyed by the id of the
* parent menu where each element is an
* array of menu items that belong to
* that parent.
* @return array An array of parsed block data.
*/
function _wp_parse_blocks_from_menu_items( $menu_items, $menu_items_by_parent_id ) {
if ( empty( $menu_items ) ) {
return array();
}
$blocks = array();
foreach ( $menu_items as $menu_item ) {
$class_name = ! empty( $menu_item->classes ) ? implode( ' ', (array) $menu_item->classes ) : null;
$id = ( null !== $menu_item->object_id && 'custom' !== $menu_item->object ) ? $menu_item->object_id : null;
$opens_in_new_tab = null !== $menu_item->target && '_blank' === $menu_item->target;
$rel = ( null !== $menu_item->xfn && '' !== $menu_item->xfn ) ? $menu_item->xfn : null;
$kind = null !== $menu_item->type ? str_replace( '_', '-', $menu_item->type ) : 'custom';
$block = array(
'blockName' => isset( $menu_items_by_parent_id[ $menu_item->ID ] ) ? 'core/navigation-submenu' : 'core/navigation-link',
'attrs' => array(
'className' => $class_name,
'description' => $menu_item->description,
'id' => $id,
'kind' => $kind,
'label' => $menu_item->title,
'opensInNewTab' => $opens_in_new_tab,
'rel' => $rel,
'title' => $menu_item->attr_title,
'type' => $menu_item->object,
'url' => $menu_item->url,
),
);
if ( isset( $menu_items_by_parent_id[ $menu_item->ID ] ) ) {
$block['innerBlocks'] = _wp_parse_blocks_from_menu_items(
$menu_items_by_parent_id[ $menu_item->ID ],
$menu_items_by_parent_id
);
} else {
$block['innerBlocks'] = array();
}
$block['innerContent'] = array_map( 'serialize_block', $block['innerBlocks'] );
$blocks[] = $block;
}
return $blocks;
}