Skip to content
This repository has been archived by the owner on Apr 4, 2020. It is now read-only.

Added User Meta Endpoints #3

Merged
merged 11 commits into from
Feb 25, 2016
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions lib/class-wp-rest-meta-users-controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

class WP_REST_Meta_Users_Controller extends WP_REST_Meta_Controller {

/**
* Associated object type.
*
* @var string "user"
*/
protected $parent_type = 'user';

/**
* Base path for parent meta type endpoints.
*
* @var string "users"
*/
protected $parent_base = 'users';

/**
* User controller class object.
*
* @var WP_REST_Users_Controller
*/
protected $parent_controller;

public function __construct() {
$this->parent_controller = new WP_REST_Users_Controller();
$this->namespace = 'wp/v2';
$this->rest_base = 'meta';
}

/**
* Check if a given request has access to get meta for a post.
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|boolean
*/
public function get_items_permissions_check( $request ) {
$parent = get_user_by( 'id', (int) $request['parent_id'] );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename this variable to $user? $parent doesn't make sense as a variable in this context.


if ( empty( $parent ) || empty( $parent->ID ) ) {
return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user id.' ), array( 'status' => 404 ) );
}

/* @todo Add a new check to read user

if ( ! $this->parent_controller->check_read_permission( $parent ) ) {
return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view this user.' ), array( 'status' => rest_authorization_required_code() ) );
}
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove this commented code?


if ( ! current_user_can( 'edit_user', $parent->ID ) ) {
return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view the meta for this user.' ), array( 'status' => rest_authorization_required_code() ) );
}
return true;
}

/**
* Check if a given request has access to get a specific meta entry for a post.
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|boolean
*/
public function get_item_permissions_check( $request ) {
return $this->get_items_permissions_check( $request );
}

/**
* Check if a given request has access to create a meta entry for a post.
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|boolean
*/
public function create_item_permissions_check( $request ) {
return $this->get_items_permissions_check( $request );
}

/**
* Check if a given request has access to update a meta entry for a post.
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|boolean
*/
public function update_item_permissions_check( $request ) {
return $this->get_items_permissions_check( $request );
}

/**
* Check if a given request has access to delete meta for a post.
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_Error|boolean
*/
public function delete_item_permissions_check( $request ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same two comments from get_items_permissions_check are applicable here.

$parent = get_user_by( 'id', (int) $request['parent_id'] );

if ( empty( $parent ) || empty( $parent->ID ) ) {
return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user id.' ), array( 'status' => 404 ) );
}

/* @todo Add a new check to read user

if ( ! $this->parent_controller->check_read_permission( $parent ) ) {
return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view this user.' ), array( 'status' => rest_authorization_required_code() ) );
}
*/

if ( ! current_user_can( 'delete_user', $parent->ID ) ) {
return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot delete the meta for this user.' ), array( 'status' => rest_authorization_required_code() ) );
}
return true;
}
}
8 changes: 8 additions & 0 deletions plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@ function meta_rest_api_init() {
require_once dirname( __FILE__ ) . '/lib/class-wp-rest-meta-posts-controller.php';
}

if ( class_exists( 'WP_REST_Controller' )
&& ! class_exists( 'WP_REST_Meta_Users_Controller' ) ) {
require_once dirname( __FILE__ ) . '/lib/class-wp-rest-meta-users-controller.php';
}

foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
if ( post_type_supports( $post_type->name, 'custom-fields' ) ) {
$meta_controller = new WP_REST_Meta_Posts_Controller( $post_type->name );
$meta_controller->register_routes();
}
}

$user_meta_controller = new WP_REST_Meta_Users_Controller();
$user_meta_controller->register_routes();
}

add_action( 'rest_api_init', 'meta_rest_api_init', 11 );
106 changes: 106 additions & 0 deletions tests/test-rest-meta-users-controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the similarity between them, it would be nice to have a common abstraction for meta unit tests. It can happen in a follow-up PR though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree and I was thinking about doing that but I wanted to keep things simple for now.


