WP_REST_Comments_Controller

Core controller used to access comments via the REST API.

Defined (1)

The class is defined in the following location(s).

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