Skip to content
Permalink
Browse files Browse the repository at this point in the history
REST API: Unify object access handling for simplicity.
Rather than repeating ourselves, unifying the access into a single method keeps everything tidy. While we're at it, add in additional schema handling for common parameters.

See #38792.
Built from https://develop.svn.wordpress.org/trunk@39954


git-svn-id: http://core.svn.wordpress.org/trunk@39891 1a063a9b-81f0-0310-95a4-ce76da25c4cd
  • Loading branch information
joehoyle committed Jan 26, 2017
1 parent 8538429 commit e357195
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 149 deletions.
10 changes: 9 additions & 1 deletion wp-includes/rest-api.php
Expand Up @@ -46,6 +46,13 @@ function register_rest_route( $namespace, $route, $args = array(), $override = f
return false;
}

if ( isset( $args['args'] ) ) {
$common_args = $args['args'];
unset( $args['args'] );
} else {
$common_args = array();
}

if ( isset( $args['callback'] ) ) {
// Upgrade a single set to multiple.
$args = array( $args );
Expand All @@ -57,12 +64,13 @@ function register_rest_route( $namespace, $route, $args = array(), $override = f
'args' => array(),
);
foreach ( $args as $key => &$arg_group ) {
if ( ! is_numeric( $arg_group ) ) {
if ( ! is_numeric( $key ) ) {
// Route option, skip here.
continue;
}

$arg_group = array_merge( $defaults, $arg_group );
$arg_group['args'] = array_merge( $common_args, $arg_group['args'] );
}

$full_route = '/' . trim( $namespace, '/' ) . '/' . trim( $route, '/' );
Expand Down
101 changes: 60 additions & 41 deletions wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php
Expand Up @@ -63,6 +63,12 @@ public function register_routes() {
) );

register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
'args' => array(
'id' => array(
'description' => __( 'Unique identifier for the object.' ),
'type' => 'integer',
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_item' ),
Expand Down Expand Up @@ -299,6 +305,36 @@ public function get_items( $request ) {
return $response;
}

/**
* Get the comment, if the ID is valid.
*
* @since 4.7.2
*
* @param int $id Supplied ID.
* @return WP_Comment|WP_Error Comment object if ID is valid, WP_Error otherwise.
*/
protected function get_comment( $id ) {
$error = new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) );
if ( (int) $id <= 0 ) {
return $error;
}

$id = (int) $id;
$comment = get_comment( $id );
if ( empty( $comment ) ) {
return $error;
}

if ( ! empty( $comment->comment_post_ID ) ) {
$post = get_post( (int) $comment->comment_post_ID );
if ( empty( $post ) ) {
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
}
}

return $comment;
}

/**
* Checks if a given request has access to read the comment.
*
Expand All @@ -309,12 +345,9 @@ public function get_items( $request ) {
* @return WP_Error|bool True if the request has read access for the item, error object otherwise.
*/
public function get_item_permissions_check( $request ) {
$id = (int) $request['id'];

$comment = get_comment( $id );

if ( ! $comment ) {
return true;
$comment = $this->get_comment( $request['id'] );
if ( is_wp_error( $comment ) ) {
return $comment;
}

if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
Expand Down Expand Up @@ -344,18 +377,9 @@ public function get_item_permissions_check( $request ) {
* @return WP_Error|WP_REST_Response Response object on success, or error object on failure.
*/
public function get_item( $request ) {
$id = (int) $request['id'];

$comment = get_comment( $id );
if ( empty( $comment ) ) {
return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) );
}

if ( ! empty( $comment->comment_post_ID ) ) {
$post = get_post( $comment->comment_post_ID );
if ( empty( $post ) ) {
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
}
$comment = $this->get_comment( $request['id'] );
if ( is_wp_error( $comment ) ) {
return $comment;
}

$data = $this->prepare_item_for_response( $comment, $request );
Expand Down Expand Up @@ -630,12 +654,12 @@ public function create_item( $request ) {
* @return WP_Error|bool True if the request has access to update the item, error object otherwise.
*/
public function update_item_permissions_check( $request ) {
$comment = $this->get_comment( $request['id'] );
if ( is_wp_error( $comment ) ) {
return $comment;
}

$id = (int) $request['id'];

$comment = get_comment( $id );

if ( $comment && ! $this->check_edit_permission( $comment ) ) {
if ( ! $this->check_edit_permission( $comment ) ) {
return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this comment.' ), array( 'status' => rest_authorization_required_code() ) );
}

Expand All @@ -652,14 +676,13 @@ public function update_item_permissions_check( $request ) {
* @return WP_Error|WP_REST_Response Response object on success, or error object on failure.
*/
public function update_item( $request ) {
$id = (int) $request['id'];

$comment = get_comment( $id );

if ( empty( $comment ) ) {
return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) );
$comment = $this->get_comment( $request['id'] );
if ( is_wp_error( $comment ) ) {
return $comment;
}

$id = $comment->comment_ID;

if ( isset( $request['type'] ) && get_comment_type( $id ) !== $request['type'] ) {
return new WP_Error( 'rest_comment_invalid_type', __( 'Sorry, you are not allowed to change the comment type.' ), array( 'status' => 404 ) );
}
Expand Down Expand Up @@ -750,11 +773,9 @@ public function update_item( $request ) {
* @return WP_Error|bool True if the request has access to delete the item, error object otherwise.
*/
public function delete_item_permissions_check( $request ) {
$id = (int) $request['id'];
$comment = get_comment( $id );

if ( ! $comment ) {
return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) );
$comment = $this->get_comment( $request['id'] );
if ( is_wp_error( $comment ) ) {
return $comment;
}

if ( ! $this->check_edit_permission( $comment ) ) {
Expand All @@ -773,15 +794,13 @@ public function delete_item_permissions_check( $request ) {
* @return WP_Error|WP_REST_Response Response object on success, or error object on failure.
*/
public function delete_item( $request ) {
$id = (int) $request['id'];
$force = isset( $request['force'] ) ? (bool) $request['force'] : false;

$comment = get_comment( $id );

if ( empty( $comment ) ) {
return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) );
$comment = $this->get_comment( $request['id'] );
if ( is_wp_error( $comment ) ) {
return $comment;
}

$force = isset( $request['force'] ) ? (bool) $request['force'] : false;

/**
* Filters whether a comment can be trashed.
*
Expand Down
Expand Up @@ -48,6 +48,12 @@ public function register_routes() {
) );

register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<status>[\w-]+)', array(
'args' => array(
'status' => array(
'description' => __( 'An alphanumeric identifier for the status.' ),
'type' => 'string',
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_item' ),
Expand Down
Expand Up @@ -48,6 +48,12 @@ public function register_routes() {
) );

register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<type>[\w-]+)', array(
'args' => array(
'type' => array(
'description' => __( 'An alphanumeric identifier for the post type.' ),
'type' => 'string',
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_item' ),
Expand Down
86 changes: 62 additions & 24 deletions wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
Expand Up @@ -88,6 +88,12 @@ public function register_routes() {
);
}
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
'args' => array(
'id' => array(
'description' => __( 'Unique identifier for the object.' ),
'type' => 'integer',
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_item' ),
Expand Down Expand Up @@ -349,6 +355,28 @@ public function get_items( $request ) {
return $response;
}

/**
* Get the post, if the ID is valid.
*
* @since 4.7.2
*
* @param int $id Supplied ID.
* @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
*/
protected function get_post( $id ) {
$error = new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
if ( (int) $id <= 0 ) {
return $error;
}

$post = get_post( (int) $id );
if ( empty( $post ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
return $error;
}

return $post;
}

/**
* Checks if a given request has access to read a post.
*
Expand All @@ -359,8 +387,10 @@ public function get_items( $request ) {
* @return bool|WP_Error True if the request has read access for the item, WP_Error object otherwise.
*/
public function get_item_permissions_check( $request ) {

$post = get_post( (int) $request['id'] );
$post = $this->get_post( $request['id'] );
if ( is_wp_error( $post ) ) {
return $post;
}

if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) );
Expand Down Expand Up @@ -428,18 +458,16 @@ public function can_access_password_content( $post, $request ) {
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_item( $request ) {
$id = (int) $request['id'];
$post = get_post( $id );

if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
$post = $this->get_post( $request['id'] );
if ( is_wp_error( $post ) ) {
return $post;
}

$data = $this->prepare_item_for_response( $post, $request );
$response = rest_ensure_response( $data );

if ( is_post_type_viewable( get_post_type_object( $post->post_type ) ) ) {
$response->link_header( 'alternate', get_permalink( $id ), array( 'type' => 'text/html' ) );
$response->link_header( 'alternate', get_permalink( $post->ID ), array( 'type' => 'text/html' ) );
}

return $response;
Expand All @@ -455,6 +483,9 @@ public function get_item( $request ) {
* @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
*/
public function create_item_permissions_check( $request ) {
if ( ! empty( $request['id'] ) ) {
return new WP_Error( 'rest_post_exists', __( 'Cannot create existing post.' ), array( 'status' => 400 ) );
}

$post_type = get_post_type_object( $this->post_type );

Expand Down Expand Up @@ -591,8 +622,11 @@ public function create_item( $request ) {
* @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
*/
public function update_item_permissions_check( $request ) {
$post = $this->get_post( $request['id'] );
if ( is_wp_error( $post ) ) {
return $post;
}

$post = get_post( $request['id'] );
$post_type = get_post_type_object( $this->post_type );

if ( $post && ! $this->check_update_permission( $post ) ) {
Expand Down Expand Up @@ -624,11 +658,9 @@ public function update_item_permissions_check( $request ) {
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function update_item( $request ) {
$id = (int) $request['id'];
$post = get_post( $id );

if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
$valid_check = $this->get_post( $request['id'] );
if ( is_wp_error( $valid_check ) ) {
return $valid_check;
}

$post = $this->prepare_item_for_database( $request );
Expand Down Expand Up @@ -714,8 +746,10 @@ public function update_item( $request ) {
* @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
*/
public function delete_item_permissions_check( $request ) {

$post = get_post( $request['id'] );
$post = $this->get_post( $request['id'] );
if ( is_wp_error( $post ) ) {
return $post;
}

if ( $post && ! $this->check_delete_permission( $post ) ) {
return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) );
Expand All @@ -734,15 +768,14 @@ public function delete_item_permissions_check( $request ) {
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function delete_item( $request ) {
$id = (int) $request['id'];
$force = (bool) $request['force'];

$post = get_post( $id );

if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
$post = $this->get_post( $request['id'] );
if ( is_wp_error( $post ) ) {
return $post;
}

$id = $post->ID;
$force = (bool) $request['force'];

$supports_trash = ( EMPTY_TRASH_DAYS > 0 );

if ( 'attachment' === $post->post_type ) {
Expand Down Expand Up @@ -901,7 +934,12 @@ protected function prepare_item_for_database( $request ) {

// Post ID.
if ( isset( $request['id'] ) ) {
$prepared_post->ID = absint( $request['id'] );
$existing_post = $this->get_post( $request['id'] );
if ( is_wp_error( $existing_post ) ) {
return $existing_post;
}

$prepared_post->ID = $existing_post->ID;
}

$schema = $this->get_item_schema();
Expand Down

0 comments on commit e357195

Please sign in to comment.