[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/rest-api/endpoints/ -> class-wp-rest-terms-controller.php (source)

   1  <?php
   2  /**
   3   * REST API: WP_REST_Terms_Controller class
   4   *
   5   * @package WordPress
   6   * @subpackage REST_API
   7   * @since 4.7.0
   8   */
   9  
  10  /**
  11   * Core class used to managed terms associated with a taxonomy via the REST API.
  12   *
  13   * @since 4.7.0
  14   *
  15   * @see WP_REST_Controller
  16   */
  17  class WP_REST_Terms_Controller extends WP_REST_Controller {
  18  
  19      /**
  20       * Taxonomy key.
  21       *
  22       * @since 4.7.0
  23       * @var string
  24       */
  25      protected $taxonomy;
  26  
  27      /**
  28       * Instance of a term meta fields object.
  29       *
  30       * @since 4.7.0
  31       * @var WP_REST_Term_Meta_Fields
  32       */
  33      protected $meta;
  34  
  35      /**
  36       * Column to have the terms be sorted by.
  37       *
  38       * @since 4.7.0
  39       * @var string
  40       */
  41      protected $sort_column;
  42  
  43      /**
  44       * Number of terms that were found.
  45       *
  46       * @since 4.7.0
  47       * @var int
  48       */
  49      protected $total_terms;
  50  
  51      /**
  52       * Constructor.
  53       *
  54       * @since 4.7.0
  55       *
  56       * @param string $taxonomy Taxonomy key.
  57       */
  58  	public function __construct( $taxonomy ) {
  59          $this->taxonomy  = $taxonomy;
  60          $this->namespace = 'wp/v2';
  61          $tax_obj         = get_taxonomy( $taxonomy );
  62          $this->rest_base = ! empty( $tax_obj->rest_base ) ? $tax_obj->rest_base : $tax_obj->name;
  63  
  64          $this->meta = new WP_REST_Term_Meta_Fields( $taxonomy );
  65      }
  66  
  67      /**
  68       * Registers the routes for the objects of the controller.
  69       *
  70       * @since 4.7.0
  71       *
  72       * @see register_rest_route()
  73       */
  74  	public function register_routes() {
  75  
  76          register_rest_route(
  77              $this->namespace,
  78              '/' . $this->rest_base,
  79              array(
  80                  array(
  81                      'methods'             => WP_REST_Server::READABLE,
  82                      'callback'            => array( $this, 'get_items' ),
  83                      'permission_callback' => array( $this, 'get_items_permissions_check' ),
  84                      'args'                => $this->get_collection_params(),
  85                  ),
  86                  array(
  87                      'methods'             => WP_REST_Server::CREATABLE,
  88                      'callback'            => array( $this, 'create_item' ),
  89                      'permission_callback' => array( $this, 'create_item_permissions_check' ),
  90                      'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
  91                  ),
  92                  'schema' => array( $this, 'get_public_item_schema' ),
  93              )
  94          );
  95  
  96          register_rest_route(
  97              $this->namespace,
  98              '/' . $this->rest_base . '/(?P<id>[\d]+)',
  99              array(
 100                  'args'   => array(
 101                      'id' => array(
 102                          'description' => __( 'Unique identifier for the term.' ),
 103                          'type'        => 'integer',
 104                      ),
 105                  ),
 106                  array(
 107                      'methods'             => WP_REST_Server::READABLE,
 108                      'callback'            => array( $this, 'get_item' ),
 109                      'permission_callback' => array( $this, 'get_item_permissions_check' ),
 110                      'args'                => array(
 111                          'context' => $this->get_context_param( array( 'default' => 'view' ) ),
 112                      ),
 113                  ),
 114                  array(
 115                      'methods'             => WP_REST_Server::EDITABLE,
 116                      'callback'            => array( $this, 'update_item' ),
 117                      'permission_callback' => array( $this, 'update_item_permissions_check' ),
 118                      'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
 119                  ),
 120                  array(
 121                      'methods'             => WP_REST_Server::DELETABLE,
 122                      'callback'            => array( $this, 'delete_item' ),
 123                      'permission_callback' => array( $this, 'delete_item_permissions_check' ),
 124                      'args'                => array(
 125                          'force' => array(
 126                              'type'        => 'boolean',
 127                              'default'     => false,
 128                              'description' => __( 'Required to be true, as terms do not support trashing.' ),
 129                          ),
 130                      ),
 131                  ),
 132                  'schema' => array( $this, 'get_public_item_schema' ),
 133              )
 134          );
 135      }
 136  
 137      /**
 138       * Checks if a request has access to read terms in the specified taxonomy.
 139       *
 140       * @since 4.7.0
 141       *
 142       * @param WP_REST_Request $request Full details about the request.
 143       * @return true|WP_Error True if the request has read access, otherwise false or WP_Error object.
 144       */
 145  	public function get_items_permissions_check( $request ) {
 146          $tax_obj = get_taxonomy( $this->taxonomy );
 147  
 148          if ( ! $tax_obj || ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) {
 149              return false;
 150          }
 151  
 152          if ( 'edit' === $request['context'] && ! current_user_can( $tax_obj->cap->edit_terms ) ) {
 153              return new WP_Error(
 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  
 160          return true;
 161      }
 162  
 163      /**
 164       * Retrieves terms associated with a taxonomy.
 165       *
 166       * @since 4.7.0
 167       *
 168       * @param WP_REST_Request $request Full details about the request.
 169       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 170       */
 171  	public function get_items( $request ) {
 172  
 173          // Retrieve the list of registered collection query parameters.
 174          $registered = $this->get_collection_params();
 175  
 176          /*
 177           * This array defines mappings between public API query parameters whose
 178           * values are accepted as-passed, and their internal WP_Query parameter
 179           * name equivalents (some are the same). Only values which are also
 180           * present in $registered will be set.
 181           */
 182          $parameter_mappings = array(
 183              'exclude'    => 'exclude',
 184              'include'    => 'include',
 185              'order'      => 'order',
 186              'orderby'    => 'orderby',
 187              'post'       => 'post',
 188              'hide_empty' => 'hide_empty',
 189              'per_page'   => 'number',
 190              'search'     => 'search',
 191              'slug'       => 'slug',
 192          );
 193  
 194          $prepared_args = array( 'taxonomy' => $this->taxonomy );
 195  
 196          /*
 197           * For each known parameter which is both registered and present in the request,
 198           * set the parameter's value on the query $prepared_args.
 199           */
 200          foreach ( $parameter_mappings as $api_param => $wp_param ) {
 201              if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) {
 202                  $prepared_args[ $wp_param ] = $request[ $api_param ];
 203              }
 204          }
 205  
 206          if ( isset( $prepared_args['orderby'] ) && isset( $request['orderby'] ) ) {
 207              $orderby_mappings = array(
 208                  'include_slugs' => 'slug__in',
 209              );
 210  
 211              if ( isset( $orderby_mappings[ $request['orderby'] ] ) ) {
 212                  $prepared_args['orderby'] = $orderby_mappings[ $request['orderby'] ];
 213              }
 214          }
 215  
 216          if ( isset( $registered['offset'] ) && ! empty( $request['offset'] ) ) {
 217              $prepared_args['offset'] = $request['offset'];
 218          } else {
 219              $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
 220          }
 221  
 222          $taxonomy_obj = get_taxonomy( $this->taxonomy );
 223  
 224          if ( $taxonomy_obj->hierarchical && isset( $registered['parent'], $request['parent'] ) ) {
 225              if ( 0 === $request['parent'] ) {
 226                  // Only query top-level terms.
 227                  $prepared_args['parent'] = 0;
 228              } else {
 229                  if ( $request['parent'] ) {
 230                      $prepared_args['parent'] = $request['parent'];
 231                  }
 232              }
 233          }
 234  
 235          /**
 236           * Filters get_terms() arguments when querying users via the REST API.
 237           *
 238           * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug.
 239           *
 240           * Possible hook names include:
 241           *
 242           *  - `rest_category_query`
 243           *  - `rest_post_tag_query`
 244           *
 245           * Enables adding extra arguments or setting defaults for a terms
 246           * collection request.
 247           *
 248           * @since 4.7.0
 249           *
 250           * @link https://developer.wordpress.org/reference/functions/get_terms/
 251           *
 252           * @param array           $prepared_args Array of arguments to be
 253           *                                       passed to get_terms().
 254           * @param WP_REST_Request $request       The REST API request.
 255           */
 256          $prepared_args = apply_filters( "rest_{$this->taxonomy}_query", $prepared_args, $request );
 257  
 258          if ( ! empty( $prepared_args['post'] ) ) {
 259              $query_result = wp_get_object_terms( $prepared_args['post'], $this->taxonomy, $prepared_args );
 260  
 261              // Used when calling wp_count_terms() below.
 262              $prepared_args['object_ids'] = $prepared_args['post'];
 263          } else {
 264              $query_result = get_terms( $prepared_args );
 265          }
 266  
 267          $count_args = $prepared_args;
 268  
 269          unset( $count_args['number'], $count_args['offset'] );
 270  
 271          $total_terms = wp_count_terms( $count_args );
 272  
 273          // wp_count_terms() can return a falsey value when the term has no children.
 274          if ( ! $total_terms ) {
 275              $total_terms = 0;
 276          }
 277  
 278          $response = array();
 279  
 280          foreach ( $query_result as $term ) {
 281              $data       = $this->prepare_item_for_response( $term, $request );
 282              $response[] = $this->prepare_response_for_collection( $data );
 283          }
 284  
 285          $response = rest_ensure_response( $response );
 286  
 287          // Store pagination values for headers.
 288          $per_page = (int) $prepared_args['number'];
 289          $page     = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
 290  
 291          $response->header( 'X-WP-Total', (int) $total_terms );
 292  
 293          $max_pages = ceil( $total_terms / $per_page );
 294  
 295          $response->header( 'X-WP-TotalPages', (int) $max_pages );
 296  
 297          $base = add_query_arg( urlencode_deep( $request->get_query_params() ), rest_url( $this->namespace . '/' . $this->rest_base ) );
 298          if ( $page > 1 ) {
 299              $prev_page = $page - 1;
 300  
 301              if ( $prev_page > $max_pages ) {
 302                  $prev_page = $max_pages;
 303              }
 304  
 305              $prev_link = add_query_arg( 'page', $prev_page, $base );
 306              $response->link_header( 'prev', $prev_link );
 307          }
 308          if ( $max_pages > $page ) {
 309              $next_page = $page + 1;
 310              $next_link = add_query_arg( 'page', $next_page, $base );
 311  
 312              $response->link_header( 'next', $next_link );
 313          }
 314  
 315          return $response;
 316      }
 317  
 318      /**
 319       * Get the term, if the ID is valid.
 320       *
 321       * @since 4.7.2
 322       *
 323       * @param int $id Supplied ID.
 324       * @return WP_Term|WP_Error Term object if ID is valid, WP_Error otherwise.
 325       */
 326  	protected function get_term( $id ) {
 327          $error = new WP_Error(
 328              'rest_term_invalid',
 329              __( 'Term does not exist.' ),
 330              array( 'status' => 404 )
 331          );
 332  
 333          if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) {
 334              return $error;
 335          }
 336  
 337          if ( (int) $id <= 0 ) {
 338              return $error;
 339          }
 340  
 341          $term = get_term( (int) $id, $this->taxonomy );
 342          if ( empty( $term ) || $term->taxonomy !== $this->taxonomy ) {
 343              return $error;
 344          }
 345  
 346          return $term;
 347      }
 348  
 349      /**
 350       * Checks if a request has access to read or edit the specified term.
 351       *
 352       * @since 4.7.0
 353       *
 354       * @param WP_REST_Request $request Full details about the request.
 355       * @return true|WP_Error True if the request has read access for the item, otherwise false or WP_Error object.
 356       */
 357  	public function get_item_permissions_check( $request ) {
 358          $term = $this->get_term( $request['id'] );
 359  
 360          if ( is_wp_error( $term ) ) {
 361              return $term;
 362          }
 363  
 364          if ( 'edit' === $request['context'] && ! current_user_can( 'edit_term', $term->term_id ) ) {
 365              return new WP_Error(
 366                  'rest_forbidden_context',
 367                  __( 'Sorry, you are not allowed to edit this term.' ),
 368                  array( 'status' => rest_authorization_required_code() )
 369              );
 370          }
 371  
 372          return true;
 373      }
 374  
 375      /**
 376       * Gets a single term from a taxonomy.
 377       *
 378       * @since 4.7.0
 379       *
 380       * @param WP_REST_Request $request Full details about the request.
 381       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 382       */
 383  	public function get_item( $request ) {
 384          $term = $this->get_term( $request['id'] );
 385          if ( is_wp_error( $term ) ) {
 386              return $term;
 387          }
 388  
 389          $response = $this->prepare_item_for_response( $term, $request );
 390  
 391          return rest_ensure_response( $response );
 392      }
 393  
 394      /**
 395       * Checks if a request has access to create a term.
 396       *
 397       * @since 4.7.0
 398       *
 399       * @param WP_REST_Request $request Full details about the request.
 400       * @return true|WP_Error True if the request has access to create items, false or WP_Error object otherwise.
 401       */
 402  	public function create_item_permissions_check( $request ) {
 403  
 404          if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) {
 405              return false;
 406          }
 407  
 408          $taxonomy_obj = get_taxonomy( $this->taxonomy );
 409  
 410          if ( ( is_taxonomy_hierarchical( $this->taxonomy )
 411                  && ! current_user_can( $taxonomy_obj->cap->edit_terms ) )
 412              || ( ! is_taxonomy_hierarchical( $this->taxonomy )
 413                  && ! current_user_can( $taxonomy_obj->cap->assign_terms ) ) ) {
 414              return new WP_Error(
 415                  'rest_cannot_create',
 416                  __( 'Sorry, you are not allowed to create terms in this taxonomy.' ),
 417                  array( 'status' => rest_authorization_required_code() )
 418              );
 419          }
 420  
 421          return true;
 422      }
 423  
 424      /**
 425       * Creates a single term in a taxonomy.
 426       *
 427       * @since 4.7.0
 428       *
 429       * @param WP_REST_Request $request Full details about the request.
 430       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 431       */
 432  	public function create_item( $request ) {
 433          if ( isset( $request['parent'] ) ) {
 434              if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) {
 435                  return new WP_Error(
 436                      'rest_taxonomy_not_hierarchical',
 437                      __( 'Cannot set parent term, taxonomy is not hierarchical.' ),
 438                      array( 'status' => 400 )
 439                  );
 440              }
 441  
 442              $parent = get_term( (int) $request['parent'], $this->taxonomy );
 443  
 444              if ( ! $parent ) {
 445                  return new WP_Error(
 446                      'rest_term_invalid',
 447                      __( 'Parent term does not exist.' ),
 448                      array( 'status' => 400 )
 449                  );
 450              }
 451          }
 452  
 453          $prepared_term = $this->prepare_item_for_database( $request );
 454  
 455          $term = wp_insert_term( wp_slash( $prepared_term->name ), $this->taxonomy, wp_slash( (array) $prepared_term ) );
 456          if ( is_wp_error( $term ) ) {
 457              /*
 458               * If we're going to inform the client that the term already exists,
 459               * give them the identifier for future use.
 460               */
 461              $term_id = $term->get_error_data( 'term_exists' );
 462              if ( $term_id ) {
 463                  $existing_term = get_term( $term_id, $this->taxonomy );
 464                  $term->add_data( $existing_term->term_id, 'term_exists' );
 465                  $term->add_data(
 466                      array(
 467                          'status'  => 400,
 468                          'term_id' => $term_id,
 469                      )
 470                  );
 471              }
 472  
 473              return $term;
 474          }
 475  
 476          $term = get_term( $term['term_id'], $this->taxonomy );
 477  
 478          /**
 479           * Fires after a single term is created or updated via the REST API.
 480           *
 481           * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug.
 482           *
 483           * Possible hook names include:
 484           *
 485           *  - `rest_insert_category`
 486           *  - `rest_insert_post_tag`
 487           *
 488           * @since 4.7.0
 489           *
 490           * @param WP_Term         $term     Inserted or updated term object.
 491           * @param WP_REST_Request $request  Request object.
 492           * @param bool            $creating True when creating a term, false when updating.
 493           */
 494          do_action( "rest_insert_{$this->taxonomy}", $term, $request, true );
 495  
 496          $schema = $this->get_item_schema();
 497          if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
 498              $meta_update = $this->meta->update_value( $request['meta'], $term->term_id );
 499  
 500              if ( is_wp_error( $meta_update ) ) {
 501                  return $meta_update;
 502              }
 503          }
 504  
 505          $fields_update = $this->update_additional_fields_for_object( $term, $request );
 506  
 507          if ( is_wp_error( $fields_update ) ) {
 508              return $fields_update;
 509          }
 510  
 511          $request->set_param( 'context', 'edit' );
 512  
 513          /**
 514           * Fires after a single term is completely created or updated via the REST API.
 515           *
 516           * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug.
 517           *
 518           * Possible hook names include:
 519           *
 520           *  - `rest_after_insert_category`
 521           *  - `rest_after_insert_post_tag`
 522           *
 523           * @since 5.0.0
 524           *
 525           * @param WP_Term         $term     Inserted or updated term object.
 526           * @param WP_REST_Request $request  Request object.
 527           * @param bool            $creating True when creating a term, false when updating.
 528           */
 529          do_action( "rest_after_insert_{$this->taxonomy}", $term, $request, true );
 530  
 531          $response = $this->prepare_item_for_response( $term, $request );
 532          $response = rest_ensure_response( $response );
 533  
 534          $response->set_status( 201 );
 535          $response->header( 'Location', rest_url( $this->namespace . '/' . $this->rest_base . '/' . $term->term_id ) );
 536  
 537          return $response;
 538      }
 539  
 540      /**
 541       * Checks if a request has access to update the specified term.
 542       *
 543       * @since 4.7.0
 544       *
 545       * @param WP_REST_Request $request Full details about the request.
 546       * @return true|WP_Error True if the request has access to update the item, false or WP_Error object otherwise.
 547       */
 548  	public function update_item_permissions_check( $request ) {
 549          $term = $this->get_term( $request['id'] );
 550  
 551          if ( is_wp_error( $term ) ) {
 552              return $term;
 553          }
 554  
 555          if ( ! current_user_can( 'edit_term', $term->term_id ) ) {
 556              return new WP_Error(
 557                  'rest_cannot_update',
 558                  __( 'Sorry, you are not allowed to edit this term.' ),
 559                  array( 'status' => rest_authorization_required_code() )
 560              );
 561          }
 562  
 563          return true;
 564      }
 565  
 566      /**
 567       * Updates a single term from a taxonomy.
 568       *
 569       * @since 4.7.0
 570       *
 571       * @param WP_REST_Request $request Full details about the request.
 572       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 573       */
 574  	public function update_item( $request ) {
 575          $term = $this->get_term( $request['id'] );
 576          if ( is_wp_error( $term ) ) {
 577              return $term;
 578          }
 579  
 580          if ( isset( $request['parent'] ) ) {
 581              if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) {
 582                  return new WP_Error(
 583                      'rest_taxonomy_not_hierarchical',
 584                      __( 'Cannot set parent term, taxonomy is not hierarchical.' ),
 585                      array( 'status' => 400 )
 586                  );
 587              }
 588  
 589              $parent = get_term( (int) $request['parent'], $this->taxonomy );
 590  
 591              if ( ! $parent ) {
 592                  return new WP_Error(
 593                      'rest_term_invalid',
 594                      __( 'Parent term does not exist.' ),
 595                      array( 'status' => 400 )
 596                  );
 597              }
 598          }
 599  
 600          $prepared_term = $this->prepare_item_for_database( $request );
 601  
 602          // Only update the term if we have something to update.
 603          if ( ! empty( $prepared_term ) ) {
 604              $update = wp_update_term( $term->term_id, $term->taxonomy, wp_slash( (array) $prepared_term ) );
 605  
 606              if ( is_wp_error( $update ) ) {
 607                  return $update;
 608              }
 609          }
 610  
 611          $term = get_term( $term->term_id, $this->taxonomy );
 612  
 613          /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */
 614          do_action( "rest_insert_{$this->taxonomy}", $term, $request, false );
 615  
 616          $schema = $this->get_item_schema();
 617          if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
 618              $meta_update = $this->meta->update_value( $request['meta'], $term->term_id );
 619  
 620              if ( is_wp_error( $meta_update ) ) {
 621                  return $meta_update;
 622              }
 623          }
 624  
 625          $fields_update = $this->update_additional_fields_for_object( $term, $request );
 626  
 627          if ( is_wp_error( $fields_update ) ) {
 628              return $fields_update;
 629          }
 630  
 631          $request->set_param( 'context', 'edit' );
 632  
 633          /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */
 634          do_action( "rest_after_insert_{$this->taxonomy}", $term, $request, false );
 635  
 636          $response = $this->prepare_item_for_response( $term, $request );
 637  
 638          return rest_ensure_response( $response );
 639      }
 640  
 641      /**
 642       * Checks if a request has access to delete the specified term.
 643       *
 644       * @since 4.7.0
 645       *
 646       * @param WP_REST_Request $request Full details about the request.
 647       * @return true|WP_Error True if the request has access to delete the item, otherwise false or WP_Error object.
 648       */
 649  	public function delete_item_permissions_check( $request ) {
 650          $term = $this->get_term( $request['id'] );
 651  
 652          if ( is_wp_error( $term ) ) {
 653              return $term;
 654          }
 655  
 656          if ( ! current_user_can( 'delete_term', $term->term_id ) ) {
 657              return new WP_Error(
 658                  'rest_cannot_delete',
 659                  __( 'Sorry, you are not allowed to delete this term.' ),
 660                  array( 'status' => rest_authorization_required_code() )
 661              );
 662          }
 663  
 664          return true;
 665      }
 666  
 667      /**
 668       * Deletes a single term from a taxonomy.
 669       *
 670       * @since 4.7.0
 671       *
 672       * @param WP_REST_Request $request Full details about the request.
 673       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 674       */
 675  	public function delete_item( $request ) {
 676          $term = $this->get_term( $request['id'] );
 677          if ( is_wp_error( $term ) ) {
 678              return $term;
 679          }
 680  
 681          $force = isset( $request['force'] ) ? (bool) $request['force'] : false;
 682  
 683          // We don't support trashing for terms.
 684          if ( ! $force ) {
 685              return new WP_Error(
 686                  'rest_trash_not_supported',
 687                  /* translators: %s: force=true */
 688                  sprintf( __( "Terms do not support trashing. Set '%s' to delete." ), 'force=true' ),
 689                  array( 'status' => 501 )
 690              );
 691          }
 692  
 693          $request->set_param( 'context', 'view' );
 694  
 695          $previous = $this->prepare_item_for_response( $term, $request );
 696  
 697          $retval = wp_delete_term( $term->term_id, $term->taxonomy );
 698  
 699          if ( ! $retval ) {
 700              return new WP_Error(
 701                  'rest_cannot_delete',
 702                  __( 'The term cannot be deleted.' ),
 703                  array( 'status' => 500 )
 704              );
 705          }
 706  
 707          $response = new WP_REST_Response();
 708          $response->set_data(
 709              array(
 710                  'deleted'  => true,
 711                  'previous' => $previous->get_data(),
 712              )
 713          );
 714  
 715          /**
 716           * Fires after a single term is deleted via the REST API.
 717           *
 718           * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug.
 719           *
 720           * Possible hook names include:
 721           *
 722           *  - `rest_delete_category`
 723           *  - `rest_delete_post_tag`
 724           *
 725           * @since 4.7.0
 726           *
 727           * @param WP_Term          $term     The deleted term.
 728           * @param WP_REST_Response $response The response data.
 729           * @param WP_REST_Request  $request  The request sent to the API.
 730           */
 731          do_action( "rest_delete_{$this->taxonomy}", $term, $response, $request );
 732  
 733          return $response;
 734      }
 735  
 736      /**
 737       * Prepares a single term for create or update.
 738       *
 739       * @since 4.7.0
 740       *
 741       * @param WP_REST_Request $request Request object.
 742       * @return object Term object.
 743       */
 744  	public function prepare_item_for_database( $request ) {
 745          $prepared_term = new stdClass;
 746  
 747          $schema = $this->get_item_schema();
 748          if ( isset( $request['name'] ) && ! empty( $schema['properties']['name'] ) ) {
 749              $prepared_term->name = $request['name'];
 750          }
 751  
 752          if ( isset( $request['slug'] ) && ! empty( $schema['properties']['slug'] ) ) {
 753              $prepared_term->slug = $request['slug'];
 754          }
 755  
 756          if ( isset( $request['taxonomy'] ) && ! empty( $schema['properties']['taxonomy'] ) ) {
 757              $prepared_term->taxonomy = $request['taxonomy'];
 758          }
 759  
 760          if ( isset( $request['description'] ) && ! empty( $schema['properties']['description'] ) ) {
 761              $prepared_term->description = $request['description'];
 762          }
 763  
 764          if ( isset( $request['parent'] ) && ! empty( $schema['properties']['parent'] ) ) {
 765              $parent_term_id   = 0;
 766              $requested_parent = (int) $request['parent'];
 767  
 768              if ( $requested_parent ) {
 769                  $parent_term = get_term( $requested_parent, $this->taxonomy );
 770  
 771                  if ( $parent_term instanceof WP_Term ) {
 772                      $parent_term_id = $parent_term->term_id;
 773                  }
 774              }
 775  
 776              $prepared_term->parent = $parent_term_id;
 777          }
 778  
 779          /**
 780           * Filters term data before inserting term via the REST API.
 781           *
 782           * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug.
 783           *
 784           * Possible hook names include:
 785           *
 786           *  - `rest_pre_insert_category`
 787           *  - `rest_pre_insert_post_tag`
 788           *
 789           * @since 4.7.0
 790           *
 791           * @param object          $prepared_term Term object.
 792           * @param WP_REST_Request $request       Request object.
 793           */
 794          return apply_filters( "rest_pre_insert_{$this->taxonomy}", $prepared_term, $request );
 795      }
 796  
 797      /**
 798       * Prepares a single term output for response.
 799       *
 800       * @since 4.7.0
 801       *
 802       * @param WP_Term         $item    Term object.
 803       * @param WP_REST_Request $request Request object.
 804       * @return WP_REST_Response Response object.
 805       */
 806  	public function prepare_item_for_response( $item, $request ) {
 807  
 808          $fields = $this->get_fields_for_response( $request );
 809          $data   = array();
 810  
 811          if ( in_array( 'id', $fields, true ) ) {
 812              $data['id'] = (int) $item->term_id;
 813          }
 814  
 815          if ( in_array( 'count', $fields, true ) ) {
 816              $data['count'] = (int) $item->count;
 817          }
 818  
 819          if ( in_array( 'description', $fields, true ) ) {
 820              $data['description'] = $item->description;
 821          }
 822  
 823          if ( in_array( 'link', $fields, true ) ) {
 824              $data['link'] = get_term_link( $item );
 825          }
 826  
 827          if ( in_array( 'name', $fields, true ) ) {
 828              $data['name'] = $item->name;
 829          }
 830  
 831          if ( in_array( 'slug', $fields, true ) ) {
 832              $data['slug'] = $item->slug;
 833          }
 834  
 835          if ( in_array( 'taxonomy', $fields, true ) ) {
 836              $data['taxonomy'] = $item->taxonomy;
 837          }
 838  
 839          if ( in_array( 'parent', $fields, true ) ) {
 840              $data['parent'] = (int) $item->parent;
 841          }
 842  
 843          if ( in_array( 'meta', $fields, true ) ) {
 844              $data['meta'] = $this->meta->get_value( $item->term_id, $request );
 845          }
 846  
 847          $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
 848          $data    = $this->add_additional_fields_to_object( $data, $request );
 849          $data    = $this->filter_response_by_context( $data, $context );
 850  
 851          $response = rest_ensure_response( $data );
 852  
 853          $response->add_links( $this->prepare_links( $item ) );
 854  
 855          /**
 856           * Filters the term data for a REST API response.
 857           *
 858           * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug.
 859           *
 860           * Possible hook names include:
 861           *
 862           *  - `rest_prepare_category`
 863           *  - `rest_prepare_post_tag`
 864           *
 865           * Allows modification of the term data right before it is returned.
 866           *
 867           * @since 4.7.0
 868           *
 869           * @param WP_REST_Response  $response  The response object.
 870           * @param WP_Term           $item      The original term object.
 871           * @param WP_REST_Request   $request   Request used to generate the response.
 872           */
 873          return apply_filters( "rest_prepare_{$this->taxonomy}", $response, $item, $request );
 874      }
 875  
 876      /**
 877       * Prepares links for the request.
 878       *
 879       * @since 4.7.0
 880       *
 881       * @param WP_Term $term Term object.
 882       * @return array Links for the given term.
 883       */
 884  	protected function prepare_links( $term ) {
 885          $base  = $this->namespace . '/' . $this->rest_base;
 886          $links = array(
 887              'self'       => array(
 888                  'href' => rest_url( trailingslashit( $base ) . $term->term_id ),
 889              ),
 890              'collection' => array(
 891                  'href' => rest_url( $base ),
 892              ),
 893              'about'      => array(
 894                  'href' => rest_url( sprintf( 'wp/v2/taxonomies/%s', $this->taxonomy ) ),
 895              ),
 896          );
 897  
 898          if ( $term->parent ) {
 899              $parent_term = get_term( (int) $term->parent, $term->taxonomy );
 900  
 901              if ( $parent_term ) {
 902                  $links['up'] = array(
 903                      'href'       => rest_url( trailingslashit( $base ) . $parent_term->term_id ),
 904                      'embeddable' => true,
 905                  );
 906              }
 907          }
 908  
 909          $taxonomy_obj = get_taxonomy( $term->taxonomy );
 910  
 911          if ( empty( $taxonomy_obj->object_type ) ) {
 912              return $links;
 913          }
 914  
 915          $post_type_links = array();
 916  
 917          foreach ( $taxonomy_obj->object_type as $type ) {
 918              $post_type_object = get_post_type_object( $type );
 919  
 920              if ( empty( $post_type_object->show_in_rest ) ) {
 921                  continue;
 922              }
 923  
 924              $rest_base         = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
 925              $post_type_links[] = array(
 926                  'href' => add_query_arg( $this->rest_base, $term->term_id, rest_url( sprintf( 'wp/v2/%s', $rest_base ) ) ),
 927              );
 928          }
 929  
 930          if ( ! empty( $post_type_links ) ) {
 931              $links['https://api.w.org/post_type'] = $post_type_links;
 932          }
 933  
 934          return $links;
 935      }
 936  
 937      /**
 938       * Retrieves the term's schema, conforming to JSON Schema.
 939       *
 940       * @since 4.7.0
 941       *
 942       * @return array Item schema data.
 943       */
 944  	public function get_item_schema() {
 945          if ( $this->schema ) {
 946              return $this->add_additional_fields_schema( $this->schema );
 947          }
 948  
 949          $schema = array(
 950              '$schema'    => 'http://json-schema.org/draft-04/schema#',
 951              'title'      => 'post_tag' === $this->taxonomy ? 'tag' : $this->taxonomy,
 952              'type'       => 'object',
 953              'properties' => array(
 954                  'id'          => array(
 955                      'description' => __( 'Unique identifier for the term.' ),
 956                      'type'        => 'integer',
 957                      'context'     => array( 'view', 'embed', 'edit' ),
 958                      'readonly'    => true,
 959                  ),
 960                  'count'       => array(
 961                      'description' => __( 'Number of published posts for the term.' ),
 962                      'type'        => 'integer',
 963                      'context'     => array( 'view', 'edit' ),
 964                      'readonly'    => true,
 965                  ),
 966                  'description' => array(
 967                      'description' => __( 'HTML description of the term.' ),
 968                      'type'        => 'string',
 969                      'context'     => array( 'view', 'edit' ),
 970                  ),
 971                  'link'        => array(
 972                      'description' => __( 'URL of the term.' ),
 973                      'type'        => 'string',
 974                      'format'      => 'uri',
 975                      'context'     => array( 'view', 'embed', 'edit' ),
 976                      'readonly'    => true,
 977                  ),
 978                  'name'        => array(
 979                      'description' => __( 'HTML title for the term.' ),
 980                      'type'        => 'string',
 981                      'context'     => array( 'view', 'embed', 'edit' ),
 982                      'arg_options' => array(
 983                          'sanitize_callback' => 'sanitize_text_field',
 984                      ),
 985                      'required'    => true,
 986                  ),
 987                  'slug'        => array(
 988                      'description' => __( 'An alphanumeric identifier for the term unique to its type.' ),
 989                      'type'        => 'string',
 990                      'context'     => array( 'view', 'embed', 'edit' ),
 991                      'arg_options' => array(
 992                          'sanitize_callback' => array( $this, 'sanitize_slug' ),
 993                      ),
 994                  ),
 995                  'taxonomy'    => array(
 996                      'description' => __( 'Type attribution for the term.' ),
 997                      'type'        => 'string',
 998                      'enum'        => array( $this->taxonomy ),
 999                      'context'     => array( 'view', 'embed', 'edit' ),
1000                      'readonly'    => true,
1001                  ),
1002              ),
1003          );
1004  
1005          $taxonomy = get_taxonomy( $this->taxonomy );
1006  
1007          if ( $taxonomy->hierarchical ) {
1008              $schema['properties']['parent'] = array(
1009                  'description' => __( 'The parent term ID.' ),
1010                  'type'        => 'integer',
1011                  'context'     => array( 'view', 'edit' ),
1012              );
1013          }
1014  
1015          $schema['properties']['meta'] = $this->meta->get_field_schema();
1016  
1017          $this->schema = $schema;
1018  
1019          return $this->add_additional_fields_schema( $this->schema );
1020      }
1021  
1022      /**
1023       * Retrieves the query params for collections.
1024       *
1025       * @since 4.7.0
1026       *
1027       * @return array Collection parameters.
1028       */
1029  	public function get_collection_params() {
1030          $query_params = parent::get_collection_params();
1031          $taxonomy     = get_taxonomy( $this->taxonomy );
1032  
1033          $query_params['context']['default'] = 'view';
1034  
1035          $query_params['exclude'] = array(
1036              'description' => __( 'Ensure result set excludes specific IDs.' ),
1037              'type'        => 'array',
1038              'items'       => array(
1039                  'type' => 'integer',
1040              ),
1041              'default'     => array(),
1042          );
1043  
1044          $query_params['include'] = array(
1045              'description' => __( 'Limit result set to specific IDs.' ),
1046              'type'        => 'array',
1047              'items'       => array(
1048                  'type' => 'integer',
1049              ),
1050              'default'     => array(),
1051          );
1052  
1053          if ( ! $taxonomy->hierarchical ) {
1054              $query_params['offset'] = array(
1055                  'description' => __( 'Offset the result set by a specific number of items.' ),
1056                  'type'        => 'integer',
1057              );
1058          }
1059  
1060          $query_params['order'] = array(
1061              'description' => __( 'Order sort attribute ascending or descending.' ),
1062              'type'        => 'string',
1063              'default'     => 'asc',
1064              'enum'        => array(
1065                  'asc',
1066                  'desc',
1067              ),
1068          );
1069  
1070          $query_params['orderby'] = array(
1071              'description' => __( 'Sort collection by term attribute.' ),
1072              'type'        => 'string',
1073              'default'     => 'name',
1074              'enum'        => array(
1075                  'id',
1076                  'include',
1077                  'name',
1078                  'slug',
1079                  'include_slugs',
1080                  'term_group',
1081                  'description',
1082                  'count',
1083              ),
1084          );
1085  
1086          $query_params['hide_empty'] = array(
1087              'description' => __( 'Whether to hide terms not assigned to any posts.' ),
1088              'type'        => 'boolean',
1089              'default'     => false,
1090          );
1091  
1092          if ( $taxonomy->hierarchical ) {
1093              $query_params['parent'] = array(
1094                  'description' => __( 'Limit result set to terms assigned to a specific parent.' ),
1095                  'type'        => 'integer',
1096              );
1097          }
1098  
1099          $query_params['post'] = array(
1100              'description' => __( 'Limit result set to terms assigned to a specific post.' ),
1101              'type'        => 'integer',
1102              'default'     => null,
1103          );
1104  
1105          $query_params['slug'] = array(
1106              'description' => __( 'Limit result set to terms with one or more specific slugs.' ),
1107              'type'        => 'array',
1108              'items'       => array(
1109                  'type' => 'string',
1110              ),
1111          );
1112  
1113          /**
1114           * Filters collection parameters for the terms controller.
1115           *
1116           * The dynamic part of the filter `$this->taxonomy` refers to the taxonomy
1117           * slug for the controller.
1118           *
1119           * This filter registers the collection parameter, but does not map the
1120           * collection parameter to an internal WP_Term_Query parameter.  Use the
1121           * `rest_{$this->taxonomy}_query` filter to set WP_Term_Query parameters.
1122           *
1123           * @since 4.7.0
1124           *
1125           * @param array       $query_params JSON Schema-formatted collection parameters.
1126           * @param WP_Taxonomy $taxonomy     Taxonomy object.
1127           */
1128          return apply_filters( "rest_{$this->taxonomy}_collection_params", $query_params, $taxonomy );
1129      }
1130  
1131      /**
1132       * Checks that the taxonomy is valid.
1133       *
1134       * @since 4.7.0
1135       *
1136       * @param string $taxonomy Taxonomy to check.
1137       * @return bool Whether the taxonomy is allowed for REST management.
1138       */
1139  	protected function check_is_taxonomy_allowed( $taxonomy ) {
1140          $taxonomy_obj = get_taxonomy( $taxonomy );
1141          if ( $taxonomy_obj && ! empty( $taxonomy_obj->show_in_rest ) ) {
1142              return true;
1143          }
1144          return false;
1145      }
1146  }


Generated: Tue May 18 01:00:05 2021 Cross-referenced by PHPXref 0.7.1