Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove parent and position validation from menu item REST API endpoint #34672

Merged
merged 10 commits into from
Sep 14, 2021
63 changes: 12 additions & 51 deletions lib/class-wp-rest-menu-items-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ protected function prepare_item_for_database( $request ) {
'menu-item-object-id' => 0,
'menu-item-object' => '',
'menu-item-parent-id' => 0,
'menu-item-position' => 0,
'menu-item-position' => 1,
'menu-item-type' => 'custom',
'menu-item-title' => '',
'menu-item-url' => '',
Expand Down Expand Up @@ -524,51 +524,6 @@ protected function prepare_item_for_database( $request ) {
}
}

// If menu id is set, valid the value of menu item position and parent id.
if ( ! empty( $prepared_nav_item['menu-id'] ) ) {
TimothyBJacobs marked this conversation as resolved.
Show resolved Hide resolved
// Check if nav menu is valid.
if ( ! is_nav_menu( $prepared_nav_item['menu-id'] ) ) {
return new WP_Error( 'invalid_menu_id', __( 'Invalid menu ID.', 'gutenberg' ), array( 'status' => 400 ) );
}

// If menu item position is set to 0, insert as the last item in the existing menu.
$menu_items = wp_get_nav_menu_items( $prepared_nav_item['menu-id'], array( 'post_status' => 'publish,draft' ) );
if ( 0 === (int) $prepared_nav_item['menu-item-position'] ) {
if ( $menu_items ) {
$last_item = $menu_items[ count( $menu_items ) - 1 ];
if ( $last_item && isset( $last_item->menu_order ) ) {
$prepared_nav_item['menu-item-position'] = $last_item->menu_order + 1;
} else {
$prepared_nav_item['menu-item-position'] = count( $menu_items ) - 1;
}
array_push( $menu_items, $last_item );
} else {
$prepared_nav_item['menu-item-position'] = 1;
}
}

// Check if existing menu position is already in use by another menu item.
$menu_item_ids = array();
foreach ( $menu_items as $menu_item ) {
$menu_item_ids[] = $menu_item->ID;
if ( $menu_item->ID !== (int) $menu_item_db_id ) {
if ( (int) $prepared_nav_item['menu-item-position'] === (int) $menu_item->menu_order ) {
return new WP_Error( 'invalid_menu_order', __( 'Invalid menu position.', 'gutenberg' ), array( 'status' => 400 ) );
}
}
}

// Check if valid parent id is valid nav menu item in menu.
if ( $prepared_nav_item['menu-item-parent-id'] ) {
if ( ! is_nav_menu_item( $prepared_nav_item['menu-item-parent-id'] ) ) {
return new WP_Error( 'invalid_menu_item_parent', __( 'Invalid menu item parent.', 'gutenberg' ), array( 'status' => 400 ) );
}
if ( ! $menu_item_ids || ! in_array( $prepared_nav_item['menu-item-parent-id'], $menu_item_ids, true ) ) {
return new WP_Error( 'invalid_item_parent', __( 'Invalid menu item parent.', 'gutenberg' ), array( 'status' => 400 ) );
}
}
}

