Skip to content

Commit

Permalink
Navigation screen: Add opt-in Navigation block rendering
Browse files Browse the repository at this point in the history
Allows themes to opt-in to having `wp_nav_menu()` render a complete
Navigation block in place of the menu. This allows arbitrarily complex
Navigation block structures can be used in an existing theme.

When a theme declares `add_theme_support( 'block-nav-menus' )`, users
are able to add non-link blocks to a Navigation using the Navigation
screen.

This block tree is stored as menu items and re-assembled into a
Navigation block for display using `render_block()` in the frontend.

Non-link blocks are stored using a menu item with type `'block'` which
renders properly in `nav-menus.php`. This allows users to switch to a
theme that does not support block menus and still see their data in WP
Admin.
  • Loading branch information
noisysocks committed Aug 13, 2020
1 parent e3d4e49 commit 3a47a1a
Show file tree
Hide file tree
Showing 8 changed files with 390 additions and 121 deletions.
10 changes: 5 additions & 5 deletions lib/class-wp-rest-menu-items-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -440,10 +440,10 @@ protected function prepare_item_for_database( $request ) {
}
}

// If menu item is type html, then content is required.
if ( 'html' === $prepared_nav_item['menu-item-type'] ) {
// If menu item is type block, then content is required.
if ( 'block' === $prepared_nav_item['menu-item-type'] ) {
if ( empty( $prepared_nav_item['menu-item-content'] ) ) {
return new WP_Error( 'rest_content_required', __( 'Content required if menu item of type html.', 'gutenberg' ), array( 'status' => 400 ) );
return new WP_Error( 'rest_content_required', __( 'Content required if menu item of type block.', 'gutenberg' ), array( 'status' => 400 ) );
}
}

Expand Down Expand Up @@ -819,7 +819,7 @@ public function get_item_schema() {
$schema['properties']['type'] = array(
'description' => __( 'The family of objects originally represented, such as "post_type" or "taxonomy".', 'gutenberg' ),
'type' => 'string',
'enum' => array( 'taxonomy', 'post_type', 'post_type_archive', 'custom', 'html' ),
'enum' => array( 'taxonomy', 'post_type', 'post_type_archive', 'custom', 'block' ),
'context' => array( 'view', 'edit', 'embed' ),
'default' => 'custom',
);
Expand Down Expand Up @@ -894,7 +894,7 @@ public function get_item_schema() {
);

$schema['properties']['content'] = array(
'description' => __( 'HTML content to display for this menu item. May contain blocks.', 'gutenberg' ),
'description' => __( 'HTML content to display for this block menu item.', 'gutenberg' ),
'context' => array( 'view', 'edit', 'embed' ),
'type' => 'object',
'arg_options' => array(
Expand Down
108 changes: 0 additions & 108 deletions lib/compat.php
Original file line number Diff line number Diff line change
Expand Up @@ -532,114 +532,6 @@ function gutenberg_render_block_with_assigned_block_context( $pre_render, $parse
}
add_filter( 'pre_render_block', 'gutenberg_render_block_with_assigned_block_context', 9, 2 );

/**
* Shim that hooks into `wp_update_nav_menu_item` and makes it so that nav menu
* items support a 'content' field. This field contains HTML and is used by nav
* menu items with `type` set to `'html'`.
*
* Specifically, this shim makes it so that:
*
* 1) The `wp_update_nav_menu_item()` function supports setting
* `'menu-item-content'` on a menu item. When merged to Core, this functionality
* should exist in `wp_update_nav_menu_item()`.
*
* 2) The `customize_save` ajax action supports setting `'content'` on a nav
* menu item. When merged to Core, this functionality should exist in
* `WP_Customize_Manager::save()`.
*
* This shim can be removed when the Gutenberg plugin requires a WordPress
* version that has the ticket below.
*
* @see https://core.trac.wordpress.org/ticket/50544
*
* @param int $menu_id ID of the updated menu.
* @param int $menu_item_db_id ID of the new menu item.
* @param array $args An array of arguments used to update/add the menu item.
*/
function gutenberg_update_nav_menu_item_content( $menu_id, $menu_item_db_id, $args ) {
global $wp_customize;

// Support setting content in customize_save admin-ajax.php requests by
// grabbing the unsanitized $_POST values.
if ( isset( $wp_customize ) ) {
$values = $wp_customize->unsanitized_post_values();
if ( isset( $values[ "nav_menu_item[$menu_item_db_id]" ]['content'] ) ) {
if ( is_string( $values[ "nav_menu_item[$menu_item_db_id]" ]['content'] ) ) {
$args['menu-item-content'] = $values[ "nav_menu_item[$menu_item_db_id]" ]['content'];
} elseif ( isset( $values[ "nav_menu_item[$menu_item_db_id]" ]['content']['raw'] ) ) {
$args['menu-item-content'] = $values[ "nav_menu_item[$menu_item_db_id]" ]['content']['raw'];
}
}
}

$defaults = array(
'menu-item-content' => '',
);

$args = wp_parse_args( $args, $defaults );

update_post_meta( $menu_item_db_id, '_menu_item_content', wp_slash( $args['menu-item-content'] ) );
}
add_action( 'wp_update_nav_menu_item', 'gutenberg_update_nav_menu_item_content', 10, 3 );

/**
* Shim that hooks into `wp_setup_nav_menu_items` and makes it so that nav menu
* items have a 'content' field. This field contains HTML and is used by nav
* menu items with `type` set to `'html'`.
*
* Specifically, this shim makes it so that the `wp_setup_nav_menu_item()`
* function sets `content` on the returned menu item. When merged to Core, this
* functionality should exist in `wp_setup_nav_menu_item()`.
*
* This shim can be removed when the Gutenberg plugin requires a WordPress
* version that has the ticket below.
*
* @see https://core.trac.wordpress.org/ticket/50544
*
* @param object $menu_item The menu item object.
*/
function gutenberg_setup_html_nav_menu_item( $menu_item ) {
if ( 'html' === $menu_item->type ) {
$menu_item->type_label = __( 'HTML', 'gutenberg' );
$menu_item->content = ! isset( $menu_item->content ) ? get_post_meta( $menu_item->db_id, '_menu_item_content', true ) : $menu_item->content;
}

return $menu_item;
}
add_filter( 'wp_setup_nav_menu_item', 'gutenberg_setup_html_nav_menu_item' );

/**
* Shim that hooks into `walker_nav_menu_start_el` and makes it so that the
* default walker which renders a menu will correctly render the HTML associated
* with any navigation menu item that has `type` set to `'html`'.
*
* Specifically, this shim makes it so that `Walker_Nav_Menu::start_el()`
* renders the `content` of a nav menu item when its `type` is `'html'`. When
* merged to Core, this functionality should exist in
* `Walker_Nav_Menu::start_el()`.
*
* This shim can be removed when the Gutenberg plugin requires a WordPress
* version that has the ticket below.
*
* @see https://core.trac.wordpress.org/ticket/50544
*
* @param string $item_output The menu item's starting HTML output.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
*/
function gutenberg_output_html_nav_menu_item( $item_output, $item, $depth, $args ) {
if ( 'html' === $item->type ) {
$item_output = $args->before;
/** This filter is documented in wp-includes/post-template.php */
$item_output .= apply_filters( 'the_content', $item->content );
$item_output .= $args->after;
}

return $item_output;
}
add_filter( 'walker_nav_menu_start_el', 'gutenberg_output_html_nav_menu_item', 10, 4 );

/**
* Amends the paths to preload when initializing edit post.
*
Expand Down
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require dirname( __FILE__ ) . '/block-directory.php';
require dirname( __FILE__ ) . '/demo.php';
require dirname( __FILE__ ) . '/widgets.php';
require dirname( __FILE__ ) . '/navigation.php';
require dirname( __FILE__ ) . '/navigation-page.php';
require dirname( __FILE__ ) . '/experiments-page.php';
require dirname( __FILE__ ) . '/customizer.php';
Expand Down
1 change: 1 addition & 0 deletions lib/navigation-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ function gutenberg_navigation_init( $hook ) {
'imageSizes' => $available_image_sizes,
'isRTL' => is_rtl(),
'maxUploadFileSize' => $max_upload_size,
'blockNavMenus' => get_theme_support( 'block-nav-menus' ),
);

list( $color_palette, ) = (array) get_theme_support( 'editor-color-palette' );
Expand Down
Loading

0 comments on commit 3a47a1a

Please sign in to comment.