Completed
Push — update/crm-integration-fixes ( a3fc2b )
by Jeremy
14:51 queued 06:26
created

class.wpcom-json-api-list-comments-endpoint.php (4 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
class WPCOM_JSON_API_List_Comments_Walker extends Walker {
4
	public $tree_type = 'comment';
5
6
	public $db_fields = array(
7
		'parent' => 'comment_parent',
8
		'id'     => 'comment_ID'
9
	);
10
11
	public function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {
12
		if ( ! is_array( $output ) ) {
13
			$output = array();
14
		}
15
16
		$output[] = $object->comment_ID;
17
	}
18
19
	/**
20
	 * Taken from WordPress's Walker_Comment::display_element()
21
	 *
22
	 * This function is designed to enhance Walker::display_element() to
23
	 * display children of higher nesting levels than selected inline on
24
	 * the highest depth level displayed. This prevents them being orphaned
25
	 * at the end of the comment list.
26
	 *
27
	 * Example: max_depth = 2, with 5 levels of nested content.
28
	 * 1
29
	 *  1.1
30
	 *    1.1.1
31
	 *    1.1.1.1
32
	 *    1.1.1.1.1
33
	 *    1.1.2
34
	 *    1.1.2.1
35
	 * 2
36
	 *  2.2
37
	 *
38
	 * @see Walker_Comment::display_element()
39
	 * @see Walker::display_element()
40
	 * @see wp_list_comments()
41
	 */
42
	public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
43
44
		if ( !$element )
45
			return;
46
47
		$id_field = $this->db_fields['id'];
48
		$id = $element->$id_field;
49
50
		parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
51
52
		// If we're at the max depth, and the current element still has children, loop over those and display them at this level
53
		// This is to prevent them being orphaned to the end of the list.
54
		if ( $max_depth <= $depth + 1 && isset( $children_elements[$id]) ) {
55
			foreach ( $children_elements[ $id ] as $child )
56
				$this->display_element( $child, $children_elements, $max_depth, $depth, $args, $output );
57
58
			unset( $children_elements[ $id ] );
59
		}
60
61
	}
62
}
63
64
new WPCOM_JSON_API_List_Comments_Endpoint( array(
65
	'description' => 'Get a list of recent comments.',
66
	'group'       => 'comments',
67
	'stat'        => 'comments',
68
69
	'method'      => 'GET',
70
	'path'        => '/sites/%s/comments/',
71
	'path_labels' => array(
72
		'$site' => '(int|string) Site ID or domain',
73
	),
74
75
	'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/comments/?number=2'
76
) );
77
78
new WPCOM_JSON_API_List_Comments_Endpoint( array(
79
	'description' => 'Get a list of recent comments on a post.',
80
	'group'       => 'comments',
81
	'stat'        => 'posts:1:replies',
82
83
	'method'      => 'GET',
84
	'path'        => '/sites/%s/posts/%d/replies/',
85
	'path_labels' => array(
86
		'$site'    => '(int|string) Site ID or domain',
87
		'$post_ID' => '(int) The post ID',
88
	),
89
90
	'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/posts/7/replies/?number=2'
91
) );
92
93
// @todo permissions
94
class WPCOM_JSON_API_List_Comments_Endpoint extends WPCOM_JSON_API_Comment_Endpoint {
95
	public $response_format = array(
96
		'found'    => '(int) The total number of comments found that match the request (ignoring limits, offsets, and pagination).',
97
		'site_ID'  => '(int) The site ID',
98
		'comments' => '(array:comment) An array of comment objects.',
99
	);
100
101
	function __construct( $args ) {
102
		parent::__construct( $args );
103
		$this->query = array_merge( $this->query, array(
104
			'number'   => '(int=20) The number of comments to return.  Limit: 100. When using hierarchical=1, number refers to the number of top-level comments returned.',
105
			'offset'   => '(int=0) 0-indexed offset. Not available if using hierarchical=1.',
106
			'page'     => '(int) Return the Nth 1-indexed page of comments.  Takes precedence over the <code>offset</code> parameter. When using hierarchical=1, pagination is a bit different.  See the note on the number parameter.',
107
			'order'    => array(
108
				'DESC' => 'Return comments in descending order from newest to oldest.',
109
				'ASC'  => 'Return comments in ascending order from oldest to newest.',
110
			),
111
			'hierarchical' => array(
112
				'false' => '',
113
				'true' => '(BETA) Order the comment list hierarchically.',
114
			),
115
			'after'    => '(ISO 8601 datetime) Return comments dated on or after the specified datetime. Not available if using hierarchical=1.',
116
			'before'   => '(ISO 8601 datetime) Return comments dated on or before the specified datetime. Not available if using hierarchical=1.',
117
			'type'     => array(
118
				'any'       => 'Return all comments regardless of type.',
119
				'comment'   => 'Return only regular comments.',
120
				'trackback' => 'Return only trackbacks.',
121
				'pingback'  => 'Return only pingbacks.',
122
				'pings'     => 'Return both trackbacks and pingbacks.',
123
			),
124
			'status'   => array(
125
				'approved'   => 'Return only approved comments.',
126
				'unapproved' => 'Return only comments in the moderation queue.',
127
				'spam'       => 'Return only comments marked as spam.',
128
				'trash'      => 'Return only comments in the trash.',
129
				'all'        => 'Return comments of all statuses.',
130
			),
131
		) );
132
	}
133
134
	// /sites/%s/comments/            -> $blog_id
135
	// /sites/%s/posts/%d/replies/    -> $blog_id, $post_id
136
	// /sites/%s/comments/%d/replies/ -> $blog_id, $comment_id
137
	function callback( $path = '', $blog_id = 0, $object_id = 0 ) {
138
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
139
		if ( is_wp_error( $blog_id ) ) {
140
			return $blog_id;
141
		}
142
143
		$args = $this->query_args();
144
145
		if ( $args['number'] < 1 ) {
146
			$args['number'] = 20;
147
		} elseif ( 100 < $args['number'] ) {
148
			return new WP_Error( 'invalid_number',  'The NUMBER parameter must be less than or equal to 100.', 400 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'invalid_number'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
149
		}
150
151
		if ( false !== strpos( $path, '/posts/' ) ) {
152
			// We're looking for comments of a particular post
153
			$post_id = $object_id;
154
			$comment_id = 0;
155
		} else {
156
			// We're looking for comments for the whole blog, or replies to a single comment
157
			$comment_id = $object_id;
158
			$post_id = 0;
159
		}
160
161
		// We can't efficiently get the number of replies to a single comment
162
		$count = false;
163
		$found = -1;
164
165
		if ( !$comment_id ) {
166
			// We can get comment counts for the whole site or for a single post, but only for certain queries
167
			if ( 'any' === $args['type'] && !isset( $args['after'] ) && !isset( $args['before'] ) ) {
168
				$count = $this->api->wp_count_comments( $post_id );
169
			}
170
		}
171
172
		switch ( $args['status'] ) {
173
		case 'approved' :
174
			$status = 'approve';
175
			if ( $count ) {
176
				$found = $count->approved;
177
			}
178
			break;
179
		default :
180
			if ( ! current_user_can( 'edit_posts' ) ) {
181
				return new WP_Error( 'unauthorized', 'User cannot read non-approved comments', 403 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'unauthorized'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
182
			}
183
			if ( 'unapproved' === $args['status'] ) {
184
				$status = 'hold';
185
				$count_status = 'moderated';
186
			} elseif ( 'all' === $args['status'] ) {
187
				$status = 'all';
188
				$count_status = 'total_comments';
189
			} else {
190
				$status = $count_status = $args['status'];
191
			}
192
			if ( $count ) {
193
				$found = $count->$count_status;
194
			}
195
		}
196
197
		/** This filter is documented in class.json-api.php */
198
		$exclude = apply_filters( 'jetpack_api_exclude_comment_types',
199
			array( 'order_note', 'webhook_delivery', 'review', 'action_log' )
200
		);
201
202
		$query = array(
203
			'order'        => $args['order'],
204
			'type'         => 'any' === $args['type'] ? false : $args['type'],
205
			'status'       => $status,
206
			'type__not_in' => $exclude,
207
		);
208
209
		if ( isset( $args['page'] ) ) {
210
			if ( $args['page'] < 1 ) {
211
				$args['page'] = 1;
212
			}
213
		} else {
214
			if ( $args['offset'] < 0 ) {
215
				$args['offset'] = 0;
216
			}
217
		}
218
219
		if ( ! $args['hierarchical'] ) {
220
			$query['number'] = $args['number'];
221
222
			if ( isset( $args['page'] ) ) {
223
				$query['offset'] = ( $args['page'] - 1 ) * $args['number'];
224
			} else {
225
				$query['offset'] = $args['offset'];
226
			}
227
228
			$is_before = isset( $args['before_gmt'] );
229
			$is_after  = isset( $args['after_gmt'] );
230
231
			if ( $is_before || $is_after ) {
232
				$query['date_query'] = array(
233
					'column' => 'comment_date_gmt',
234
					'inclusive' => true,
235
				);
236
237
				if ( $is_before ) {
238
					$query['date_query']['before'] = $args['before_gmt'];
239
				}
240
241
				if ( $is_after ) {
242
					$query['date_query']['after'] = $args['after_gmt'];
243
				}
244
			}
245
		}
246
247
		if ( $post_id ) {
248
			$post = get_post( $post_id );
249
			if ( !$post || is_wp_error( $post ) ) {
250
				return new WP_Error( 'unknown_post', 'Unknown post', 404 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'unknown_post'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
251
			}
252
			$query['post_id'] = $post->ID;
253
			if ( $this->api->ends_with( $this->path, '/replies' ) ) {
254
				$query['parent'] = 0;
255
			}
256
		} elseif ( $comment_id ) {
257
			$comment = get_comment( $comment_id );
258
			if ( !$comment || is_wp_error( $comment ) ) {
259
				return new WP_Error( 'unknown_comment', 'Unknown comment', 404 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'unknown_comment'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
260
			}
261
			$query['parent'] = $comment_id;
262
		}
263
264
		$comments = get_comments( $query );
265
266
		update_comment_cache( $comments );
267
268
		if ( $args['hierarchical'] ) {
269
			$walker = new WPCOM_JSON_API_List_Comments_Walker;
270
			$comment_ids = $walker->paged_walk( $comments, get_option( 'thread_comments_depth', -1 ), isset( $args['page'] ) ? $args['page'] : 1 , $args['number'] );
271
			if ( ! empty( $comment_ids ) ) {
272
				$comments = array_map( 'get_comment', $comment_ids );
273
			}
274
		}
275
276
		$return = array();
277
278
		foreach ( array_keys( $this->response_format ) as $key ) {
279
			switch ( $key ) {
280
			case 'found' :
281
				$return[ $key ] = (int) $found;
282
				break;
283
			case 'site_ID' :
284
				$return[ $key ] = (int) $blog_id;
285
				break;
286
			case 'comments' :
287
				$return_comments = array();
288
				if ( ! empty( $comments ) ) {
289
					foreach ( $comments as $comment ) {
290
						$the_comment = $this->get_comment( $comment->comment_ID, $args['context'] );
291
						if ( $the_comment && !is_wp_error( $the_comment ) ) {
292
							$return_comments[] = $the_comment;
293
						}
294
					}
295
				}
296
297
				if ( $return_comments ) {
298
					/** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
299
					do_action( 'wpcom_json_api_objects', 'comments', count( $return_comments ) );
300
				}
301
302
				$return[ $key ] = $return_comments;
303
				break;
304
			}
305
		}
306
307
		return $return;
308
	}
309
}
310