Completed
Push — master ( 74a956...f01bc4 )
by Gary
02:17
created

lib/class-wp-rest-react-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
 * Class WP_REST_React_Controller
5
 */
6
class WP_REST_React_Controller {
7
	/**
8
	 * The namespace of this controller's route.
9
	 *
10
	 * @var string
11
	 */
12
	protected $namespace;
13
14
	/**
15
	 * The base of this controller's route.
16
	 *
17
	 * @var string
18
	 */
19
	protected $rest_base;
20
21
	/**
22
	 * Constructor.
23
	 */
24
	public function __construct() {
25
		$this->namespace = 'wp/v2';
26
		$this->rest_base = 'react';
27
	}
28
29
	/**
30
	 * Register the routes for the objects of the controller.
31
	 */
32
	public function register_routes() {
33
		register_rest_route( $this->namespace, $this->rest_base, array(
34
			array(
35
				'methods'             => WP_Rest_Server::READABLE,
36
				'callback'            => array( $this, 'get_items' ),
37
				'permission_callback' => array( $this, 'get_items_permission_callback' ),
38
				'args'                => $this->get_collection_params(),
39
			),
40
			array(
41
				'methods'             => WP_Rest_Server::CREATABLE,
42
				'callback'            => array( $this, 'create_item' ),
43
				'permission_callback' => array( $this, 'create_item_permission_callback' ),
44
				'args'                => $this->get_creation_params(),
45
			),
46
			'schema' => array( $this, 'get_public_item_schema' ),
47
		) );
48
	}
49
50
	/**
51
	 * Check if a given request has access to read reactions.
52
	 *
53
	 * @param  WP_REST_Request $request Full details about the request.
54
	 * @return WP_Error|boolean
55
	 */
56
	public function get_items_permissions_check( $request ) {
57
		if ( ! empty( $request['post'] ) ) {
58
			foreach ( (array) $request['post'] as $post_id ) {
59
				$post = get_post( $post_id );
60
				if ( ! empty( $post_id ) && $post && ! $this->check_read_post_permission( $post ) ) {
61
					return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this reaction.' ), array( 'status' => rest_authorization_required_code() ) );
62
				} else if ( 0 === $post_id && ! current_user_can( 'moderate_comments' ) ) {
63
					return new WP_Error( 'rest_cannot_read', __( 'Sorry, you cannot read reactions without a post.' ), array( 'status' => rest_authorization_required_code() ) );
64
				}
65
			}
66
		}
67
68
		return true;
69
	}
70
71
	/**
72
	 * Get a list of reactions.
73
	 *
74
	 * @param  WP_REST_Request $request Full details about the request.
75
	 * @return WP_Error|WP_REST_Response
76
	 */
77
	public function get_items( $request ) {
78
		$prepared_args = array(
79
			'post__in' => $request['post'],
80
			'type'     => 'reaction',
81
		);
82
83
		/**
84
		 * Filter arguments, before passing to WP_Comment_Query, when querying reactions via the REST API.
85
		 *
86
		 * @see https://developer.wordpress.org/reference/classes/wp_comment_query/
87
		 *
88
		 * @param array           $prepared_args Array of arguments for WP_Comment_Query.
89
		 * @param WP_REST_Request $request       The current request.
90
		 */
91
		$prepared_args = apply_filters( 'rest_reaction_query', $prepared_args, $request );
92
93
		$query = new WP_Comment_Query;
94
		$query_result = $query->query( $prepared_args );
95
96
		$reactions_count = array();
97
		foreach( $query_result as $reaction ) {
98
			if ( empty( $reactions_count[ $reaction->comment_content ] ) ) {
99
				$reactions_count[ $reaction->comment_content ] = array(
100
					'count'   => 0,
101
					'post_id' => $reaction->comment_post_ID,
102
				);
103
			}
104
105
			$reactions_count[ $reaction->comment_content ]++;
106
		}
107
108
		$reactions = array();
109
		foreach( $reactions_count as $emoji => $data ) {
110
			$reaction = array(
111
				'emoji'   => $emoji,
112
				'count'   => $data['count'],
113
				'post_id' => $data['post_id'],
114
			);
115
116
			$data = $this->prepare_item_for_response( $reaction, $request );
117
			$reactions[] = $this->prepare_response_for_collection( $data );
118
		}
119
120
		$total_reactions = (int) $query->found_comments;
121
		$reaction_groups = count( $reactions );
122
123
		$response = rest_ensure_response( $reactions );
124
		$response->header( 'X-WP-Total', $total_reactions );
125
		$response->header( 'X-WP-TotalGroups', $reaction_groups );
126
127
		return $response;
128
	}
129
130
	/**
131
	 * Check if a given request has access to create a reaction
132
	 *
133
	 * @param  WP_REST_Request $request Full details about the request.
134
	 * @return WP_Error|boolean
135
	 */
136
	public function create_item_permissions_check( $request ) {
0 ignored issues
show
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
137
		return true;
138
	}
139
140
	/**
141
	 * Create a reaction.
142
	 *
143
	 * @param  WP_REST_Request $request Full details about the request.
144
	 * @return WP_Error|WP_REST_Response
145
	 */
146
	public function create_item( $request ) {
0 ignored issues
show
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
147
	}
148
149
	/**
150
	 * Check if we can read a post.
151
	 *
152
	 * Correctly handles posts with the inherit status.
153
	 *
154
	 * @param object $post Post object.
155
	 * @return boolean Can we read it?
156
	 */
157
	public function check_read_post_permission( $post ) {
158
		if ( ! empty( $post->post_password ) && ! $this->check_update_post_permission( $post ) ) {
159
			return false;
160
		}
161
162
		$post_type = get_post_type_object( $post->post_type );
163
		if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
164
			return false;
165
		}
166
167
		// Can we read the post?
168
		if ( 'publish' === $post->post_status || current_user_can( $post_type->cap->read_post, $post->ID ) ) {
169
			return true;
170
		}
171
172
		$post_status_obj = get_post_status_object( $post->post_status );
173
		if ( $post_status_obj && $post_status_obj->public ) {
174
			return true;
175
		}
176
177
		// Can we read the parent if we're inheriting?
178
		if ( 'inherit' === $post->post_status && $post->post_parent > 0 ) {
179
			$parent = get_post( $post->post_parent );
180
			return $this->check_read_post_permission( $parent );
181
		}
182
183
		// If we don't have a parent, but the status is set to inherit, assume
184
		// it's published (as per get_post_status()).
185
		if ( 'inherit' === $post->post_status ) {
186
			return true;
187
		}
188
189
		return false;
190
	}