foreach ( array( 'menu-item-object-id', 'menu-item-parent-id' ) as $key ) {
// Note we need to allow negative-integer IDs for previewed objects not inserted yet.
$prepared_nav_item[ $key ] = (int) $prepared_nav_item[ $key ];
Expand Down Expand Up @@ -747,8 +702,13 @@ public function prepare_item_for_response( $post, $request ) {
$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;

if ( in_array( $base, $fields, true ) ) {
$terms = get_the_terms( $post, $taxonomy->name );
$data[ $base ] = $terms ? array_values( wp_list_pluck( $terms, 'term_id' ) ) : array();
$terms = get_the_terms( $post, $taxonomy->name );
$term_ids = $terms ? array_values( wp_list_pluck( $terms, 'term_id' ) ) : array();
if ( 'nav_menu' === $taxonomy->name ) {
$data[ $base ] = $term_ids ? array_shift( $term_ids ) : 0;
} else {
$data[ $base ] = $term_ids;
}
spacedmonkey marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -959,10 +919,11 @@ public function get_item_schema() {
'description' => __( 'The DB ID of the nav_menu_item that is this item\'s menu parent, if any, otherwise 0.', 'gutenberg' ),
'context' => array( 'view', 'edit', 'embed' ),
'type' => 'integer',
'minimum' => 0,
'default' => 0,
'minimum' => 1,
TimothyBJacobs marked this conversation as resolved.
Show resolved Hide resolved
'default' => 1,
);
$schema['properties']['object'] = array(

$schema['properties']['object'] = array(
'description' => __( 'The type of object originally represented, such as "category," "post", or "attachment."', 'gutenberg' ),
'context' => array( 'view', 'edit', 'embed' ),
'type' => 'string',
Expand Down
71 changes: 23 additions & 48 deletions phpunit/class-rest-nav-menu-items-controller-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -277,39 +277,47 @@ public function test_create_item_invalid_term() {
public function test_create_item_change_position() {
wp_set_current_user( self::$admin_id );
$new_menu_id = wp_create_nav_menu( rand_str() );
$expected = array();
$actual = array();
for ( $i = 1; $i < 5; $i ++ ) {
$request = new WP_REST_Request( 'POST', '/__experimental/menu-items' );
$request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
$params = $this->set_menu_item_data(
array(
'menus' => $new_menu_id,
'menu_order' => $i,
'menus' => $new_menu_id,
)
);
$request->set_body_params( $params );
$response = rest_get_server()->dispatch( $request );
$this->check_create_menu_item_response( $response );
$data = $response->get_data();
$this->assertEquals( $data['menu_order'], $i );

$expected[] = $i;
$actual[] = $data['menu_order'];
}
$this->assertEquals( $actual, $expected );
}

/**
*
*/
public function test_create_item_invalid_position() {
public function test_menu_order_must_be_set() {
wp_set_current_user( self::$admin_id );
$new_menu_id = wp_create_nav_menu( rand_str() );
$request = new WP_REST_Request( 'POST', '/__experimental/menu-items' );

$request = new WP_REST_Request( 'POST', '/__experimental/menu-items' );
$request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
$params = $this->set_menu_item_data(
array(
'menu_order' => 1,
'menu_order' => 0,
'menus' => $new_menu_id,
)
);
$request->set_body_params( $params );
$response = rest_get_server()->dispatch( $request );
$this->check_create_menu_item_response( $response );
$this->assertErrorResponse( 'invalid_menu_order', $response, 400 );

$request = new WP_REST_Request( 'POST', '/__experimental/menu-items' );
$request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
$params = $this->set_menu_item_data(
Expand All @@ -320,8 +328,7 @@ public function test_create_item_invalid_position() {
);
$request->set_body_params( $params );
$response = rest_get_server()->dispatch( $request );

$this->assertErrorResponse( 'invalid_menu_order', $response, 400 );
$this->assertEquals( 201, $response->get_status() );
}

/**
Expand Down Expand Up @@ -380,43 +387,6 @@ public function test_create_item_invalid_parent() {
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
}

/**
*
*/
public function test_create_item_invalid_parent_menu_item() {
wp_set_current_user( self::$admin_id );
$new_menu_id = wp_create_nav_menu( rand_str() );
$request = new WP_REST_Request( 'POST', '/__experimental/menu-items' );
$request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
$params = $this->set_menu_item_data(
array(
'menus' => $new_menu_id,
'parent' => $this->menu_item_id,
)
);
$request->set_body_params( $params );
$response = rest_get_server()->dispatch( $request );
$this->assertErrorResponse( 'invalid_item_parent', $response, 400 );
}

/**
*
*/
public function test_create_item_invalid_parent_post() {
wp_set_current_user( self::$admin_id );
$post_id = self::factory()->post->create();
$request = new WP_REST_Request( 'POST', '/__experimental/menu-items' );
$request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
$params = $this->set_menu_item_data(
array(
'parent' => $post_id,
)
);
$request->set_body_params( $params );
$response = rest_get_server()->dispatch( $request );
$this->assertErrorResponse( 'invalid_menu_item_parent', $response, 400 );
}

/**
*
*/
Expand Down Expand Up @@ -830,8 +800,13 @@ protected function check_menu_item_data( $post, $data, $context, $links ) {
$this->assertTrue( isset( $data[ $taxonomy->rest_base ] ) );
$terms = wp_get_object_terms( $post->ID, $taxonomy->name, array( 'fields' => 'ids' ) );
sort( $terms );
sort( $data[ $taxonomy->rest_base ] );
$this->assertEquals( $terms, $data[ $taxonomy->rest_base ] );
if ( 'nav_menu' === $taxonomy->name ) {
$term_id = $terms ? array_shift( $terms ) : 0;
$this->assertEquals( $term_id, $data[ $taxonomy->rest_base ] );
} else {
sort( $data[ $taxonomy->rest_base ] );
$this->assertEquals( $terms, $data[ $taxonomy->rest_base ] );
}
}

// test links.
Expand Down Expand Up @@ -908,7 +883,7 @@ protected function set_menu_item_data( $args = array() ) {
$defaults = array(
'object_id' => 0,
'parent' => 0,
'menu_order' => 0,
'menu_order' => 1,
'menus' => $this->menu_id,
'type' => 'custom',
'title' => 'Custom Link Title',
Expand Down