[ Index ]

PHP Cross Reference of WordPress

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


Generated: Sat Sep 21 01:00:03 2019 Cross-referenced by PHPXref 0.7.1