191
192
	/**
193
	 * Check if we can edit a post.
194
	 *
195
	 * @param object $post Post object.
196
	 * @return boolean Can we edit it?
197
	 */
198
	protected function check_update_post_permission( $post ) {
199
		$post_type = get_post_type_object( $post->post_type );
200
201
		if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
202
			return false;
203
		}
204
205
		return current_user_can( $post_type->cap->edit_post, $post->ID );
206
	}
207
208
	/**
209
	 * Check if a given post type should be viewed or managed.
210
	 *
211
	 * @param object|string $post_type
212
	 * @return boolean Is post type allowed?
213
	 */
214
	protected function check_is_post_type_allowed( $post_type ) {
215
		if ( ! is_object( $post_type ) ) {
216
			$post_type = get_post_type_object( $post_type );
217
		}
218
219
		if ( ! empty( $post_type ) && ! empty( $post_type->show_in_rest ) ) {
220
			return true;
221
		}
222
223
		return false;
224
	}
225
226
	/**
227
	 * Prepare a reaction group output for response.
228
	 *
229
	 * @param  array            $reaction Reaction data.
230
	 * @param  WP_REST_Request  $request  Request object.
231
	 * @return WP_REST_Response $response
232
	 */
233
	public function prepare_item_for_response( $reaction, $request ) {
234
		$data = array(
235
			'emoji'   => $reaction['emoji'],
236
			'count'   => (int) $reaction['count'],
237
			'post_id' => (int) $reaction['post_id'],
238
		);
239
240
		// Wrap the data in a response object
241
		$response = rest_ensure_response( $data );
242
243
		$response->add_links( $this->prepare_links( $reaction ) );
244
245
		/**
246
		 * Filter a reaction group returned from the API.
247
		 *
248
		 * Allows modification of the reaction right before it is returned.
249
		 *
250
		 * @param WP_REST_Response  $response   The response object.
251
		 * @param array             $reaction   The original reaction data.
252
		 * @param WP_REST_Request   $request    Request used to generate the response.
253
		 */
254
		return apply_filters( 'rest_prepare_comment', $response, $reaction, $request );
255
	}
