Skip to content
This repository has been archived by the owner on Jan 13, 2022. It is now read-only.

Commit

Permalink
Merge pull request #34 from WP-API/set-menu-locations
Browse files Browse the repository at this point in the history
Add menu location assignment
  • Loading branch information
TimothyBJacobs authored Feb 18, 2020
2 parents 3b286e9 + 439be5d commit a04c248
Show file tree
Hide file tree
Showing 2 changed files with 279 additions and 14 deletions.
122 changes: 121 additions & 1 deletion lib/class-wp-rest-menus-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* REST API: WP_REST_Menus_Controller class
*
* @package WordPress
* @package WordPress
* @subpackage REST_API
*/

Expand Down Expand Up @@ -72,6 +72,71 @@ protected function get_term( $id ) {
return $nav_term;
}

/**
* Checks if a request has access to create a term.
* Also check if request can assign menu locations.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return bool|WP_Error True if the request has access to create items, false or WP_Error object otherwise.
*/
public function create_item_permissions_check( $request ) {
$check = $this->check_assign_locations_permission( $request );
if ( is_wp_error( $check ) ) {
return $check;
}

return parent::create_item_permissions_check( $request );
}

/**
* Checks if a request has access to update the specified term.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return bool|WP_Error True if the request has access to update the item, false or WP_Error object otherwise.
*/
public function update_item_permissions_check( $request ) {
$check = $this->check_assign_locations_permission( $request );
if ( is_wp_error( $check ) ) {
return $check;
}

return parent::update_item_permissions_check( $request );
}

/**
* Checks whether current user can assign all locations sent with the current request.
*
* @param WP_REST_Request $request The request object with post and locations data.
*
* @return bool Whether the current user can assign the provided terms.
*/
protected function check_assign_locations_permission( $request ) {
if ( ! isset( $request['locations'] ) ) {
return true;
}

if ( ! current_user_can( 'edit_theme_options' ) ) {
return new WP_Error( 'rest_cannot_assign_location', __( 'Sorry, you are not allowed to assign the provided locations.' ), array( 'status' => rest_authorization_required_code() ) );
}

foreach ( $request['locations'] as $location ) {
if ( ! array_key_exists( $location, get_registered_nav_menus() ) ) {
return new WP_Error(
'rest_menu_location_invalid',
__( 'Invalid menu location.' ),
array(
'status' => 400,
'location' => $location,
)
);
}
}

return true;
}

/**
* Prepares a single term output for response.
*
Expand Down Expand Up @@ -196,6 +261,12 @@ public function create_item( $request ) {
}
}

$locations_update = $this->handle_locations( $term->term_id, $request );

if ( is_wp_error( $locations_update ) ) {
return $locations_update;
}

$fields_update = $this->update_additional_fields_for_object( $term, $request );

if ( is_wp_error( $fields_update ) ) {
Expand Down Expand Up @@ -276,6 +347,12 @@ public function update_item( $request ) {
}
}

$locations_update = $this->handle_locations( $term->term_id, $request );

if ( is_wp_error( $locations_update ) ) {
return $locations_update;
}

$fields_update = $this->update_additional_fields_for_object( $term, $request );

if ( is_wp_error( $fields_update ) ) {
Expand Down Expand Up @@ -345,6 +422,40 @@ public function delete_item( $request ) {
return $response;
}

/**
* Updates the menu's locations from a REST request.
*
* @param int $menu_id The menu id to update the location form.
* @param WP_REST_Request $request The request object with menu and locations data.
*
* @return true|WP_Error WP_Error on an error assigning any of the locations, otherwise null.
*/
protected function handle_locations( $menu_id, $request ) {
if ( ! isset( $request['locations'] ) ) {
return true;
}

$menu_locations = get_registered_nav_menus();
$menu_locations = array_keys( $menu_locations );
$new_locations = array();
foreach ( $request['locations'] as $location ) {
if ( ! in_array( $location, $menu_locations, true ) ) {
return new WP_Error( 'invalid_menu_location', __( 'Menu location does not exist.' ), array( 'status' => 400 ) );
}
$new_locations[ $location ] = $menu_id;
}
$assigned_menu = get_nav_menu_locations();
foreach ( $assigned_menu as $location => $term_id ) {
if ( $term_id === $menu_id ) {
unset( $assigned_menu[ $location ] );
}
}
$new_assignments = array_merge( $assigned_menu, $new_locations );
set_theme_mod( 'nav_menu_locations', $new_assignments );

return true;
}