/**
* Unit tests covering WP_REST_Users meta functionality.
*
* @package WordPress
* @subpackage JSON API
*/
class WP_Test_REST_Meta_Users_Controller extends WP_Test_REST_Controller_Testcase {
public function setUp() {
parent::setUp();

$this->user = $this->factory->user->create();
wp_set_current_user( $this->user );
$this->user_obj = wp_get_current_user();
$this->user_obj->add_role( 'adminstrator' );
$this->user_obj->add_cap( 'edit_users' );
$this->user_obj->add_cap( 'delete_users' );
}

public function test_register_routes() {
$routes = $this->server->get_routes();

$this->assertArrayHasKey( '/wp/v2/users/(?P<parent_id>[\d]+)/meta', $routes );
$this->assertCount( 2, $routes['/wp/v2/users/(?P<parent_id>[\d]+)/meta'] );
$this->assertArrayHasKey( '/wp/v2/users/(?P<parent_id>[\d]+)/meta/(?P<id>[\d]+)', $routes );
$this->assertCount( 3, $routes['/wp/v2/users/(?P<parent_id>[\d]+)/meta/(?P<id>[\d]+)'] );
}

public function test_context_param() {
$user_id = $this->factory->user->create();
// Collection
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/users/' . $user_id . '/meta' );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 'edit', $data['endpoints'][0]['args']['context']['default'] );
$this->assertEquals( array( 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
// Single
$meta_id_basic = add_user_meta( $user_id, 'testkey', 'testvalue' );
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/users/' . $user_id . '/meta/' . $meta_id_basic );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 'edit', $data['endpoints'][0]['args']['context']['default'] );
$this->assertEquals( array( 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
}

public function test_get_items() {
$user_id = $this->factory->user->create();
$meta_id_serialized = add_user_meta( $user_id, 'testkey_serialized', array( 'testvalue1', 'testvalue2' ) );
$meta_id_serialized_object = add_user_meta( $user_id, 'testkey_serialized_object', (object) array( 'testvalue' => 'test' ) );
$meta_id_serialized_array = add_user_meta( $user_id, 'testkey_serialized_array', serialize( array( 'testkey1' => 'testvalue1', 'testkey2' => 'testvalue2' ) ) );
$meta_id_protected = add_user_meta( $user_id, '_testkey', 'testvalue' );

$request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/users/%d/meta', $user_id ) );
$response = $this->server->dispatch( $request );

$this->assertEquals( 200, $response->get_status() );
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the line that is failing to pass in travis. I am not 100% sure why it is failing since it is passing on my local install via VVV.

Failed: https://travis-ci.org/WP-API/wp-api-meta-endpoints/jobs/111276918#L300

@danielbachhuber thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

am not 100% sure why it is failing since it is passing on my local install via VVV.

How would it be passing locally?

In this test, you're requesting user meta in an unauthenticated request. I'd expect the test to fail with a 403 response, which it does locally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I see why it would be passing locally. For some reason when I use the $this->factory->user->create() function I create a new temp user with the ID of 2, but my local admin user has an ID of 2. I am not 100% sure if this is effecting it but I just looked over the way you test in the wp-api repo and I think I understand how to authenticate properly now.

https://github.com/WP-API/WP-API/blob/develop/tests/test-rest-users-controller.php#L74

$data = $response->get_data();

foreach ( $data as $row ) {
$row = (array) $row;
$this->assertArrayHasKey( 'id', $row );
$this->assertArrayHasKey( 'key', $row );
$this->assertArrayHasKey( 'value', $row );

if ( $row['id'] === $meta_id_serialized ) {
$this->assertEquals( 'testkey_serialized', $row['key'] );
$this->assertEquals( array( 'testvalue1', 'testvalue2' ), $row['value'] );
}

if ( $row['id'] === $meta_id_serialized_object ) {
$this->assertEquals( 'testkey_serialized_object', $row['key'] );
$this->assertEquals( (object) array( 'testvalue' => 'test' ), $row['value'] );
}

if ( $row['id'] === $meta_id_serialized_array ) {
$this->assertEquals( 'testkey_serialized_array', $row['key'] );
$this->assertEquals( serialize( array( 'testkey1' => 'testvalue1', 'testkey2' => 'testvalue2' ) ), $row['value'] );
}
}
}

public function test_get_item() {
// No op
}

public function test_create_item() {
// No op
}

public function test_update_item() {
// No op
}

public function test_delete_item() {
// No op
}

public function test_prepare_item() {
// No op
}

public function test_get_item_schema() {
// No op
}
}