From 0ba364411de4127eee3d077dc8a4255647965a90 Mon Sep 17 00:00:00 2001 From: Gary Pendergast Date: Wed, 11 Jul 2018 06:23:27 +0000 Subject: [PATCH] REST API: Declare user capabilities using JSON Hyper Schema's "targetSchema". There are a variety of operations a WordPress user can only perform if they have the correct capabilities. A REST API client should only display UI for one of these operations if the WordPress user can perform the operation. Rather than requiring REST API clients to calculate whether to display UI based on potentially complicated combinations of user capabilities, `targetSchema` allows us to expose a single flag to show whether the corresponding UI should be displayed. This change also includes flags on post objects for the following actions: - `action-publish`: The current user can publish this post. - `action-sticky`: The current user can make this post sticky, and the post type supports sticking. - `action-assign-author': The current user can change the author on this post. - `action-assign-{$taxonomy}`: The current user can assign terms from the "$taxonomy" taxonomy to this post. - `action-create-{$taxonomy}`: The current user can create terms int the "$taxonomy" taxonomy. Props TimothyBlynJacobs, danielbachhuber. Fixes #44287. Built from https://develop.svn.wordpress.org/trunk@43437 git-svn-id: http://core.svn.wordpress.org/trunk@43264 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- .../class-wp-rest-attachments-controller.php | 5 +- .../class-wp-rest-posts-controller.php | 183 +++++++++++++++++- wp-includes/version.php | 2 +- 3 files changed, 186 insertions(+), 4 deletions(-) diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index 26f462e4f3..5854539a37 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -368,10 +368,11 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { $data = $this->filter_response_by_context( $data, $context ); + $links = $response->get_links(); + // Wrap the data in a response object. $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $post ) ); + $response->add_links( $links ); /** * Filters an attachment returned from the REST API. diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index b7b2dd79f6..7cd6497c0b 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -1590,7 +1590,18 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { // Wrap the data in a response object. $response = rest_ensure_response( $data ); - $response->add_links( $this->prepare_links( $post ) ); + $links = $this->prepare_links( $post ); + $response->add_links( $links ); + + if ( ! empty( $links['self']['href'] ) ) { + $actions = $this->get_available_actions( $post, $request ); + + $self = $links['self']['href']; + + foreach ( $actions as $rel ) { + $response->add_link( $rel, $self ); + } + } /** * Filters the post data for a response. @@ -1729,6 +1740,60 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { return $links; } + /** + * Get the link relations available for the post and current user. + * + * @since 4.9.7 + * + * @param WP_Post $post Post object. + * @param WP_REST_Request Request object. + * + * @return array List of link relations. + */ + protected function get_available_actions( $post, $request ) { + + if ( 'edit' !== $request['context'] ) { + return array(); + } + + $rels = array(); + + $post_type = get_post_type_object( $post->post_type ); + + if ( 'attachment' !== $this->post_type && current_user_can( $post_type->cap->publish_posts ) ) { + $rels[] = 'https://api.w.org/action-publish'; + } + + if ( 'post' === $post_type->name ) { + if ( current_user_can( $post_type->cap->edit_others_posts ) && current_user_can( $post_type->cap->publish_posts ) ) { + $rels[] = 'https://api.w.org/action-sticky'; + } + } + + if ( post_type_supports( $post_type->name, 'author' ) ) { + if ( current_user_can( $post_type->cap->edit_others_posts ) ) { + $rels[] = 'https://api.w.org/action-assign-author'; + } + } + + $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); + + foreach ( $taxonomies as $tax ) { + $tax_base = ! empty( $tax->rest_base ) ? $tax->rest_base : $tax->name; + $create_cap = is_taxonomy_hierarchical( $tax->name ) ? $tax->cap->edit_terms : $tax->cap->assign_terms; + + if ( current_user_can( $create_cap ) ) { + $rels[] = 'https://api.w.org/action-create-' . $tax_base; + } + + if ( current_user_can( $tax->cap->assign_terms ) ) { + $rels[] = 'https://api.w.org/action-assign-' . $tax_base; + } + } + + return $rels; + } + /** * Retrieves the post's schema, conforming to JSON Schema. * @@ -2069,9 +2134,125 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { ); } + $schema_links = $this->get_schema_links(); + + if ( $schema_links ) { + $schema['links'] = $schema_links; + } + return $this->add_additional_fields_schema( $schema ); } + /** + * Retrieve Link Description Objects that should be added to the Schema for the posts collection. + * + * @since 4.9.7 + * + * @return array + */ + protected function get_schema_links() { + + $href = rest_url( "{$this->namespace}/{$this->rest_base}/{id}" ); + + $links = array(); + + if ( 'attachment' !== $this->post_type ) { + $links[] = array( + 'rel' => 'https://api.w.org/action-publish', + 'title' => __( 'The current user can publish this post.' ), + 'href' => $href, + 'targetSchema' => array( + 'type' => 'object', + 'properties' => array( + 'status' => array( + 'type' => 'string', + 'enum' => array( 'publish', 'future' ), + ), + ), + ), + ); + } + + if ( 'post' === $this->post_type ) { + $links[] = array( + 'rel' => 'https://api.w.org/action-sticky', + 'title' => __( 'The current user can sticky this post.' ), + 'href' => $href, + 'targetSchema' => array( + 'type' => 'object', + 'properties' => array( + 'sticky' => array( + 'type' => 'boolean', + ), + ), + ), + ); + } + + if ( post_type_supports( $this->post_type, 'author' ) ) { + $links[] = array( + 'rel' => 'https://api.w.org/action-assign-author', + 'title' => __( 'The current user can change the author on this post.' ), + 'href' => $href, + 'targetSchema' => array( + 'type' => 'object', + 'properties' => array( + 'author' => array( + 'type' => 'integer', + ), + ), + ), + ); + } + + $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); + + foreach ( $taxonomies as $tax ) { + $tax_base = ! empty( $tax->rest_base ) ? $tax->rest_base : $tax->name; + + /* translators: %s: taxonomy name */ + $assign_title = sprintf( __( 'The current user can assign terms in the %s taxonomy.' ), $tax->name ); + /* translators: %s: taxonomy name */ + $create_title = sprintf( __( 'The current user can create terms in the %s taxonomy.' ), $tax->name ); + + $links[] = array( + 'rel' => 'https://api.w.org/action-assign-' . $tax_base, + 'title' => $assign_title, + 'href' => $href, + 'targetSchema' => array( + 'type' => 'object', + 'properties' => array( + $tax_base => array( + 'type' => 'array', + 'items' => array( + 'type' => 'integer', + ), + ), + ), + ), + ); + + $links[] = array( + 'rel' => 'https://api.w.org/action-create-' . $tax_base, + 'title' => $create_title, + 'href' => $href, + 'targetSchema' => array( + 'type' => 'object', + 'properties' => array( + $tax_base => array( + 'type' => 'array', + 'items' => array( + 'type' => 'integer', + ), + ), + ), + ), + ); + } + + return $links; + } + /** * Retrieves the query params for the posts collection. * diff --git a/wp-includes/version.php b/wp-includes/version.php index f277e6ea0c..ccbffc2807 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -4,7 +4,7 @@ * * @global string $wp_version */ -$wp_version = '5.0-alpha-43436'; +$wp_version = '5.0-alpha-43437'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.