diff --git a/lib/full-site-editing/edit-site-page.php b/lib/full-site-editing/edit-site-page.php index 8a02d2755c1e2..1bd0a26ff63b5 100644 --- a/lib/full-site-editing/edit-site-page.php +++ b/lib/full-site-editing/edit-site-page.php @@ -179,7 +179,6 @@ static function( $classes ) { 'edit-site', array( 'preload_paths' => array_merge( - gutenberg_get_navigation_areas_paths_to_preload(), array( array( '/wp/v2/media', 'OPTIONS' ), '/', diff --git a/lib/load.php b/lib/load.php index 1e1a0168cfd79..c6ae7f160d702 100644 --- a/lib/load.php +++ b/lib/load.php @@ -9,6 +9,8 @@ die( 'Silence is golden.' ); } +define( 'IS_GUTENBERG_PLUGIN', true ); + require_once __DIR__ . '/init.php'; require_once __DIR__ . '/upgrade.php'; diff --git a/lib/navigation.php b/lib/navigation.php index 816e2582b8840..26b6ced8de23e 100644 --- a/lib/navigation.php +++ b/lib/navigation.php @@ -181,112 +181,6 @@ function gutenberg_get_navigation_areas() { return $gutenberg_navigation_areas; } -/** - * Returns the API paths to preload to make the navigation area block load fast. - * - * @return array A list of paths. - */ -function gutenberg_get_navigation_areas_paths_to_preload() { - $areas = gutenberg_get_navigation_areas_menus(); - $active_areas = array_intersect_key( $areas, gutenberg_get_navigation_areas() ); - $paths = array( - '/wp/v2/block-navigation-areas?context=edit', - ); - foreach ( $active_areas as $post_id ) { - if ( 0 !== $post_id ) { - $paths[] = "/wp/v2/navigation/$post_id?context=edit"; - } - } - return $paths; -} - -/** - * Migrates classic menus to a block-based navigation post on theme switch. - * Assigns the created navigation post to the corresponding navigation area. - * - * @param string $new_name Name of the new theme. - * @param WP_Theme $new_theme New theme. - * @param WP_Theme $old_theme Old theme. - * @see switch_theme WordPress action. - */ -function gutenberg_migrate_menu_to_navigation_post( $new_name, $new_theme, $old_theme ) { - // Do nothing when switching to a theme that does not support site editor. - if ( ! gutenberg_experimental_is_site_editor_available() ) { - 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 = function() use ( $old_theme ) { - return $old_theme->get_stylesheet(); - }; - add_filter( 'option_stylesheet', $get_old_theme_stylesheet ); - - $locations = get_nav_menu_locations(); - $area_mapping = gutenberg_get_navigation_areas_menus(); - - 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 = gutenberg_get_menu_items_at_location( $location_name ); - if ( empty( $menu_items ) ) { - continue; - } - - $post_name = 'classic_menu_' . $menu_id; - $post_status = 'publish'; - - // 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' => $post_status, - 'post_type' => 'wp_navigation', - 'posts_per_page' => 1, - ) - ); - - if ( count( $matching_posts ) ) { - $navigation_post_id = $matching_posts[0]->ID; - } else { - $menu_items_by_parent_id = gutenberg_sort_menu_items_by_parent_id( $menu_items ); - $parsed_blocks = gutenberg_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', 'gutenberg' ), - $menu->name - ), - 'post_name' => $post_name, - 'post_content' => serialize_blocks( $parsed_blocks ), - 'post_status' => $post_status, - ); - $navigation_post_id = wp_insert_post( $post_data, true ); - // If wp_insert_post fails *at any time*, then bale 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 ); -} - -add_action( 'switch_theme', 'gutenberg_migrate_menu_to_navigation_post', 99, 3 ); - /** * Retrieves navigation areas. * @@ -304,119 +198,6 @@ function gutenberg_get_navigation_areas_menus() { return $areas; } -// The functions below are copied over from packages/block-library/src/navigation/index.php -// Let's figure out a better way of managing these global PHP dependencies. - -/** - * Returns the menu items for a WordPress menu location. - * - * @param string $location The menu location. - * @return array Menu items for the location. - */ -function gutenberg_get_menu_items_at_location( $location ) { - if ( empty( $location ) ) { - return; - } - - // Build menu data. The following approximates the code in - // `wp_nav_menu()` and `gutenberg_output_block_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. - * - * @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 gutenberg_sort_menu_items_by_parent_id( $menu_items ) { - $sorted_menu_items = array(); - foreach ( (array) $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 - * - * @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 gutenberg_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, - ), - ); - - $block['innerBlocks'] = isset( $menu_items_by_parent_id[ $menu_item->ID ] ) - ? gutenberg_parse_blocks_from_menu_items( $menu_items_by_parent_id[ $menu_item->ID ], $menu_items_by_parent_id ) - : array(); - $block['innerContent'] = array_map( 'serialize_block', $block['innerBlocks'] ); - - $blocks[] = $block; - } - - return $blocks; -} - /** * Shim that hides ability to edit visibility and status for wp_navigation type posts. * When merged to Core, the CSS below should be moved to wp-admin/css/edit.css. diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index a3ec232e7e60b..11205500c16f0 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -5,6 +5,120 @@ * @package WordPress */ +// These functions are used for the __unstableLocation feature and only active +// when the gutenberg plugin is active. +if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { + /** + * Returns the menu items for a WordPress menu location. + * + * @param string $location The menu location. + * @return array Menu items for the location. + */ + function block_core_navigation_get_menu_items_at_location( $location ) { + if ( empty( $location ) ) { + return; + } + + // Build menu data. The following approximates the code in + // `wp_nav_menu()` and `gutenberg_output_block_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. + * + * @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 block_core_navigation_sort_menu_items_by_parent_id( $menu_items ) { + $sorted_menu_items = array(); + foreach ( (array) $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 + * + * @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 block_core_navigation_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, + ), + ); + + $block['innerBlocks'] = isset( $menu_items_by_parent_id[ $menu_item->ID ] ) + ? block_core_navigation_parse_blocks_from_menu_items( $menu_items_by_parent_id[ $menu_item->ID ], $menu_items_by_parent_id ) + : array(); + $block['innerContent'] = array_map( 'serialize_block', $block['innerBlocks'] ); + + $blocks[] = $block; + } + + return $blocks; + } +} + /** * Build an array with CSS classes and inline styles defining the colors * which will be applied to the navigation markup in the front-end. @@ -278,29 +392,33 @@ function render_block_core_navigation( $attributes, $content, $block ) { $inner_blocks = $block->inner_blocks; - // If `__unstableLocation` is defined and: + // 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 `navigationMenuId`). + // - 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( 'navigationMenuId', $attributes ) && - ! empty( gutenberg_get_menu_items_at_location( $attributes['__unstableLocation'] ) ) + ! array_key_exists( 'ref', $attributes ) && + ! empty( block_core_navigation_get_menu_items_at_location( $attributes['__unstableLocation'] ) ) ) { - $menu_items = gutenberg_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 = gutenberg_sort_menu_items_by_parent_id( $menu_items ); - $parsed_blocks = gutenberg_parse_blocks_from_menu_items( $menu_items_by_parent_id[0], $menu_items_by_parent_id ); + $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 ); } - // Ensure that blocks saved with the legacy ref attribute name (navigationMenuId) continue to render. - if ( array_key_exists( 'navigationMenuId', $attributes ) ) { - $attributes['ref'] = $attributes['navigationMenuId']; - } // Load inner blocks from the navigation post. if ( array_key_exists( 'ref', $attributes ) ) { $navigation_post = get_post( $attributes['ref'] );