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