diff -r c7c34916027a -r 177826044cd9 wp/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php --- a/wp/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php Mon Oct 14 18:06:33 2019 +0200 +++ b/wp/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php Mon Oct 14 18:28:13 2019 +0200 @@ -56,9 +56,9 @@ * @param string $taxonomy Taxonomy key. */ public function __construct( $taxonomy ) { - $this->taxonomy = $taxonomy; + $this->taxonomy = $taxonomy; $this->namespace = 'wp/v2'; - $tax_obj = get_taxonomy( $taxonomy ); + $tax_obj = get_taxonomy( $taxonomy ); $this->rest_base = ! empty( $tax_obj->rest_base ) ? $tax_obj->rest_base : $tax_obj->name; $this->meta = new WP_REST_Term_Meta_Fields( $taxonomy ); @@ -73,57 +73,65 @@ */ public function register_routes() { - register_rest_route( $this->namespace, '/' . $this->rest_base, array( - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - 'args' => $this->get_collection_params(), - ), + register_rest_route( + $this->namespace, + '/' . $this->rest_base, array( - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'create_item' ), - 'permission_callback' => array( $this, 'create_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) ); - - register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( - 'args' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the term.' ), - 'type' => 'integer', + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => $this->get_collection_params(), ), - ), - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_item_permissions_check' ), - 'args' => array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'create_item' ), + 'permission_callback' => array( $this, 'create_item_permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), ), - ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[\d]+)', array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_item' ), - 'permission_callback' => array( $this, 'update_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), - ), - array( - 'methods' => WP_REST_Server::DELETABLE, - 'callback' => array( $this, 'delete_item' ), - 'permission_callback' => array( $this, 'delete_item_permissions_check' ), - 'args' => array( - 'force' => array( - 'type' => 'boolean', - 'default' => false, - 'description' => __( 'Required to be true, as terms do not support trashing.' ), + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the term.' ), + 'type' => 'integer', ), ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) ); + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_item' ), + 'permission_callback' => array( $this, 'get_item_permissions_check' ), + 'args' => array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_item' ), + 'permission_callback' => array( $this, 'update_item_permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), + ), + array( + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => array( $this, 'delete_item' ), + 'permission_callback' => array( $this, 'delete_item_permissions_check' ), + 'args' => array( + 'force' => array( + 'type' => 'boolean', + 'default' => false, + 'description' => __( 'Required to be true, as terms do not support trashing.' ), + ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); } /** @@ -235,7 +243,7 @@ */ $prepared_args = apply_filters( "rest_{$this->taxonomy}_query", $prepared_args, $request ); - if ( ! empty( $prepared_args['post'] ) ) { + if ( ! empty( $prepared_args['post'] ) ) { $query_result = wp_get_object_terms( $prepared_args['post'], $this->taxonomy, $prepared_args ); // Used when calling wp_count_terms() below. @@ -258,7 +266,7 @@ $response = array(); foreach ( $query_result as $term ) { - $data = $this->prepare_item_for_response( $term, $request ); + $data = $this->prepare_item_for_response( $term, $request ); $response[] = $this->prepare_response_for_collection( $data ); } @@ -274,7 +282,7 @@ $response->header( 'X-WP-TotalPages', (int) $max_pages ); - $base = add_query_arg( $request->get_query_params(), rest_url( $this->namespace . '/' . $this->rest_base ) ); + $base = add_query_arg( urlencode_deep( $request->get_query_params() ), rest_url( $this->namespace . '/' . $this->rest_base ) ); if ( $page > 1 ) { $prev_page = $page - 1; @@ -418,7 +426,12 @@ if ( $term_id = $term->get_error_data( 'term_exists' ) ) { $existing_term = get_term( $term_id, $this->taxonomy ); $term->add_data( $existing_term->term_id, 'term_exists' ); - $term->add_data( array( 'status' => 409, 'term_id' => $term_id ) ); + $term->add_data( + array( + 'status' => 400, + 'term_id' => $term_id, + ) + ); } return $term; @@ -441,7 +454,7 @@ $schema = $this->get_item_schema(); if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { - $meta_update = $this->meta->update_value( $request['meta'], (int) $request['id'] ); + $meta_update = $this->meta->update_value( $request['meta'], $term->term_id ); if ( is_wp_error( $meta_update ) ) { return $meta_update; @@ -456,6 +469,19 @@ $request->set_param( 'context', 'view' ); + /** + * Fires after a single term is completely created or updated via the REST API. + * + * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. + * + * @since 5.0.0 + * + * @param WP_Term $term Inserted or updated term object. + * @param WP_REST_Request $request Request object. + * @param bool $creating True when creating a term, false when updating. + */ + do_action( "rest_after_insert_{$this->taxonomy}", $term, $request, true ); + $response = $this->prepare_item_for_response( $term, $request ); $response = rest_ensure_response( $response ); @@ -545,6 +571,9 @@ $request->set_param( 'context', 'view' ); + /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */ + do_action( "rest_after_insert_{$this->taxonomy}", $term, $request, false ); + $response = $this->prepare_item_for_response( $term, $request ); return rest_ensure_response( $response ); @@ -604,7 +633,12 @@ } $response = new WP_REST_Response(); - $response->set_data( array( 'deleted' => true, 'previous' => $previous->get_data() ) ); + $response->set_data( + array( + 'deleted' => true, + 'previous' => $previous->get_data(), + ) + ); /** * Fires after a single term is deleted via the REST API. @@ -651,11 +685,15 @@ } if ( isset( $request['parent'] ) && ! empty( $schema['properties']['parent'] ) ) { - $parent_term_id = 0; - $parent_term = get_term( (int) $request['parent'], $this->taxonomy ); + $parent_term_id = 0; + $requested_parent = (int) $request['parent']; - if ( $parent_term ) { - $parent_term_id = $parent_term->term_id; + if ( $requested_parent ) { + $parent_term = get_term( $requested_parent, $this->taxonomy ); + + if ( $parent_term instanceof WP_Term ) { + $parent_term_id = $parent_term->term_id; + } } $prepared_term->parent = $parent_term_id; @@ -757,7 +795,7 @@ * @return array Links for the given term. */ protected function prepare_links( $term ) { - $base = $this->namespace . '/' . $this->rest_base; + $base = $this->namespace . '/' . $this->rest_base; $links = array( 'self' => array( 'href' => rest_url( trailingslashit( $base ) . $term->term_id ), @@ -796,7 +834,7 @@ continue; } - $rest_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; + $rest_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; $post_type_links[] = array( 'href' => add_query_arg( $this->rest_base, $term->term_id, rest_url( sprintf( 'wp/v2/%s', $rest_base ) ) ), ); @@ -823,52 +861,52 @@ 'type' => 'object', 'properties' => array( 'id' => array( - 'description' => __( 'Unique identifier for the term.' ), - 'type' => 'integer', - 'context' => array( 'view', 'embed', 'edit' ), - 'readonly' => true, + 'description' => __( 'Unique identifier for the term.' ), + 'type' => 'integer', + 'context' => array( 'view', 'embed', 'edit' ), + 'readonly' => true, ), 'count' => array( - 'description' => __( 'Number of published posts for the term.' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, + 'description' => __( 'Number of published posts for the term.' ), + 'type' => 'integer', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, ), 'description' => array( - 'description' => __( 'HTML description of the term.' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), + 'description' => __( 'HTML description of the term.' ), + 'type' => 'string', + 'context' => array( 'view', 'edit' ), ), 'link' => array( - 'description' => __( 'URL of the term.' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view', 'embed', 'edit' ), - 'readonly' => true, + 'description' => __( 'URL of the term.' ), + 'type' => 'string', + 'format' => 'uri', + 'context' => array( 'view', 'embed', 'edit' ), + 'readonly' => true, ), 'name' => array( - 'description' => __( 'HTML title for the term.' ), - 'type' => 'string', - 'context' => array( 'view', 'embed', 'edit' ), - 'arg_options' => array( + 'description' => __( 'HTML title for the term.' ), + 'type' => 'string', + 'context' => array( 'view', 'embed', 'edit' ), + 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), - 'required' => true, + 'required' => true, ), 'slug' => array( - 'description' => __( 'An alphanumeric identifier for the term unique to its type.' ), - 'type' => 'string', - 'context' => array( 'view', 'embed', 'edit' ), - 'arg_options' => array( + 'description' => __( 'An alphanumeric identifier for the term unique to its type.' ), + 'type' => 'string', + 'context' => array( 'view', 'embed', 'edit' ), + 'arg_options' => array( 'sanitize_callback' => array( $this, 'sanitize_slug' ), ), ), 'taxonomy' => array( - 'description' => __( 'Type attribution for the term.' ), - 'type' => 'string', - 'enum' => array_keys( get_taxonomies() ), - 'context' => array( 'view', 'embed', 'edit' ), - 'readonly' => true, + 'description' => __( 'Type attribution for the term.' ), + 'type' => 'string', + 'enum' => array_keys( get_taxonomies() ), + 'context' => array( 'view', 'embed', 'edit' ), + 'readonly' => true, ), ), ); @@ -877,9 +915,9 @@ if ( $taxonomy->hierarchical ) { $schema['properties']['parent'] = array( - 'description' => __( 'The parent term ID.' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), + 'description' => __( 'The parent term ID.' ), + 'type' => 'integer', + 'context' => array( 'view', 'edit' ), ); } @@ -897,50 +935,50 @@ */ public function get_collection_params() { $query_params = parent::get_collection_params(); - $taxonomy = get_taxonomy( $this->taxonomy ); + $taxonomy = get_taxonomy( $this->taxonomy ); $query_params['context']['default'] = 'view'; $query_params['exclude'] = array( - 'description' => __( 'Ensure result set excludes specific IDs.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', + 'description' => __( 'Ensure result set excludes specific IDs.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'integer', ), - 'default' => array(), + 'default' => array(), ); $query_params['include'] = array( - 'description' => __( 'Limit result set to specific IDs.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', + 'description' => __( 'Limit result set to specific IDs.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'integer', ), - 'default' => array(), + 'default' => array(), ); if ( ! $taxonomy->hierarchical ) { $query_params['offset'] = array( - 'description' => __( 'Offset the result set by a specific number of items.' ), - 'type' => 'integer', + 'description' => __( 'Offset the result set by a specific number of items.' ), + 'type' => 'integer', ); } $query_params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.' ), - 'type' => 'string', - 'default' => 'asc', - 'enum' => array( + 'description' => __( 'Order sort attribute ascending or descending.' ), + 'type' => 'string', + 'default' => 'asc', + 'enum' => array( 'asc', 'desc', ), ); $query_params['orderby'] = array( - 'description' => __( 'Sort collection by term attribute.' ), - 'type' => 'string', - 'default' => 'name', - 'enum' => array( + 'description' => __( 'Sort collection by term attribute.' ), + 'type' => 'string', + 'default' => 'name', + 'enum' => array( 'id', 'include', 'name', @@ -953,29 +991,29 @@ ); $query_params['hide_empty'] = array( - 'description' => __( 'Whether to hide terms not assigned to any posts.' ), - 'type' => 'boolean', - 'default' => false, + 'description' => __( 'Whether to hide terms not assigned to any posts.' ), + 'type' => 'boolean', + 'default' => false, ); if ( $taxonomy->hierarchical ) { $query_params['parent'] = array( - 'description' => __( 'Limit result set to terms assigned to a specific parent.' ), - 'type' => 'integer', + 'description' => __( 'Limit result set to terms assigned to a specific parent.' ), + 'type' => 'integer', ); } $query_params['post'] = array( - 'description' => __( 'Limit result set to terms assigned to a specific post.' ), - 'type' => 'integer', - 'default' => null, + 'description' => __( 'Limit result set to terms assigned to a specific post.' ), + 'type' => 'integer', + 'default' => null, ); $query_params['slug'] = array( - 'description' => __( 'Limit result set to terms with one or more specific slugs.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'string' + 'description' => __( 'Limit result set to terms with one or more specific slugs.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'string', ), );