142 * @param WP_REST_Request $request Full details about the request. |
142 * @param WP_REST_Request $request Full details about the request. |
143 * @return bool|WP_Error True if the request has read access, otherwise false or WP_Error object. |
143 * @return bool|WP_Error True if the request has read access, otherwise false or WP_Error object. |
144 */ |
144 */ |
145 public function get_items_permissions_check( $request ) { |
145 public function get_items_permissions_check( $request ) { |
146 $tax_obj = get_taxonomy( $this->taxonomy ); |
146 $tax_obj = get_taxonomy( $this->taxonomy ); |
|
147 |
147 if ( ! $tax_obj || ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { |
148 if ( ! $tax_obj || ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { |
148 return false; |
149 return false; |
149 } |
150 } |
|
151 |
150 if ( 'edit' === $request['context'] && ! current_user_can( $tax_obj->cap->edit_terms ) ) { |
152 if ( 'edit' === $request['context'] && ! current_user_can( $tax_obj->cap->edit_terms ) ) { |
151 return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit terms in this taxonomy.' ), array( 'status' => rest_authorization_required_code() ) ); |
153 return new WP_Error( |
152 } |
154 'rest_forbidden_context', |
|
155 __( 'Sorry, you are not allowed to edit terms in this taxonomy.' ), |
|
156 array( 'status' => rest_authorization_required_code() ) |
|
157 ); |
|
158 } |
|
159 |
153 return true; |
160 return true; |
154 } |
161 } |
155 |
162 |
156 /** |
163 /** |
157 * Retrieves terms associated with a taxonomy. |
164 * Retrieves terms associated with a taxonomy. |
182 'per_page' => 'number', |
189 'per_page' => 'number', |
183 'search' => 'search', |
190 'search' => 'search', |
184 'slug' => 'slug', |
191 'slug' => 'slug', |
185 ); |
192 ); |
186 |
193 |
187 $prepared_args = array(); |
194 $prepared_args = array( 'taxonomy' => $this->taxonomy ); |
188 |
195 |
189 /* |
196 /* |
190 * For each known parameter which is both registered and present in the request, |
197 * For each known parameter which is both registered and present in the request, |
191 * set the parameter's value on the query $prepared_args. |
198 * set the parameter's value on the query $prepared_args. |
192 */ |
199 */ |
247 $query_result = wp_get_object_terms( $prepared_args['post'], $this->taxonomy, $prepared_args ); |
254 $query_result = wp_get_object_terms( $prepared_args['post'], $this->taxonomy, $prepared_args ); |
248 |
255 |
249 // Used when calling wp_count_terms() below. |
256 // Used when calling wp_count_terms() below. |
250 $prepared_args['object_ids'] = $prepared_args['post']; |
257 $prepared_args['object_ids'] = $prepared_args['post']; |
251 } else { |
258 } else { |
252 $query_result = get_terms( $this->taxonomy, $prepared_args ); |
259 $query_result = get_terms( $prepared_args ); |
253 } |
260 } |
254 |
261 |
255 $count_args = $prepared_args; |
262 $count_args = $prepared_args; |
256 |
263 |
257 unset( $count_args['number'], $count_args['offset'] ); |
264 unset( $count_args['number'], $count_args['offset'] ); |
258 |
265 |
259 $total_terms = wp_count_terms( $this->taxonomy, $count_args ); |
266 $total_terms = wp_count_terms( $this->taxonomy, $count_args ); |
260 |
267 |
261 // wp_count_terms can return a falsy value when the term has no children. |
268 // wp_count_terms() can return a falsey value when the term has no children. |
262 if ( ! $total_terms ) { |
269 if ( ! $total_terms ) { |
263 $total_terms = 0; |
270 $total_terms = 0; |
264 } |
271 } |
265 |
272 |
266 $response = array(); |
273 $response = array(); |
310 * |
317 * |
311 * @param int $id Supplied ID. |
318 * @param int $id Supplied ID. |
312 * @return WP_Term|WP_Error Term object if ID is valid, WP_Error otherwise. |
319 * @return WP_Term|WP_Error Term object if ID is valid, WP_Error otherwise. |
313 */ |
320 */ |
314 protected function get_term( $id ) { |
321 protected function get_term( $id ) { |
315 $error = new WP_Error( 'rest_term_invalid', __( 'Term does not exist.' ), array( 'status' => 404 ) ); |
322 $error = new WP_Error( |
|
323 'rest_term_invalid', |
|
324 __( 'Term does not exist.' ), |
|
325 array( 'status' => 404 ) |
|
326 ); |
316 |
327 |
317 if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { |
328 if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { |
318 return $error; |
329 return $error; |
319 } |
330 } |
320 |
331 |
338 * @param WP_REST_Request $request Full details about the request. |
349 * @param WP_REST_Request $request Full details about the request. |
339 * @return bool|WP_Error True if the request has read access for the item, otherwise false or WP_Error object. |
350 * @return bool|WP_Error True if the request has read access for the item, otherwise false or WP_Error object. |
340 */ |
351 */ |
341 public function get_item_permissions_check( $request ) { |
352 public function get_item_permissions_check( $request ) { |
342 $term = $this->get_term( $request['id'] ); |
353 $term = $this->get_term( $request['id'] ); |
|
354 |
343 if ( is_wp_error( $term ) ) { |
355 if ( is_wp_error( $term ) ) { |
344 return $term; |
356 return $term; |
345 } |
357 } |
346 |
358 |
347 if ( 'edit' === $request['context'] && ! current_user_can( 'edit_term', $term->term_id ) ) { |
359 if ( 'edit' === $request['context'] && ! current_user_can( 'edit_term', $term->term_id ) ) { |
348 return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this term.' ), array( 'status' => rest_authorization_required_code() ) ); |
360 return new WP_Error( |
349 } |
361 'rest_forbidden_context', |
|
362 __( 'Sorry, you are not allowed to edit this term.' ), |
|
363 array( 'status' => rest_authorization_required_code() ) |
|
364 ); |
|
365 } |
|
366 |
350 return true; |
367 return true; |
351 } |
368 } |
352 |
369 |
353 /** |
370 /** |
354 * Gets a single term from a taxonomy. |
371 * Gets a single term from a taxonomy. |
382 if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { |
399 if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { |
383 return false; |
400 return false; |
384 } |
401 } |
385 |
402 |
386 $taxonomy_obj = get_taxonomy( $this->taxonomy ); |
403 $taxonomy_obj = get_taxonomy( $this->taxonomy ); |
|
404 |
387 if ( ( is_taxonomy_hierarchical( $this->taxonomy ) |
405 if ( ( is_taxonomy_hierarchical( $this->taxonomy ) |
388 && ! current_user_can( $taxonomy_obj->cap->edit_terms ) ) |
406 && ! current_user_can( $taxonomy_obj->cap->edit_terms ) ) |
389 || ( ! is_taxonomy_hierarchical( $this->taxonomy ) |
407 || ( ! is_taxonomy_hierarchical( $this->taxonomy ) |
390 && ! current_user_can( $taxonomy_obj->cap->assign_terms ) ) ) { |
408 && ! current_user_can( $taxonomy_obj->cap->assign_terms ) ) ) { |
391 return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to create new terms.' ), array( 'status' => rest_authorization_required_code() ) ); |
409 return new WP_Error( |
|
410 'rest_cannot_create', |
|
411 __( 'Sorry, you are not allowed to create terms in this taxonomy.' ), |
|
412 array( 'status' => rest_authorization_required_code() ) |
|
413 ); |
392 } |
414 } |
393 |
415 |
394 return true; |
416 return true; |
395 } |
417 } |
396 |
418 |
403 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. |
425 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. |
404 */ |
426 */ |
405 public function create_item( $request ) { |
427 public function create_item( $request ) { |
406 if ( isset( $request['parent'] ) ) { |
428 if ( isset( $request['parent'] ) ) { |
407 if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) { |
429 if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) { |
408 return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Cannot set parent term, taxonomy is not hierarchical.' ), array( 'status' => 400 ) ); |
430 return new WP_Error( |
|
431 'rest_taxonomy_not_hierarchical', |
|
432 __( 'Cannot set parent term, taxonomy is not hierarchical.' ), |
|
433 array( 'status' => 400 ) |
|
434 ); |
409 } |
435 } |
410 |
436 |
411 $parent = get_term( (int) $request['parent'], $this->taxonomy ); |
437 $parent = get_term( (int) $request['parent'], $this->taxonomy ); |
412 |
438 |
413 if ( ! $parent ) { |
439 if ( ! $parent ) { |
414 return new WP_Error( 'rest_term_invalid', __( 'Parent term does not exist.' ), array( 'status' => 400 ) ); |
440 return new WP_Error( |
|
441 'rest_term_invalid', |
|
442 __( 'Parent term does not exist.' ), |
|
443 array( 'status' => 400 ) |
|
444 ); |
415 } |
445 } |
416 } |
446 } |
417 |
447 |
418 $prepared_term = $this->prepare_item_for_database( $request ); |
448 $prepared_term = $this->prepare_item_for_database( $request ); |
419 |
449 |
421 if ( is_wp_error( $term ) ) { |
451 if ( is_wp_error( $term ) ) { |
422 /* |
452 /* |
423 * If we're going to inform the client that the term already exists, |
453 * If we're going to inform the client that the term already exists, |
424 * give them the identifier for future use. |
454 * give them the identifier for future use. |
425 */ |
455 */ |
426 if ( $term_id = $term->get_error_data( 'term_exists' ) ) { |
456 $term_id = $term->get_error_data( 'term_exists' ); |
|
457 if ( $term_id ) { |
427 $existing_term = get_term( $term_id, $this->taxonomy ); |
458 $existing_term = get_term( $term_id, $this->taxonomy ); |
428 $term->add_data( $existing_term->term_id, 'term_exists' ); |
459 $term->add_data( $existing_term->term_id, 'term_exists' ); |
429 $term->add_data( |
460 $term->add_data( |
430 array( |
461 array( |
431 'status' => 400, |
462 'status' => 400, |
465 |
496 |
466 if ( is_wp_error( $fields_update ) ) { |
497 if ( is_wp_error( $fields_update ) ) { |
467 return $fields_update; |
498 return $fields_update; |
468 } |
499 } |
469 |
500 |
470 $request->set_param( 'context', 'view' ); |
501 $request->set_param( 'context', 'edit' ); |
471 |
502 |
472 /** |
503 /** |
473 * Fires after a single term is completely created or updated via the REST API. |
504 * Fires after a single term is completely created or updated via the REST API. |
474 * |
505 * |
475 * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. |
506 * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. |
499 * @param WP_REST_Request $request Full details about the request. |
530 * @param WP_REST_Request $request Full details about the request. |
500 * @return bool|WP_Error True if the request has access to update the item, false or WP_Error object otherwise. |
531 * @return bool|WP_Error True if the request has access to update the item, false or WP_Error object otherwise. |
501 */ |
532 */ |
502 public function update_item_permissions_check( $request ) { |
533 public function update_item_permissions_check( $request ) { |
503 $term = $this->get_term( $request['id'] ); |
534 $term = $this->get_term( $request['id'] ); |
|
535 |
504 if ( is_wp_error( $term ) ) { |
536 if ( is_wp_error( $term ) ) { |
505 return $term; |
537 return $term; |
506 } |
538 } |
507 |
539 |
508 if ( ! current_user_can( 'edit_term', $term->term_id ) ) { |
540 if ( ! current_user_can( 'edit_term', $term->term_id ) ) { |
509 return new WP_Error( 'rest_cannot_update', __( 'Sorry, you are not allowed to edit this term.' ), array( 'status' => rest_authorization_required_code() ) ); |
541 return new WP_Error( |
|
542 'rest_cannot_update', |
|
543 __( 'Sorry, you are not allowed to edit this term.' ), |
|
544 array( 'status' => rest_authorization_required_code() ) |
|
545 ); |
510 } |
546 } |
511 |
547 |
512 return true; |
548 return true; |
513 } |
549 } |
514 |
550 |
526 return $term; |
562 return $term; |
527 } |
563 } |
528 |
564 |
529 if ( isset( $request['parent'] ) ) { |
565 if ( isset( $request['parent'] ) ) { |
530 if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) { |
566 if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) { |
531 return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Cannot set parent term, taxonomy is not hierarchical.' ), array( 'status' => 400 ) ); |
567 return new WP_Error( |
|
568 'rest_taxonomy_not_hierarchical', |
|
569 __( 'Cannot set parent term, taxonomy is not hierarchical.' ), |
|
570 array( 'status' => 400 ) |
|
571 ); |
532 } |
572 } |
533 |
573 |
534 $parent = get_term( (int) $request['parent'], $this->taxonomy ); |
574 $parent = get_term( (int) $request['parent'], $this->taxonomy ); |
535 |
575 |
536 if ( ! $parent ) { |
576 if ( ! $parent ) { |
537 return new WP_Error( 'rest_term_invalid', __( 'Parent term does not exist.' ), array( 'status' => 400 ) ); |
577 return new WP_Error( |
|
578 'rest_term_invalid', |
|
579 __( 'Parent term does not exist.' ), |
|
580 array( 'status' => 400 ) |
|
581 ); |
538 } |
582 } |
539 } |
583 } |
540 |
584 |
541 $prepared_term = $this->prepare_item_for_database( $request ); |
585 $prepared_term = $this->prepare_item_for_database( $request ); |
542 |
586 |
543 // Only update the term if we haz something to update. |
587 // Only update the term if we have something to update. |
544 if ( ! empty( $prepared_term ) ) { |
588 if ( ! empty( $prepared_term ) ) { |
545 $update = wp_update_term( $term->term_id, $term->taxonomy, wp_slash( (array) $prepared_term ) ); |
589 $update = wp_update_term( $term->term_id, $term->taxonomy, wp_slash( (array) $prepared_term ) ); |
546 |
590 |
547 if ( is_wp_error( $update ) ) { |
591 if ( is_wp_error( $update ) ) { |
548 return $update; |
592 return $update; |
567 |
611 |
568 if ( is_wp_error( $fields_update ) ) { |
612 if ( is_wp_error( $fields_update ) ) { |
569 return $fields_update; |
613 return $fields_update; |
570 } |
614 } |
571 |
615 |
572 $request->set_param( 'context', 'view' ); |
616 $request->set_param( 'context', 'edit' ); |
573 |
617 |
574 /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */ |
618 /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */ |
575 do_action( "rest_after_insert_{$this->taxonomy}", $term, $request, false ); |
619 do_action( "rest_after_insert_{$this->taxonomy}", $term, $request, false ); |
576 |
620 |
577 $response = $this->prepare_item_for_response( $term, $request ); |
621 $response = $this->prepare_item_for_response( $term, $request ); |
587 * @param WP_REST_Request $request Full details about the request. |
631 * @param WP_REST_Request $request Full details about the request. |
588 * @return bool|WP_Error True if the request has access to delete the item, otherwise false or WP_Error object. |
632 * @return bool|WP_Error True if the request has access to delete the item, otherwise false or WP_Error object. |
589 */ |
633 */ |
590 public function delete_item_permissions_check( $request ) { |
634 public function delete_item_permissions_check( $request ) { |
591 $term = $this->get_term( $request['id'] ); |
635 $term = $this->get_term( $request['id'] ); |
|
636 |
592 if ( is_wp_error( $term ) ) { |
637 if ( is_wp_error( $term ) ) { |
593 return $term; |
638 return $term; |
594 } |
639 } |
595 |
640 |
596 if ( ! current_user_can( 'delete_term', $term->term_id ) ) { |
641 if ( ! current_user_can( 'delete_term', $term->term_id ) ) { |
597 return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this term.' ), array( 'status' => rest_authorization_required_code() ) ); |
642 return new WP_Error( |
|
643 'rest_cannot_delete', |
|
644 __( 'Sorry, you are not allowed to delete this term.' ), |
|
645 array( 'status' => rest_authorization_required_code() ) |
|
646 ); |
598 } |
647 } |
599 |
648 |
600 return true; |
649 return true; |
601 } |
650 } |
602 |
651 |
616 |
665 |
617 $force = isset( $request['force'] ) ? (bool) $request['force'] : false; |
666 $force = isset( $request['force'] ) ? (bool) $request['force'] : false; |
618 |
667 |
619 // We don't support trashing for terms. |
668 // We don't support trashing for terms. |
620 if ( ! $force ) { |
669 if ( ! $force ) { |
621 /* translators: %s: force=true */ |
670 return new WP_Error( |
622 return new WP_Error( 'rest_trash_not_supported', sprintf( __( "Terms do not support trashing. Set '%s' to delete." ), 'force=true' ), array( 'status' => 501 ) ); |
671 'rest_trash_not_supported', |
|
672 /* translators: %s: force=true */ |
|
673 sprintf( __( "Terms do not support trashing. Set '%s' to delete." ), 'force=true' ), |
|
674 array( 'status' => 501 ) |
|
675 ); |
623 } |
676 } |
624 |
677 |
625 $request->set_param( 'context', 'view' ); |
678 $request->set_param( 'context', 'view' ); |
626 |
679 |
627 $previous = $this->prepare_item_for_response( $term, $request ); |
680 $previous = $this->prepare_item_for_response( $term, $request ); |
628 |
681 |
629 $retval = wp_delete_term( $term->term_id, $term->taxonomy ); |
682 $retval = wp_delete_term( $term->term_id, $term->taxonomy ); |
630 |
683 |
631 if ( ! $retval ) { |
684 if ( ! $retval ) { |
632 return new WP_Error( 'rest_cannot_delete', __( 'The term cannot be deleted.' ), array( 'status' => 500 ) ); |
685 return new WP_Error( |
|
686 'rest_cannot_delete', |
|
687 __( 'The term cannot be deleted.' ), |
|
688 array( 'status' => 500 ) |
|
689 ); |
633 } |
690 } |
634 |
691 |
635 $response = new WP_REST_Response(); |
692 $response = new WP_REST_Response(); |
636 $response->set_data( |
693 $response->set_data( |
637 array( |
694 array( |
660 * Prepares a single term for create or update. |
717 * Prepares a single term for create or update. |
661 * |
718 * |
662 * @since 4.7.0 |
719 * @since 4.7.0 |
663 * |
720 * |
664 * @param WP_REST_Request $request Request object. |
721 * @param WP_REST_Request $request Request object. |
665 * @return object $prepared_term Term object. |
722 * @return object Term object. |
666 */ |
723 */ |
667 public function prepare_item_for_database( $request ) { |
724 public function prepare_item_for_database( $request ) { |
668 $prepared_term = new stdClass; |
725 $prepared_term = new stdClass; |
669 |
726 |
670 $schema = $this->get_item_schema(); |
727 $schema = $this->get_item_schema(); |
715 /** |
772 /** |
716 * Prepares a single term output for response. |
773 * Prepares a single term output for response. |
717 * |
774 * |
718 * @since 4.7.0 |
775 * @since 4.7.0 |
719 * |
776 * |
720 * @param obj $item Term object. |
777 * @param WP_Term $item Term object. |
721 * @param WP_REST_Request $request Request object. |
778 * @param WP_REST_Request $request Request object. |
722 * @return WP_REST_Response $response Response object. |
779 * @return WP_REST_Response Response object. |
723 */ |
780 */ |
724 public function prepare_item_for_response( $item, $request ) { |
781 public function prepare_item_for_response( $item, $request ) { |
725 |
782 |
726 $fields = $this->get_fields_for_response( $request ); |
783 $fields = $this->get_fields_for_response( $request ); |
727 $data = array(); |
784 $data = array(); |
778 * Allows modification of the term data right before it is returned. |
835 * Allows modification of the term data right before it is returned. |
779 * |
836 * |
780 * @since 4.7.0 |
837 * @since 4.7.0 |
781 * |
838 * |
782 * @param WP_REST_Response $response The response object. |
839 * @param WP_REST_Response $response The response object. |
783 * @param object $item The original term object. |
840 * @param WP_Term $item The original term object. |
784 * @param WP_REST_Request $request Request used to generate the response. |
841 * @param WP_REST_Request $request Request used to generate the response. |
785 */ |
842 */ |
786 return apply_filters( "rest_prepare_{$this->taxonomy}", $response, $item, $request ); |
843 return apply_filters( "rest_prepare_{$this->taxonomy}", $response, $item, $request ); |
787 } |
844 } |
788 |
845 |
789 /** |
846 /** |
790 * Prepares links for the request. |
847 * Prepares links for the request. |
791 * |
848 * |
792 * @since 4.7.0 |
849 * @since 4.7.0 |
793 * |
850 * |
794 * @param object $term Term object. |
851 * @param WP_Term $term Term object. |
795 * @return array Links for the given term. |
852 * @return array Links for the given term. |
796 */ |
853 */ |
797 protected function prepare_links( $term ) { |
854 protected function prepare_links( $term ) { |
798 $base = $this->namespace . '/' . $this->rest_base; |
855 $base = $this->namespace . '/' . $this->rest_base; |
799 $links = array( |
856 $links = array( |
853 * @since 4.7.0 |
910 * @since 4.7.0 |
854 * |
911 * |
855 * @return array Item schema data. |
912 * @return array Item schema data. |
856 */ |
913 */ |
857 public function get_item_schema() { |
914 public function get_item_schema() { |
|
915 if ( $this->schema ) { |
|
916 return $this->add_additional_fields_schema( $this->schema ); |
|
917 } |
|
918 |
858 $schema = array( |
919 $schema = array( |
859 '$schema' => 'http://json-schema.org/draft-04/schema#', |
920 '$schema' => 'http://json-schema.org/draft-04/schema#', |
860 'title' => 'post_tag' === $this->taxonomy ? 'tag' : $this->taxonomy, |
921 'title' => 'post_tag' === $this->taxonomy ? 'tag' : $this->taxonomy, |
861 'type' => 'object', |
922 'type' => 'object', |
862 'properties' => array( |
923 'properties' => array( |