PluginProbe
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI / 8.0.5
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI v8.0.5
8.0.5 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 All 47 releases
← All changes | includes/api/class-wp-rest-gamipress-posts-controller.php +492 -0 7.9.4 → 8.0.5 View file →
@@ -1,0 +1,492 @@
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 +}