[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * REST API: WP_REST_Comments_Controller class
   4   *
   5   * @package WordPress
   6   * @subpackage REST_API
   7   * @since 4.7.0
   8   */
   9  
  10  /**
  11   * Core controller used to access comments via the REST API.
  12   *
  13   * @since 4.7.0
  14   *
  15   * @see WP_REST_Controller
  16   */
  17  class WP_REST_Comments_Controller extends WP_REST_Controller {
  18  
  19      /**
  20       * Instance of a comment meta fields object.
  21       *
  22       * @since 4.7.0
  23       * @var WP_REST_Comment_Meta_Fields
  24       */
  25      protected $meta;
  26  
  27      /**
  28       * Constructor.
  29       *
  30       * @since 4.7.0
  31       */
  32  	public function __construct() {
  33          $this->namespace = 'wp/v2';
  34          $this->rest_base = 'comments';
  35  
  36          $this->meta = new WP_REST_Comment_Meta_Fields();
  37      }
  38  
  39      /**
  40       * Registers the routes for comments.
  41       *
  42       * @since 4.7.0
  43       *
  44       * @see register_rest_route()
  45       */
  46  	public function register_routes() {
  47  
  48          register_rest_route(
  49              $this->namespace,
  50              '/' . $this->rest_base,
  51              array(
  52                  array(
  53                      'methods'             => WP_REST_Server::READABLE,
  54                      'callback'            => array( $this, 'get_items' ),
  55                      'permission_callback' => array( $this, 'get_items_permissions_check' ),
  56                      'args'                => $this->get_collection_params(),
  57                  ),
  58                  array(
  59                      'methods'             => WP_REST_Server::CREATABLE,
  60                      'callback'            => array( $this, 'create_item' ),
  61                      'permission_callback' => array( $this, 'create_item_permissions_check' ),
  62                      'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
  63                  ),
  64                  'schema' => array( $this, 'get_public_item_schema' ),
  65              )
  66          );
  67  
  68          register_rest_route(
  69              $this->namespace,
  70              '/' . $this->rest_base . '/(?P<id>[\d]+)',
  71              array(
  72                  'args'   => array(
  73                      'id' => array(
  74                          'description' => __( 'Unique identifier for the comment.' ),
  75                          'type'        => 'integer',
  76                      ),
  77                  ),
  78                  array(
  79                      'methods'             => WP_REST_Server::READABLE,
  80                      'callback'            => array( $this, 'get_item' ),
  81                      'permission_callback' => array( $this, 'get_item_permissions_check' ),
  82                      'args'                => array(
  83                          'context'  => $this->get_context_param( array( 'default' => 'view' ) ),
  84                          'password' => array(
  85                              'description' => __( 'The password for the parent post of the comment (if the post is password protected).' ),
  86                              'type'        => 'string',
  87                          ),
  88                      ),
  89                  ),
  90                  array(
  91                      'methods'             => WP_REST_Server::EDITABLE,
  92                      'callback'            => array( $this, 'update_item' ),
  93                      'permission_callback' => array( $this, 'update_item_permissions_check' ),
  94                      'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
  95                  ),
  96                  array(
  97                      'methods'             => WP_REST_Server::DELETABLE,
  98                      'callback'            => array( $this, 'delete_item' ),
  99                      'permission_callback' => array( $this, 'delete_item_permissions_check' ),
 100                      'args'                => array(
 101                          'force'    => array(
 102                              'type'        => 'boolean',
 103                              'default'     => false,
 104                              'description' => __( 'Whether to bypass Trash and force deletion.' ),
 105                          ),
 106                          'password' => array(
 107                              'description' => __( 'The password for the parent post of the comment (if the post is password protected).' ),
 108                              'type'        => 'string',
 109                          ),
 110                      ),
 111                  ),
 112                  'schema' => array( $this, 'get_public_item_schema' ),
 113              )
 114          );
 115      }
 116  
 117      /**
 118       * Checks if a given request has access to read comments.
 119       *
 120       * @since 4.7.0
 121       *
 122       * @param WP_REST_Request $request Full details about the request.
 123       * @return true|WP_Error True if the request has read access, error object otherwise.
 124       */
 125  	public function get_items_permissions_check( $request ) {
 126          $is_note          = 'note' === $request['type'];
 127          $is_edit_context  = 'edit' === $request['context'];
 128          $protected_params = array( 'author', 'author_exclude', 'author_email', 'type', 'status' );
 129          $forbidden_params = array();
 130  
 131          if ( ! empty( $request['post'] ) ) {
 132              foreach ( (array) $request['post'] as $post_id ) {
 133                  $post = get_post( $post_id );
 134  
 135                  if ( ! empty( $post_id ) && $post && ! $this->check_read_post_permission( $post, $request ) ) {
 136                      return new WP_Error(
 137                          'rest_cannot_read_post',
 138                          __( 'Sorry, you are not allowed to read the post for this comment.' ),
 139                          array( 'status' => rest_authorization_required_code() )
 140                      );
 141                  } elseif ( 0 === $post_id && ! current_user_can( 'moderate_comments' ) ) {
 142                      return new WP_Error(
 143                          'rest_cannot_read',
 144                          __( 'Sorry, you are not allowed to read comments without a post.' ),
 145                          array( 'status' => rest_authorization_required_code() )
 146                      );
 147                  }
 148  
 149                  if ( $post && $is_note && ! $this->check_post_type_supports_notes( $post->post_type ) ) {
 150                      if ( current_user_can( 'edit_post', $post->ID ) ) {
 151                          return new WP_Error(
 152                              'rest_comment_not_supported_post_type',
 153                              __( 'Sorry, this post type does not support notes.' ),
 154                              array( 'status' => 403 )
 155                          );
 156                      }
 157  
 158                      foreach ( $protected_params as $param ) {
 159                          if ( 'status' === $param ) {
 160                              if ( 'approve' !== $request[ $param ] ) {
 161                                  $forbidden_params[] = $param;
 162                              }
 163                          } elseif ( 'type' === $param ) {
 164                              if ( 'comment' !== $request[ $param ] ) {
 165                                  $forbidden_params[] = $param;
 166                              }
 167                          } elseif ( ! empty( $request[ $param ] ) ) {
 168                              $forbidden_params[] = $param;
 169                          }
 170                      }
 171                      return new WP_Error(
 172                          'rest_forbidden_param',
 173                          /* translators: %s: List of forbidden parameters. */
 174                          sprintf( __( 'Query parameter not permitted: %s' ), implode( ', ', $forbidden_params ) ),
 175                          array( 'status' => rest_authorization_required_code() )
 176                      );
 177                  }
 178              }
 179          }
 180  
 181          // Re-map edit context capabilities when requesting `note` for a post.
 182          if ( $is_edit_context && $is_note && ! empty( $request['post'] ) ) {
 183              foreach ( (array) $request['post'] as $post_id ) {
 184                  if ( ! current_user_can( 'edit_post', $post_id ) ) {
 185                      return new WP_Error(
 186                          'rest_forbidden_context',
 187                          __( 'Sorry, you are not allowed to edit comments.' ),
 188                          array( 'status' => rest_authorization_required_code() )
 189                      );
 190                  }
 191              }
 192          } elseif ( $is_edit_context && ! current_user_can( 'moderate_comments' ) ) {
 193              return new WP_Error(
 194                  'rest_forbidden_context',
 195                  __( 'Sorry, you are not allowed to edit comments.' ),
 196                  array( 'status' => rest_authorization_required_code() )
 197              );
 198          }
 199  
 200          if ( ! current_user_can( 'edit_posts' ) ) {
 201              foreach ( $protected_params as $param ) {
 202                  if ( 'status' === $param ) {
 203                      if ( 'approve' !== $request[ $param ] ) {
 204                          $forbidden_params[] = $param;
 205                      }
 206                  } elseif ( 'type' === $param ) {
 207                      if ( 'comment' !== $request[ $param ] ) {
 208                          $forbidden_params[] = $param;
 209                      }
 210                  } elseif ( ! empty( $request[ $param ] ) ) {
 211                      $forbidden_params[] = $param;
 212                  }
 213              }
 214  
 215              if ( ! empty( $forbidden_params ) ) {
 216                  return new WP_Error(
 217                      'rest_forbidden_param',
 218                      /* translators: %s: List of forbidden parameters. */
 219                      sprintf( __( 'Query parameter not permitted: %s' ), implode( ', ', $forbidden_params ) ),
 220                      array( 'status' => rest_authorization_required_code() )
 221                  );
 222              }
 223          }
 224  
 225          return true;
 226      }
 227  
 228      /**
 229       * Retrieves a list of comment items.
 230       *
 231       * @since 4.7.0
 232       *
 233       * @param WP_REST_Request $request Full details about the request.
 234       * @return WP_REST_Response|WP_Error Response object on success, or error object on failure.
 235       */
 236  	public function get_items( $request ) {
 237  
 238          // Retrieve the list of registered collection query parameters.
 239          $registered = $this->get_collection_params();
 240  
 241          /*
 242           * This array defines mappings between public API query parameters whose
 243           * values are accepted as-passed, and their internal WP_Query parameter
 244           * name equivalents (some are the same). Only values which are also
 245           * present in $registered will be set.
 246           */
 247          $parameter_mappings = array(
 248              'author'         => 'author__in',
 249              'author_email'   => 'author_email',
 250              'author_exclude' => 'author__not_in',
 251              'exclude'        => 'comment__not_in',
 252              'include'        => 'comment__in',
 253              'offset'         => 'offset',
 254              'order'          => 'order',
 255              'parent'         => 'parent__in',
 256              'parent_exclude' => 'parent__not_in',
 257              'per_page'       => 'number',
 258              'post'           => 'post__in',
 259              'search'         => 'search',
 260              'status'         => 'status',
 261              'type'           => 'type',
 262          );
 263  
 264          $prepared_args = array();
 265  
 266          /*
 267           * For each known parameter which is both registered and present in the request,
 268           * set the parameter's value on the query $prepared_args.
 269           */
 270          foreach ( $parameter_mappings as $api_param => $wp_param ) {
 271              if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) {
 272                  $prepared_args[ $wp_param ] = $request[ $api_param ];
 273              }
 274          }
 275  
 276          // Ensure certain parameter values default to empty strings.
 277          foreach ( array( 'author_email', 'search' ) as $param ) {
 278              if ( ! isset( $prepared_args[ $param ] ) ) {
 279                  $prepared_args[ $param ] = '';
 280              }
 281          }
 282  
 283          if ( isset( $registered['orderby'] ) ) {
 284              $prepared_args['orderby'] = $this->normalize_query_param( $request['orderby'] );
 285          }
 286  
 287          $prepared_args['no_found_rows'] = false;
 288  
 289          $prepared_args['update_comment_post_cache'] = true;
 290  
 291          $prepared_args['date_query'] = array();
 292  
 293          // Set before into date query. Date query must be specified as an array of an array.
 294          if ( isset( $registered['before'], $request['before'] ) ) {
 295              $prepared_args['date_query'][0]['before'] = $request['before'];
 296          }
 297  
 298          // Set after into date query. Date query must be specified as an array of an array.
 299          if ( isset( $registered['after'], $request['after'] ) ) {
 300              $prepared_args['date_query'][0]['after'] = $request['after'];
 301          }
 302  
 303          if ( isset( $registered['page'] ) && empty( $request['offset'] ) ) {
 304              $prepared_args['offset'] = $prepared_args['number'] * ( absint( $request['page'] ) - 1 );
 305          }
 306  
 307          $is_head_request = $request->is_method( 'HEAD' );
 308          if ( $is_head_request ) {
 309              // Force the 'fields' argument. For HEAD requests, only post IDs are required to calculate pagination.
 310              $prepared_args['fields'] = 'ids';
 311              // Disable priming comment meta for HEAD requests to improve performance.
 312              $prepared_args['update_comment_meta_cache'] = false;
 313          }
 314  
 315          /**
 316           * Filters WP_Comment_Query arguments when querying comments via the REST API.
 317           *
 318           * @since 4.7.0
 319           *
 320           * @link https://developer.wordpress.org/reference/classes/wp_comment_query/
 321           *
 322           * @param array           $prepared_args Array of arguments for WP_Comment_Query.
 323           * @param WP_REST_Request $request       The REST API request.
 324           */
 325          $prepared_args = apply_filters( 'rest_comment_query', $prepared_args, $request );
 326  
 327          $query        = new WP_Comment_Query();
 328          $query_result = $query->query( $prepared_args );
 329  
 330          if ( ! $is_head_request ) {
 331              $comments = array();
 332  
 333              foreach ( $query_result as $comment ) {
 334                  if ( ! $this->check_read_permission( $comment, $request ) ) {
 335                      continue;
 336                  }
 337  
 338                  $data       = $this->prepare_item_for_response( $comment, $request );
 339                  $comments[] = $this->prepare_response_for_collection( $data );
 340              }
 341          }
 342  
 343          $total_comments = (int) $query->found_comments;
 344          $max_pages      = (int) $query->max_num_pages;
 345  
 346          if ( $total_comments < 1 ) {
 347              // Out-of-bounds, run the query without pagination/offset to get the total count.
 348              unset( $prepared_args['number'], $prepared_args['offset'] );
 349  
 350              $query                                      = new WP_Comment_Query();
 351              $prepared_args['count']                     = true;
 352              $prepared_args['orderby']                   = 'none';
 353              $prepared_args['update_comment_meta_cache'] = false;
 354  
 355              $total_comments = $query->query( $prepared_args );
 356              $max_pages      = (int) ceil( $total_comments / $request['per_page'] );
 357          }
 358  
 359          $response = $is_head_request ? new WP_REST_Response( array() ) : rest_ensure_response( $comments );
 360          $response->header( 'X-WP-Total', (string) $total_comments );
 361          $response->header( 'X-WP-TotalPages', (string) $max_pages );
 362  
 363          $base = add_query_arg( urlencode_deep( $request->get_query_params() ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
 364  
 365          if ( $request['page'] > 1 ) {
 366              $prev_page = $request['page'] - 1;
 367  
 368              if ( $prev_page > $max_pages ) {
 369                  $prev_page = $max_pages;
 370              }
 371  
 372              $prev_link = add_query_arg( 'page', $prev_page, $base );
 373              $response->link_header( 'prev', $prev_link );
 374          }
 375  
 376          if ( $max_pages > $request['page'] ) {
 377              $next_page = $request['page'] + 1;
 378              $next_link = add_query_arg( 'page', $next_page, $base );
 379  
 380              $response->link_header( 'next', $next_link );
 381          }
 382  
 383          return $response;
 384      }
 385  
 386      /**
 387       * Get the comment, if the ID is valid.
 388       *
 389       * @since 4.7.2
 390       *
 391       * @param int $id Supplied ID.
 392       * @return WP_Comment|WP_Error Comment object if ID is valid, WP_Error otherwise.
 393       */
 394  	protected function get_comment( $id ) {
 395          $error = new WP_Error(
 396              'rest_comment_invalid_id',
 397              __( 'Invalid comment ID.' ),
 398              array( 'status' => 404 )
 399          );
 400  
 401          if ( (int) $id <= 0 ) {
 402              return $error;
 403          }
 404  
 405          $id      = (int) $id;
 406          $comment = get_comment( $id );
 407          if ( empty( $comment ) ) {
 408              return $error;
 409          }
 410  
 411          if ( ! empty( $comment->comment_post_ID ) ) {
 412              $post = get_post( (int) $comment->comment_post_ID );
 413  
 414              if ( empty( $post ) ) {
 415                  return new WP_Error(
 416                      'rest_post_invalid_id',
 417                      __( 'Invalid post ID.' ),
 418                      array( 'status' => 404 )
 419                  );
 420              }
 421          }
 422  
 423          return $comment;
 424      }
 425  
 426      /**
 427       * Checks if a given request has access to read the comment.
 428       *
 429       * @since 4.7.0
 430       *
 431       * @param WP_REST_Request $request Full details about the request.
 432       * @return true|WP_Error True if the request has read access for the item, error object otherwise.
 433       */
 434  	public function get_item_permissions_check( $request ) {
 435          $comment = $this->get_comment( $request['id'] );
 436          if ( is_wp_error( $comment ) ) {
 437              return $comment;
 438          }
 439  
 440          // Re-map edit context capabilities when requesting `note` type.
 441          $edit_cap = 'note' === $comment->comment_type ? array( 'edit_comment', $comment->comment_ID ) : array( 'moderate_comments' );
 442          if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( ...$edit_cap ) ) {
 443              return new WP_Error(
 444                  'rest_forbidden_context',
 445                  __( 'Sorry, you are not allowed to edit comments.' ),
 446                  array( 'status' => rest_authorization_required_code() )
 447              );
 448          }
 449  
 450          $post = get_post( $comment->comment_post_ID );
 451  
 452          if ( ! $this->check_read_permission( $comment, $request ) ) {
 453              return new WP_Error(
 454                  'rest_cannot_read',
 455                  __( 'Sorry, you are not allowed to read this comment.' ),
 456                  array( 'status' => rest_authorization_required_code() )
 457              );
 458          }
 459  
 460          if ( $post && ! $this->check_read_post_permission( $post, $request ) ) {
 461              return new WP_Error(
 462                  'rest_cannot_read_post',
 463                  __( 'Sorry, you are not allowed to read the post for this comment.' ),
 464                  array( 'status' => rest_authorization_required_code() )
 465              );
 466          }
 467  
 468          return true;
 469      }
 470  
 471      /**
 472       * Retrieves a comment.
 473       *
 474       * @since 4.7.0
 475       *
 476       * @param WP_REST_Request $request Full details about the request.
 477       * @return WP_REST_Response|WP_Error Response object on success, or error object on failure.
 478       */
 479  	public function get_item( $request ) {
 480          $comment = $this->get_comment( $request['id'] );
 481          if ( is_wp_error( $comment ) ) {
 482              return $comment;
 483          }
 484  
 485          $data     = $this->prepare_item_for_response( $comment, $request );
 486          $response = rest_ensure_response( $data );
 487  
 488          return $response;
 489      }
 490  
 491      /**
 492       * Checks if a given request has access to create a comment.
 493       *
 494       * @since 4.7.0
 495       *
 496       * @param WP_REST_Request $request Full details about the request.
 497       * @return true|WP_Error True if the request has access to create items, error object otherwise.
 498       */
 499  	public function create_item_permissions_check( $request ) {
 500          $is_note = ! empty( $request['type'] ) && 'note' === $request['type'];
 501  
 502          if ( ! is_user_logged_in() && $is_note ) {
 503              return new WP_Error(
 504                  'rest_comment_login_required',
 505                  __( 'Sorry, you must be logged in to comment.' ),
 506                  array( 'status' => 401 )
 507              );
 508          }
 509  
 510          if ( ! is_user_logged_in() ) {
 511              if ( get_option( 'comment_registration' ) ) {
 512                  return new WP_Error(
 513                      'rest_comment_login_required',
 514                      __( 'Sorry, you must be logged in to comment.' ),
 515                      array( 'status' => 401 )
 516                  );
 517              }
 518  
 519              /**
 520               * Filters whether comments can be created via the REST API without authentication.
 521               *
 522               * Enables creating comments for anonymous users.
 523               *
 524               * @since 4.7.0
 525               *
 526               * @param bool $allow_anonymous Whether to allow anonymous comments to
 527               *                              be created. Default `false`.
 528               * @param WP_REST_Request $request Request used to generate the
 529               *                                 response.
 530               */
 531              $allow_anonymous = apply_filters( 'rest_allow_anonymous_comments', false, $request );
 532  
 533              if ( ! $allow_anonymous ) {
 534                  return new WP_Error(
 535                      'rest_comment_login_required',
 536                      __( 'Sorry, you must be logged in to comment.' ),
 537                      array( 'status' => 401 )
 538                  );
 539              }
 540          }
 541  
 542          // Limit who can set comment `author`, `author_ip` or `status` to anything other than the default.
 543          if ( isset( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( 'moderate_comments' ) ) {
 544              return new WP_Error(
 545                  'rest_comment_invalid_author',
 546                  /* translators: %s: Request parameter. */
 547                  sprintf( __( "Sorry, you are not allowed to edit '%s' for comments." ), 'author' ),
 548                  array( 'status' => rest_authorization_required_code() )
 549              );
 550          }
 551  
 552          if ( isset( $request['author_ip'] ) && ! current_user_can( 'moderate_comments' ) ) {
 553              if ( empty( $_SERVER['REMOTE_ADDR'] ) || $request['author_ip'] !== $_SERVER['REMOTE_ADDR'] ) {
 554                  return new WP_Error(
 555                      'rest_comment_invalid_author_ip',
 556                      /* translators: %s: Request parameter. */
 557                      sprintf( __( "Sorry, you are not allowed to edit '%s' for comments." ), 'author_ip' ),
 558                      array( 'status' => rest_authorization_required_code() )
 559                  );
 560              }
 561          }
 562  
 563          $edit_cap = $is_note ? array( 'edit_post', (int) $request['post'] ) : array( 'moderate_comments' );
 564          if ( isset( $request['status'] ) && ! current_user_can( ...$edit_cap ) ) {
 565              return new WP_Error(
 566                  'rest_comment_invalid_status',
 567                  /* translators: %s: Request parameter. */
 568                  sprintf( __( "Sorry, you are not allowed to edit '%s' for comments." ), 'status' ),
 569                  array( 'status' => rest_authorization_required_code() )
 570              );
 571          }
 572  
 573          if ( empty( $request['post'] ) ) {
 574              return new WP_Error(
 575                  'rest_comment_invalid_post_id',
 576                  __( 'Sorry, you are not allowed to create this comment without a post.' ),
 577                  array( 'status' => 403 )
 578              );
 579          }
 580  
 581          $post = get_post( (int) $request['post'] );
 582  
 583          if ( ! $post ) {
 584              return new WP_Error(
 585                  'rest_comment_invalid_post_id',
 586                  __( 'Sorry, you are not allowed to create this comment without a post.' ),
 587                  array( 'status' => 403 )
 588              );
 589          }
 590  
 591          if ( $is_note && ! $this->check_post_type_supports_notes( $post->post_type ) ) {
 592              return new WP_Error(
 593                  'rest_comment_not_supported_post_type',
 594                  __( 'Sorry, this post type does not support notes.' ),
 595                  array( 'status' => 403 )
 596              );
 597          }
 598  
 599          if ( 'draft' === $post->post_status && ! $is_note ) {
 600              return new WP_Error(
 601                  'rest_comment_draft_post',
 602                  __( 'Sorry, you are not allowed to create a comment on this post.' ),
 603                  array( 'status' => 403 )
 604              );
 605          }
 606  
 607          if ( 'trash' === $post->post_status ) {
 608              return new WP_Error(
 609                  'rest_comment_trash_post',
 610                  __( 'Sorry, you are not allowed to create a comment on this post.' ),
 611                  array( 'status' => 403 )
 612              );
 613          }
 614  
 615          if ( ! $this->check_read_post_permission( $post, $request ) ) {
 616              return new WP_Error(
 617                  'rest_cannot_read_post',
 618                  __( 'Sorry, you are not allowed to read the post for this comment.' ),
 619                  array( 'status' => rest_authorization_required_code() )
 620              );
 621          }
 622  
 623          if ( ! comments_open( $post->ID ) && ! $is_note ) {
 624              return new WP_Error(
 625                  'rest_comment_closed',
 626                  __( 'Sorry, comments are closed for this item.' ),
 627                  array( 'status' => 403 )
 628              );
 629          }
 630  
 631          return true;
 632      }
 633  
 634      /**
 635       * Creates a comment.
 636       *
 637       * @since 4.7.0
 638       *
 639       * @param WP_REST_Request $request Full details about the request.
 640       * @return WP_REST_Response|WP_Error Response object on success, or error object on failure.
 641       */
 642  	public function create_item( $request ) {
 643          if ( ! empty( $request['id'] ) ) {
 644              return new WP_Error(
 645                  'rest_comment_exists',
 646                  __( 'Cannot create existing comment.' ),
 647                  array( 'status' => 400 )
 648              );
 649          }
 650  
 651          // Do not allow comments to be created with a non-core type.
 652          if ( ! empty( $request['type'] ) && ! in_array( $request['type'], array( 'comment', 'note' ), true ) ) {
 653              return new WP_Error(
 654                  'rest_invalid_comment_type',
 655                  __( 'Cannot create a comment with that type.' ),
 656                  array( 'status' => 400 )
 657              );
 658          }
 659  
 660          $prepared_comment = $this->prepare_item_for_database( $request );
 661          if ( is_wp_error( $prepared_comment ) ) {
 662              return $prepared_comment;
 663          }
 664  
 665          $prepared_comment['comment_type'] = $request['type'];
 666  
 667          if ( ! isset( $prepared_comment['comment_content'] ) ) {
 668              $prepared_comment['comment_content'] = '';
 669          }
 670  
 671          // Include note metadata into check_is_comment_content_allowed.
 672          if ( isset( $request['meta']['_wp_note_status'] ) ) {
 673              $prepared_comment['meta']['_wp_note_status'] = $request['meta']['_wp_note_status'];
 674          }
 675  
 676          if ( ! $this->check_is_comment_content_allowed( $prepared_comment ) ) {
 677              return new WP_Error(
 678                  'rest_comment_content_invalid',
 679                  __( 'Invalid comment content.' ),
 680                  array( 'status' => 400 )
 681              );
 682          }
 683  
 684          // Setting remaining values before wp_insert_comment so we can use wp_allow_comment().
 685          if ( ! isset( $prepared_comment['comment_date_gmt'] ) ) {
 686              $prepared_comment['comment_date_gmt'] = current_time( 'mysql', true );
 687          }
 688  
 689          // Set author data if the user's logged in.
 690          $missing_author = empty( $prepared_comment['user_id'] )
 691              && empty( $prepared_comment['comment_author'] )
 692              && empty( $prepared_comment['comment_author_email'] )
 693              && empty( $prepared_comment['comment_author_url'] );
 694  
 695          if ( is_user_logged_in() && $missing_author ) {
 696              $user = wp_get_current_user();
 697  
 698              $prepared_comment['user_id']              = $user->ID;
 699              $prepared_comment['comment_author']       = $user->display_name;
 700              $prepared_comment['comment_author_email'] = $user->user_email;
 701              $prepared_comment['comment_author_url']   = $user->user_url;
 702          }
 703  
 704          // Honor the discussion setting that requires a name and email address of the comment author.
 705          if ( get_option( 'require_name_email' ) ) {
 706              if ( empty( $prepared_comment['comment_author'] ) || empty( $prepared_comment['comment_author_email'] ) ) {
 707                  return new WP_Error(
 708                      'rest_comment_author_data_required',
 709                      __( 'Creating a comment requires valid author name and email values.' ),
 710                      array( 'status' => 400 )
 711                  );
 712              }
 713          }
 714  
 715          if ( ! isset( $prepared_comment['comment_author_email'] ) ) {
 716              $prepared_comment['comment_author_email'] = '';
 717          }
 718  
 719          if ( ! isset( $prepared_comment['comment_author_url'] ) ) {
 720              $prepared_comment['comment_author_url'] = '';
 721          }
 722  
 723          if ( ! isset( $prepared_comment['comment_agent'] ) ) {
 724              $prepared_comment['comment_agent'] = '';
 725          }
 726  
 727          $check_comment_lengths = wp_check_comment_data_max_lengths( $prepared_comment );
 728  
 729          if ( is_wp_error( $check_comment_lengths ) ) {
 730              $error_code = $check_comment_lengths->get_error_code();
 731              return new WP_Error(
 732                  $error_code,
 733                  __( 'Comment field exceeds maximum length allowed.' ),
 734                  array( 'status' => 400 )
 735              );
 736          }
 737  
 738          // Don't check for duplicates or flooding for notes.
 739          $prepared_comment['comment_approved'] =
 740              'note' === $prepared_comment['comment_type'] ?
 741              '1' :
 742              wp_allow_comment( $prepared_comment, true );
 743  
 744          if ( is_wp_error( $prepared_comment['comment_approved'] ) ) {
 745              $error_code    = $prepared_comment['comment_approved']->get_error_code();
 746              $error_message = $prepared_comment['comment_approved']->get_error_message();
 747  
 748              if ( 'comment_duplicate' === $error_code ) {
 749                  return new WP_Error(
 750                      $error_code,
 751                      $error_message,
 752                      array( 'status' => 409 )
 753                  );
 754              }
 755  
 756              if ( 'comment_flood' === $error_code ) {
 757                  return new WP_Error(
 758                      $error_code,
 759                      $error_message,
 760                      array( 'status' => 400 )
 761                  );
 762              }
 763  
 764              return $prepared_comment['comment_approved'];
 765          }
 766  
 767          /**
 768           * Filters a comment before it is inserted via the REST API.
 769           *
 770           * Allows modification of the comment right before it is inserted via wp_insert_comment().
 771           * Returning a WP_Error value from the filter will short-circuit insertion and allow
 772           * skipping further processing.
 773           *
 774           * @since 4.7.0
 775           * @since 4.8.0 `$prepared_comment` can now be a WP_Error to short-circuit insertion.
 776           *
 777           * @param array|WP_Error  $prepared_comment The prepared comment data for wp_insert_comment().
 778           * @param WP_REST_Request $request          Request used to insert the comment.
 779           */
 780          $prepared_comment = apply_filters( 'rest_pre_insert_comment', $prepared_comment, $request );
 781          if ( is_wp_error( $prepared_comment ) ) {
 782              return $prepared_comment;
 783          }
 784  
 785          $comment_id = wp_insert_comment( wp_filter_comment( wp_slash( (array) $prepared_comment ) ) );
 786  
 787          if ( ! $comment_id ) {
 788              return new WP_Error(
 789                  'rest_comment_failed_create',
 790                  __( 'Creating comment failed.' ),
 791                  array( 'status' => 500 )
 792              );
 793          }
 794  
 795          if ( isset( $request['status'] ) ) {
 796              $this->handle_status_param( $request['status'], $comment_id );
 797          }
 798  
 799          $comment = get_comment( $comment_id );
 800  
 801          /**
 802           * Fires after a comment is created or updated via the REST API.
 803           *
 804           * @since 4.7.0
 805           *
 806           * @param WP_Comment      $comment  Inserted or updated comment object.
 807           * @param WP_REST_Request $request  Request object.
 808           * @param bool            $creating True when creating a comment, false
 809           *                                  when updating.
 810           */
 811          do_action( 'rest_insert_comment', $comment, $request, true );
 812  
 813          $schema = $this->get_item_schema();
 814  
 815          if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
 816              $meta_update = $this->meta->update_value( $request['meta'], $comment_id );
 817  
 818              if ( is_wp_error( $meta_update ) ) {
 819                  return $meta_update;
 820              }
 821          }
 822  
 823          $fields_update = $this->update_additional_fields_for_object( $comment, $request );
 824  
 825          if ( is_wp_error( $fields_update ) ) {
 826              return $fields_update;
 827          }
 828  
 829          $context = current_user_can( 'moderate_comments' ) ? 'edit' : 'view';
 830          $request->set_param( 'context', $context );
 831  
 832          /**
 833           * Fires completely after a comment is created or updated via the REST API.
 834           *
 835           * @since 5.0.0
 836           *
 837           * @param WP_Comment      $comment  Inserted or updated comment object.
 838           * @param WP_REST_Request $request  Request object.
 839           * @param bool            $creating True when creating a comment, false
 840           *                                  when updating.
 841           */
 842          do_action( 'rest_after_insert_comment', $comment, $request, true );
 843  
 844          $response = $this->prepare_item_for_response( $comment, $request );
 845          $response = rest_ensure_response( $response );
 846  
 847          $response->set_status( 201 );
 848          $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment_id ) ) );
 849  
 850          return $response;
 851      }
 852  
 853      /**
 854       * Checks if a given REST request has access to update a comment.
 855       *
 856       * @since 4.7.0
 857       *
 858       * @param WP_REST_Request $request Full details about the request.
 859       * @return true|WP_Error True if the request has access to update the item, error object otherwise.
 860       */
 861  	public function update_item_permissions_check( $request ) {
 862          $comment = $this->get_comment( $request['id'] );
 863          if ( is_wp_error( $comment ) ) {
 864              return $comment;
 865          }
 866  
 867          if ( ! $this->check_edit_permission( $comment ) ) {
 868              return new WP_Error(
 869                  'rest_cannot_edit',
 870                  __( 'Sorry, you are not allowed to edit this comment.' ),
 871                  array( 'status' => rest_authorization_required_code() )
 872              );
 873          }
 874  
 875          return true;
 876      }
 877  
 878      /**
 879       * Updates a comment.
 880       *
 881       * @since 4.7.0
 882       *
 883       * @param WP_REST_Request $request Full details about the request.
 884       * @return WP_REST_Response|WP_Error Response object on success, or error object on failure.
 885       */
 886  	public function update_item( $request ) {
 887          $comment = $this->get_comment( $request['id'] );
 888          if ( is_wp_error( $comment ) ) {
 889              return $comment;
 890          }
 891  
 892          $id = $comment->comment_ID;
 893  
 894          if ( isset( $request['type'] ) && get_comment_type( $id ) !== $request['type'] ) {
 895              return new WP_Error(
 896                  'rest_comment_invalid_type',
 897                  __( 'Sorry, you are not allowed to change the comment type.' ),
 898                  array( 'status' => 404 )
 899              );
 900          }
 901  
 902          $prepared_args = $this->prepare_item_for_database( $request );
 903  
 904          if ( is_wp_error( $prepared_args ) ) {
 905              return $prepared_args;
 906          }
 907  
 908          if ( ! empty( $prepared_args['comment_post_ID'] ) ) {
 909              $post = get_post( $prepared_args['comment_post_ID'] );
 910  
 911              if ( empty( $post ) ) {
 912                  return new WP_Error(
 913                      'rest_comment_invalid_post_id',
 914                      __( 'Invalid post ID.' ),
 915                      array( 'status' => 403 )
 916                  );
 917              }
 918          }
 919  
 920          if ( empty( $prepared_args ) && isset( $request['status'] ) ) {
 921              // Only the comment status is being changed.
 922              $change = $this->handle_status_param( $request['status'], $id );
 923  
 924              if ( ! $change ) {
 925                  return new WP_Error(
 926                      'rest_comment_failed_edit',
 927                      __( 'Updating comment status failed.' ),
 928                      array( 'status' => 500 )
 929                  );
 930              }
 931          } elseif ( ! empty( $prepared_args ) ) {
 932              if ( is_wp_error( $prepared_args ) ) {
 933                  return $prepared_args;
 934              }
 935              if ( ! $this->check_is_comment_content_allowed( $prepared_args ) ) {
 936                  return new WP_Error(
 937                      'rest_comment_content_invalid',
 938                      __( 'Invalid comment content.' ),
 939                      array( 'status' => 400 )
 940                  );
 941              }
 942  
 943              $prepared_args['comment_ID'] = $id;
 944  
 945              $check_comment_lengths = wp_check_comment_data_max_lengths( $prepared_args );
 946  
 947              if ( is_wp_error( $check_comment_lengths ) ) {
 948                  $error_code = $check_comment_lengths->get_error_code();
 949                  return new WP_Error(
 950                      $error_code,
 951                      __( 'Comment field exceeds maximum length allowed.' ),
 952                      array( 'status' => 400 )
 953                  );
 954              }
 955  
 956              $updated = wp_update_comment( wp_slash( (array) $prepared_args ), true );
 957  
 958              if ( is_wp_error( $updated ) ) {
 959                  return new WP_Error(
 960                      'rest_comment_failed_edit',
 961                      __( 'Updating comment failed.' ),
 962                      array( 'status' => 500 )
 963                  );
 964              }
 965  
 966              if ( isset( $request['status'] ) ) {
 967                  $this->handle_status_param( $request['status'], $id );
 968              }
 969          }
 970  
 971          $comment = get_comment( $id );
 972  
 973          /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php */
 974          do_action( 'rest_insert_comment', $comment, $request, false );
 975  
 976          $schema = $this->get_item_schema();
 977  
 978          if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
 979              $meta_update = $this->meta->update_value( $request['meta'], $id );
 980  
 981              if ( is_wp_error( $meta_update ) ) {
 982                  return $meta_update;
 983              }
 984          }
 985  
 986          $fields_update = $this->update_additional_fields_for_object( $comment, $request );
 987  
 988          if ( is_wp_error( $fields_update ) ) {
 989              return $fields_update;
 990          }
 991  
 992          $request->set_param( 'context', 'edit' );
 993  
 994          /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php */
 995          do_action( 'rest_after_insert_comment', $comment, $request, false );
 996  
 997          $response = $this->prepare_item_for_response( $comment, $request );
 998  
 999          return rest_ensure_response( $response );
