Issues (90)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

endpoints/class-wp-rest-comments-controller.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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