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 ( false === $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 ) && isset( $request['status'] ) ) { 
  545. // Only the comment status is being changed. 
  546. $change = $this->handle_status_param( $request['status'], $id ); 
  547.  
  548. if ( ! $change ) { 
  549. return new WP_Error( 'rest_comment_failed_edit', __( 'Updating comment status failed.' ), array( 'status' => 500 ) ); 
  550. } elseif ( ! empty( $prepared_args ) ) { 
  551. if ( is_wp_error( $prepared_args ) ) { 
  552. return $prepared_args; 
  553.  
  554. if ( isset( $prepared_args['comment_content'] ) && empty( $prepared_args['comment_content'] ) ) { 
  555. return new WP_Error( 'rest_comment_content_invalid', __( 'Invalid comment content.' ), array( 'status' => 400 ) ); 
  556.  
  557. $prepared_args['comment_ID'] = $id; 
  558.  
  559. $check_comment_lengths = wp_check_comment_data_max_lengths( $prepared_args ); 
  560. if ( is_wp_error( $check_comment_lengths ) ) { 
  561. $error_code = $check_comment_lengths->get_error_code(); 
  562. return new WP_Error( $error_code, __( 'Comment field exceeds maximum length allowed.' ), array( 'status' => 400 ) ); 
  563.  
  564. $updated = wp_update_comment( wp_slash( (array) $prepared_args ) ); 
  565.  
  566. if ( 0 === $updated ) { 
  567. return new WP_Error( 'rest_comment_failed_edit', __( 'Updating comment failed.' ), array( 'status' => 500 ) ); 
  568.  
  569. if ( isset( $request['status'] ) ) { 
  570. $this->handle_status_param( $request['status'], $id ); 
  571.  
  572. $comment = get_comment( $id ); 
  573.  
  574. /** This action is documented in lib/endpoints/class-wp-rest-comments-controller.php */ 
  575. do_action( 'rest_insert_comment', $comment, $request, false ); 
  576.  
  577. $schema = $this->get_item_schema(); 
  578.  
  579. if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { 
  580. $meta_update = $this->meta->update_value( $request['meta'], $id ); 
  581.  
  582. if ( is_wp_error( $meta_update ) ) { 
  583. return $meta_update; 
  584.  
  585. $fields_update = $this->update_additional_fields_for_object( $comment, $request ); 
  586.  
  587. if ( is_wp_error( $fields_update ) ) { 
  588. return $fields_update; 
  589.  
  590. $request->set_param( 'context', 'edit' ); 
  591.  
  592. $response = $this->prepare_item_for_response( $comment, $request ); 
  593.  
  594. return rest_ensure_response( $response ); 
  595.  
  596. /** 
  597. * Checks if a given request has access to delete a comment. 
  598. * @since 4.7.0 
  599. * @access public 
  600. * @param WP_REST_Request $request Full details about the request. 
  601. * @return WP_Error|bool True if the request has access to delete the item, error object otherwise. 
  602. */ 
  603. public function delete_item_permissions_check( $request ) { 
  604. $id = (int) $request['id']; 
  605. $comment = get_comment( $id ); 
  606.  
  607. if ( ! $comment ) { 
  608. return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) ); 
  609.  
  610. if ( ! $this->check_edit_permission( $comment ) ) { 
  611. return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this comment.' ), array( 'status' => rest_authorization_required_code() ) ); 
  612. return true; 
  613.  
  614. /** 
  615. * Deletes a comment. 
  616. * @since 4.7.0 
  617. * @access public 
  618. * @param WP_REST_Request $request Full details about the request. 
  619. * @return WP_Error|WP_REST_Response Response object on success, or error object on failure. 
  620. */ 
  621. public function delete_item( $request ) { 
  622. $id = (int) $request['id']; 
  623. $force = isset( $request['force'] ) ? (bool) $request['force'] : false; 
  624.  
  625. $comment = get_comment( $id ); 
  626.  
  627. if ( empty( $comment ) ) { 
  628. return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) ); 
  629.  
  630. /** 
  631. * Filters whether a comment can be trashed. 
  632. * Return false to disable trash support for the post. 
  633. * @since 4.7.0 
  634. * @param bool $supports_trash Whether the post type support trashing. 
  635. * @param WP_Post $comment The comment object being considered for trashing support. 
  636. */ 
  637. $supports_trash = apply_filters( 'rest_comment_trashable', ( EMPTY_TRASH_DAYS > 0 ), $comment ); 
  638.  
  639. $request->set_param( 'context', 'edit' ); 
  640.  
  641. if ( $force ) { 
  642. $previous = $this->prepare_item_for_response( $comment, $request ); 
  643. $result = wp_delete_comment( $comment->comment_ID, true ); 
  644. $response = new WP_REST_Response(); 
  645. $response->set_data( array( 'deleted' => true, 'previous' => $previous->get_data() ) ); 
  646. } else { 
  647. // If this type doesn't support trashing, error out. 
  648. if ( ! $supports_trash ) { 
  649. return new WP_Error( 'rest_trash_not_supported', __( 'The comment does not support trashing. Set force=true to delete.' ), array( 'status' => 501 ) ); 
  650.  
  651. if ( 'trash' === $comment->comment_approved ) { 
  652. return new WP_Error( 'rest_already_trashed', __( 'The comment has already been trashed.' ), array( 'status' => 410 ) ); 
  653.  
  654. $result = wp_trash_comment( $comment->comment_ID ); 
  655. $comment = get_comment( $comment->comment_ID ); 
  656. $response = $this->prepare_item_for_response( $comment, $request ); 
  657.  
  658. if ( ! $result ) { 
  659. return new WP_Error( 'rest_cannot_delete', __( 'The comment cannot be deleted.' ), array( 'status' => 500 ) ); 
  660.  
  661. /** 
  662. * Fires after a comment is deleted via the REST API. 
  663. * @since 4.7.0 
  664. * @param WP_Comment $comment The deleted comment data. 
  665. * @param WP_REST_Response $response The response returned from the API. 
  666. * @param WP_REST_Request $request The request sent to the API. 
  667. */ 
  668. do_action( 'rest_delete_comment', $comment, $response, $request ); 
  669.  
  670. return $response; 
  671.  
  672. /** 
  673. * Prepares a single comment output for response. 
  674. * @since 4.7.0 
  675. * @access public 
  676. * @param WP_Comment $comment Comment object. 
  677. * @param WP_REST_Request $request Request object. 
  678. * @return WP_REST_Response Response object. 
  679. */ 
  680. public function prepare_item_for_response( $comment, $request ) { 
  681. $data = array( 
  682. 'id' => (int) $comment->comment_ID,  
  683. 'post' => (int) $comment->comment_post_ID,  
  684. 'parent' => (int) $comment->comment_parent,  
  685. 'author' => (int) $comment->user_id,  
  686. 'author_name' => $comment->comment_author,  
  687. 'author_email' => $comment->comment_author_email,  
  688. 'author_url' => $comment->comment_author_url,  
  689. 'author_ip' => $comment->comment_author_IP,  
  690. 'author_user_agent' => $comment->comment_agent,  
  691. 'date' => mysql_to_rfc3339( $comment->comment_date ),  
  692. 'date_gmt' => mysql_to_rfc3339( $comment->comment_date_gmt ),  
  693. 'content' => array( 
  694. /** This filter is documented in wp-includes/comment-template.php */ 
  695. 'rendered' => apply_filters( 'comment_text', $comment->comment_content, $comment ),  
  696. 'raw' => $comment->comment_content,  
  697. ),  
  698. 'link' => get_comment_link( $comment ),  
  699. 'status' => $this->prepare_status_response( $comment->comment_approved ),  
  700. 'type' => get_comment_type( $comment->comment_ID ),  
  701. ); 
  702.  
  703. $schema = $this->get_item_schema(); 
  704.  
  705. if ( ! empty( $schema['properties']['author_avatar_urls'] ) ) { 
  706. $data['author_avatar_urls'] = rest_get_avatar_urls( $comment->comment_author_email ); 
  707.  
  708. if ( ! empty( $schema['properties']['meta'] ) ) { 
  709. $data['meta'] = $this->meta->get_value( $comment->comment_ID, $request ); 
  710.  
  711. $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; 
  712. $data = $this->add_additional_fields_to_object( $data, $request ); 
  713. $data = $this->filter_response_by_context( $data, $context ); 
  714.  
  715. // Wrap the data in a response object. 
  716. $response = rest_ensure_response( $data ); 
  717.  
  718. $response->add_links( $this->prepare_links( $comment ) ); 
  719.  
  720. /** 
  721. * Filters a comment returned from the API. 
  722. * Allows modification of the comment right before it is returned. 
  723. * @since 4.7.0 
  724. * @param WP_REST_Response $response The response object. 
  725. * @param WP_Comment $comment The original comment object. 
  726. * @param WP_REST_Request $request Request used to generate the response. 
  727. */ 
  728. return apply_filters( 'rest_prepare_comment', $response, $comment, $request ); 
  729.  
  730. /** 
  731. * Prepares links for the request. 
  732. * @since 4.7.0 
  733. * @access protected 
  734. * @param WP_Comment $comment Comment object. 
  735. * @return array Links for the given comment. 
  736. */ 
  737. protected function prepare_links( $comment ) { 
  738. $links = array( 
  739. 'self' => array( 
  740. 'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment->comment_ID ) ),  
  741. ),  
  742. 'collection' => array( 
  743. 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),  
  744. ),  
  745. ); 
  746.  
  747. if ( 0 !== (int) $comment->user_id ) { 
  748. $links['author'] = array( 
  749. 'href' => rest_url( 'wp/v2/users/' . $comment->user_id ),  
  750. 'embeddable' => true,  
  751. ); 
  752.  
  753. if ( 0 !== (int) $comment->comment_post_ID ) { 
  754. $post = get_post( $comment->comment_post_ID ); 
  755.  
  756. if ( ! empty( $post->ID ) ) { 
  757. $obj = get_post_type_object( $post->post_type ); 
  758. $base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name; 
  759.  
  760. $links['up'] = array( 
  761. 'href' => rest_url( 'wp/v2/' . $base . '/' . $comment->comment_post_ID ),  
  762. 'embeddable' => true,  
  763. 'post_type' => $post->post_type,  
  764. ); 
  765.  
  766. if ( 0 !== (int) $comment->comment_parent ) { 
  767. $links['in-reply-to'] = array( 
  768. 'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment->comment_parent ) ),  
  769. 'embeddable' => true,  
  770. ); 
  771.  
  772. // Only grab one comment to verify the comment has children. 
  773. $comment_children = $comment->get_children( array( 
  774. 'number' => 1,  
  775. 'count' => true 
  776. ) ); 
  777.  
  778. if ( ! empty( $comment_children ) ) { 
  779. $args = array( 
  780. 'parent' => $comment->comment_ID 
  781. ); 
  782.  
  783. $rest_url = add_query_arg( $args, rest_url( $this->namespace . '/' . $this->rest_base ) ); 
  784.  
  785. $links['children'] = array( 
  786. 'href' => $rest_url,  
  787. ); 
  788.  
  789. return $links; 
  790.  
  791. /** 
  792. * Prepends internal property prefix to query parameters to match our response fields. 
  793. * @since 4.7.0 
  794. * @access protected 
  795. * @param string $query_param Query parameter. 
  796. * @return string The normalized query parameter. 
  797. */ 
  798. protected function normalize_query_param( $query_param ) { 
  799. $prefix = 'comment_'; 
  800.  
  801. switch ( $query_param ) { 
  802. case 'id': 
  803. $normalized = $prefix . 'ID'; 
  804. break; 
  805. case 'post': 
  806. $normalized = $prefix . 'post_ID'; 
  807. break; 
  808. case 'parent': 
  809. $normalized = $prefix . 'parent'; 
  810. break; 
  811. case 'include': 
  812. $normalized = 'comment__in'; 
  813. break; 
  814. default: 
  815. $normalized = $prefix . $query_param; 
  816. break; 
  817.  
  818. return $normalized; 
  819.  
  820. /** 
  821. * Checks comment_approved to set comment status for single comment output. 
  822. * @since 4.7.0 
  823. * @access protected 
  824. * @param string|int $comment_approved comment status. 
  825. * @return string Comment status. 
  826. */ 
  827. protected function prepare_status_response( $comment_approved ) { 
  828.  
  829. switch ( $comment_approved ) { 
  830. case 'hold': 
  831. case '0': 
  832. $status = 'hold'; 
  833. break; 
  834.  
  835. case 'approve': 
  836. case '1': 
  837. $status = 'approved'; 
  838. break; 
  839.  
  840. case 'spam': 
  841. case 'trash': 
  842. default: 
  843. $status = $comment_approved; 
  844. break; 
  845.  
  846. return $status; 
  847.  
  848. /** 
  849. * Prepares a single comment to be inserted into the database. 
  850. * @since 4.7.0 
  851. * @access protected 
  852. * @param WP_REST_Request $request Request object. 
  853. * @return array|WP_Error Prepared comment, otherwise WP_Error object. 
  854. */ 
  855. protected function prepare_item_for_database( $request ) { 
  856. $prepared_comment = array(); 
  857.  
  858. /** 
  859. * Allow the comment_content to be set via the 'content' or 
  860. * the 'content.raw' properties of the Request object. 
  861. */ 
  862. if ( isset( $request['content'] ) && is_string( $request['content'] ) ) { 
  863. $prepared_comment['comment_content'] = $request['content']; 
  864. } elseif ( isset( $request['content']['raw'] ) && is_string( $request['content']['raw'] ) ) { 
  865. $prepared_comment['comment_content'] = $request['content']['raw']; 
  866.  
  867. if ( isset( $request['post'] ) ) { 
  868. $prepared_comment['comment_post_ID'] = (int) $request['post']; 
  869.  
  870. if ( isset( $request['parent'] ) ) { 
  871. $prepared_comment['comment_parent'] = $request['parent']; 
  872.  
  873. if ( isset( $request['author'] ) ) { 
  874. $user = new WP_User( $request['author'] ); 
  875.  
  876. if ( $user->exists() ) { 
  877. $prepared_comment['user_id'] = $user->ID; 
  878. $prepared_comment['comment_author'] = $user->display_name; 
  879. $prepared_comment['comment_author_email'] = $user->user_email; 
  880. $prepared_comment['comment_author_url'] = $user->user_url; 
  881. } else { 
  882. return new WP_Error( 'rest_comment_author_invalid', __( 'Invalid comment author ID.' ), array( 'status' => 400 ) ); 
  883.  
  884. if ( isset( $request['author_name'] ) ) { 
  885. $prepared_comment['comment_author'] = $request['author_name']; 
  886.  
  887. if ( isset( $request['author_email'] ) ) { 
  888. $prepared_comment['comment_author_email'] = $request['author_email']; 
  889.  
  890. if ( isset( $request['author_url'] ) ) { 
  891. $prepared_comment['comment_author_url'] = $request['author_url']; 
  892.  
  893. if ( isset( $request['author_ip'] ) && current_user_can( 'moderate_comments' ) ) { 
  894. $prepared_comment['comment_author_IP'] = $request['author_ip']; 
  895. } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) && rest_is_ip_address( $_SERVER['REMOTE_ADDR'] ) ) { 
  896. $prepared_comment['comment_author_IP'] = $_SERVER['REMOTE_ADDR']; 
  897. } else { 
  898. $prepared_comment['comment_author_IP'] = '127.0.0.1'; 
  899.  
  900. if ( ! empty( $request['author_user_agent'] ) ) { 
  901. $prepared_comment['comment_agent'] = $request['author_user_agent']; 
  902. } elseif ( $request->get_header( 'user_agent' ) ) { 
  903. $prepared_comment['comment_agent'] = $request->get_header( 'user_agent' ); 
  904.  
  905. if ( ! empty( $request['date'] ) ) { 
  906. $date_data = rest_get_date_with_gmt( $request['date'] ); 
  907.  
  908. if ( ! empty( $date_data ) ) { 
  909. list( $prepared_comment['comment_date'], $prepared_comment['comment_date_gmt'] ) = $date_data; 
  910. } elseif ( ! empty( $request['date_gmt'] ) ) { 
  911. $date_data = rest_get_date_with_gmt( $request['date_gmt'], true ); 
  912.  
  913. if ( ! empty( $date_data ) ) { 
  914. list( $prepared_comment['comment_date'], $prepared_comment['comment_date_gmt'] ) = $date_data; 
  915.  
  916. /** 
  917. * Filters a comment after it is prepared for the database. 
  918. * Allows modification of the comment right after it is prepared for the database. 
  919. * @since 4.7.0 
  920. * @param array $prepared_comment The prepared comment data for `wp_insert_comment`. 
  921. * @param WP_REST_Request $request The current request. 
  922. */ 
  923. return apply_filters( 'rest_preprocess_comment', $prepared_comment, $request ); 
  924.  
  925. /** 
  926. * Retrieves the comment's schema, conforming to JSON Schema. 
  927. * @since 4.7.0 
  928. * @access public 
  929. * @return array 
  930. */ 
  931. public function get_item_schema() { 
  932. $schema = array( 
  933. '$schema' => 'http://json-schema.org/schema#',  
  934. 'title' => 'comment',  
  935. 'type' => 'object',  
  936. 'properties' => array( 
  937. 'id' => array( 
  938. 'description' => __( 'Unique identifier for the object.' ),  
  939. 'type' => 'integer',  
  940. 'context' => array( 'view', 'edit', 'embed' ),  
  941. 'readonly' => true,  
  942. ),  
  943. 'author' => array( 
  944. 'description' => __( 'The ID of the user object, if author was a user.' ),  
  945. 'type' => 'integer',  
  946. 'context' => array( 'view', 'edit', 'embed' ),  
  947. ),  
  948. 'author_email' => array( 
  949. 'description' => __( 'Email address for the object author.' ),  
  950. 'type' => 'string',  
  951. 'format' => 'email',  
  952. 'context' => array( 'edit' ),  
  953. 'arg_options' => array( 
  954. 'sanitize_callback' => array( $this, 'check_comment_author_email' ),  
  955. 'validate_callback' => null, // skip built-in validation of 'email'. 
  956. ),  
  957. ),  
  958. 'author_ip' => array( 
  959. 'description' => __( 'IP address for the object author.' ),  
  960. 'type' => 'string',  
  961. 'format' => 'ip',  
  962. 'context' => array( 'edit' ),  
  963. ),  
  964. 'author_name' => array( 
  965. 'description' => __( 'Display name for the object author.' ),  
  966. 'type' => 'string',  
  967. 'context' => array( 'view', 'edit', 'embed' ),  
  968. 'arg_options' => array( 
  969. 'sanitize_callback' => 'sanitize_text_field',  
  970. ),  
  971. ),  
  972. 'author_url' => array( 
  973. 'description' => __( 'URL for the object author.' ),  
  974. 'type' => 'string',  
  975. 'format' => 'uri',  
  976. 'context' => array( 'view', 'edit', 'embed' ),  
  977. ),  
  978. 'author_user_agent' => array( 
  979. 'description' => __( 'User agent for the object author.' ),  
  980. 'type' => 'string',  
  981. 'context' => array( 'edit' ),  
  982. 'arg_options' => array( 
  983. 'sanitize_callback' => 'sanitize_text_field',  
  984. ),  
  985. ),  
  986. 'content' => array( 
  987. 'description' => __( 'The content for the object.' ),  
  988. 'type' => 'object',  
  989. 'context' => array( 'view', 'edit', 'embed' ),  
  990. 'arg_options' => array( 
  991. 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database() 
  992. ),  
  993. 'properties' => array( 
  994. 'raw' => array( 
  995. 'description' => __( 'Content for the object, as it exists in the database.' ),  
  996. 'type' => 'string',  
  997. 'context' => array( 'edit' ),  
  998. ),  
  999. 'rendered' => array( 
  1000. 'description' => __( 'HTML content for the object, transformed for display.' ),  
  1001. 'type' => 'string',  
  1002. 'context' => array( 'view', 'edit', 'embed' ),  
  1003. 'readonly' => true,  
  1004. ),  
  1005. ),  
  1006. ),  
  1007. 'date' => array( 
  1008. 'description' => __( "The date the object was published, in the site's timezone." ),  
  1009. 'type' => 'string',  
  1010. 'format' => 'date-time',  
  1011. 'context' => array( 'view', 'edit', 'embed' ),  
  1012. ),  
  1013. 'date_gmt' => array( 
  1014. 'description' => __( 'The date the object was published, as GMT.' ),  
  1015. 'type' => 'string',  
  1016. 'format' => 'date-time',  
  1017. 'context' => array( 'view', 'edit' ),  
  1018. ),  
  1019. 'link' => array( 
  1020. 'description' => __( 'URL to the object.' ),  
  1021. 'type' => 'string',  
  1022. 'format' => 'uri',  
  1023. 'context' => array( 'view', 'edit', 'embed' ),  
  1024. 'readonly' => true,  
  1025. ),  
  1026. 'parent' => array( 
  1027. 'description' => __( 'The ID for the parent of the object.' ),  
  1028. 'type' => 'integer',  
  1029. 'context' => array( 'view', 'edit', 'embed' ),  
  1030. 'default' => 0,  
  1031. ),  
  1032. 'post' => array( 
  1033. 'description' => __( 'The ID of the associated post object.' ),  
  1034. 'type' => 'integer',  
  1035. 'context' => array( 'view', 'edit' ),  
  1036. 'default' => 0,  
  1037. ),  
  1038. 'status' => array( 
  1039. 'description' => __( 'State of the object.' ),  
  1040. 'type' => 'string',  
  1041. 'context' => array( 'view', 'edit' ),  
  1042. 'arg_options' => array( 
  1043. 'sanitize_callback' => 'sanitize_key',  
  1044. ),  
  1045. ),  
  1046. 'type' => array( 
  1047. 'description' => __( 'Type of Comment for the object.' ),  
  1048. 'type' => 'string',  
  1049. 'context' => array( 'view', 'edit', 'embed' ),  
  1050. 'readonly' => true,  
  1051. ),  
  1052. ),  
  1053. ); 
  1054.  
  1055. if ( get_option( 'show_avatars' ) ) { 
  1056. $avatar_properties = array(); 
  1057.  
  1058. $avatar_sizes = rest_get_avatar_sizes(); 
  1059. foreach ( $avatar_sizes as $size ) { 
  1060. $avatar_properties[ $size ] = array( 
  1061. /** translators: %d: avatar image size in pixels */ 
  1062. 'description' => sprintf( __( 'Avatar URL with image size of %d pixels.' ), $size ),  
  1063. 'type' => 'string',  
  1064. 'format' => 'uri',  
  1065. 'context' => array( 'embed', 'view', 'edit' ),  
  1066. ); 
  1067.  
  1068. $schema['properties']['author_avatar_urls'] = array( 
  1069. 'description' => __( 'Avatar URLs for the object author.' ),  
  1070. 'type' => 'object',  
  1071. 'context' => array( 'view', 'edit', 'embed' ),  
  1072. 'readonly' => true,  
  1073. 'properties' => $avatar_properties,  
  1074. ); 
  1075.  
  1076. $schema['properties']['meta'] = $this->meta->get_field_schema(); 
  1077.  
  1078. return $this->add_additional_fields_schema( $schema ); 
  1079.  
  1080. /** 
  1081. * Retrieves the query params for collections. 
  1082. * @since 4.7.0 
  1083. * @access public 
  1084. * @return array Comments collection parameters. 
  1085. */ 
  1086. public function get_collection_params() { 
  1087. $query_params = parent::get_collection_params(); 
  1088.  
  1089. $query_params['context']['default'] = 'view'; 
  1090.  
  1091. $query_params['after'] = array( 
  1092. 'description' => __( 'Limit response to comments published after a given ISO8601 compliant date.' ),  
  1093. 'type' => 'string',  
  1094. 'format' => 'date-time',  
  1095. ); 
  1096.  
  1097. $query_params['author'] = array( 
  1098. 'description' => __( 'Limit result set to comments assigned to specific user IDs. Requires authorization.' ),  
  1099. 'type' => 'array',  
  1100. 'items' => array( 
  1101. 'type' => 'integer',  
  1102. ),  
  1103. ); 
  1104.  
  1105. $query_params['author_exclude'] = array( 
  1106. 'description' => __( 'Ensure result set excludes comments assigned to specific user IDs. Requires authorization.' ),  
  1107. 'type' => 'array',  
  1108. 'items' => array( 
  1109. 'type' => 'integer',  
  1110. ),  
  1111. ); 
  1112.  
  1113. $query_params['author_email'] = array( 
  1114. 'default' => null,  
  1115. 'description' => __( 'Limit result set to that from a specific author email. Requires authorization.' ),  
  1116. 'format' => 'email',  
  1117. 'type' => 'string',  
  1118. ); 
  1119.  
  1120. $query_params['before'] = array( 
  1121. 'description' => __( 'Limit response to comments published before a given ISO8601 compliant date.' ),  
  1122. 'type' => 'string',  
  1123. 'format' => 'date-time',  
  1124. ); 
  1125.  
  1126. $query_params['exclude'] = array( 
  1127. 'description' => __( 'Ensure result set excludes specific IDs.' ),  
  1128. 'type' => 'array',  
  1129. 'items' => array( 
  1130. 'type' => 'integer',  
  1131. ),  
  1132. 'default' => array(),  
  1133. ); 
  1134.  
  1135. $query_params['include'] = array( 
  1136. 'description' => __( 'Limit result set to specific IDs.' ),  
  1137. 'type' => 'array',  
  1138. 'items' => array( 
  1139. 'type' => 'integer',  
  1140. ),  
  1141. 'default' => array(),  
  1142. ); 
  1143.  
  1144. $query_params['offset'] = array( 
  1145. 'description' => __( 'Offset the result set by a specific number of items.' ),  
  1146. 'type' => 'integer',  
  1147. ); 
  1148.  
  1149. $query_params['order'] = array( 
  1150. 'description' => __( 'Order sort attribute ascending or descending.' ),  
  1151. 'type' => 'string',  
  1152. 'default' => 'desc',  
  1153. 'enum' => array( 
  1154. 'asc',  
  1155. 'desc',  
  1156. ),  
  1157. ); 
  1158.  
  1159. $query_params['orderby'] = array( 
  1160. 'description' => __( 'Sort collection by object attribute.' ),  
  1161. 'type' => 'string',  
  1162. 'default' => 'date_gmt',  
  1163. 'enum' => array( 
  1164. 'date',  
  1165. 'date_gmt',  
  1166. 'id',  
  1167. 'include',  
  1168. 'post',  
  1169. 'parent',  
  1170. 'type',  
  1171. ),  
  1172. ); 
  1173.  
  1174. $query_params['parent'] = array( 
  1175. 'default' => array(),  
  1176. 'description' => __( 'Limit result set to comments of specific parent IDs.' ),  
  1177. 'type' => 'array',  
  1178. 'items' => array( 
  1179. 'type' => 'integer',  
  1180. ),  
  1181. ); 
  1182.  
  1183. $query_params['parent_exclude'] = array( 
  1184. 'default' => array(),  
  1185. 'description' => __( 'Ensure result set excludes specific parent IDs.' ),  
  1186. 'type' => 'array',  
  1187. 'items' => array( 
  1188. 'type' => 'integer',  
  1189. ),  
  1190. ); 
  1191.  
  1192. $query_params['post'] = array( 
  1193. 'default' => array(),  
  1194. 'description' => __( 'Limit result set to comments assigned to specific post IDs.' ),  
  1195. 'type' => 'array',  
  1196. 'items' => array( 
  1197. 'type' => 'integer',  
  1198. ),  
  1199. ); 
  1200.  
  1201. $query_params['status'] = array( 
  1202. 'default' => 'approve',  
  1203. 'description' => __( 'Limit result set to comments assigned a specific status. Requires authorization.' ),  
  1204. 'sanitize_callback' => 'sanitize_key',  
  1205. 'type' => 'string',  
  1206. 'validate_callback' => 'rest_validate_request_arg',  
  1207. ); 
  1208.  
  1209. $query_params['type'] = array( 
  1210. 'default' => 'comment',  
  1211. 'description' => __( 'Limit result set to comments assigned a specific type. Requires authorization.' ),  
  1212. 'sanitize_callback' => 'sanitize_key',  
  1213. 'type' => 'string',  
  1214. 'validate_callback' => 'rest_validate_request_arg',  
  1215. ); 
  1216.  
  1217. $query_params['password'] = array( 
  1218. 'description' => __( 'The password for the post if it is password protected.' ),  
  1219. 'type' => 'string',  
  1220. ); 
  1221.  
  1222. /** 
  1223. * Filter collection parameters for the comments controller. 
  1224. * This filter registers the collection parameter, but does not map the 
  1225. * collection parameter to an internal WP_Comment_Query parameter. Use the 
  1226. * `rest_comment_query` filter to set WP_Comment_Query parameters. 
  1227. * @since 4.7.0 
  1228. * @param $params JSON Schema-formatted collection parameters. 
  1229. */ 
  1230. return apply_filters( 'rest_comment_collection_params', $query_params ); 
  1231.  
  1232. /** 
  1233. * Sets the comment_status of a given comment object when creating or updating a comment. 
  1234. * @since 4.7.0 
  1235. * @access protected 
  1236. * @param string|int $new_status New comment status. 
  1237. * @param int $comment_id Comment ID. 
  1238. * @return bool Whether the status was changed. 
  1239. */ 
  1240. protected function handle_status_param( $new_status, $comment_id ) { 
  1241. $old_status = wp_get_comment_status( $comment_id ); 
  1242.  
  1243. if ( $new_status === $old_status ) { 
  1244. return false; 
  1245.  
  1246. switch ( $new_status ) { 
  1247. case 'approved' : 
  1248. case 'approve': 
  1249. case '1': 
  1250. $changed = wp_set_comment_status( $comment_id, 'approve' ); 
  1251. break; 
  1252. case 'hold': 
  1253. case '0': 
  1254. $changed = wp_set_comment_status( $comment_id, 'hold' ); 
  1255. break; 
  1256. case 'spam' : 
  1257. $changed = wp_spam_comment( $comment_id ); 
  1258. break; 
  1259. case 'unspam' : 
  1260. $changed = wp_unspam_comment( $comment_id ); 
  1261. break; 
  1262. case 'trash' : 
  1263. $changed = wp_trash_comment( $comment_id ); 
  1264. break; 
  1265. case 'untrash' : 
  1266. $changed = wp_untrash_comment( $comment_id ); 
  1267. break; 
  1268. default : 
  1269. $changed = false; 
  1270. break; 
  1271.  
  1272. return $changed; 
  1273.  
  1274. /** 
  1275. * Checks if the post can be read. 
  1276. * Correctly handles posts with the inherit status. 
  1277. * @since 4.7.0 
  1278. * @access protected 
  1279. * @param WP_Post $post Post object. 
  1280. * @param WP_REST_Request $request Request data to check. 
  1281. * @return bool Whether post can be read. 
  1282. */ 
  1283. protected function check_read_post_permission( $post, $request ) { 
  1284. $posts_controller = new WP_REST_Posts_Controller( $post->post_type ); 
  1285. $post_type = get_post_type_object( $post->post_type ); 
  1286.  
  1287. $has_password_filter = false; 
  1288.  
  1289. // Only check password if a specific post was queried for or a single comment 
  1290. $requested_post = ! empty( $request['post'] ) && 1 === count( $request['post'] ); 
  1291. $requested_comment = ! empty( $request['id'] ); 
  1292. if ( ( $requested_post || $requested_comment ) && $posts_controller->can_access_password_content( $post, $request ) ) { 
  1293. add_filter( 'post_password_required', '__return_false' ); 
  1294.  
  1295. $has_password_filter = true; 
  1296.  
  1297. if ( post_password_required( $post ) ) { 
  1298. $result = current_user_can( $post_type->cap->edit_post, $post->ID ); 
  1299. } else { 
  1300. $result = $posts_controller->check_read_permission( $post ); 
  1301.  
  1302. if ( $has_password_filter ) { 
  1303. remove_filter( 'post_password_required', '__return_false' ); 
  1304.  
  1305. return $result; 
  1306.  
  1307. /** 
  1308. * Checks if the comment can be read. 
  1309. * @since 4.7.0 
  1310. * @access protected 
  1311. * @param WP_Comment $comment Comment object. 
  1312. * @param WP_REST_Request $request Request data to check. 
  1313. * @return bool Whether the comment can be read. 
  1314. */ 
  1315. protected function check_read_permission( $comment, $request ) { 
  1316. if ( ! empty( $comment->comment_post_ID ) ) { 
  1317. $post = get_post( $comment->comment_post_ID ); 
  1318. if ( $post ) { 
  1319. if ( $this->check_read_post_permission( $post, $request ) && 1 === (int) $comment->comment_approved ) { 
  1320. return true; 
  1321.  
  1322. if ( 0 === get_current_user_id() ) { 
  1323. return false; 
  1324.  
  1325. if ( empty( $comment->comment_post_ID ) && ! current_user_can( 'moderate_comments' ) ) { 
  1326. return false; 
  1327.  
  1328. if ( ! empty( $comment->user_id ) && get_current_user_id() === (int) $comment->user_id ) { 
  1329. return true; 
  1330.  
  1331. return current_user_can( 'edit_comment', $comment->comment_ID ); 
  1332.  
  1333. /** 
  1334. * Checks if a comment can be edited or deleted. 
  1335. * @since 4.7.0 
  1336. * @access protected 
  1337. * @param object $comment Comment object. 
  1338. * @return bool Whether the comment can be edited or deleted. 
  1339. */ 
  1340. protected function check_edit_permission( $comment ) { 
  1341. if ( 0 === (int) get_current_user_id() ) { 
  1342. return false; 
  1343.  
  1344. if ( ! current_user_can( 'moderate_comments' ) ) { 
  1345. return false; 
  1346.  
  1347. return current_user_can( 'edit_comment', $comment->comment_ID ); 
  1348.  
  1349. /** 
  1350. * Checks a comment author email for validity. 
  1351. * Accepts either a valid email address or empty string as a valid comment 
  1352. * author email address. Setting the comment author email to an empty 
  1353. * string is allowed when a comment is being updated. 
  1354. * @since 4.7.0 
  1355. * @param string $value Author email value submitted. 
  1356. * @param WP_REST_Request $request Full details about the request. 
  1357. * @param string $param The parameter name. 
  1358. * @return WP_Error|string The sanitized email address, if valid,  
  1359. * otherwise an error. 
  1360. */ 
  1361. public function check_comment_author_email( $value, $request, $param ) { 
  1362. $email = (string) $value; 
  1363. if ( empty( $email ) ) { 
  1364. return $email; 
  1365.  
  1366. $check_email = rest_validate_request_arg( $email, $request, $param ); 
  1367. if ( is_wp_error( $check_email ) ) { 
  1368. return $check_email; 
  1369.  
  1370. return $email;