Skip to content

Commit

Permalink
REST API: Introduce Menu management endpoints.
Browse files Browse the repository at this point in the history
This commit introduces the `/wp/v2/menus`, `/wp/v2/menu-items` and `/wp/v2/menu-locations` REST API endpoints. These endpoints are fully available to users with the `edit_theme_options` capability, but can be read by any user who can edit a REST API available post type.

The `nav_menu` taxonomy and `nav_menu_item` post type now map their capabilities to the `edit_theme_options` primitive capability. This allows developers to provide more fine-grained access control. However, if a developer is currently dynamically removing the `edit_theme_options` capability using `map_meta_cap`, they should use the `user_has_cap` filter instead.

The `wp_update_nav_menu_item()` function has been adjusted to return an error if saving the menu item post or assigning the menu item to a menu generate an error.

Lastly, a new menu item type is introduced, `block`, that can be used to store a Block as a menu item.

Props andraganescu, antonvlasenko, dingo_d, dlh, isabel_brison, kadamwhite, Mamaduka, NateWr, noisysocks, peterwilsoncc, ryelle, schlessera, soean, Spacedmonkey, talldanwp, TimothyBlynJacobs, tobifjellner, westonruter, wpscholar, zieladam.
Fixes #40878.

Built from https://develop.svn.wordpress.org/trunk@52079


git-svn-id: http://core.svn.wordpress.org/trunk@51671 1a063a9b-81f0-0310-95a4-ce76da25c4cd
  • Loading branch information