256
257
	/**
258
	 * Prepare a response for inserting into a collection.
259
	 *
260
	 * @param WP_REST_Response $response Response object.
261
	 * @return array Response data, ready for insertion into collection data.
262
	 */
263
	public function prepare_response_for_collection( $response ) {
264
		if ( ! ( $response instanceof WP_REST_Response ) ) {
265
			return $response;
266
		}
267
268
		$data = (array) $response->get_data();
269
		$links = WP_REST_Server::get_response_links( $response );
270
		if ( ! empty( $links ) ) {
271
			$data['_links'] = $links;
272
		}
273
274
		return $data;
275
	}
276
277
	/**
278
	 * Prepare links for the request.
279
	 *
280
	 * @param array $reaction Reaction.
281
	 * @return array Links for the given reaction.
282
	 */
283
	protected function prepare_links( $reaction ) {
284
		$links = array(
285
			'self' => array(
286
				'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $reaction->emoji ) ),
287
			),
288
			'collection' => array(
289
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
290
			),
291
		);
292
293
		if ( 0 !== (int) $reaction['post_id'] ) {
294
			$post = get_post( $reaction['post_id'] );
295
			if ( ! empty( $post->ID ) ) {
296
				$obj = get_post_type_object( $post->post_type );
297
				$base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name;
298
				$links['up'] = array(
299
					'href'       => rest_url( '/wp/v2/' . $base . '/' . $reaction['post_id'] ),
300
					'embeddable' => true,
301
					'post_type'  => $post->post_type,
302
				);
303
			}
304
		}
305
306
		return $links;
307
	}
308
309
	/**
310
	 * Get the query params for collections
311
	 *
312
	 * @return array
313
	 */
314
	public function get_collection_params() {
315
		$query_params = array();
316
317
		$query_params['post']   = array(
318
			'default'           => array(),
319
			'description'       => __( 'Limit result set to resources assigned to specific post ids.' ),
320
			'type'              => 'array',
321
			'sanitize_callback' => 'wp_parse_id_list',
322
			'validate_callback' => 'rest_validate_request_arg',
323
		);
324
325
		return $query_params;
326
	}
327
	/**
328
	 * Get the query params for collections
329
	 *
330
	 * @return array
331
	 */
332
	public function get_creation_params() {
333
		$query_params = array();
334
335
		$query_params['post']   = array(
336
			'default'           => array(),
337
			'description'       => __( 'The post ID to add a reaction to.' ),
338
			'type'              => 'integer',
339
			'sanitize_callback' => 'absint',
340
			'validate_callback' => 'rest_validate_request_arg',
341
		);
342
343
		$query_params['emoji']  = array(
344
			'default'           => array(),
345
			'description'       => __( 'The reaction emoji.' ),
346
			'type'              => 'string',
347
			'validate_callback' => 'rest_validate_request_arg',
348
		);
349
350
		return $query_params;
351
	}
352
}