1000      }
1001  
1002      /**
1003       * Checks if a given request has access to delete a comment.
1004       *
1005       * @since 4.7.0
1006       *
1007       * @param WP_REST_Request $request Full details about the request.
1008       * @return true|WP_Error True if the request has access to delete the item, error object otherwise.
1009       */
1010  	public function delete_item_permissions_check( $request ) {
1011          $comment = $this->get_comment( $request['id'] );
1012          if ( is_wp_error( $comment ) ) {
1013              return $comment;
1014          }
1015  
1016          if ( ! $this->check_edit_permission( $comment ) ) {
1017              return new WP_Error(
1018                  'rest_cannot_delete',
1019                  __( 'Sorry, you are not allowed to delete this comment.' ),
1020                  array( 'status' => rest_authorization_required_code() )
1021              );
1022          }
1023          return true;
1024      }
1025  
1026      /**
1027       * Deletes a comment.
1028       *
1029       * @since 4.7.0
1030       *
1031       * @param WP_REST_Request $request Full details about the request.
1032       * @return WP_REST_Response|WP_Error Response object on success, or error object on failure.
1033       */
1034  	public function delete_item( $request ) {
1035          $comment = $this->get_comment( $request['id'] );
1036          if ( is_wp_error( $comment ) ) {
1037              return $comment;
1038          }
1039  
1040          $force = isset( $request['force'] ) ? (bool) $request['force'] : false;
1041  
1042          /**
1043           * Filters whether a comment can be trashed via the REST API.
1044           *
1045           * Return false to disable trash support for the comment.
1046           *
1047           * @since 4.7.0
1048           *
1049           * @param bool       $supports_trash Whether the comment supports trashing.
1050           * @param WP_Comment $comment        The comment object being considered for trashing support.
1051           */
1052          $supports_trash = apply_filters( 'rest_comment_trashable', ( EMPTY_TRASH_DAYS > 0 ), $comment );
1053  
1054          $request->set_param( 'context', 'edit' );
1055  
1056          if ( $force ) {
1057              $previous = $this->prepare_item_for_response( $comment, $request );
1058              $result   = wp_delete_comment( $comment->comment_ID, true );
1059              $response = new WP_REST_Response();
1060              $response->set_data(
1061                  array(
1062                      'deleted'  => true,
1063                      'previous' => $previous->get_data(),
1064                  )
1065              );
1066          } else {
1067              // If this type doesn't support trashing, error out.
1068              if ( ! $supports_trash ) {
1069                  return new WP_Error(
1070                      'rest_trash_not_supported',
1071                      /* translators: %s: force=true */
1072                      sprintf( __( "The comment does not support trashing. Set '%s' to delete." ), 'force=true' ),
1073                      array( 'status' => 501 )
1074                  );
1075              }
1076  
1077              if ( 'trash' === $comment->comment_approved ) {
1078                  return new WP_Error(
1079                      'rest_already_trashed',
1080                      __( 'The comment has already been trashed.' ),
1081                      array( 'status' => 410 )
1082                  );
1083              }
1084  
1085              $result   = wp_trash_comment( $comment->comment_ID );
1086              $comment  = get_comment( $comment->comment_ID );
1087              $response = $this->prepare_item_for_response( $comment, $request );
1088          }
1089  
1090          if ( ! $result ) {
1091              return new WP_Error(
1092                  'rest_cannot_delete',
1093                  __( 'The comment cannot be deleted.' ),
1094                  array( 'status' => 500 )
1095              );
1096          }
1097  
1098          /**
1099           * Fires after a comment is deleted via the REST API.
1100           *
1101           * @since 4.7.0
1102           *
1103           * @param WP_Comment       $comment  The deleted comment data.
1104           * @param WP_REST_Response $response The response returned from the API.
1105           * @param WP_REST_Request  $request  The request sent to the API.
1106           */
1107          do_action( 'rest_delete_comment', $comment, $response, $request );
1108  
1109          return $response;
1110      }
1111  
1112      /**
1113       * Prepares a single comment output for response.
1114       *
1115       * @since 4.7.0
1116       * @since 5.9.0 Renamed `$comment` to `$item` to match parent class for PHP 8 named parameter support.
1117       *
1118       * @param WP_Comment      $item    Comment object.
1119       * @param WP_REST_Request $request Request object.
1120       * @return WP_REST_Response Response object.
1121       */
1122  	public function prepare_item_for_response( $item, $request ) {
1123          // Restores the more descriptive, specific name for use within this method.
1124          $comment = $item;
1125  
1126          // Don't prepare the response body for HEAD requests.
1127          if ( $request->is_method( 'HEAD' ) ) {
1128              /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php */
1129              return apply_filters( 'rest_prepare_comment', new WP_REST_Response( array() ), $comment, $request );
1130          }
1131  
1132          $fields = $this->get_fields_for_response( $request );
1133          $data   = array();
1134  
1135          if ( in_array( 'id', $fields, true ) ) {
1136              $data['id'] = (int) $comment->comment_ID;
1137          }
1138  
1139          if ( in_array( 'post', $fields, true ) ) {
1140              $data['post'] = (int) $comment->comment_post_ID;
1141          }
1142  
1143          if ( in_array( 'parent', $fields, true ) ) {
1144              $data['parent'] = (int) $comment->comment_parent;
1145          }
1146  
1147          if ( in_array( 'author', $fields, true ) ) {
1148              $data['author'] = (int) $comment->user_id;
1149          }
1150  
1151          if ( in_array( 'author_name', $fields, true ) ) {
1152              $data['author_name'] = $comment->comment_author;
1153          }
1154  
1155          if ( in_array( 'author_email', $fields, true ) ) {
1156              $data['author_email'] = $comment->comment_author_email;
1157          }
1158  
1159          if ( in_array( 'author_url', $fields, true ) ) {
1160              $data['author_url'] = $comment->comment_author_url;
1161          }
1162  
1163          if ( in_array( 'author_ip', $fields, true ) ) {
1164              $data['author_ip'] = $comment->comment_author_IP;
1165          }
1166  
1167          if ( in_array( 'author_user_agent', $fields, true ) ) {
1168              $data['author_user_agent'] = $comment->comment_agent;
1169          }
1170  
1171          if ( in_array( 'date', $fields, true ) ) {
1172              $data['date'] = mysql_to_rfc3339( $comment->comment_date );
1173          }
1174  
1175          if ( in_array( 'date_gmt', $fields, true ) ) {
1176              $data['date_gmt'] = mysql_to_rfc3339( $comment->comment_date_gmt );
1177          }
1178  
1179          if ( in_array( 'content', $fields, true ) ) {
1180              $data['content'] = array(
1181                  /** This filter is documented in wp-includes/comment-template.php */
1182                  'rendered' => apply_filters( 'comment_text', $comment->comment_content, $comment, array() ),
1183                  'raw'      => $comment->comment_content,
1184              );
1185          }
1186  
1187          if ( in_array( 'link', $fields, true ) ) {
1188              $data['link'] = get_comment_link( $comment );
1189          }
1190  
1191          if ( in_array( 'status', $fields, true ) ) {
1192              $data['status'] = $this->prepare_status_response( $comment->comment_approved );
1193          }
1194  
1195          if ( in_array( 'type', $fields, true ) ) {
1196              $data['type'] = get_comment_type( $comment->comment_ID );
1197          }
1198  
1199          if ( in_array( 'author_avatar_urls', $fields, true ) ) {
1200              $data['author_avatar_urls'] = rest_get_avatar_urls( $comment );
1201          }
1202  
1203          if ( in_array( 'meta', $fields, true ) ) {
1204              $data['meta'] = $this->meta->get_value( $comment->comment_ID, $request );
1205          }
1206  
1207          $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
1208          $data    = $this->add_additional_fields_to_object( $data, $request );
1209          $data    = $this->filter_response_by_context( $data, $context );
1210  
1211          // Wrap the data in a response object.
1212          $response = rest_ensure_response( $data );
1213  
1214          if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
1215              $response->add_links( $this->prepare_links( $comment ) );
1216          }
1217  
1218          /**
1219           * Filters a comment returned from the REST API.
1220           *
1221           * Allows modification of the comment right before it is returned.
1222           *
1223           * @since 4.7.0
1224           *
1225           * @param WP_REST_Response  $response The response object.
1226           * @param WP_Comment        $comment  The original comment object.
1227           * @param WP_REST_Request   $request  Request used to generate the response.
1228           */
1229          return apply_filters( 'rest_prepare_comment', $response, $comment, $request );
1230      }
1231  
1232      /**
1233       * Prepares links for the request.
1234       *
1235       * @since 4.7.0
1236       *
1237       * @param WP_Comment $comment Comment object.
1238       * @return array Links for the given comment.
1239       */
1240  	protected function prepare_links( $comment ) {
1241          $links = array(
1242              'self'       => array(
1243                  'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment->comment_ID ) ),
1244              ),
1245              'collection' => array(
1246                  'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
1247              ),
1248          );
1249  
1250          if ( 0 !== (int) $comment->user_id ) {
1251              $links['author'] = array(
1252                  'href'       => rest_url( 'wp/v2/users/' . $comment->user_id ),
1253                  'embeddable' => true,
1254              );
1255          }
1256  
1257          if ( 0 !== (int) $comment->comment_post_ID ) {
1258              $post       = get_post( $comment->comment_post_ID );
1259              $post_route = rest_get_route_for_post( $post );
1260  
1261              if ( ! empty( $post->ID ) && $post_route ) {
1262                  $links['up'] = array(
1263                      'href'       => rest_url( $post_route ),
1264                      'embeddable' => true,
1265                      'post_type'  => $post->post_type,
1266                  );
1267              }
1268          }
1269  
1270          if ( 0 !== (int) $comment->comment_parent ) {
1271              $links['in-reply-to'] = array(
1272                  'href'       => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment->comment_parent ) ),
1273                  'embeddable' => true,
1274              );
1275          }
1276  
1277          // Only grab one comment to verify the comment has children.
1278          $comment_children = $comment->get_children(
1279              array(
1280                  'count'   => true,
1281                  'orderby' => 'none',
1282                  'type'    => 'all',
1283              )
1284          );
1285  
1286          if ( ! empty( $comment_children ) ) {
1287              $args = array(
1288                  'parent' => $comment->comment_ID,
1289              );
1290  
1291              $rest_url = add_query_arg( $args, rest_url( $this->namespace . '/' . $this->rest_base ) );
1292  
1293              $links['children'] = array(
1294                  'href'       => $rest_url,
1295                  'embeddable' => true,
1296              );
1297          }
1298  
1299          // Embedding children for notes requires `type` and `status` inheritance.
1300          if ( isset( $links['children'] ) && 'note' === $comment->comment_type ) {
1301              $args = array(
1302                  'parent' => $comment->comment_ID,
1303                  'type'   => $comment->comment_type,
1304                  'status' => 'all',
1305              );
1306  
1307              $rest_url = add_query_arg( $args, rest_url( $this->namespace . '/' . $this->rest_base ) );
1308  
1309              $links['children'] = array(
1310                  'href'       => $rest_url,
1311                  'embeddable' => true,
1312              );
1313          }
1314  
1315          return $links;
1316      }
1317  
1318      /**
1319       * Prepends internal property prefix to query parameters to match our response fields.
1320       *
1321       * @since 4.7.0
1322       *
1323       * @param string $query_param Query parameter.
1324       * @return string The normalized query parameter.
1325       */
1326  	protected function normalize_query_param( $query_param ) {
1327          $prefix = 'comment_';
1328  
1329          switch ( $query_param ) {
1330              case 'id':
1331                  $normalized = $prefix . 'ID';
1332                  break;
1333              case 'post':
1334                  $normalized = $prefix . 'post_ID';
1335                  break;
1336              case 'parent':
1337                  $normalized = $prefix . 'parent';
1338                  break;
1339              case 'include':
1340                  $normalized = 'comment__in';
1341                  break;
1342              default:
1343                  $normalized = $prefix . $query_param;
1344                  break;
1345          }
1346  
1347          return $normalized;
1348      }
1349  
1350      /**
1351       * Checks comment_approved to set comment status for single comment output.
1352       *
1353       * @since 4.7.0
1354       *
1355       * @param string $comment_approved Comment status.
1356       * @return string Comment status.
1357       */
1358  	protected function prepare_status_response( $comment_approved ) {
1359  
1360          switch ( $comment_approved ) {
1361              case 'hold':
1362              case '0':
1363                  $status = 'hold';
1364                  break;
1365  
1366              case 'approve':
1367              case '1':
1368                  $status = 'approved';
1369                  break;
1370  
1371              case 'spam':
1372              case 'trash':
1373              default:
1374                  $status = $comment_approved;
1375                  break;
1376          }
1377  
1378          return $status;
1379      }
1380  
1381      /**
1382       * Prepares a single comment to be inserted into the database.
1383       *
1384       * @since 4.7.0
1385       *
1386       * @param WP_REST_Request $request Request object.
1387       * @return array|WP_Error Prepared comment, otherwise WP_Error object.
1388       */
1389  	protected function prepare_item_for_database( $request ) {
1390          $prepared_comment = array();
1391  
1392          /*
1393           * Allow the comment_content to be set via the 'content' or
1394           * the 'content.raw' properties of the Request object.
1395           */
1396          if ( isset( $request['content'] ) && is_string( $request['content'] ) ) {
1397              $prepared_comment['comment_content'] = trim( $request['content'] );
1398          } elseif ( isset( $request['content']['raw'] ) && is_string( $request['content']['raw'] ) ) {
1399              $prepared_comment['comment_content'] = trim( $request['content']['raw'] );
1400          }
1401  
1402          if ( isset( $request['post'] ) ) {
1403              $prepared_comment['comment_post_ID'] = (int) $request['post'];
1404          }
1405  
1406          if ( isset( $request['parent'] ) ) {
1407              $prepared_comment['comment_parent'] = $request['parent'];
1408          }
1409  
1410          if ( isset( $request['author'] ) ) {
1411              $user = new WP_User( $request['author'] );
1412  
1413              if ( $user->exists() ) {
1414                  $prepared_comment['user_id']              = $user->ID;
1415                  $prepared_comment['comment_author']       = $user->display_name;
1416                  $prepared_comment['comment_author_email'] = $user->user_email;
1417                  $prepared_comment['comment_author_url']   = $user->user_url;
1418              } else {
1419                  return new WP_Error(
1420                      'rest_comment_author_invalid',
1421                      __( 'Invalid comment author ID.' ),
1422                      array( 'status' => 400 )
1423                  );
1424              }
1425          }
1426  
1427          if ( isset( $request['author_name'] ) ) {
1428              $prepared_comment['comment_author'] = $request['author_name'];
1429          }
1430  
1431          if ( isset( $request['author_email'] ) ) {
1432              $prepared_comment['comment_author_email'] = $request['author_email'];
1433          }
1434  
1435          if ( isset( $request['author_url'] ) ) {
1436              $prepared_comment['comment_author_url'] = $request['author_url'];
1437          }
1438  
1439          if ( isset( $request['author_ip'] ) && current_user_can( 'moderate_comments' ) ) {
1440              $prepared_comment['comment_author_IP'] = $request['author_ip'];
1441          } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) && rest_is_ip_address( $_SERVER['REMOTE_ADDR'] ) ) {
1442              $prepared_comment['comment_author_IP'] = $_SERVER['REMOTE_ADDR'];
1443          } else {
1444              $prepared_comment['comment_author_IP'] = '127.0.0.1';
1445          }
1446  
1447          if ( ! empty( $request['author_user_agent'] ) ) {
1448              $prepared_comment['comment_agent'] = $request['author_user_agent'];
1449          } elseif ( $request->get_header( 'user_agent' ) ) {
1450              $prepared_comment['comment_agent'] = $request->get_header( 'user_agent' );
1451          }
1452  
1453          if ( ! empty( $request['date'] ) ) {
1454              $date_data = rest_get_date_with_gmt( $request['date'] );
1455  
1456              if ( ! empty( $date_data ) ) {
1457                  list( $prepared_comment['comment_date'], $prepared_comment['comment_date_gmt'] ) = $date_data;
1458              }
1459          } elseif ( ! empty( $request['date_gmt'] ) ) {
1460              $date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
1461  
1462              if ( ! empty( $date_data ) ) {
1463                  list( $prepared_comment['comment_date'], $prepared_comment['comment_date_gmt'] ) = $date_data;
1464              }
1465          }
1466  
1467          /**
1468           * Filters a comment added via the REST API after it is prepared for insertion into the database.
1469           *
1470           * Allows modification of the comment right after it is prepared for the database.
1471           *
1472           * @since 4.7.0
1473           *
1474           * @param array           $prepared_comment The prepared comment data for `wp_insert_comment`.
1475           * @param WP_REST_Request $request          The current request.
1476           */
1477          return apply_filters( 'rest_preprocess_comment', $prepared_comment, $request );
1478      }
1479  
1480      /**
1481       * Retrieves the comment's schema, conforming to JSON Schema.
1482       *
1483       * @since 4.7.0
1484       *
1485       * @return array
1486       */
1487  	public function get_item_schema() {
1488          if ( $this->schema ) {
1489              return $this->add_additional_fields_schema( $this->schema );
1490          }
1491  
1492          $schema = array(
1493              '$schema'    => 'http://json-schema.org/draft-04/schema#',
1494              'title'      => 'comment',
1495              'type'       => 'object',
1496              'properties' => array(
1497                  'id'                => array(
1498                      'description' => __( 'Unique identifier for the comment.' ),
1499                      'type'        => 'integer',
1500                      'context'     => array( 'view', 'edit', 'embed' ),
1501                      'readonly'    => true,
1502                  ),
1503                  'author'            => array(
1504                      'description' => __( 'The ID of the user object, if author was a user.' ),
1505                      'type'        => 'integer',
1506                      'context'     => array( 'view', 'edit', 'embed' ),
1507                  ),
1508                  'author_email'      => array(
1509                      'description' => __( 'Email address for the comment author.' ),
1510                      'type'        => 'string',
1511                      'format'      => 'email',
1512                      'context'     => array( 'edit' ),
1513                      'arg_options' => array(
1514                          'sanitize_callback' => array( $this, 'check_comment_author_email' ),
1515                          'validate_callback' => null, // Skip built-in validation of 'email'.
1516                      ),
1517                  ),
1518                  'author_ip'         => array(
1519                      'description' => __( 'IP address for the comment author.' ),
1520                      'type'        => 'string',
1521                      'format'      => 'ip',
1522                      'context'     => array( 'edit' ),
1523                  ),
1524                  'author_name'       => array(
1525                      'description' => __( 'Display name for the comment author.' ),
1526                      'type'        => 'string',
1527                      'context'     => array( 'view', 'edit', 'embed' ),
1528                      'arg_options' => array(
1529                          'sanitize_callback' => 'sanitize_text_field',
1530                      ),
1531                  ),
1532                  'author_url'        => array(
1533                      'description' => __( 'URL for the comment author.' ),
1534                      'type'        => 'string',
1535                      'format'      => 'uri',
1536                      'context'     => array( 'view', 'edit', 'embed' ),
1537                  ),
1538                  'author_user_agent' => array(
1539                      'description' => __( 'User agent for the comment author.' ),
1540                      'type'        => 'string',
1541                      'context'     => array( 'edit' ),
1542                      'arg_options' => array(
1543                          'sanitize_callback' => 'sanitize_text_field',
1544                      ),
1545                  ),
1546                  'content'           => array(
1547                      'description' => __( 'The content for the comment.' ),
1548                      'type'        => 'object',
1549                      'context'     => array( 'view', 'edit', 'embed' ),
1550                      'arg_options' => array(
1551                          'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database().
1552                          'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database().
1553                      ),
1554                      'properties'  => array(
1555                          'raw'      => array(
1556                              'description' => __( 'Content for the comment, as it exists in the database.' ),
1557                              'type'        => 'string',
1558                              'context'     => array( 'edit' ),
1559                          ),
1560                          'rendered' => array(
1561                              'description' => __( 'HTML content for the comment, transformed for display.' ),
1562                              'type'        => 'string',
1563                              'context'     => array( 'view', 'edit', 'embed' ),
1564                              'readonly'    => true,
1565                          ),
1566                      ),
1567                  ),
1568                  'date'              => array(
1569                      'description' => __( "The date the comment was published, in the site's timezone." ),
1570                      'type'        => 'string',
1571                      'format'      => 'date-time',
1572                      'context'     => array( 'view', 'edit', 'embed' ),
1573                  ),
1574                  'date_gmt'          => array(
1575                      'description' => __( 'The date the comment was published, as GMT.' ),
1576                      'type'        => 'string',
1577                      'format'      => 'date-time',
1578                      'context'     => array( 'view', 'edit' ),
1579                  ),
1580                  'link'              => array(
1581                      'description' => __( 'URL to the comment.' ),
1582                      'type'        => 'string',
1583                      'format'      => 'uri',
1584                      'context'     => array( 'view', 'edit', 'embed' ),
1585                      'readonly'    => true,
1586                  ),
1587                  'parent'            => array(
1588                      'description' => __( 'The ID for the parent of the comment.' ),
1589                      'type'        => 'integer',
1590                      'context'     => array( 'view', 'edit', 'embed' ),
1591                      'default'     => 0,
1592                  ),
1593                  'post'              => array(
1594                      'description' => __( 'The ID of the associated post object.' ),
1595                      'type'        => 'integer',
1596                      'context'     => array( 'view', 'edit' ),
1597                      'default'     => 0,
1598                  ),
1599                  'status'            => array(
1600                      'description' => __( 'State of the comment.' ),
1601                      'type'        => 'string',
1602                      'context'     => array( 'view', 'edit' ),
1603                      'arg_options' => array(
1604                          'sanitize_callback' => 'sanitize_key',
1605                      ),
1606                  ),
1607                  'type'              => array(
1608                      'description' => __( 'Type of the comment.' ),
1609                      'type'        => 'string',
1610                      'context'     => array( 'view', 'edit', 'embed' ),
1611                      'readonly'    => true,
1612                      'default'     => 'comment',
1613                  ),
1614              ),
1615          );
1616  
1617          if ( get_option( 'show_avatars' ) ) {
1618              $avatar_properties = array();
1619  
1620              $avatar_sizes = rest_get_avatar_sizes();
1621  
1622              foreach ( $avatar_sizes as $size ) {
1623                  $avatar_properties[ $size ] = array(
1624                      /* translators: %d: Avatar image size in pixels. */
1625                      'description' => sprintf( __( 'Avatar URL with image size of %d pixels.' ), $size ),
1626                      'type'        => 'string',
1627                      'format'      => 'uri',
1628                      'context'     => array( 'embed', 'view', 'edit' ),
1629                  );
1630              }
1631  
1632              $schema['properties']['author_avatar_urls'] = array(
1633                  'description' => __( 'Avatar URLs for the comment author.' ),
1634                  'type'        => 'object',
1635                  'context'     => array( 'view', 'edit', 'embed' ),
1636                  'readonly'    => true,
1637                  'properties'  => $avatar_properties,
1638              );
1639          }
1640  
1641          $schema['properties']['meta'] = $this->meta->get_field_schema();
1642  
1643          $this->schema = $schema;
1644  
1645          return $this->add_additional_fields_schema( $this->schema );
1646      }
1647  
1648      /**
1649       * Retrieves the query params for collections.
1650       *
1651       * @since 4.7.0
1652       *
1653       * @return array Comments collection parameters.
1654       */
1655  	public function get_collection_params() {
1656          $query_params = parent::get_collection_params();
1657  
1658          $query_params['context']['default'] = 'view';
1659  
1660          $query_params['after'] = array(
1661              'description' => __( 'Limit response to comments published after a given ISO8601 compliant date.' ),
1662              'type'        => 'string',
1663              'format'      => 'date-time',
1664          );
1665  
1666          $query_params['author'] = array(
1667              'description' => __( 'Limit result set to comments assigned to specific user IDs. Requires authorization.' ),
1668              'type'        => 'array',
1669              'items'       => array(
1670                  'type' => 'integer',
1671              ),
1672          );
1673  
1674          $query_params['author_exclude'] = array(
1675              'description' => __( 'Ensure result set excludes comments assigned to specific user IDs. Requires authorization.' ),
1676              'type'        => 'array',
1677              'items'       => array(
1678                  'type' => 'integer',
1679              ),
1680          );
1681  
1682          $query_params['author_email'] = array(
1683              'default'     => null,
1684              'description' => __( 'Limit result set to that from a specific author email. Requires authorization.' ),
1685              'format'      => 'email',
1686              'type'        => 'string',
1687          );
1688  
1689          $query_params['before'] = array(
1690              'description' => __( 'Limit response to comments published before a given ISO8601 compliant date.' ),
1691              'type'        => 'string',
1692              'format'      => 'date-time',
1693          );
1694  
1695          $query_params['exclude'] = array(
1696              'description' => __( 'Ensure result set excludes specific IDs.' ),
1697              'type'        => 'array',
1698              'items'       => array(
1699                  'type' => 'integer',
1700              ),
1701              'default'     => array(),
1702          );
1703  
1704          $query_params['include'] = array(
1705              'description' => __( 'Limit result set to specific IDs.' ),
1706              'type'        => 'array',
1707              'items'       => array(
1708                  'type' => 'integer',
1709              ),
1710              'default'     => array(),
1711          );
1712  
1713          $query_params['offset'] = array(
1714              'description' => __( 'Offset the result set by a specific number of items.' ),
1715              'type'        => 'integer',
1716          );
1717  
1718          $query_params['order'] = array(
1719              'description' => __( 'Order sort attribute ascending or descending.' ),
1720              'type'        => 'string',
1721              'default'     => 'desc',
1722              'enum'        => array(
1723                  'asc',
1724                  'desc',
1725              ),
1726          );
1727  
1728          $query_params['orderby'] = array(
1729              'description' => __( 'Sort collection by comment attribute.' ),
1730              'type'        => 'string',
1731              'default'     => 'date_gmt',
1732              'enum'        => array(
1733                  'date',
1734                  'date_gmt',
1735                  'id',
1736                  'include',
1737                  'post',
1738                  'parent',
1739                  'type',
1740              ),
1741          );
1742  
1743          $query_params['parent'] = array(
1744              'default'     => array(),
1745              'description' => __( 'Limit result set to comments of specific parent IDs.' ),
1746              'type'        => 'array',
1747              'items'       => array(
1748                  'type' => 'integer',
1749              ),
1750          );
1751  
1752          $query_params['parent_exclude'] = array(
1753              'default'     => array(),
1754              'description' => __( 'Ensure result set excludes specific parent IDs.' ),
1755              'type'        => 'array',
1756              'items'       => array(
1757                  'type' => 'integer',
1758              ),
1759          );
1760  
1761          $query_params['post'] = array(
1762              'default'     => array(),
1763              'description' => __( 'Limit result set to comments assigned to specific post IDs.' ),
1764              'type'        => 'array',
1765              'items'       => array(
1766                  'type' => 'integer',
1767              ),
1768          );
1769  
1770          $query_params['status'] = array(
1771              'default'           => 'approve',
1772              'description'       => __( 'Limit result set to comments assigned a specific status. Requires authorization.' ),
1773              'sanitize_callback' => 'sanitize_key',
1774              'type'              => 'string',
1775              'validate_callback' => 'rest_validate_request_arg',
1776          );
1777  
1778          $query_params['type'] = array(
1779              'default'           => 'comment',
1780              'description'       => __( 'Limit result set to comments assigned a specific type. Requires authorization.' ),
1781              'sanitize_callback' => 'sanitize_key',
1782              'type'              => 'string',
1783              'validate_callback' => 'rest_validate_request_arg',
1784          );
1785  
1786          $query_params['password'] = array(
1787              'description' => __( 'The password for the post if it is password protected.' ),
1788              'type'        => 'string',
1789          );
1790  
1791          /**
1792           * Filters REST API collection parameters for the comments controller.
1793           *
1794           * This filter registers the collection parameter, but does not map the
1795           * collection parameter to an internal WP_Comment_Query parameter. Use the
1796           * `rest_comment_query` filter to set WP_Comment_Query parameters.
1797           *
1798           * @since 4.7.0
1799           *
1800           * @param array $query_params JSON Schema-formatted collection parameters.
1801           */
1802          return apply_filters( 'rest_comment_collection_params', $query_params );
1803      }
1804  
1805      /**
1806       * Sets the comment_status of a given comment object when creating or updating a comment.
1807       *
1808       * @since 4.7.0
1809       *
1810       * @param string|int $new_status New comment status.
1811       * @param int        $comment_id Comment ID.
1812       * @return bool Whether the status was changed.
1813       */
1814  	protected function handle_status_param( $new_status, $comment_id ) {
1815          $old_status = wp_get_comment_status( $comment_id );
1816  
1817          if ( $new_status === $old_status ) {
1818              return false;
1819          }
1820  
1821          switch ( $new_status ) {
1822              case 'approved':
1823              case 'approve':
1824              case '1':
1825                  $changed = wp_set_comment_status( $comment_id, 'approve' );
1826                  break;
1827              case 'hold':
1828              case '0':
1829                  $changed = wp_set_comment_status( $comment_id, 'hold' );
1830                  break;
1831              case 'spam':
1832                  $changed = wp_spam_comment( $comment_id );
1833                  break;
1834              case 'unspam':
1835                  $changed = wp_unspam_comment( $comment_id );
1836                  break;
1837              case 'trash':
1838                  $changed = wp_trash_comment( $comment_id );
1839                  break;
1840              case 'untrash':
1841                  $changed = wp_untrash_comment( $comment_id );
1842                  break;
1843              default:
1844                  $changed = false;
1845                  break;
1846          }
1847  
1848          return $changed;
1849      }
1850  
1851      /**
1852       * Checks if the post can be read.
1853       *
1854       * Correctly handles posts with the inherit status.
1855       *
1856       * @since 4.7.0
1857       *
1858       * @param WP_Post         $post    Post object.
1859       * @param WP_REST_Request $request Request data to check.
1860       * @return bool Whether post can be read.
1861       */
1862  	protected function check_read_post_permission( $post, $request ) {
1863          $post_type = get_post_type_object( $post->post_type );
1864  
1865          // Return false if custom post type doesn't exist
1866          if ( ! $post_type ) {
1867              return false;
1868          }
1869  
1870          $posts_controller = $post_type->get_rest_controller();
1871  
1872          /*
1873           * Ensure the posts controller is specifically a WP_REST_Posts_Controller instance
1874           * before using methods specific to that controller.
1875           */
1876          if ( ! $posts_controller instanceof WP_REST_Posts_Controller ) {
1877              $posts_controller = new WP_REST_Posts_Controller( $post->post_type );
1878          }
1879  
1880          $has_password_filter = false;
1881  
1882          // Only check password if a specific post was queried for or a single comment
1883          $requested_post    = ! empty( $request['post'] ) && ( ! is_array( $request['post'] ) || 1 === count( $request['post'] ) );
1884          $requested_comment = ! empty( $request['id'] );
1885          if ( ( $requested_post || $requested_comment ) && $posts_controller->can_access_password_content( $post, $request ) ) {
1886              add_filter( 'post_password_required', '__return_false' );
1887  
1888              $has_password_filter = true;
1889          }
1890  
1891          if ( post_password_required( $post ) ) {
1892              $result = current_user_can( 'edit_post', $post->ID );
1893          } else {
1894              $result = $posts_controller->check_read_permission( $post );
1895          }
1896  
1897          if ( $has_password_filter ) {
1898              remove_filter( 'post_password_required', '__return_false' );
1899          }
1900  
1901          return $result;
1902      }
1903  
1904      /**
1905       * Checks if the comment can be read.
1906       *
1907       * @since 4.7.0
1908       *
1909       * @param WP_Comment      $comment Comment object.
1910       * @param WP_REST_Request $request Request data to check.
1911       * @return bool Whether the comment can be read.
1912       */
1913  	protected function check_read_permission( $comment, $request ) {
1914          if ( 'note' !== $comment->comment_type && ! empty( $comment->comment_post_ID ) ) {
1915              $post = get_post( $comment->comment_post_ID );
1916              if ( $post ) {
1917                  if ( $this->check_read_post_permission( $post, $request ) && 1 === (int) $comment->comment_approved ) {
1918                      return true;
1919                  }
1920              }
1921          }
1922  
1923          if ( 0 === get_current_user_id() ) {
1924              return false;
1925          }
1926  
1927          if ( empty( $comment->comment_post_ID ) && ! current_user_can( 'moderate_comments' ) ) {
1928              return false;
1929          }
1930  
1931          if ( ! empty( $comment->user_id ) && get_current_user_id() === (int) $comment->user_id ) {
1932              return true;
1933          }
1934  
1935          return current_user_can( 'edit_comment', $comment->comment_ID );
1936      }
1937  
1938      /**
1939       * Checks if a comment can be edited or deleted.
1940       *
1941       * @since 4.7.0
1942       *
1943       * @param WP_Comment $comment Comment object.
1944       * @return bool Whether the comment can be edited or deleted.
1945       */
1946  	protected function check_edit_permission( $comment ) {
1947          if ( 0 === (int) get_current_user_id() ) {
1948              return false;
1949          }
1950  
1951          if ( current_user_can( 'moderate_comments' ) ) {
1952              return true;
1953          }
1954  
1955          return current_user_can( 'edit_comment', $comment->comment_ID );
1956      }
1957  
1958      /**
1959       * Checks a comment author email for validity.
1960       *
1961       * Accepts either a valid email address or empty string as a valid comment
1962       * author email address. Setting the comment author email to an empty
1963       * string is allowed when a comment is being updated.
1964       *
1965       * @since 4.7.0
1966       *
1967       * @param string          $value   Author email value submitted.
1968       * @param WP_REST_Request $request Full details about the request.
1969       * @param string          $param   The parameter name.
1970       * @return string|WP_Error The sanitized email address, if valid,
1971       *                         otherwise an error.
1972       */
1973  	public function check_comment_author_email( $value, $request, $param ) {
1974          $email = (string) $value;
1975          if ( empty( $email ) ) {
1976              return $email;
1977          }
1978  
1979          $check_email = rest_validate_request_arg( $email, $request, $param );
1980          if ( is_wp_error( $check_email ) ) {
1981              return $check_email;
1982          }
1983  
1984          return $email;
1985      }
1986  
1987      /**
1988       * If empty comments are not allowed, checks if the provided comment content is not empty.
1989       *
1990       * @since 5.6.0
1991       *
1992       * @param array $prepared_comment The prepared comment data.
1993       * @return bool True if the content is allowed, false otherwise.
1994       */
1995  	protected function check_is_comment_content_allowed( $prepared_comment ) {
1996          if ( ! isset( $prepared_comment['comment_content'] ) ) {
1997              return true;
1998          }
1999  
2000          $check = wp_parse_args(
2001              $prepared_comment,
2002              array(
2003                  'comment_post_ID'      => 0,
2004                  'comment_author'       => null,
2005                  'comment_author_email' => null,
2006                  'comment_author_url'   => null,
2007                  'comment_parent'       => 0,
2008                  'user_id'              => 0,
2009              )
2010          );
2011  
2012          /** This filter is documented in wp-includes/comment.php */
2013          $allow_empty = apply_filters( 'allow_empty_comment', false, $check );
2014  
2015          if ( $allow_empty ) {
2016              return true;
2017          }
2018  
2019          // Allow empty notes only when resolution metadata is valid.
2020          if (
2021              isset( $check['comment_type'] ) &&
2022              'note' === $check['comment_type'] &&
2023              isset( $check['meta']['_wp_note_status'] ) &&
2024              in_array( $check['meta']['_wp_note_status'], array( 'resolved', 'reopen' ), true )
2025          ) {
2026              return true;
2027          }
2028  
2029          /*
2030           * Do not allow a comment to be created with missing or empty
2031           * comment_content. See wp_handle_comment_submission().
2032           */
2033          return '' !== $check['comment_content'];
2034      }
2035  
2036      /**
2037       * Check if post type supports notes.
2038       *
2039       * @param string $post_type Post type name.
2040       * @return bool True if post type supports notes, false otherwise.
2041       */
2042  	private function check_post_type_supports_notes( $post_type ) {
2043          $supports = get_all_post_type_supports( $post_type );
2044          if ( ! isset( $supports['editor'] ) ) {
2045              return false;
2046          }
2047          if ( ! is_array( $supports['editor'] ) ) {
2048              return false;
2049          }
2050          foreach ( $supports['editor'] as $item ) {
2051              if ( ! empty( $item['notes'] ) ) {
2052                  return true;
2053              }
2054          }
2055          return false;
2056      }
2057  }


Generated : Wed Apr 15 08:20:10 2026 Cross-referenced by PHPXref