TimothyBJacobs committed Nov 9, 2021
1 parent c86f41d commit e2d4385
Show file tree
Hide file tree
Showing 10 changed files with 2,017 additions and 22 deletions.
24 changes: 19 additions & 5 deletions wp-includes/nav-menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,11 @@ function wp_update_nav_menu_object( $menu_id = 0, $menu_data = array() ) {
/**
* Save the properties of a menu item or create a new one.
*
* The menu-item-title, menu-item-description, and menu-item-attr-title are expected
* to be pre-slashed since they are passed directly into `wp_insert_post()`.
* The menu-item-title, menu-item-description, menu-item-attr-title, and menu-item-content are expected
* to be pre-slashed since they are passed directly to APIs that expect slashed data.
*
* @since 3.0.0
* @since 5.9.0 Added the menu-item-content parameter.
*
* @param int $menu_id The ID of the menu. Required. If "0", makes the menu item a draft orphan.
* @param int $menu_item_db_id The ID of the menu item. If "0", creates a new menu item.
Expand Down Expand Up @@ -448,6 +449,7 @@ function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item
'menu-item-attr-title' => '',
'menu-item-target' => '',
'menu-item-classes' => '',
'menu-item-content' => '',
'menu-item-xfn' => '',
'menu-item-status' => '',
'menu-item-post-date' => '',
Expand Down Expand Up @@ -526,7 +528,7 @@ function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item
if ( ! $update ) {
$post['ID'] = 0;
$post['post_status'] = 'publish' === $args['menu-item-status'] ? 'publish' : 'draft';
$menu_item_db_id = wp_insert_post( $post );
$menu_item_db_id = wp_insert_post( $post, true );
if ( ! $menu_item_db_id || is_wp_error( $menu_item_db_id ) ) {
return $menu_item_db_id;
}
Expand All @@ -548,7 +550,10 @@ function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item
// Associate the menu item with the menu term.
// Only set the menu term if it isn't set to avoid unnecessary wp_get_object_terms().
if ( $menu_id && ( ! $update || ! is_object_in_term( $menu_item_db_id, 'nav_menu', (int) $menu->term_id ) ) ) {
wp_set_object_terms( $menu_item_db_id, array( $menu->term_id ), 'nav_menu' );
$update_terms = wp_set_object_terms( $menu_item_db_id, array( $menu->term_id ), 'nav_menu' );
if ( is_wp_error( $update_terms ) ) {
return $update_terms;
}
}

if ( 'custom' === $args['menu-item-type'] ) {
Expand All @@ -569,6 +574,7 @@ function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item
update_post_meta( $menu_item_db_id, '_menu_item_classes', $args['menu-item-classes'] );
update_post_meta( $menu_item_db_id, '_menu_item_xfn', $args['menu-item-xfn'] );
update_post_meta( $menu_item_db_id, '_menu_item_url', esc_url_raw( $args['menu-item-url'] ) );
update_post_meta( $menu_item_db_id, '_menu_item_content', $args['menu-item-content'] );

if ( 0 == $menu_id ) {
update_post_meta( $menu_item_db_id, '_menu_item_orphaned', (string) time() );
Expand All @@ -580,7 +586,11 @@ function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item
if ( $update ) {
$post['ID'] = $menu_item_db_id;
$post['post_status'] = ( 'draft' === $args['menu-item-status'] ) ? 'draft' : 'publish';
wp_update_post( $post );

$update_post = wp_update_post( $post, true );
if ( is_wp_error( $update_post ) ) {
return $update_post;
}
}

/**
Expand Down Expand Up @@ -903,6 +913,10 @@ function wp_setup_nav_menu_item( $menu_item ) {

$menu_item->title = ( '' === $menu_item->post_title ) ? $original_title : $menu_item->post_title;

} elseif ( 'block' === $menu_item->type ) {
$menu_item->type_label = __( 'Block' );
$menu_item->title = $menu_item->post_title;
$menu_item->menu_item_content = ! isset( $menu_item->menu_item_content ) ? get_post_meta( $menu_item->ID, '_menu_item_content', true ) : $menu_item->menu_item_content;
} else {
$menu_item->type_label = __( 'Custom Link' );
$menu_item->title = $menu_item->post_title;
Expand Down
37 changes: 30 additions & 7 deletions wp-includes/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,39 @@ function create_initial_post_types() {
register_post_type(
'nav_menu_item',
array(
'labels' => array(
'labels' => array(
'name' => __( 'Navigation Menu Items' ),
'singular_name' => __( 'Navigation Menu Item' ),
),
'public' => false,
'_builtin' => true, /* internal use only. don't use this when registering your own post type. */
'hierarchical' => false,
'rewrite' => false,
'delete_with_user' => false,
'query_var' => false,
'public' => false,
'_builtin' => true, /* internal use only. don't use this when registering your own post type. */
'hierarchical' => false,
'rewrite' => false,
'delete_with_user' => false,
'query_var' => false,
'map_meta_cap' => true,
'capability_type' => array( 'edit_theme_options', 'edit_theme_options' ),
'capabilities' => array(
// Meta Capabilities.
'edit_post' => 'edit_post',
'read_post' => 'read_post',
'delete_post' => 'delete_post',
// Primitive Capabilities.
'edit_posts' => 'edit_theme_options',
'edit_others_posts' => 'edit_theme_options',
'delete_posts' => 'edit_theme_options',
'publish_posts' => 'edit_theme_options',
'read_private_posts' => 'edit_theme_options',
'read' => 'read',
'delete_private_posts' => 'edit_theme_options',
'delete_published_posts' => 'edit_theme_options',
'delete_others_posts' => 'edit_theme_options',
'edit_private_posts' => 'edit_theme_options',
'edit_published_posts' => 'edit_theme_options',
),
'show_in_rest' => true,
'rest_base' => 'menu-items',
'rest_controller_class' => 'WP_REST_Menu_Items_Controller',
)
);

Expand Down
4 changes: 4 additions & 0 deletions wp-includes/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ function create_initial_rest_routes() {
// URL Details.
$controller = new WP_REST_URL_Details_Controller();
$controller->register_routes();

// Menu Locations.
$controller = new WP_REST_Menu_Locations_Controller();
$controller->register_routes();
}

/**
Expand Down
Loading

0 comments on commit e2d4385

Please sign in to comment.