/**
* Retrieves the term's schema, conforming to JSON Schema.
*
Expand All @@ -356,6 +467,15 @@ public function get_item_schema() {
unset( $schema['properties']['link'] );
unset( $schema['properties']['taxonomy'] );

$schema['properties']['locations'] = array(
'description' => __( 'The locations assigned to the menu.' ),
'type' => 'array',
'items' => array(
'type' => 'string',
),
'context' => array( 'view', 'edit' ),
);

return $schema;
}
}
171 changes: 158 additions & 13 deletions tests/test-rest-nav-menus-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,19 @@ public static function wpSetUpBeforeClass( $factory ) {
*/
public function setUp() {
parent::setUp();
$this->menu_id = wp_create_nav_menu( rand_str() );
// Unregister all nav menu locations.
foreach ( array_keys( get_registered_nav_menus() ) as $location ) {
unregister_nav_menu( $location );
}

$orig_args = array(
'name' => 'Original Name',
'description' => 'Original Description',
'slug' => 'original-slug',
'taxonomy' => 'nav_menu',
);

$this->menu_id = $this->factory->term->create( $orig_args );

register_meta(
'term',
Expand All @@ -74,6 +86,17 @@ public function setUp() {
);
}

/**
* Register nav menu locations.
*
* @param array $locations Location slugs.
*/
public function register_nav_menu_locations( $locations ) {
foreach ( $locations as $location ) {
register_nav_menu( $location, ucfirst( $location ) );
}
}

/**
*
*/
Expand Down Expand Up @@ -190,17 +213,7 @@ public function test_create_item() {
public function test_update_item() {
wp_set_current_user( self::$admin_id );

$nav_menu_id = wp_update_nav_menu_object(
0,
array(
'description' => 'Original Description',
'menu-name' => 'Original Name',
)
);

$term = get_term_by( 'id', $nav_menu_id, self::TAXONOMY );

$request = new WP_REST_Request( 'POST', '/wp/v2/menus/' . $term->term_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/menus/' . $this->menu_id );
$request->set_param( 'name', 'New Name' );
$request->set_param( 'description', 'New Description' );
$request->set_param(
Expand Down Expand Up @@ -273,12 +286,107 @@ public function test_get_item_schema() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$properties = $data['schema']['properties'];
$this->assertEquals( 5, count( $properties ) );
$this->assertEquals( 6, count( $properties ) );
$this->assertArrayHasKey( 'id', $properties );
$this->assertArrayHasKey( 'description', $properties );
$this->assertArrayHasKey( 'meta', $properties );
$this->assertArrayHasKey( 'name', $properties );
$this->assertArrayHasKey( 'slug', $properties );
$this->assertArrayHasKey( 'locations', $properties );
}

/**
*
*/
public function test_create_item_with_location_permission_correct() {
$this->register_nav_menu_locations( array( 'primary', 'secondary' ) );
wp_set_current_user( self::$admin_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/menus' );
$request->set_param( 'name', 'My Awesome Term' );
$request->set_param( 'slug', 'so-awesome' );
$request->set_param( 'locations', 'primary' );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( 201, $response->get_status() );
$data = $response->get_data();
$term_id = $data['id'];
$locations = get_nav_menu_locations();
$this->assertEquals( $locations['primary'], $term_id );
}

/**
*
*/
public function test_create_item_with_location_permission_incorrect() {
wp_set_current_user( self::$subscriber_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/menus' );
$request->set_param( 'name', 'My Awesome Term' );
$request->set_param( 'slug', 'so-awesome' );
$request->set_param( 'locations', 'primary' );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( rest_authorization_required_code(), $response->get_status() );
$this->assertErrorResponse( 'rest_cannot_assign_location', $response, rest_authorization_required_code() );
}

/**
*
*/
public function test_create_item_with_location_permission_no_location() {
wp_set_current_user( self::$admin_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/menus' );
$request->set_param( 'name', 'My Awesome Term' );
$request->set_param( 'slug', 'so-awesome' );
$request->set_param( 'locations', 'bar' );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( 400, $response->get_status() );
$this->assertErrorResponse( 'rest_menu_location_invalid', $response, 400 );
}

/**
*
*/
public function test_update_item_with_no_location() {
$this->register_nav_menu_locations( array( 'primary', 'secondary' ) );
wp_set_current_user( self::$admin_id );

$request = new WP_REST_Request( 'POST', '/wp/v2/menus/' . $this->menu_id );
$request->set_param( 'name', 'New Name' );
$request->set_param( 'description', 'New Description' );
$request->set_param( 'slug', 'new-slug' );
$request->set_param( 'locations', 'bar' );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( 400, $response->get_status() );
}

/**
*
*/
public function test_update_item_with_location_permission_correct() {
$this->register_nav_menu_locations( array( 'primary', 'secondary' ) );
wp_set_current_user( self::$admin_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/menus/' . $this->menu_id );
$request->set_param( 'name', 'New Name' );
$request->set_param( 'description', 'New Description' );
$request->set_param( 'slug', 'new-slug' );
$request->set_param( 'locations', 'primary' );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$locations = get_nav_menu_locations();
$this->assertEquals( $locations['primary'], $this->menu_id );
}

/**
*
*/
public function test_update_item_with_location_permission_incorrect() {
$this->register_nav_menu_locations( array( 'primary', 'secondary' ) );
wp_set_current_user( self::$subscriber_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/menus/' . $this->menu_id );
$request->set_param( 'name', 'New Name' );
$request->set_param( 'description', 'New Description' );
$request->set_param( 'slug', 'new-slug' );
$request->set_param( 'locations', 'primary' );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( rest_authorization_required_code(), $response->get_status() );
}

/**
Expand Down Expand Up @@ -309,6 +417,43 @@ public function test_get_item_links() {
$this->assertEquals( $location_url, $links['https://api.w.org/menu-location'][0]['href'] );
}

/**
*
*/
public function test_change_menu_location() {
$this->register_nav_menu_locations( array( 'primary', 'secondary' ) );
$secondary_id = self::factory()->term->create(
array(
'name' => 'Secondary Name',
'description' => 'Secondary Description',
'slug' => 'secondary-slug',
'taxonomy' => 'nav_menu',
)
);

$locations = get_nav_menu_locations();
$locations['primary'] = $this->menu_id;
$locations['secondary'] = $secondary_id;
set_theme_mod( 'nav_menu_locations', $locations );

wp_set_current_user( self::$admin_id );

$request = new WP_REST_Request( 'POST', '/wp/v2/menus/' . $this->menu_id );
$request->set_body_params(
array(
'locations' => array( 'secondary' ),
)
);
$response = rest_get_server()->dispatch( $request );

$this->assertEquals( 200, $response->get_status() );

$locations = get_nav_menu_locations();
$this->assertArrayNotHasKey( 'primary', $locations );
$this->assertArrayHasKey( 'secondary', $locations );
$this->assertEquals( $this->menu_id, $locations['secondary'] );
}

/**
*
*/
Expand Down

0 comments on commit a04c248

Please sign in to comment.