PluginProbe
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI / 7.9.8
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI v7.9.8
8.0.4 8.0.3 8.0.1 8.0.2 8.0.0 7.9.9.7 7.9.9.6 7.9.9.5 7.9.9.4 7.9.9.3 7.9.9.2 7.9.9.1 7.9.9 7.9.8 7.9.7 7.9.6 7.9.5 7.9.4 7.9.3 7.9.2 7.9.1 7.9.0 7.8.9 7.8.8 7.8.7 All 46 releases
gamipress / includes / api / class-wp-rest-gamipress-posts-controller.php

class-wp-rest-gamipress-posts-controller.php in GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI 7.9.8, at includes/api/class-wp-rest-gamipress-posts-controller.php

492 lines 18.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Custom endpoint for querying multiple post-types.
4 * Mimics `WP_REST_Posts_Controller` as closely as possible.
5 *
6 * New filters:
7 * - `rest_gamipress_posts_query` Filters the query arguments as generated from the request parameters.
8 *
9 * @author Ruben Vreeken
10 */
11 class WP_REST_GamiPress_Posts_Controller extends WP_REST_Controller {
12
13 public function __construct() {
14 $this->namespace = 'wp/v2';
15 $this->rest_base = 'gamipress-posts';
16 }
17
18 /**
19 * Register the routes for the objects of the controller.
20 */
21 public function register_routes() {
22 register_rest_route( $this->namespace, '/' . $this->rest_base, array(
23 array(
24 'methods' => WP_REST_Server::READABLE,
25 'callback' => array($this, 'get_items'),
26 'permission_callback' => array($this, 'get_items_permissions_check'),
27 'args' => $this->get_collection_params(),
28 ),
29 ) );
30 }
31
32 /**
33 * Check if a given request has access to get items
34 *
35 * @return bool
36 */
37 public function get_items_permissions_check( $request ) {
38 return true;
39 }
40
41 /**
42 * Get a collection of items
43 *
44 * @param WP_REST_Request $request Full data about the request.
45 * @return WP_Error|WP_REST_Response
46 */
47 public function get_items($request) {
48
49 $args = array();
50 $args['author__in'] = $request['author'];
51 $args['author__not_in'] = $request['author_exclude'];
52 $args['menu_order'] = $request['menu_order'];
53 $args['offset'] = $request['offset'];
54 $args['order'] = $request['order'];
55 $args['orderby'] = $request['orderby'];
56 $args['paged'] = $request['page'];
57 $args['post__in'] = $request['include'];
58 $args['post__not_in'] = $request['exclude'];
59 $args['posts_per_page'] = $request['per_page'];
60 $args['name'] = $request['slug'];
61 $args['post_type'] = $request['type'];
62 $args['post_parent__in'] = $request['parent'];
63 $args['post_parent__not_in'] = $request['parent_exclude'];
64 $args['post_status'] = $request['status'];
65 $args['s'] = $request['search'];
66
67 $args['date_query'] = array();
68 // Set before into date query. Date query must be specified as an array
69 // of an array.
70 if (isset($request['before'])) {
71 $args['date_query'][0]['before'] = $request['before'];
72 }
73
74 // Set after into date query. Date query must be specified as an array
75 // of an array.
76 if (isset($request['after'])) {
77 $args['date_query'][0]['after'] = $request['after'];
78 }
79
80 if (is_array($request['filter'])) {
81 $args = array_merge($args, $request['filter']);
82 unset($args['filter']);
83 }
84
85 if( $args['post_type'] === null ) {
86 $args['post_type'] = array_merge(
87 gamipress_get_points_types_slugs(),
88 gamipress_get_achievement_types_slugs(),
89 gamipress_get_rank_types_slugs(),
90 );
91 }
92
93 // Ensure array of post_types
94 if ( ! is_array( $args['post_type'] ) ) {
95 $args['post_type'] = explode( ',', $args['post_type'] );
96 }
97
98 /**
99 * Filter the query arguments for a request.
100 *
101 * Enables adding extra arguments or setting defaults for a post
102 * collection request.
103 *
104 * @see https://developer.wordpress.org/reference/classes/wp_user_query/
105 *
106 * @param array $args Key value array of query var to query value.
107 * @param WP_REST_Request $request The request used.
108 *
109 * @var Function
110 */
111 $args = apply_filters("rest_gamipress_posts_query", $args, $request);
112 $query_args = $this->prepare_items_query($args, $request);
113
114 // Get taxonomies for each of the requested post_types
115 $taxonomies = wp_list_filter(get_object_taxonomies($query_args['post_type'], 'objects'), array('show_in_rest' => true));
116
117 // Construct taxonomy query
118 foreach ($taxonomies as $taxonomy) {
119 $base = !empty($taxonomy->rest_base) ? $taxonomy->rest_base : $taxonomy->name;
120
121 if (!empty($request[$base])) {
122 $query_args['tax_query'][] = array(
123 'taxonomy' => $taxonomy->name,
124 'field' => 'term_id',
125 'terms' => $request[$base],
126 'include_children' => false,
127 );
128 }
129 }
130
131 // Execute the query
132 $posts_query = new WP_Query();
133 $query_result = $posts_query->query( $query_args );
134
135 // Handle query results
136 $posts = array();
137 foreach ($query_result as $post) {
138 // Get PostController for Post Type
139 $controller = new WP_REST_Posts_Controller( $post->post_type );
140
141 if ( ! $controller->check_read_permission($post) ) {
142 continue;
143 }
144
145 $data = $controller->prepare_item_for_response( $post, $request );
146 $posts[] = $controller->prepare_response_for_collection( $data );
147 }
148
149 // Calc total post count
150 $page = (int) $query_args['paged'];
151 $total_posts = $posts_query->found_posts;
152
153 // Out-of-bounds, run the query again without LIMIT for total count
154 if ($total_posts < 1) {
155 unset($query_args['paged']);
156 $count_query = new WP_Query();
157 $count_query->query($query_args);
158 $total_posts = $count_query->found_posts;
159 }
160
161 // Calc total page count
162 $max_pages = ceil($total_posts / (int) $query_args['posts_per_page']);
163
164 // Construct response
165 $response = rest_ensure_response($posts);
166 $response->header('X-WP-Total', (int) $total_posts);
167 $response->header('X-WP-TotalPages', (int) $max_pages);
168
169 // Construct base url for pagination links
170 $request_params = $request->get_query_params();
171 if (!empty($request_params['filter'])) {
172 // Normalize the pagination params.
173 unset($request_params['filter']['posts_per_page']);
174 unset($request_params['filter']['paged']);
175 }
176 $base = add_query_arg($request_params, rest_url(sprintf('/%s/%s', $this->namespace, $this->rest_base)));
177
178 // Create link for previous page, if needed
179 if ($page > 1) {
180 $prev_page = $page - 1;
181 if ($prev_page > $max_pages) {
182 $prev_page = $max_pages;
183 }
184 $prev_link = add_query_arg('page', $prev_page, $base);
185 $response->link_header('prev', $prev_link);
186 }
187
188 // Create link for next page, if needed
189 if ($max_pages > $page) {
190 $next_page = $page + 1;
191 $next_link = add_query_arg('page', $next_page, $base);
192 $response->link_header('next', $next_link);
193 }
194
195 return $response;
196
197 }
198
199 /**
200 * Determine the allowed query_vars for a get_items() response and prepare
201 * for WP_Query.
202 *
203 * @param array $prepared_args
204 * @param WP_REST_Request $request
205 *
206 * @return array $query_args
207 */
208 protected function prepare_items_query($prepared_args = array(), $request = null) {
209
210 $valid_vars = array_flip($this->get_allowed_query_vars($request['type']));
211 $query_args = array();
212
213 foreach ($valid_vars as $var => $index) {
214 if (isset($prepared_args[$var])) {
215 /**
216 * Filter the query_vars used in `get_items` for the constructed
217 * query.
218 *
219 * The dynamic portion of the hook name, $var, refers to the
220 * query_var key.
221 *
222 * @param mixed $prepared_args[ $var ] The query_var value.
223 */
224 $query_args[$var] = apply_filters("rest_query_var-{$var}", $prepared_args[$var]);
225 }
226 }
227
228 // Only allow sticky psts if 'post' is one of the requested post types.
229 if ( in_array('post', $query_args['post_type'] ) || !isset( $query_args['ignore_sticky_posts'] ) ) {
230 $query_args['ignore_sticky_posts'] = true;
231 }
232
233 if ('include' === $query_args['orderby']) {
234 $query_args['orderby'] = 'post__in';
235 }
236
237 return $query_args;
238
239 }
240
241 /**
242 * Get all the WP Query vars that are allowed for the API request.
243 *
244 * @return array
245 */
246 protected function get_allowed_query_vars($post_types) {
247
248 global $wp;
249
250 $editPosts = true;
251
252 /**
253 * Filter the publicly allowed query vars.
254 *
255 * Allows adjusting of the default query vars that are made public.
256 *
257 * @param array Array of allowed WP_Query query vars.
258 *
259 * @var Function
260 */
261 $valid_vars = apply_filters('query_vars', $wp->public_query_vars);
262
263 /**
264 * We allow 'private' query vars for authorized users only.
265 *
266 * It the user has `edit_posts` capabilty for *every* requested post
267 * type, we also allow use of private query parameters, which are only
268 * undesirable on the frontend, but are safe for use in query strings.
269 *
270 * To disable anyway, use `add_filter( 'rest_private_query_vars',
271 * '__return_empty_array' );`
272 *
273 * @param array $private_query_vars Array of allowed query vars for
274 * authorized users.
275 *
276 * @var boolean
277 */
278 $edit_posts = true;
279
280 foreach ($post_types as $post_type) {
281
282 $post_type_obj = get_post_type_object($post_type);
283
284 if ( ! current_user_can( $post_type_obj->cap->edit_posts ) ) {
285 $edit_posts = false;
286 break;
287 }
288 }
289
290 if ($edit_posts) {
291 $private = apply_filters('rest_private_query_vars', $wp->private_query_vars);
292 $valid_vars = array_merge($valid_vars, $private);
293 }
294
295 // Define our own in addition to WP's normal vars.
296 $rest_valid = array(
297 'author__in',
298 'author__not_in',
299 'ignore_sticky_posts',
300 'menu_order',
301 'offset',
302 'post__in',
303 'post__not_in',
304 'post_parent',
305 'post_parent__in',
306 'post_parent__not_in',
307 'posts_per_page',
308 'date_query',
309 );
310
311 $valid_vars = array_merge( $valid_vars, $rest_valid );
312
313 /**
314 * Filter allowed query vars for the REST API.
315 *
316 * This filter allows you to add or remove query vars from the final
317 * allowed list for all requests, including unauthenticated ones. To
318 * alter the vars for editors only, {@see rest_private_query_vars}.
319 *
320 * @param array {
321 * Array of allowed WP_Query query vars.
322 *
323 * @param string $allowed_query_var The query var to allow.
324 * }
325 */
326 $valid_vars = apply_filters( 'rest_query_vars', $valid_vars );
327
328 return $valid_vars;
329
330 }
331
332 /**
333 * Get the query params for collections of attachments.
334 *
335 * @return array
336 */
337 public function get_collection_params() {
338
339 $params = parent::get_collection_params();
340
341 $params['context']['default'] = 'view';
342
343 $params['after'] = array(
344 'description' => __('Limit response to resources published after a given ISO8601 compliant date.'),
345 'type' => 'string',
346 'format' => 'date-time',
347 'validate_callback' => 'rest_validate_request_arg',
348 );
349
350 $params['author'] = array(
351 'description' => __('Limit result set to posts assigned to specific authors.'),
352 'type' => 'array',
353 'default' => array(),
354 'sanitize_callback' => 'wp_parse_id_list',
355 'validate_callback' => 'rest_validate_request_arg',
356 );
357 $params['author_exclude'] = array(
358 'description' => __('Ensure result set excludes posts assigned to specific authors.'),
359 'type' => 'array',
360 'default' => array(),
361 'sanitize_callback' => 'wp_parse_id_list',
362 'validate_callback' => 'rest_validate_request_arg',
363 );
364
365 $params['before'] = array(
366 'description' => __('Limit response to resources published before a given ISO8601 compliant date.'),
367 'type' => 'string',
368 'format' => 'date-time',
369 'validate_callback' => 'rest_validate_request_arg',
370 );
371 $params['exclude'] = array(
372 'description' => __('Ensure result set excludes specific ids.'),
373 'type' => 'array',
374 'default' => array(),
375 'sanitize_callback' => 'wp_parse_id_list',
376 );
377 $params['include'] = array(
378 'description' => __('Limit result set to specific ids.'),
379 'type' => 'array',
380 'default' => array(),
381 'sanitize_callback' => 'wp_parse_id_list',
382 );
383
384 $params['menu_order'] = array(
385 'description' => __('Limit result set to resources with a specific menu_order value.'),
386 'type' => 'integer',
387 'sanitize_callback' => 'absint',
388 'validate_callback' => 'rest_validate_request_arg',
389 );
390
391 $params['offset'] = array(
392 'description' => __('Offset the result set by a specific number of items.'),
393 'type' => 'integer',
394 'sanitize_callback' => 'absint',
395 'validate_callback' => 'rest_validate_request_arg',
396 );
397 $params['order'] = array(
398 'description' => __('Order sort attribute ascending or descending.'),
399 'type' => 'string',
400 'default' => 'desc',
401 'enum' => array('asc', 'desc'),
402 'validate_callback' => 'rest_validate_request_arg',
403 );
404 $params['orderby'] = array(
405 'description' => __('Sort collection by object attribute.'),
406 'type' => 'string',
407 'default' => 'date',
408 'enum' => array(
409 'date',
410 'id',
411 'include',
412 'title',
413 'slug',
414 ),
415 'validate_callback' => 'rest_validate_request_arg',
416 );
417
418 $params['orderby']['enum'][] = 'menu_order';
419
420 $params['parent'] = array(
421 'description' => __('Limit result set to those of particular parent ids.'),
422 'type' => 'array',
423 'sanitize_callback' => 'wp_parse_id_list',
424 'default' => array(),
425 );
426 $params['parent_exclude'] = array(
427 'description' => __('Limit result set to all items except those of a particular parent id.'),
428 'type' => 'array',
429 'sanitize_callback' => 'wp_parse_id_list',
430 'default' => array(),
431 );
432
433 $params['slug'] = array(
434 'description' => __('Limit result set to posts with a specific slug.'),
435 'type' => 'string',
436 'validate_callback' => 'rest_validate_request_arg',
437 );
438 $params['status'] = array(
439 'default' => 'publish',
440 'description' => __('Limit result set to posts assigned a specific status.'),
441 'sanitize_callback' => 'sanitize_key',
442 'type' => 'string',
443 'validate_callback' => array($this, 'validate_user_can_query_private_statuses'),
444 );
445 $params['filter'] = array(
446 'description' => __('Use WP Query arguments to modify the response; private query vars require appropriate authorization.'),
447 );
448
449 $taxonomies = wp_list_filter(get_object_taxonomies(get_post_types(array(), 'names'), 'objects'), array('show_in_rest' => true));
450 foreach ($taxonomies as $taxonomy) {
451 $base = !empty($taxonomy->rest_base) ? $taxonomy->rest_base : $taxonomy->name;
452
453 $params[$base] = array(
454 'description' => sprintf(__('Limit result set to all items that have the specified term assigned in the %s taxonomy.'), $base),
455 'type' => 'array',
456 'sanitize_callback' => 'wp_parse_id_list',
457 'default' => array(),
458 );
459 }
460 return $params;
461 }
462
463 /**
464 * Validate whether the user can query private statuses
465 *
466 * @param mixed $value
467 * @param WP_REST_Request $request
468 * @param string $parameter
469 *
470 * @return WP_Error|boolean
471 */
472 public function validate_user_can_query_private_statuses($value, $request, $parameter) {
473 if ('publish' === $value) {
474 return true;
475 }
476
477 foreach ( $request["type"] as $post_type ) {
478
479 $post_type_obj = get_post_type_object( $post_type );
480
481 if ( ! current_user_can( $post_type_obj->cap->edit_posts ) ) {
482 return new WP_Error('rest_forbidden_status', __('Status is forbidden'), array(
483 'status' => rest_authorization_required_code(),
484 'post_type' => $post_type_obj->name,
485 ));
486 }
487 }
488
489 return true;
490 }
491
492 }